Перенос данных на чистую базу (постановка)
SYSDBA (обсуждение | вклад) |
SYSDBA (обсуждение | вклад) |
||
| Строка 39: | Строка 39: | ||
## При этом, перед занесением ссылки в М проверяем ее наличие в М<sup>'</sup>. Если она присутствует там, то помещаем ее в М<sup>'</sup><sup>'</sup> и в М. | ## При этом, перед занесением ссылки в М проверяем ее наличие в М<sup>'</sup>. Если она присутствует там, то помещаем ее в М<sup>'</sup><sup>'</sup> и в М. | ||
# Обрабатываем таблицы из T<sup>'</sup><sup>'</sup><sup>'</sup>. Сканируем каждую запись: | # Обрабатываем таблицы из T<sup>'</sup><sup>'</sup><sup>'</sup>. Сканируем каждую запись: | ||
| − | ## Если ИД в М, то заносим все ссылки на таблицы из T | + | ## Если ИД в М, то заносим все ссылки на таблицы из T<sup>'</sup><sup>'</sup> в М. |
## При этом, перед занесением ссылки в М проверяем ее наличие в М<sup>'</sup>. Если она присутствует там, то помещаем ее в М<sup>'</sup><sup>'</sup> и в М. | ## При этом, перед занесением ссылки в М проверяем ее наличие в М<sup>'</sup>. Если она присутствует там, то помещаем ее в М<sup>'</sup><sup>'</sup> и в М. | ||
# Обрабатываем все ИД из М<sup>'</sup><sup>'</sup> по алгоритму для таблиц из T<sup>'</sup><sup>'</sup><sup>'</sup>. Данный пункт повторяем, пока М<sup>'</sup><sup>'</sup> не станет пустым множеством. | # Обрабатываем все ИД из М<sup>'</sup><sup>'</sup> по алгоритму для таблиц из T<sup>'</sup><sup>'</sup><sup>'</sup>. Данный пункт повторяем, пока М<sup>'</sup><sup>'</sup> не станет пустым множеством. | ||
| Строка 47: | Строка 47: | ||
==== gd_document ==== | ==== gd_document ==== | ||
| + | |||
| + | Если на таблицу наложены условия, то она обрабатывается последней из всех таблиц T<sup>'</sup><sup>'</sup>. | ||
==== gd_ruid ==== | ==== gd_ruid ==== | ||
| − | + | ||
| + | Таблица не участвует в общем алгоритме. По окончании процесса, удаляем из нее записи с ИД из М<sup>'</sup>. | ||
=== Замечания по реализации === | === Замечания по реализации === | ||
Версия 13:07, 29 сентября 2011
Принципы ускорения:
- последовательное неиндексированное чтение исходных данных
- отключение индексов, триггеров, ключей, ограничений на конечной базе данных
- обработка множества идентификаторов в оперативной памяти
Пусть:
- A -- исходная база данных.
- B -- конечная база данных.
- T -- множество всех таблиц в базе данных.
- T' -- множество таблиц, целиком переносимых на конечную БД.
- T'' -- множество главных таблиц БО, частично переносимых на конечную БД.
- M -- множество идентификаторов, требуемых в базе В по условиям целостности.
- М' -- множество идентификаторов, подлежащих удалению из В.
- М'' -- множество идентификаторов, подлежащих перепроверке.
Выполняется условие:
T = T' U T''
Алгоритм усечения базы В построим следующим образом:
- Перед началом процесса проверяем базу А:
- отключенные внешние ключи должны быть включены
- блокировка периода должна быть снята
- кэш не должен превышать 500 Мб
- аудит должен быть отключен
- Пользователю рекомендуется провести бэкап-разбэкап исходной базы перед началом процесса.
- База В получается из базы А путем копирования и присвоения нового идентификатора базы данных.
- Удаляем из базы В все индексы, первичные ключи, внешние ключи. Деактивируем все триггеры.
- Обрабатываем таблицы из T'. Сканируем каждую запись:
- Заносим ИД в М.
- Заносим все ссылки на таблицы из T'' и T''' в М.
- Обрабатываем таблицы из T''.Сканируем каждую запись:
- Если ИД в М, то заносим все ссылки на таблицы из T'' и T''' в М.
- Если ИД не в М, то проверяем на условие переноса. Если запись подлежит переносу, то заносим в М ИД и все ссылки на таблицы из T''.
- Если запись подлежит удалению, то заносим ее ИД в М'.
- При этом, перед занесением ссылки в М проверяем ее наличие в М'. Если она присутствует там, то помещаем ее в М'' и в М.
- Обрабатываем таблицы из T'''. Сканируем каждую запись:
- Если ИД в М, то заносим все ссылки на таблицы из T'' в М.
- При этом, перед занесением ссылки в М проверяем ее наличие в М'. Если она присутствует там, то помещаем ее в М'' и в М.
- Обрабатываем все ИД из М'' по алгоритму для таблиц из T'''. Данный пункт повторяем, пока М'' не станет пустым множеством.
- Восстанавливаем на В удаленные ограничения, индексы и триггеры, руководствуясь информацией из А.
Специфика обработки отдельных видов таблиц
gd_document
Если на таблицу наложены условия, то она обрабатывается последней из всех таблиц T''.
gd_ruid
Таблица не участвует в общем алгоритме. По окончании процесса, удаляем из нее записи с ИД из М'.
Замечания по реализации
- Системные записи (ИД менее 147 000 000) и ссылки на системные записи игнорируем полностью.
- Подключение к базе В выполняем с отключенной сборкой мусора.
- М и М' -- битовые массивы, изначальные размеры которым устанавливаем исходя из диапазона идентификаторов, вычисленного как GD_G_UNIQUE - 147000000.Используем класс TgsHugeIntSet.
- М'' список именованных списков. Для каждой таблицы заводим отдельный список.
- М'' организуем с помощью класса TStringList:
Устаревшее
База B создается из копии метаданных А. На время переноса данных отключается принудительная запись и размер кэша устанавливается не более 500 Мб. Подключение осуществляется в режиме с отключенными триггерами базы данных. Генератор идентификаторов базы B увеличивается на заданную дельту.
В базе B создаем структуры:
- для хранения структуры БД в части удаленных\отключенных объектов метаданных
- для хранения информации о записях с начальным сальдо
- для хранения лога изменения данных
- для хранения информации об исходной БД
Считываем и запоминаем структуру базы B. Деактивируем или удаляем следующие объекты метаданных: триггеры, индексы, чеки, внешние ключи, первичные ключи, вычисляемые поля.
Создаем множество R для идентификаторов объектов, подлежащих переносу из А в B.
Выводим сальдовые значения и помещаем их в B. Помещаем в R идентификаторы объектов, необходимых для суммарных значений.
Все таблицы мы подразделяем на:
- таблицу gd_document
- главные таблицы БО (кроме gd_document)
- таблицы, связанные 1-к-1 в реляционной модели
- детальные таблицы (таблицы с дополнительной информацией, таблицы с позициями документов, таблицы документов)
- таблицы-связки для атрибутов типа множество
- прочие таблицы (без идентификатора ИД или со сложным первичным ключем)
- таблица GD_RUID (id в этой таблице будет обязательно дублироваться с одним из id из другой таблицы в БД)
- таблицы, которые используют свои генераторы для присвоения идентификаторов (например, GD_USERGROUP)
Списки таблиц упорядочиваем по возрастанию количества внешних ссылок на таблицу.
Алгоритм переноса:
- Проходимся по таблицам-связкам для множеств и помещаем в R все встреченные идентификаторы элементов множеств.
- Организуем цикл по прочим таблицам. Для каждой сканируем все записи и добавляем все встреченные ссылки в R.
- Сканируем шапки из таблицы gd_document. Проверям на условия переноса. Если условия выполнены и ИД записи еще нет в R то:
- сканируем все поля-ссылки в этой записи и добавляем в R идентификаторы.
- добавляем ее идентификатор в R.
- Сканируем позиции из таблицы gd_document. Если ИД записи еще нет в R и ИД шапки находится в R, то:
- сканируем все поля-ссылки в этой записи и добавляем в R идентификаторы.
- добавляем ее идентификатор в R.
- Организуем цикл по главным таблицам БО. Внутри каждой таблицы организуем цикл по всем записям. Если ИД записи не в R и по условиям она подлегает переносу, то:
- сканируем все поля-ссылки в этой записи и добавляем в R идентификаторы.
- добавляем ее идентификатор в R.
- Организуем цикл по детальным таблицам и по таблицам 1-к-1. Если ИД главной записи находится в R, то:
- сканируем все поля-ссылки в этой записи и добавляем в R идентификаторы.
- добавляем ее идентификатор в R.
- Если в процессе выполнения пунктов 3-6 в R добавлен хотя бы один новый идентификатор, то повторяем цикл начиная с шага 3.
- Переносим данные из А в B:
- все данные прочих таблиц
- для таблиц с идентификаторами -- все записи, которые зафиксированы в R
- для таблиц-связок -- все записи, относящиеся к объектам из R
Примечание: при выполнении шагов 5 и 6, если таблица имеет древовидную структуру, то организуется цикл от первой до последней записи, который повторяется пока в процессе обработки не было добавлено ни одного нового ИД в R.
Восстанавливаем в B удаленные и деактивированные объекты метаданных.
В базе B на каждую таблицу создаются триггеры после изменения и после удаления, которые синхронизируют изменения с базой А. (или фиксируют изменения для последующей синхронизации).
Создаем в А таблицу и записываем параметры базы B.
Работа с базой А после разделения
IDA -- значение генератора из БД А на момент старта процесса. IDB = IDA + 1000000 -- значение генератора в БД B, устанавливаемое на момент окончания процесса.
В базе А создается триггер на коммит транзакции, который проверяет, если значение генератора больше, чем IDB - 1000, то выдается исключение.
Слияние баз А и B
Сначала выполняются все отложенные операции синхронизации изменений в B.
Создается база C, как копия метаданных базы B. Запоминается ее структура. Отключаются индексы, чеки, триггеры, ключи. Переносится на нее информация из A и из B. Не переносятся суммарные данные. Восстанавливаются ключи, индексы, триггеры, чеки.
В общем случае потребуется на базу А накатить все настройки, которые были установлены на B с момента разъединения этих баз.
Альтернативный способ переноса данных на чистую базу
- Базу В делаем из базы А с помощью утилиты nbackup. В начальном состоянии -- это точная копия базы А.
- Определяем зависимости на базе А.
- Все таблицы подразделяем на:
- Таблицы с целочисленным первичным ключем (без разницы, это главная таблица БО или присоединенная 1-к-1)
- Таблицы-связки для множеств
- Прочие таблицы (без ключа, с нецелочисленным ключем, со сложным ключем)
- Для хранения зависимостей нам понадобятся структуры:
- Массив M ссылок на блоки памяти. Номер ячейки в массиве соответствует идентификатору. Содержимое ячейки -- это номер блока со ссылками.
- Массив S блоков ссылок. Каждый блок имеет размер 16 байт и содержит в себе три ссылки и номер следующего блока в цепочке.
- Организуем цикл по всем таблицам в БД
- Прочие таблицы пропускаем.
- Таблицы с целочисленным ключем обрабатываем следующим образом:
- Проверяем ячейку с номером, равным идентификатору, в М. Если она пуста, то берем очередной свободный блок в S и его номер заносим в ячейку. Блок заполняем ссылками из рассматриваемой записи. Если ссылки не умещаются в один блок, то берем следующий. В каждом предыдущем блоке хранится номер следующего в цепочке. При этом, не заносим дважды повторяющиеся ключи.
- Если ячейка не пуста, то берем из нее номер первого блока в цепочке и считываем все идентификаторы (они понадобятся нам, чтобы избежать дублирования). Дописываем в цепочку блоков идентификаторы из рассматриваемой записи. При необходимости выделяем новые блоки и добавляем их в цепочку.
- Таблицы-связки обрабатываем следующим образом: заносим в цепочку идентификатор элемента множества для объекта, которому принадлежит данный атрибут типа множество.
- Все таблицы подразделяем на:
- Удаляем ключи, индексы, деактивируем триггеры.