Share via


Кэширование уровня сессии в 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.

image

Рис.1

Не отходя от кассы, экспортните отчет в какой-нибудь другой формат:

image

Рис.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

image

Рис.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

image

Рис.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. Их можно в ней править:

image

Рис.5

Поскольку это кэширование уровня сессии, то для каждой сессии Reporting Services создается свой кэш. Информация по открытым сессиям хранится в таблице ReportServerTempDB.dbo.SessionData. Сейчас там видна всего одна текущая сессия:

select SessionID, SnapshotDataID, IsPermanentSnapshot, ReportPath, EffectiveParams, CreationTime, Timeout, Expiration from ReportServerTempDB.dbo.SessionData

image

Рис.6

Сейчас я откроую браузер на хосте, зайду с него на сайт Reporting Services на виртуалке и выполню тот же самый отчет. Вот, что мы наблюдаем в таблице SessionData теперь:

image

Рис.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.

 

Алексей Шуленин