SideBar    The Logical Puzzle

Life is full of incorrect assumptions, many of which concern things that we take for granted and never bother to check, just because they seem so trivial, or because we make wrong conclusions based on our observations and understanding of reality, or because we misinterpret other explanations. Try answering the following questions before you read on, and when you’re done reading the article, answer them again: When you use the Read Uncommitted isolation (e.g., when specifying the NOLOCK hint) to query data, what would change in SQL Server’s treatment of SELECT queries compared with the default Read Committed isolation? When you query data using the Read Uncommitted or Read Committed isolation, can you get the same row multiple times? When you query data using the Read Uncommitted or Read Committed isolation, is it possible that your query will skip a row? When you examine a query’s execution plan and see an Index Scan or Clustered Index Scan operator with Ordered: False, how will SQL Server scan the data?

In this article, I’ll try to shed some light on the way reads behave in different scenarios and the consistency level you should expect in each scenario. Most of this article’s findings are fairly recent—in the past year or so— and my answers to the questions above before those findings were wrong or incomplete. I found myself surprised by the true answers to these questions (at least true to the best of my current understanding), and my current understanding led me to revisit my past choices and recommendations regarding reading data. I described some of these findings in my blog, but here I’ll provide a more complete picture, as well as some new findings. But first, I’ll set some groundwork with a few important fundamentals.

Some Important Fundamentals
Bear with me if you’re already familiar with the concepts that follow. I believe they’re integral to understanding this article’s contents.

Heap table. A heap table is an organization of a table with no clustered index. SQL Server maps heap data with one or more Index Allocation Map (IAM) pages that map the table’s data in file order. No logical ordering is enforced in a heap. When a new row is inserted into a heap, SQL Server uses a special type of page called Page Free Space (PFS) to locate free space in which to insert the new row. When you update a variable-length column in a row in a heap with a longer value such that the row doesn’t have room to expand in the original page, SQL Server will move the row to another page, leaving a forwarding pointer in the row’s original location, pointing to the row’s new address. It’s important to note that neither inserts nor updates can cause page splits (which I’ll describe in a moment) in a heap.

Clustered table. A clustered table is an organization of a table with a clustered index in a B-Tree data structure. For the purpose of this discussion, I’ll focus on the leaf level of the index, which contains the full data rows. The pages in the leaf level are organized in a doubly linked list, maintaining logical index key ordering. Besides the linked list, SQL Server also maps the leaf level’s data with IAM pages in file order. The logical order of the pages maintained by the linked list (index key order) can be different from the order of the pages in the files. The discrepancy between index order of pages and file order of pages is called logical fragmentation. This fragmentation is measured as the percentage of out-of-order pages as you scan the pages in index order. An out-of-order page is one that appears in the file before the page that points to it in the linked list.

Nonclustered index. Like a clustered index, a nonclustered index is organized as a B-Tree. The leaf level contains as many rows as the number of rows in the table, each with a copy of a subset of columns from the corresponding data row, plus a row locator that’s used to locate the full data row. The index leaf level organizes the data in a doubly linked list, maintaining logical index key ordering. SQL Server also maps the nonclustered index leaf level’s data with IAM pages in file order. The aforementioned fragmentation is also relevant to nonclustered indexes.

Page splits. When a new row is inserted into an index (clustered or nonclustered), SQL Server identifies the target page based on the new row’s index key order. If the target page has no room to accommodate the new row, SQL Server will split the page—that is, it will allocate a new page (before or after the current page in the file or in another file), move half the rows from the original page to the new one, insert the row to one of the two pages depending on the index key order of the new row, and update the pointers in the linked list to preserve logical index key ordering among the pages. The only exception to this rule is when the new row has a higher index key value than the highest existing key, in which case SQL Server won’t move half the rows from the original page to the new one. A similar split process will take place if a row is expanded as a result of an update to a variable-length column when there’s no room for the row to expand in the page.

Allocation-order scan. This type of scan can be used to scan heap or index (clustered and nonclustered) data in file order based on IAM pages. With heap data, that’s the only type of scan that can be used. With index data, that’s one of the two possible alternatives. The performance of allocation- order scans isn’t affected by logical fragmentation because this scan is done in file order, not in index order. However, the performance of allocation-order scans is affected by file system fragmentation of the database data files. SQL Server Enterprise Edition supports Advanced Scanning (aka merry-go-round scans), which is applicable only to allocation-order scans (not to index-order scans, which I’ll describe next). Advanced scanning allows a request for an allocation-order scan to piggyback an already-running allocation-order scan, and when both processes being served by the same scan are done, the one that joined later will scan the remaining data (before the point it joined the scan).

Index-order scan. This type of scan can be used to scan index data in logical index key order—that is, scanning the leaf-level pages by following the linked list either from head to tail (forward scan) or from tail to head (backward scan). When this type of scan is used, the data is returned to the next operator in the plan (or to the caller, if that’s the last operator being processed) in logical index key order. The performance of index-order scans is affected by logical fragmentation since out-of-order pages require the disk arm to go back and forth. With zero fragmentation, the performance of an index-order scan should be similar to that of an allocation-order scan: The higher the fragmentation, the worse the performance of an index-order scan compared with an allocation-order scan—up to several times slower. The only exception is when the table is very small (up to 64 pages); an allocation-order scan can be more expensive than an index-order scan because of some overhead involved with this type of scan.

Consistency Problems with Read Uncommitted
The use of the Read Uncommitted isolation level (e.g., with the NOLOCK hint) is a common practice in attempt to overcome blocking problems under the default Read Committed isolation. However, with Read Uncommitted, there are several changes in the way SQL Server handles reads that you might not be aware of. This month and next, I’ll describe read-consistency problems under the Read Uncommitted isolation, resulting from inserts running while processes are reading data. In a later article, I’ll cover read-consistency problems under both Read Uncommitted and Read Committed isolations resulting from updates running while processes are reading data.

A common perception programmers have regarding behavioral changes of reads when working with Read Uncommitted is that the only change is that SQL Server won’t request shared locks when reading data. Therefore, readers can get “dirty data”—that is, uncommitted changes. But the reality is that there are other behavioral changes of readers besides not acquiring shared locks that can result in reading the same row multiple times or skipping rows that existed when the read started.

Continued on page 2.

   Prev. page   [1] 2     next page



You must log on before posting a comment.

If you don't have a username & password, please register now.

Reader Comments

Hi Itzik,

I am suprised with the fact you find, it sounds make sense. I would like to know whether page split might happen where the primary key is an identity column such as in sales.salesorderheader.

Thank you.

Kasim Wirama

[Reply: Hi Kasim,

As I mentioned in the article, when the new row has a higher index key value than the highest existing key, SQL Server won’t split the page. Still, the fact that there are no splits in indexes on ever-increasing keys, you might still get splits in other indexes.

Itzik]

wirama@cbn.net.id

Article Rating 5 out of 5

Hi Itzik,

One more thing, having read your article, I conclude that when Ordered is true, it indicates index-order scan, and when Ordered is false, it probably indicates either index-order scan or allocation-order scan, is my conclusion correct?

Thank you.

Kasim Wirama

[Reply:

Yes, you understood correctly.

Itzik]

wirama@cbn.net.id

Article Rating 5 out of 5

Hi Itzik,

Regarding the Logical Fragmentation : it is the pages in the leaf level are organized in a doubly linked list, maintaining logical index key ordering. Besides the linked list, SQL Server also maps the leaf level’s data with IAM pages in file order. The logical order of the pages maintained by the linked list (index key order) can be different from the order of the pages in the files. The discrepancy between index order of pages and file order of pages is called logical fragmentation.

Does SQL Server maintain altogether a separate file to Map the leaf level Page with IAM Page ? As per my understanding then it should refer the physical address of the linked Page from the referencial Page (Page which holds the address of the linked Page) and put it into the file. And there is all probability for the out of order Page appearance. In that scenario how the Index defragmentation facility of SQL Server comes into play ?

I am bit of confused. Need your help to carify.

Thanks & Warm Regards, Arindam Ganguly.

arindamg

Article Rating 4 out of 5

Check out this page with Firefox 2.0; I see, under Rate this Article, the scale (left to right) 1 1 3 1 5.

If this is intentional given the article's topic, it's hilarious. I rated it a 1, and by 1, I mean 4. At least I think that's what I rated.

rick.white

Article Rating 4 out of 5

Hi rick.white, Ah, you noticed our new article rating system ;-) Actually this is a bug, and I'll report it to our Web team ASAP. (I'll let you know if it's database related :-) Thanks for pointing it out--and for being good-humored about it. Anne Grubb, Web site strategic editor, SQL Server Magazine

AnneG_editor

Article Rating 5 out of 5

 
 

ADS BY GOOGLE