I recently came across a couple databases that didn’t have any recovery model set. Gotta love vendors. Anyway, I know that turning on the checksum recovery model doesn’t put a checksum on each page. Rather, it’s written the next time the page is written to disk. This can lead to a gap where some pages never get a checksum written to them. Since there’s no “DBCC CREATE_CHECKSUM” command we need to look for another way to do it to write the checksum for tables that aren’t modified often.
Verifying Checksum
Before we do anything else we need to know how to verify whether or not there’s a checksum on the page. To do that I used the method Colleen Morrow used when she discussed switching from torn page to checksum recovery models. As she shows, the checksum is stored in the m_tornBits field of the page header instead of a separate field. We’ll be using that to confirm when the checksum is written.
Setup
We’re going to create a database, modify the recovery model and create a clustered table. Finally, we’re going to insert some data and do a checkpoint to make sure all the data is written to disk.
CREATE DATABASE ChecksumTest; GO ALTER DATABASE ChecksumTest SET PAGE_VERIFY NONE; GO USE ChecksumTest CREATE TABLE dbo. clusteredTable ( id INT IDENTITY ( 1,1 ) NOT NULL, name VARCHAR (255) NOT NULL, CONSTRAINT PK_ClusteredTable PRIMARY KEY (id ) ); INSERT INTO dbo. clusteredTable SELECT TOP 1000 b. name FROM sys. objects a CROSS APPLY sys. objects b ; GO CHECKPOINT GO
Now that we have the data there we should confirm that there isn’t a checksum on the page already. First we’ll set trace flag 3604 so we can see the output of one of the commands in the query window then we’ll use DBCC IND to see what pages are on the table. Lastly, we’ll use DBCC PAGE to look at the page itself.
DBCC TRACEON (3604 ); GO DBCC IND ('ChecksumTest' ,'clusteredTable', 1); GO /* DBCC IND Results: PageFID PagePID IAMFID IAMPID ObjectID IndexID PartitionNumber PartitionID iam_chain_type PageType IndexLevel NextPageFID NextPagePID PrevPageFID PrevPagePID ------- ----------- ------ ----------- ----------- ----------- --------------- -------------------- -------------------- -------- ---------- ----------- ----------- ----------- ----------- 1 154 NULL NULL 2105058535 1 1 72057594038779904 In-row data 10 NULL 0 0 0 0 1 153 1 154 2105058535 1 1 72057594038779904 In-row data 1 0 1 156 0 0 1 155 1 154 2105058535 1 1 72057594038779904 In-row data 2 1 0 0 0 0 1 156 1 154 2105058535 1 1 72057594038779904 In-row data 1 0 1 157 1 153 1 157 1 154 2105058535 1 1 72057594038779904 In-row data 1 0 1 158 1 156 1 158 1 154 2105058535 1 1 72057594038779904 In-row data 1 0 0 0 1 157 (6 row(s) affected) */ --Picking a random page type 1 row from above DBCC PAGE (ChecksumTest, 1,156 ,1) GO
And here is the relevent part of the results from the DBCC PAGE call:
PAGE: (1:156) <snip> PAGE HEADER: Page @0x0000000085C2C000 m_pageId = (1:156) m_headerVersion = 1 m_type = 1 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x0 m_objId (AllocUnitId.idObj) = 27 m_indexId (AllocUnitId.idInd) = 256 Metadata: AllocUnitId = 72057594039697408 Metadata: PartitionId = 72057594038779904 Metadata: IndexId = 1 Metadata: ObjectId = 2105058535 m_prevPage = (1:153) m_nextPage = (1:157) pminlen = 8 m_slotCnt = 313 m_freeCnt = 13 m_freeData = 7553 m_reservedCnt = 0 m_lsn = (21:184:538) m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 m_tornBits = 0
Notice that the m_tornBits value is 0 meaning there’s no page recovery written to this page.
Write that Checksum
Qe’re going to enable the checksum recovery model and see what we can to to get a checksum written to the page. First we’re going to try an index reorg. We’re also going to do a checkpoint after the reorg just to make absolutely sure that there’s nothing just floating around in memory.
ALTER DATABASE ChecksumTest SET PAGE_VERIFY CHECKSUM GO ALTER INDEX PK_ClusteredTable ON clusteredTable REORGANIZE GO CHECKPOINT GO DBCC IND ('ChecksumTest' ,'clusteredTable', 1); GO --The output is the same as above so we check the same page. DBCC PAGE (ChecksumTest, 1,156 ,1) GO
And that same section of DBCC PAGE from above:
PAGE: (1:156) PAGE HEADER: Page @0x0000000085C2C000 m_pageId = (1:156) m_headerVersion = 1 m_type = 1 m_typeFlagBits = 0x4 m_level = 0 m_flagBits = 0x0 m_objId (AllocUnitId.idObj) = 27 m_indexId (AllocUnitId.idInd) = 256 Metadata: AllocUnitId = 72057594039697408 Metadata: PartitionId = 72057594038779904 Metadata: IndexId = 1 Metadata: ObjectId = 2105058535 m_prevPage = (1:153) m_nextPage = (1:157) pminlen = 8 m_slotCnt = 313 m_freeCnt = 13 m_freeData = 7553 m_reservedCnt = 0 m_lsn = (21:184:538) m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 m_tornBits = 0
m_tornBits is still 0 so that didn’t do it. There may be cases where it does, such as if the pages are actually out of order, but this does confirm it won’t always happen. Let’s see what happens when we do a rebuild instead of a reorg. Again, we’re doing a reindex just to make sure everything gets to disk and verify the page numbers with DBCC IND.
ALTER INDEX PK_ClusteredTable ON clusteredTable REBUILD GO CHECKPOINT GO DBCC IND ('ChecksumTest' ,'clusteredTable', 1); GO
If we look at the output of DBCC IND this time we see a different output:
PageFID PagePID IAMFID IAMPID ObjectID IndexID PartitionNumber PartitionID iam_chain_type PageType IndexLevel NextPageFID NextPagePID PrevPageFID PrevPagePID ------- ----------- ------ ----------- ----------- ----------- --------------- -------------------- -------------------- -------- ---------- ----------- ----------- ----------- ----------- 1 168 NULL NULL 2105058535 1 1 72057594038845440 In-row data 10 NULL 0 0 0 0 1 159 1 168 2105058535 1 1 72057594038845440 In-row data 1 0 1 169 0 0 1 169 1 168 2105058535 1 1 72057594038845440 In-row data 1 0 1 171 1 159 1 170 1 168 2105058535 1 1 72057594038845440 In-row data 2 1 0 0 0 0 1 171 1 168 2105058535 1 1 72057594038845440 In-row data 1 0 1 172 1 169 1 172 1 168 2105058535 1 1 72057594038845440 In-row data 1 0 0 0 1 171
All the page numbers are different. This is an indication we’re probably going to see a checksum. Again, picking a random page with a page type of 1:
DBCC PAGE (ChecksumTest, 1,171 ,1) GO
We now see that m_tornBits has a value indicating that the checksum has been written.
PAGE: (1:171) <snip> PAGE HEADER: Page @0x0000000085AA0000 m_pageId = (1:171) m_headerVersion = 1 m_type = 1 m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x200 m_objId (AllocUnitId.idObj) = 28 m_indexId (AllocUnitId.idInd) = 256 Metadata: AllocUnitId = 72057594039762944 Metadata: PartitionId = 72057594038845440 Metadata: IndexId = 1 Metadata: ObjectId = 2105058535 m_prevPage = (1:169) m_nextPage = (1:172) pminlen = 8 m_slotCnt = 303 m_freeCnt = 23 m_freeData = 7563 m_reservedCnt = 0 m_lsn = (22:16:48) m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 m_tornBits = 1602034681
Conclusion
This still isn’t a low-intensity way to get checksums written for each page. It is something that can be done an index at a time that could have a side benefit as well. Care should be taken when using this, such as doing it online where available or doing it offline, but it’s a useful tool to make sure that after switching to full recovery model that the checksum gets written to little modified pages.
The post Writing Checksums by Reindexing appeared first on FradenSQL.