Кэширование уровня сессии в 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:
select top 2 ItemPath, RequestType, Format, TimeDataRetrieval, TimeProcessing, TimeRendering, Source, ByteCount, TimeStart,TimeEnd
from ReportServer.dbo.ExecutionLog3 order by TimeStart desc
Скрипт1
Рис.3
Обратите внимание, что во втором случае (первая запись) время выполнения датасетов нулевое, а время процессинга отчета близкое к нулю по сравнению с первой записью. Фактически - это просто время выборки данных из вышеупомянутых таблиц ReportServerTempDB без какой-либо дополнительной умственной работы. Что означает, что при экспорте в Excel была использована закэшированная версия отчета, оставшаяся после первого выполнения, на что недвусмысленно указывает поле Source. Источником при втором выполнении является Session (Cache), т.е. данные текущей сессии, в отличие от живого выполнения в первом разе. Выполните скрипт
select * from ReportServerTempDB.dbo.SegmentedChunk
select * from ReportServerTempDB.dbo.ChunkSegmentMapping
select * from ReportServerTempDB.dbo.Segment
select * from ReportServerTempDB.dbo.SnapshotData
Скрипт2
Рис.4
Это лежит в ReportServerTempDB закэшированный отчет.
select
sess.SessionID, sess.CreationTime, sess.Expiration, sess.IsPermanentSnapshot, sess.ReportPath, sess.Timeout,
sd.SnapshotDataID, sd.CreatedDate, sd.ExpirationDate, sd.QueryParams, sd.EffectiveParams, sd.PageCount, sd.IsCached,
sc.ChunkId, sc.ChunkName, sc.Version,
csm.StartByte, csm.LogicalByteCount, csm.ActualByteCount,
s.SegmentId, s.Content from ReportServerTempDB.dbo.SessionData sess
join ReportServerTempDB.dbo.SnapshotData sd on sess.SnapshotDataID = sd.SnapshotDataID
join ReportServerTempDB.dbo.SegmentedChunk sc on sd.SnapshotDataID = sc.SnapshotDataID
join ReportServerTempDB.dbo.ChunkSegmentMapping csm on sc.ChunkId = csm.ChunkId
join ReportServerTempDB.dbo.Segment s on csm.SegmentId = s.SegmentId
Скрипт 3
SessionID |
CreationTime |
Expiration |
IsPermanentSnapshot |
ReportPath |
Timeout |
SnapshotDataID |
IsCached |
ChunkId |
ChunkName |
StartByte |
LogicalByteCount |
ActualByteCount |
SegmentId |
wqsaa43v0qve3sev2ornvpi5 |
09/04/2010 16:37:27 |
09/04/2010 16:48:07 |
0 |
/Report_SQL |
600 |
50AB9B27-6C62-4FC7-A7CC-9BAE9897976C |
0 |
E3FCBEA9-D443-DF11-A63F-00155D00011D |
GroupTree |
0 |
20848 |
6059 |
E6FCBEA9-D443-DF11-A63F-00155D00011D |
wqsaa43v0qve3sev2ornvpi5 |
09/04/2010 16:37:27 |
09/04/2010 16:48:07 |
0 |
/Report_SQL |
600 |
50AB9B27-6C62-4FC7-A7CC-9BAE9897976C |
0 |
E4FCBEA9-D443-DF11-A63F-00155D00011D |
Metadata |
0 |
1524 |
1180 |
E5FCBEA9-D443-DF11-A63F-00155D00011D |
wqsaa43v0qve3sev2ornvpi5 |
09/04/2010 16:37:27 |
09/04/2010 16:48:07 |
0 |
/Report_SQL |
600 |
50AB9B27-6C62-4FC7-A7CC-9BAE9897976C |
0 |
DFFCBEA9-D443-DF11-A63F-00155D00011D |
DataChunkx4 |
0 |
20691 |
4052 |
E0FCBEA9-D443-DF11-A63F-00155D00011D |
wqsaa43v0qve3sev2ornvpi5 |
09/04/2010 16:37:27 |
09/04/2010 16:48:07 |
0 |
/Report_SQL |
600 |
50AB9B27-6C62-4FC7-A7CC-9BAE9897976C |
0 |
E1FCBEA9-D443-DF11-A63F-00155D00011D |
PaginationInfo |
0 |
72 |
174 |
E2FCBEA9-D443-DF11-A63F-00155D00011D |
Я сознательно опустил некоторые поля, т.к. опасался, что ввиду малой ширины блоговских страниц весь результат сюда не влезет. В частности, CreatedDate и.ExpirationDate для снэпшота, т.к. они совпадают с CreationTime и Expiration для сессии. Очевидно, что Expiration более или менее = CreationTime + Timeout. SessionTimeout для сессии берется из конфигурационных настроек сервера отчетности в таблице ReportServer.. ConfigurationInfo. Их можно в ней править:
Рис.5
Поскольку это кэширование уровня сессии, то для каждой сессии Reporting Services создается свой кэш. Информация по открытым сессиям хранится в таблице ReportServerTempDB.dbo.SessionData. Сейчас там видна всего одна текущая сессия:
select SessionID, SnapshotDataID, IsPermanentSnapshot, ReportPath, EffectiveParams, CreationTime, Timeout, Expiration from 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.
Алексей Шуленин
Comments
- Anonymous
November 07, 2013
Спасибо, приятель. Хоть что-то прояснилось. Инфы по топку, действительно не найдёшь просто так, с наскоку, тока на буржуйских сайтах.