Кэширование уровня сессии в Reporting Services
С момента своего выхода в начале 2004 г. Розетта обладала способностью кэшировать отчеты. Существовало три уровня кэширования – уровня сессии; собственно, кэширование отчета и генерация последовательности мгновенных снимков. В 2008 R2 к ним добавилось кэширование датасетов. Сначала освежим в памяти первые три. Кэширование уровня сессии происходит автоматически и отключить его нельзя. Как известно, процесс получения отчета занимает три этапа. На первом Reporting Service лезет в источники данных и выполняет датасетные запросы. На втором читает оставшийся rdl, чтобы понять, как устроен отчет, какие где у него текстбоксы и табликсы и как в них напхать полученные на первом этапе данные. На третьем отрисовывает все это хозяйство в заказанный формат, будь то HTML, Excel, Word, pdf и т.д., так называемый рендеринг. Полуфабрикат между вторым и третьим этапами сохраняется в таблицы ReportServerTempDB. В таблицу SnapshotData ложится информация о выполнении отчета: время, параметры и т.д. Сам отчет делится на куски. Первый кусок (chunk) - это метаданные, второй - скомпилированное определение, третий - информация о разбиении по страницам и т.д. Раньше куски хранились в таблице ChunkData, сейчас она осталась, но пустует. Чтобы окончательно всех запутать, каждый кусок теперь дробится еще на сегменты. Куски лежат в таблице SegmentedChunk, сегменты - в таблице Segment, соответствие сегментов кускам - в таблице ChunkSegmentMapping. Полуфабрикат в виде бинарщины размазан по сегментам в поле Content. Разумеется, структура нигде не документирована, так что разобраться со всем этим решительно невозможно, если не найти и не почитать патентную заявку "Optimistic Versioning Concurrency Scheme for Database Streams". Из нее следует, что дробление кусков на сегменты сделано с целью уменьшения конфликтов и расхода места для обеспечения версионности при многопользовательской работе. В заявке иллюстрируется, как к куску С1 (таблица SegmentedChunk(VersionID, ChunkID), запись {1, C1}), побитому на сегменты Seg1 и Seg2 (таблица ChunkSegmentMapping(ChunkID, SegmentID, StartByte), записи {C1, Seg1, 0}, {C1, Seg2, 32000}), несущих в себе данные полуфабриката (таблица Segment(SegmentID, Content), записи {Seg1, 0x00AA}, {Seg2, 0x00BB}) обращается новый пользователь и его меняет. Предположим, он меняет его не весь, а что-нибудь в Seg2. Измененный сегмент добавляется в таблицу Segment(SegmentID, Content) - записи стали {Seg1, 0x00AA}, {Seg2, 0x00BB}, {Seg3, 0x00BС}. Создается новая версия 2 куска в таблице SegmentedChunk(VersionID, ChunkID) - записи стали {1, C1}, {2, C2}. Ей сопоставляется изменившийся сегмент - таблица ChunkSegmentMapping(ChunkID, SegmentID, StartByte), записи {C1, Seg1, 0}, {C1, Seg2, 32000}, {C2, Seg1, 0}, {C2, Seg3, 32000}. Таким образом, каждой пользовательской сессии соответствует своя версия куска, при этом они шарят неизменившиеся сегменты. В таком вот аксепте. Продеплойте и выполните отчет Report_SQL из предыдущего поста на Report Server. Рис. 1 Не отходя от кассы, экспортните отчет в какой-нибудь другой формат: Рис. 2 Посмотрите, что осело в ReportServer..ExecutionLog. Кстати, теперь это не таблица, а вьюшка. А еще лучше посмотрите вьюху ExecutionLog3:
Скрипт 1 Рис. 3 Обратите внимание, что во втором случае (первая запись) время выполнения датасетов нулевое, а время процессинга отчета близкое к нулю по сравнению с первой записью. Фактически - это просто время выборки данных из вышеупомянутых таблиц ReportServerTempDB без какой-либо дополнительной умственной работы. Что означает, что при экспорте в Excel была использована закэшированная версия отчета, оставшаяся после первого выполнения, на что недвусмысленно указывает поле Source. Источником при втором выполнении является Session (Cache), т.е. данные текущей сессии, в отличие от живого выполнения в первом разе. Выполните скрипт
Скрипт 2 Рис. 4 Это лежит в ReportServerTempDB закэшированный отчет.
Скрипт 3
Я сознательно опустил некоторые поля, т.к. опасался, что ввиду малой ширины страниц весь результат сюда не влезет. В частности, CreatedDate и.ExpirationDate для снэпшота, т.к. они совпадают с CreationTime и Expiration для сессии. Очевидно, что Expiration более или менее = CreationTime + Timeout. SessionTimeout для сессии берется из конфигурационных настроек сервера отчетности в таблице ReportServer.. ConfigurationInfo. Их можно в ней править: Рис. 5 Поскольку это кэширование уровня сессии, то для каждой сессии Reporting Services создается свой кэш. Информация по открытым сессиям хранится в таблице ReportServerTempDB.dbo.SessionData. Сейчас там видна всего одна текущая сессия:
Рис. 6 Сейчас я откроую браузер на хосте, зайду с него на сайт Reporting Services на виртуалке и выполню тот же самый отчет. Вот, что мы наблюдаем в таблице SessionData теперь: Рис. 7 Появилась новая строчка с новым SessionID и, соответственно, со своим SnapshotID, то есть со своим кэшем - сравните с Рис. 4 и Скрипт 3, где SnapshotID был везде один, потому что там была всего одна сессия. Только не нужно пытаться сопоставить между собой их значения – поскольку статья писалась урывками, то и SessionID, и SnapshotID на рис.6-7 уже совсем другие. В идеале, конечно, можно считать, что SessionID = yaxejvjrsueha4uoqls2cl55, это wqsaa43v0qve3sev2ornvpi5, а SnapshotID = DAEB8C62-CD39-402D-BEBE-12BCE2015989, это 50AB9B27-6C62-4FC7-A7CC-9BAE9897976C. По истечении сессии будут запущены хранимые процедуры, которые приберут проэкспайрившуюся сессию вместе с кэшем отчета из ReportServerTempDB. Автор: Алексей Шуленин |