Расширение Qbim, пример реализации битемпоральной модели данных
Примеры:
Предпосылки
Модель представления данных близка к шестой нормальной форме, но без избыточной нормализации.
Шестая нормальная форма введена Кристофером Дейтом как обобщение пятой нормальной формы для хронологической базы данных в своей книге Введение в системы баз данных.
Расширение Qbim представляет собой конструктор для разработки ERM на темпоральной основе, то есть, моделируемые объекты представляются временными срезами, последовательностями своих состояний во времени. Основное внимание и акцент в данном расширении сделан не на "декомпозиции до конца", а на поддержании темпоральности данных. Расширение реализует краткий набор логики хранения (создания, изменения) объектов в реляционной СУБД (на примере qhb), реализованный в слое хранимых процедур.
Модель данных
В рамках расширения Qbim рассматривается пример реализации модели хранения данных с указанием интервалов времени их действия. В отличие от нехронологических баз данных, хранящих только текущие данные, такая модель предполагает хранение данных, относящихся к прошлым, а также будущим периодам времени.
В предлагаемой реализации наряду с идентификатором кортежа данных указывается еще и интервал времени, в течение которого эти данные считаются актуальными. Эти интервалы могут относиться или пересекаться с настоящим временем, находиться в прошлом или описывать будущие значения. Интервалы хранятся в двух специально выделенных для этого полях типа timestamp, образующих полузакрытый интервал диапазонного типа tsrange.
Серия данных с одним идентификатором (далее объект), может образовывать непересекающиеся отрезки, или «версии», на временной оси «действия» этих данных (business time).
Дополнительная временная ось (system time) фиксирует, когда эти версии были созданы или изменены.
Реализация модели
Терминология
Термин | Определение |
---|---|
Объект | Некоторая оцифрованная, смоделированная из реального мира сущность. Обладает свойствами (атрибутами) и операциями над ними (методами). В определённые временные интервалы имеет фиксированный набор атрибутов. Но во времени, от интервала к интервалу, может менять набор атрибутов. В базе данных набор Объектов представляется сущностью/таблицей, его атрибуты представлены полями таблицы, методы реализованы хранимыми процедурами и функциями. |
Реализация объекта | Один из представителей объекта, выделяемый среди других с помощью идентификатора. В базе данных представляет собой серию записей одной таблицы/сущности. |
Версия реализации объекта | Один из наборов атрибутов объекта, постоянный в течении определенного временного интервала. В базе данных - одна из записей таблицы/сущности, принадлежащая объекту (по идентификатору), с определенным интервалом действия. |
Идентификатор реализации объекта | Идентификатор (id - обычно целое число, но может быть UUID-ом), однозначно определяющий реализацию объекта среди других объектов сущности. Все версии одного и того же объекта имеют один и тот же идентификатор. |
Идентификатор версии реализации объекта | Кортеж из идентификатора и интервала действия версии ( <id[, sd, ed]> ), однозначно определяющий объект и версию. |
Ограничения и умолчания
Зарезервированные поля
Для идентификатора и границ интервала действия в версионированных таблицах резервируются три поля:
Резервируемое поле | Тип | Назначение |
---|---|---|
id | bigint или uuid | идентификатор |
sd | timestamp | start date, начало периода действия |
ed | timestamp | end date, окончание периода действия |
Далее для bigint может использоваться термин «локальный идентификатор», а для uuid — «глобальный идентификатор».
Любой версионированный объект представляется набором своих версий реализации, связанных уникальным идентификатором реализации
(id) и набором атрибутов / свойств.
Каждая его версия - кортежем id и периодом действия этой версии (период представляется интервалом [sd, ed)
- включается
левая граница и не включается правая, типы границ - timestamp).
Первичный ключ версии объекта - составной: <id,[sd,ed)>
.
Версии не пересекаются и не содержат разрывов
Интервалы действия версий образуются как tsrange(sd,ed,'[)')
— полуоткрытый
интервал. Отсутствие пересечений интервалов реализуется ограничением вида:
constraint ... exclude using gist (id with =, tsrange(sd, ed) with &&) where ((sd <> ed))
Следует заметить, что при таких условиях допустимыми являются версии, в которых sd == ed
.
Это допущение сделано намеренно, поскольку:
- такие версии имеют нулевую длину, не пересекаются с другими версиями, не влияют на многие сценарии выбора версий и серий версий (непустых);
- при наличии фиксации времени изменения (system time) не пропадает и не требует изменения привязка к версии события (поле action.link_dt), поскольку привязка осуществляется именно к значению поля sd версионированной таблицы.
Описание версионируемых таблиц
Все версионируемые таблицы и их поля описываются в специально выделяемых таблицах obj и obj_item.
Таблица obj:
Поле | Тип | Назначение |
---|---|---|
id | bigserial | Идентификатор |
tab | text | Название таблицы, где хранится объект. Если требуется, вместе с именем схемы |
dsc | text | Описание |
parent | bigint | Ссылка на ID верхнего в иерархии объекта |
is_temporal | bool | Признак темпоральности объекта |
is_logging | bool | Признак логирования действий пользователя для объекта |
is_global | bool | Признак глобального идентификатора у объекта. Для глобальных идентификаторов используется тип UUID, для локальных — bigint |
Таблица obj_item:
Поле | Тип | Назначение |
---|---|---|
id | bigserial | Идентификатор |
up | bigint | Ссылка на объект (obj.id) |
ord | bigint | Порядковый номер атрибута |
name | text | Имя |
r_cls | bigint | Если имеется словарное поле, номер словаря (@CLS.ID) |
r_obj | bigint | Если имеется ссылка на объект, номер объекта |
option | jsonb | Дополнительные параметры. Например, указание о замене типа на массив для методов |
dsc | text | Описание атрибута |
Таблица action:
Поле | Тип | Назначение |
---|---|---|
id | bigserial | Идентификатор |
r_object | uref | Ссылка на изменяемый объект и его идентификатор |
lnk_dt | timestamp | Дата привязки к версии реализации события (SD) |
fct_dt | timestamp | Реальная дата версии |
action | bigint | Произведенное действие. Словарь |
database | text | База данных, где расположен измененный объект |
schema | text | Схема, где расположен измененный объект |
dbuser | text | Пользователь базы данных, совершивший действие |
pid | text | Идентификатор процесса (Process ID), в рамках работы которого произведено изменение |
userid | uuid | UUID пользователя, вошедшего в систему и совершившего действие |
В таблицу action, если указано в obj.is_logging, записываются основания действий с объектами.
«Действие» (поле action) имеет следующую кодировку:
Код | Действие |
---|---|
1 | Создание |
2 | Изменение (при создании новой версии) |
3 | Изменение (при сокращении периода действия) |
4 | Закрытие версии |
ВАЖНО!
Для функционирования приложения достаточно важно, чтобы записи в эту таблицу записывались максимально быстро. Для этого есть возможность создавать таблицу как unlogged, а при включенном TARQ — также ... using append_only.
Методы описания объектов
Для описания объектов в таблицах obj и obj_item предоставляется функция qbim_describe_object(...)
.
Параметры функции:
Параметр | Тип | Значение по умолчанию | Назначение |
---|---|---|---|
i_table_name | text | Имя объекта (таблицы), в комбинации с именем схемы или без него. При отсутствии явного указания на имя схемы таблица ищется в схемах, указанных в параметре search_path. | |
i_obj_id | bigint | Номер объекта. Если указать null, номер будет присвоен автоматически из последовательности для obj.id | |
i_parent_obj_id | bigint | null | Номер родительского объекта, если создаётся иерархия объектов. |
i_temporal | bool | true | Признак версионированного объекта (темпоральности). В противном случае - неверсионированный, такие тоже можно описывать в таблицах объектов. |
i_logging | bool | true | Признак логирования изменений в таблице регистрации действий action |
i_global | bool | false | Признак "глобальности" идентификатора экземпляра объекта (тип uuid), в противном случае идентификатор "локальный" (тип bigint/int8). |
При описании используются комментарии таблицы и её полей.
При заполнении описания полей объекта в комментарии могут применяться шаблоны ссылок на словари и связанные объекты (поля r_cls и r_obj в таблице obj_item). Шаблоны соответственно - CLS#[[:digit:]] и OBJ#[[:digit:]].
Например, следующий комментарий:
COMMENT ON COLUMN object_tbl.r_domain IS 'Ссылка на домен. OBJ#22';
при описании объекта заполнит в obj_item для данного поля указание, что он является ссылкой на другой объект:
id | up | ord | name | r_cls | r_obj | option | dsc |
---|---|---|---|---|---|---|---|
... | ... | ... | r_domain | ... | 22 | ... | Ссылка на домен. OBJ#22 |
а комментарий:
COMMENT ON COLUMN object_tbl.model_status IS 'Состояние когнитивной модели. CLS#18';
при описании объекта заполнит в obj_item для данного поля указание, что поле словарное:
id | up | ord | name | r_cls | r_obj | option | dsc |
---|---|---|---|---|---|---|---|
... | ... | ... | model_status | 18 | ... | ... | Состояние когнитивной модели. CLS#18 |
Методы работы с объектами
Для создания, изменения и закрытия объектов предлагаются методы, которые корректно отслеживают ограничения модели.
Методы выборки оставляются на выбор пользователя или приложения. Возможно выбрать
конкретную версию, действующую в указанный момент времени, или серии версий за
указанный период вместе со схлопнувшимися версиями (промежуточные версии с нулевой
длительностью, sd == ed
) или без них. Также возможны различные выборки совместно со
связанными
объектами.
Методы создания объекта
Для создания объекта (начальной версии объекта) предоставляются методы qbim_create()
:
CREATE FUNCTION qbim_create(
obj_id int8, tab text,
id int8,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','create_local_reg' language c volatile security definer;
CREATE FUNCTION qbim_create(
obj_id int8, tab text,
id uuid,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns uuid as 'MODULE_PATHNAME','create_global_reg' language c volatile security definer;
В одном из первых двух параметрах метода указывается идентификатор создаваемого объекта: по идентификатору в таблице obj описателя или по имени объекта (таблицы); возможно указание и идентификатора, и имени, но они должны соответствовать описателю.
В зависимости от типа идентификатора, используемого в объекте, третий параметр может
иметь тип bigint или uuid; его пропуск (указание null::bigint
или null::uuid
)
требует автоматической генерации идентификатора. В случае bigint значение может
выбираться из соответствующей последовательности, а в случае uuid — генерируется
случайное значение uuid.
Следующая пара параметров задает интервал действия версии, при пропуске (указании
null:timestamp
) используются значения по умолчанию, указанные при создании таблицы.
Как правило, для sd это текущее время (CURRENT_TIMESTAMP), а для ed —
«бесконечность» (infintity::timestamp
).
В параметре userid можно указать идентификатор конечного пользователя,
производящего операцию создания объекта, если таковой используется в приложении.
Если не используется, можно пропустить (указать null::uuid
).
Далее в переменном списке параметров (variadic "any") следует передавать параметры парами — имя и значение, или не передавать вовсе. В любом случае количество переменных должно быть четным. Атрибуты объекта, пропущенные в этом списке, получат значения NULL или будут установлены в соответствии с умолчаниями, заданными при создании таблицы.
При создании объекта, если это указано в obj, будет зафиксировано действие в таблице action.
Методы изменения объекта (создания версии)
Для изменения объекта предоставляются методы qbim_change()
. Создание версии
возможно в любой период, пересекающий периоды имеющихся у объекта версий (не должно
получаться разрывов).
Параметры аналогичны методам qbim_create()
. Умолчания для sd и ed — текущее
время и «бесконечность» соответственно.
Новая версия может дополнять (с любой стороны) имеющиеся, пересекать любое количество имеющихся версий, покрывать полностью или частично уже имеющиеся версии.
В случае, если в переменном списке параметров опущено какое-то из полей, оно будет подставлено из соответствующей более ранней версии объекта, подходящей по времени.
Если в результате изменения какие-то из имеющихся версий будут полностью перекрыты
новой версией, они будут считаться схлопнувшимися (sd == ed
).
Методы qbim_change()
:
CREATE FUNCTION qbim_change(
obj_id int8, tab text,
id int8,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','change_local_reg' language c volatile security definer;
CREATE FUNCTION qbim_change(
obj_id int8, tab text,
id uuid,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','change_global_reg' language c volatile security definer;
При изменении объекта, если это указано в obj, будет зафиксировано действие в таблице action.
Методы закрытия объекта (версии)
Пара методов, закрывающих версии объекта текущей или указанной датой:
CREATE FUNCTION qbim_close(
obj_id int8, tab text,
id int8,
ed timestamp,
userid uuid)
returns int8 as 'MODULE_PATHNAME','close_local' language c volatile security definer;
CREATE FUNCTION qbim_close(
obj_id int8, tab text,
id uuid,
ed timestamp,
userid uuid)
returns int8 as 'MODULE_PATHNAME','close_global' language c volatile security definer;
В параметре ed указывается необходимое время для закрытия версий. Если пропущено
(указано null::timestamp
), то устанавливается текущее время. Остальные параметры
аналогичны параметрам в методах qbim_create
и qbim_change
.
При закрытии объекта, если это указано в obj, будет зафиксировано действие в таблице action.
Примечание
Для неверсионированных объектов точно также доступно описание в таблицах obj и obj_item, а также с ними работают методы создания, изменения и закрытия объектов. Для неверсионированных объектов системным является только один атрибут - идентификатор (id). В соответствующих методах параметры sd и ed не имеют смысла и игнорируются. Метод изменения подразумевает простой update экземпляра объекта. Метод закрытия подразумевает удаление экземпляра объекта. Регистрация действий (в таблице action) также производится, но поля lnk_dt и fct_dt заполняются одним и тем же значением - временем регистрации действия.
Установка расширения
Установка расширения производится командой CREATE EXTENSION. Поскольку есть расширения, от которых зависит расширение Qbim, рекомендуется использовать установку с предложением CASCADE. Расширение Qbim переносимое, поэтому его можно установить в различные схемы базы данных, используя SCHEMA имя_схемы.
Начиная с версии расширения 1.2 есть возможность использовать различные опции таблицы action. Для управления созданием этой таблицы используется группа локальных параметров (см. Таблицу ниже).
При обновлении расширения до версии 1.2 с предыдущих версий используйте предложение UPDATE команды ALTER EXTENSION.
Таблица Локальных параметров для управления созданием таблицы action
Параметр | Назначение | Значение по-умолчанию |
---|---|---|
qbim.action.use_partitions | Создавать таблицу партиционированную по полю fct_dt. | 'true' |
qbim.action.partition_period | - при создании партиционированной таблицы - размер секции, которые будут созданы при создании. Возможны варианты: 'day', 'week', 'month', 'year'. | 'month' |
qbim.action.partition_count | - при создании партиционированной таблицы - количество создаваемых секций, не считая двух секций для "самых старых" и "самых новых" значений. | 5 |
qbim.action.use_unlogged | Создавать таблицу с параметром unlogged. Внимательно отнеситесь к этому параметры, его лучше не использовать при работе в кластере. | 'false' |
qbim.action.use_appendonly | Создавать таблицу как неизменяемую (append_only). Возможно только при включённом TARQ. | 'true' |
qbim.action.upgrade_exists | Мигрировать данные из уже существующей таблицы action, при обновлении с ранних версии расширения Qbim, до версии 1.2. Если расширение устанавливается впервые, параметр не имеет смысла. | 'true' |
Параметры нужно установить перед вызовом команд CREATE EXTENSION или ALTER EXTENSION.
Примеры установки:
create extension qbim cascade;
set qbim.action.use_partitions='true';
set qbim.action.partition_period='year';
set qbim.action.partition_count=2;
create extension qbim schema my_schema cascade;