В статье Партицирование OLAP-кубов в SSAS 2008 я рассказывал зачем и как партицировать кубы в SSAS. Бывают случаи, когда партицировать надо часто и руками это делать скучно. Поэтому хочется этот процесс автоматизировать.
В моем случае мне нужно партицировать в трех кубах в общей сложности 10 групп мер. Если этого не делать каждую неделю, то время процессинга за две недели возрастает с 3,5 часов до 5 с лишним.
В статье Взаимодействие с OLAP кубами (SSAS 2008) на C# я рассказывал, как работать с кубами из кода. Сначала я начал решать задачу автоматического партицирования через разработку проги, но что то сильно углубился в программирование и мне эта идея перестала нравиться, поэтому я выбрал другой путь - создание пакета SQL Server Integration Services (SSIS). Здесь я и опишу этот способ.
Идея следующая - группы мер партицируются по годам плюс есть текущая партиция, которая процесситься каждый день. Т.е., например, для продаж есть партиции за 2010 год, 2011 и current. Раз в неделю для каждой группы мер необходимо изменять партицию за текущий год (2011) и партицию current, так что бы текущая партиция содержала данные только за несколько дней. Потом процессить партицию за текущий год, что бы не было пробелов в данных. После нового года надо будет руками создавать новую годовую партицию и менять процедуру изменения партиций.
Начальное партицирование куба надо провести руками, автоматически все с нуля не создается.
Для примера возьмем, что партиции группы мер продаж формируются следующими SQL-запросами:
2010: SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date >= '2010-01-01' AND sale_date < '2011-01-01'
2011: SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date >= '2011-01-01' AND sale_date < '2011-05-31'
current: SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date >= '2010-05-31'
Эти запросы и надо будет менять в партициях за 2011 год и в текущей партиции.
Для начала необходимо сгенерить скрипты XMLA для изменения партиций. Я буду делать все на примере только одной группы мер. Пакет SSIS будет сделан так, что количество не будет иметь значения.
Cоздаем папку: C:\XMLAScripts. Далее, для генерации скрипта в Management Studio открываем список партиций нужной группы мер, на текущей партиции тыкаем правой кнопкой, а далее как на картинке:
В последнем меню выбираем File... и сохраняем в созданной папке под названием sales_current.xmla. Далее тоже самое повторяем для партиции текущего года и сохраняем в другой файл sales_2011.xmla.
Далее открываем файлы по-очереди и видим что-то похожее на это:
<Alter ObjectExpansion="ExpandFull" xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
<Object>
<DatabaseID>cubeDbSales</DatabaseID>
<CubeID>cubeSales</CubeID>
<MeasureGroupID>Vw Sales 1</MeasureGroupID>
<PartitionID>Vw Sales 1</PartitionID>
</Object>
<ObjectDefinition>
<Partition ...>
<ID>Vw Sales 1</ID>
<Name>Vw Sales</Name>
<Source xsi:type="QueryBinding">
<DataSourceID>dbOLAP</DataSourceID>
<QueryDefinition>SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date >= '2010-05-31'</QueryDefinition>
</Source>
<StorageMode>Molap</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<ProactiveCaching>
<SilenceInterval>-PT1S</SilenceInterval>
<Latency>-PT1S</Latency>
<SilenceOverrideInterval>-PT1S</SilenceOverrideInterval>
<ForceRebuildInterval>-PT1S</ForceRebuildInterval>
<Source xsi:type="ProactiveCachingInheritedBinding" />
</ProactiveCaching>
</Partition>
</ObjectDefinition>
</Alter>
В каждом файле заменяем дату, которая разделяет партицию текущего года и текущую партицию, в данном примере это 2011-05-31, на какую-нибудь последовательность символов. Я поменял на "###dt###", т.е. запрос в скрипте будет выглядеть вот так:
<QueryDefinition>SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date >= '###dt###'</QueryDefinition>
Далее эта последовательность символов будет меняться на нужную дату.
В BIDS создаем Integration Services Project. Из Toolbox'а перетаскиваем Foreach Loop Container, а в него Script Task и Analysis Services Execute DDL Task. В итоге должно получиться вот так:
Далее создаем переменные:
Розовые уголки в иконках - это у меня плагин стоит, который я описываю в статье Как поменять область видимости переменной в BIDS (SSIS).
Для переменных dt и dt_str устанавливаем свойство EvaluateAsExpression = True. Для dt свойство Expression: GETDATE(). Для dt_str свойство Expression:
(DT_WSTR, 4)(YEAR(@[User::dt]))+"-"+
(LEN((DT_WSTR, 2)(MONTH(@[User::dt])))==1 ? "0"+(DT_WSTR, 2)(MONTH(@[User::dt])) : (DT_WSTR, 2)(MONTH(@[User::dt])))+"-"+
(LEN((DT_WSTR, 2)(DAY(@[User::dt])))==1 ? "0"+(DT_WSTR, 2)(DAY(@[User::dt])) : (DT_WSTR, 2)(DAY(@[User::dt])))
Такое выражение формирует из даты строку формата yyyy-MM-dd, другого пути я не нашел.
В совойствах Foreach контенера устанавливаем:
- В разделе Collection:
- Enumerator = Foreach File Enumerator
- Folder = C:\XMLAScripts
- Files = *.xmla
- В разделе Variable Mappings: Variable=User:file_name, Index = 0
- ReadOnlyVariables = User::dt_str,User::file_name
- ReadWriteVariables = User::alter_code
public void Main()
{
string file_name = Dts.Variables["file_name"].Value.ToString();
StreamReader sr = new StreamReader(file_name);
string content = sr.ReadToEnd();
sr.Close();
sr.Dispose();
Dts.Variables["alter_code"].Value = content.Replace("###dt###", Dts.Variables["dt_str"].Value.ToString());
Dts.TaskResult = (int)ScriptResults.Success;
}
В свойствах блока Alter Cube в разделе DDL:
- Connection -> создаем новое соединение с кубом
- SourceType =Variable
- Source = User::alter_code
На этом создание SSIS пакета закончено. Foreach контейнер выбирает из папки C:\XMLAScripts все файлы с расширением xmla, в блоке Script текущий файл читается, в нем производиться замена строки "###dt###" на сегодняшнюю дату в формате yyyy-MM-dd, а блок Alter Cube передает полученную таким образом команду OLAP-серверу. Для того, что бы добавить другие группы мер для партицирования, надо в папку C:\XMLAScripts добавить XMLA-файлы с альтерами для этих групп, в которых дата в SQL-запросе будет заменена на "###dt###".
Далее нужно создать джоб, в котором первым шагом сделать бэкап куба на всякий случай, вторым шагом запустить этот SSIS пакет, а далее отпроцессить все партиции за текущий год.
а не проще сделать так:
ОтветитьУдалитьразбить меры на партиции:
1. Факты_2010_2011: с 2010-по 31.05.2011 (с учетом того что май уже не трогают для изменения)
2. Факты_текущий_месяц:
SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date >=
dateadd(day,-Day(getdate())+1 and sale_date <= getdate()),getdate()-2
3. Факты_текущая_дата:
SELECT id, sale_date, user_id, money_sum, product_id FROM sales WHERE sale_date between getdate()-1 and getdate()
Создать SSIS пакет ("Задача обработка средствами AS") с обработкой 2 и 3 секций, и все.
Вообще 2 и 3 можно объединить, но в моем случае например этого не сделаешь, так как вчерашнюю и сегодняшнюю дату постоянно обновляет внешняя система и заливка не всегда происходит (есть insert, delete).
Итого:
первую секцию я залил и вообще не трогаю
вторую загружаю раз в сутки в 4:00 (когда нет интеграции)
третью каждый час
Да, так много проще. Сейчас сижу и думаю, почему я так не догадался сделать :))
ОтветитьУдалитьНу а по поводу того, какими частями бить куб на партиции, думаю тут индивидуально все. Зависит от требований к оперативности данных и от объемов данных. Мне удобнее делать текущие партиции в субботу утром. Да и 2011 год иногда еще перепроцешивается, поэтому я его отделил от 2010-го.
Вот насчет того что текущий год могут менять, что не есть гуд, как тут можно разрулится, не подскажите?
ОтветитьУдалитьНапример в декабре 2011 могут изменить апрель 2011, июль 2011... процессить весь год не есть хорошо и долго.
Что можно придумать?
ЗЫ: при использовании более трех партиций на секцию пишет ошибку:
"Ошибки доступности и конфигурации компонентов: Превышение максимального количества секций в группе мер. Установленные выпуски сервера допускают использование не более 3-х секций и не более 1-ой секции обратной записи."
Использую SQL Server 2008 Russian Standard х32
Обойти ограничение можно, создав сколько угодно IS-пакетов, запускающихся последовательно (в джобе например) (или, наверное, можно даже в одном пакете последовательность тасков), каждый из которых запускает наборы не более трёх секций (партиций).
УдалитьНиколай, создать сколько угодно секций по Вашему примеру смог, но как их обработать? В Management Studio при запуске скрипта на обработку первой же секции вылетает
Удалить...
...
xml скушался.
УдалитьОшибки доступности и конфигурации компонентов: Превышение максимального количества секций в группе мер. Установленные выпуски сервера допускают использование не более 3-х секций и не более 1-ой секции обратной записи.
Это ограничение Standard Edition, что бы было больше нужен Enterprise
УдалитьПро изменение исторических данных - ничего не остается как перепроцесить. Если такое случается редко, то можно сделать руками. Если часто, то можно сделать автоматический перепроцессинг, используя лог изменений.
УдалитьПо поводу ограничения - это видимо ограничение Standard Edition, у меня стоит SQL Server 2008 R2 Enterprise Edition, максимум используется 4 партиции и не ругается.
ОтветитьУдалитьПо поводу перепроцешивания - кубы еще пока не в продакшене, а еще в стадии вялотекущей разработки, так что бывает приходиться перепроцешивать.
По поводу перепроцешивания нескольких месяцев - ну первое что приходит в голову - для текущего года создавать месячные партиции, но в вашем случае с ограничением это не вариант. Есть еще команда Merge, которая сливает партиции в одну. Т.е. идея такая - создаем годовую партицию, допустим только за январь, процессим, потом за февраль и мерджим ее с январской, потом за март и т.д. Но на больших объемах команда Merge будет очень медленно работать, так что может не подойти. У меня кубы порядка 100 гигов, на самых емких мерах я так и не смог дождаться, когда Merdge выполниться :) Еще один вариант - завести еще 1 сервер или хотя бы инстанс SSAS'a, на нем развернуть кубы, перепроцесить, например в выходные, потом забэкапить и развернуть бэкап на рабочий сервер, разворачивание бэкапа проходит много быстрее перепроцессинга всего. У меня кубы, если полностью процессить, наверное будут процесситься часов 7, из бэкапа разворачиваются за час.
хм... спасибо, надо подумать :)
ОтветитьУдалитьПожалуйста. На самом деле надо поэкспериментировать, очень советую, если еще нет, завести тестовый инстанс или лучше сервер и эксперименты ставить на нем. В какой то момент мне показалось, что Merge это прекрасная возможность изменять партиции, поэкспериментировал на маленьком кубе - все отлично, но как только запустил на больших объемах - стало понятно, что не катит совершенно такая идея. Так что теперь все эксперименты только на тестовых кубах, которые разворачиваю их бэкапов рабочих кубов.
ОтветитьУдалитьВозникает еще один момент непонятный мне:
ОтветитьУдалитьесть партиция Факты_2010_2011 (до 31.05.2011)
потом например автоматически разбиваю меру на партиции, как в моем варианте:
Факты_текущий_месяц и Факты_текущая_дата, т. е. в июне система автоматом обрабатывает июнь, в июле июль и т.д.... но по факту через пол года у меня будут партиции: Факты_2010_2011 (до 31.05.2011) и Факты_текущий_месяц и Факты_текущая_дата ЗА ДЕКАБРЬ, а вот с июня по ноябрь нету.
Как вот здесь потом, в случае необходимости, обработать данные с июня по ноябрь?
или может можно руками в самом SQL Manager создавать секции и они будут работать?
Не очень понял, что имеете в виду, что не будет отдельных партиций с июня по ноябрь? Если партиция Факты_текущий_месяц в SQL-запросе будет сверху ограничена функцией getdate(), то она будет расти и вбирать в себя другие месяца, пока руками не создать новую месячную партицию, а предыдущую жестко ограничить сверху < '2011-06-01'
ОтветитьУдалитьДа расти то она (Факты_текущий_месяц) будет, но только в текущем месяце неболее, смысл ее такой:
ОтветитьУдалитьв where стоит sale_date between
dateadd(day,-Day(getdate())+1,getdate()) and getdate()
т. е. сегодня это: с 01.06.2011 по 14.06.2011
завтра это: с 01.06.2011 по 15.06.2011
и т.д.
А в след месяце это автоматом будет:
первого июля:с 01.07.2011 по 01.07.2011
второго: с 01.07.2011 по 02.07.2011
и т.д.
....
в итоге в декабре это будет:
с 01.12.2011 по 31.12.2011
Но по факту в декабре секции с 01.06.2011 по 30.11.2011 нет! Или SQL сам их будет создавать как-то? если нет то как их потом процессить, в случае необходимости?
ЗЫ: или в секцию Факты_2010_2011 нужно каждый месяц добавлять ограничение по конечной дате предыдущего месяца...
ОтветитьУдалитьтипа в июне это как щас стоит 31.05.2011
в июле ставить руками 30.06.2011
...
в декабре 30.11.2011
...
а в 2012 переименовывать ее в Факты_2010_2012 и т.д.?
что то я протупил
ОтветитьУдалитьв секцию Факты_2010_2011 поставить в конец
ДАТА<dateadd(day,-Day(getdate())+1,getdate())
и все :).
минус в том что эта секция будет разростатся и через пару лет обработать ее будет нереально.
ЗЫ: подскажите где можно за эти merge почитать? куда нажать?
ну да, надо партиции жестко ограничивать снизу, например для мая dt >= '2011-01-01' и по getdate() сверху, например dt < cast(getdate()-1 as date). Тогда партиция будет разрастаться вверх неограничено, потом, когда надо, ограничить ее сверху жестко dt < '2011-06-01' и создать новую партицию текущего месяца dt >= '2011-06-01' and dt < cast(getdate()-1 as date). Перенастроить задания процессинга так, что бы он процесил не майскую партицию, а июньскую. Текущая партиция всегда будет ограничена снизу dt >= cast(getdate()-1 as date), ее менять не надо.
ОтветитьУдалитьПро Merge - спросите у Гугла :)) http://www.google.com/#hl=en&source=hp&q=ssas+2008+merge&aq=f&aqi=&aql=f&oq=&pbx=1&fp=1&biw=1646&bih=967&cad=b&bav=on.2,or.r_gc.r_pw.
Что то сейчас посидел подумал, что в моем случае getdate() не очень подходит, т.к. если я партицирую кубы по годам, и текущий год будет ограничен сверху функцией getdate(), а текущая мера снизу функцией getdate(), то тогда мне надо будет процессить каждый день и текущий год тоже, что не очень прикольно. Можно конечно создавать более мелкие партиции, которые будут ограничены getdate() сверху, например месяц или неделя, но тогда надо будет раз в какое то время изменять сами партиции, т.е. ограничивать сверху жесткой датой и изменять процедуры процессинга, что бы они больше не процессили старые партиции и процессили новые. Можно этот процесс автоматизировать, но сейчас я к этому не готов :)
ОтветитьУдалитьда вариантов много в общем :). спасибо.
ОтветитьУдалить