Совет прошлого месяца ясно определил приём медленно изменяющихся
измерений типа 2 и использование суррогатных ключей. В этом месяце
мы имеем дело с неприятной проблемой обработки медленно изменяющихся
измерений во время начальной загрузки новой предметной области в
хранилище. Это происходит, когда вы вводите новый показатель (факт)
в существующее хранилище данных. Такие измерения как "Продукт",
"Клиент" и "Время" уже, возможно, определены
и имеют богатую историю, отражая многие "медленные изменения".
Для начала я опишу обычную процедуру ETL (извлечение, преобразование,
загрузка), которая должна происходить каждую ночь:
Сначала обрабатываются измерения, любые новые записи из системы-источника
вставляются в таблицы измерений, и им присваиваются следующий по
порядку суррогатный ключ. Обнаруживаются существующие записи в таблице
измерений, изменившиеся с момента последней загрузки хранилища и
определяется характер их изменения. На основе того, какие поля изменились,
политика поддержки медленно меняющихся измерений повлияет на то,
будет ли текущая запись деструктивно обновлена с потерей старых
значений (тип 1), или должна быть создана новая запись для того
же естественного ключа, и для неё должен быть сгенерирован суррогатный
ключ (тип 2).
После этих запутанных сравнений и решений по поводу каждого из
измерений наступает время загрузки фактов. Здесь скорость является
сутью процесса. Естественные ключи должны быть отброшены и как можно
быстрее заменены на правильные суррогатные ключи. В идеале мы хотим
воспользоваться преимуществом операций подстановки, выполняемых
в памяти, которые предлагает большинство инструментов класса ETL.
Небольшие таблицы подстановки для каждого измерения, которые транслируют
естественные ключи в суррогатные, строятся из таблиц измерений в
СУБД хранилища при помощи операторов, подобных приведённым ниже:
SELECT customer_ID, max(customer_key) from customer
group by customer
или
SELECT customer_id, customer_key from customer
where current='Y'
После выполнения этих операторов таблицы подстановки будут содержать
суррогатные ключи, которые подходят к новым фактам, и будут поддерживать
быструю подстановку с помощью алгоритма хеширования.
Однако простого и эффективного метода подстановки будет недостаточно,
если мы загружаем в эту первую загрузку большое количество исторических
фактов. Например, представьте ситуацию, когда мы выполняем начальную
загрузку транзакций за период в два года, и мы "благословлены"
иметь двухлетнюю историю изменений записей о клиентах. Эти двое
могли никогда раньше не встречаться в одном отчёте, но нам необходимо
их точно сопоставить в этой новой витрине данных и совершенным образом
разбить историю.
Операция подстановки для измерения должна будет содержать исторические
суррогатные ключи для всех изменений деталей о клиенте, произошедших
за эти два года. Простая хеш-подстановка, подобная оператору SQL:
select customer_key from customer_lookup CL
where CL.customer_ID=factfile.customer_id
должна быть замена на
select customer_key from customer_lookup CL
where CL.customer_ID=factfile.customer_id and factfile.transaction_date
between CL.effective_date and CL.end_date
Логика выборки за определённый временной интервал, которая здесь
необходима, замедлит обработку нескольких сотен миллионов строк
с фактами с десятью подобными измерениями до скорости передвижения
ползком. Вдобавок к этому, у вас теперь в наличие две различных
процедуры загрузки таблицы фактов. Если вам и удастся дождаться
до конца процедуры начальной загрузки, вам нужно будет выбросить
её и построить, отладить и сопровождать более простую версию, которая
с этого момента будет инкрементально обновлять таблицу фактов.
Представляя что-то вроде компромисса, ответ на эту задачу обладает
умиляющей простотой. Не разрабатывайте сложную пакетную процедуру
загрузки, постройте только процедуру инкрементального обновления!
Этот метод предоставляет более правдивую версию прошлого, чем простое
присоединение всех исторических фактов к текущим записям таблиц
измерений, а также даёт клятву впредь медленно изменяться, что часто
является стратегией, применяющейся при столкновении с пакетной начальной
загрузкой. Вот, что я вам предлагаю делать:
Возьмите ваши данные за два года и разбейте процедуру загрузки
на сто четыре задачи, каждая из которых будет загружать транзакции
за одну неделю, и запустите эти задачи одну за другой. Начните каждую
задачу с загрузки истории изменения измерений, относящуюся только
к той неделе, то есть когда effective_date деталей измерения меньше
либо равна минимальной дате транзакции. В то время как это означает
немного дополнительной логики в шагах обработки измерений, процедуру
загрузки фактов можно оставить неизменной. Я могу это делать, поскольку
простые таблицы подстановки содержат максимальные значения суррогатных
ключей, применимые для загружаемого периода. Вы просто выполнили
104 процедуры инкрементального обновления. Компромисс заключается
в том, что некоторые фактические записи будут присоединены к записям
таблицы измерений, устаревшим максимум на неделю. В большинстве
случаев это приведёт к незначительным ошибкам, поскольку измерения
изменяются медленно.
Если вы нашли в сети интересные ссылки на ресурсы по технологиям
хранилищ данных, OLAP, CRM или data mining, и хотите поделиться
ими с другими, присылайте их.
Я с удовольствием размещу их на этом сайте.