RCSI and LOB data – how tempdb version store is used?
Row Version Store
We know RCSI (Read Committed Snapshot Isolation), as well as SI (Snapshot Isolation) use row versioning to store previous verions of rows in part of tempdb called “row version store”. If you are using default isolation level RC (Read Committed) and think that you are not using row versioning and version store at all – you are wrong! From SQL 2005 version store is used to store previous row versions for tables with triggers (“deleted” special pseudo-table rows are there) and online index (re)builds. Row versions which are not needed anymore are cleaned up periodically from tempdb.
Query the space version store uses within the tempdb:
1 2 |
SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs |
LOB (BLOB, CLOB) and row versioning
The question arised from my colleague SQL Master Denny Cherry (@mrdenny), are also BLOBs previous versions stored in tempdb? By LOB we think of large data types stored in separate allocation unit called LOB_DATA. For example “MAX” types (VARCHAR(MAX), VARBINARY(MAX) NVARCHAR(MAX)), XML, plus old deprecated types like TEXT and IMAGE. The short answer is: yes, old versions of LOBs are also stored in row version store! Let us prove it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
-- RCSI and BLOB data - how tempdb version store is used -- Vedran Kesegic 2015 create database BlobTestDb; GO alter database BlobTestDb set READ_COMMITTED_SNAPSHOT ON; use BlobTestDb; GO -- Create table with BLOB data create table dbo.TestBlob ( Id int identity primary key, BlobValue nvarchar(MAX) DEFAULT (REPLICATE(CONVERT(NVARCHAR(MAX), 'Huge!'), 200000)) -- about 2MB ) GO -- version store allocation: 0 SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs insert into TestBlob(BlobValue) values(REPLICATE(CONVERT(NVARCHAR(MAX), 'Huge!'), 50000)) -- about 500KB -- version store allocation: 0, because INSERT DOES NOT get copied into tempdb! SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs GO update TestBlob set BlobValue = 'nothing' output DATALENGTH(deleted.BlobValue)/1000 as OldBlob_KB, -- 500 KB DATALENGTH(inserted.BlobValue)/1000 as NewBlob_KB -- 0 KB -- version store allocation: 1024 KB (double of old value!) SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs GO update TestBlob set BlobValue = DEFAULT output DATALENGTH(deleted.BlobValue)/1000 as OldBlob_KB, -- 0 KB DATALENGTH(inserted.BlobValue)/1000 as NewBlob_KB -- 2000 KB -- version store allocation: 1088 KB (not yet cleaned up!) SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs GO update TestBlob set BlobValue = 'nothing' output DATALENGTH(deleted.BlobValue)/1000 as OldBlob_KB, -- 2000 KB DATALENGTH(inserted.BlobValue)/1000 as NewBlob_KB -- 0 KB -- version store allocation: 4992 KB (double of old value + 1000 from previus not cleaned versions) SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs -- Wait some time... -- Zaro again! (Version store cleanup occured) SELECT VersionStoreSize_KB = fs.version_store_reserved_page_count*8 FROM sys.dm_db_file_space_usage fs -- Cleanup use master; drop database BlobTestDb --drop table dbo.TestBlob |
Summary
SQL Server uses version store in tempdb to store old version of the row, not just IN_ROW_DATA, but also LOB_DATA and ROW_OVERFLOW_DATA allocation units. It is used for DELETE and UPDATE statements, but NOT for INSERT (since there is no “old” row to store when you insert a new one). Strangely, version store allocates twice the size of old (deleted/updated) version of the row. Therefore, beware of UPDATE and DELETEs of the LOBs in RCSI isolation, they might take a huge amount of version store space!
In my case it is always showing NULL for space usage dm_db_file_space_usage sql server 2012.
Have you turned on READ_COMMITTED_SNAPSHOT (RCSI) for the current database?
select d.name, d.compatibility_level,
d.snapshot_isolation_state_desc, d.is_read_committed_snapshot_on
from sys.databases d
where database_id=DB_ID()
Try running the complete script in one go, without any changes.