Expert SQL Server Transactions and Locking: Concurrency Internals for SQL Server Practitioners
()
About this ebook
SQL Server’s Concurrency Model is one of the least understood parts of the SQL Server Database Engine. Almost every SQL Server system experiences hard-to-explain concurrency and blocking issues, and it can be extremely confusing to solve those issues without a base of knowledge in the internals of the Engine. While confusing from the outside, the SQL Server Concurrency Model is based on several well-defined principles that are covered in this book.
Understanding the internals surrounding SQL Server’s Concurrency Model helps you build high-throughput systems in multi-user environments. This book guides you through the Concurrency Model and elaborates how SQL Server supports transactional consistency in the databases. The book covers all versions of SQL Server, including Microsoft Azure SQL Database, and it includes coverage of new technologies such as In-Memory OLTP and Columnstore Indexes.
What You'll Learn
- Know how transaction isolation levels affect locking behavior and concurrency
- Troubleshoot and address blocking issues and deadlocks
- Provide required data consistency while minimizing concurrency issues
- Design efficient transaction strategies that lead to scalable code
- Reduce concurrency problems through good schema design
- Understand concurrency models for In-Memory OLTP and Columnstore Indexes
- Reduce blocking during index maintenance, batch data load, and similar tasks
Who This Book Is For
SQL Server developers, database administrators, and application architects who are developing highly-concurrent applications. The book is for anyone interested in the technical aspects of creating and troubleshooting high-throughput systems that respond swiftly to user requests.
Related to Expert SQL Server Transactions and Locking
Related ebooks
Pro SQL Server 2019 Wait Statistics: A Practical Guide to Analyzing Performance in SQL Server Rating: 0 out of 5 stars0 ratingsSql : The Ultimate Beginner to Advanced Guide To Master SQL Quickly with Step-by-Step Practical Examples Rating: 0 out of 5 stars0 ratingsIntroducing the MySQL 8 Document Store Rating: 0 out of 5 stars0 ratingsMySQL Concurrency: Locking and Transactions for MySQL Developers and DBAs Rating: 0 out of 5 stars0 ratingsSecuring SQL Server: Protecting Your Database from Attackers Rating: 0 out of 5 stars0 ratingsSQLite Database Programming for Xamarin: Cross-platform C# database development for iOS and Android using SQLite.XM Rating: 0 out of 5 stars0 ratingsOracle SQL Revealed: Executing Business Logic in the Database Engine Rating: 0 out of 5 stars0 ratingsPro Oracle SQL Development: Best Practices for Writing Advanced Queries Rating: 0 out of 5 stars0 ratingsHigh Performance SQL Server: Consistent Response for Mission-Critical Applications Rating: 0 out of 5 stars0 ratingsPractical Oracle SQL: Mastering the Full Power of Oracle Database Rating: 0 out of 5 stars0 ratingsIntroducing InnoDB Cluster: Learning the MySQL High Availability Stack Rating: 0 out of 5 stars0 ratingsDynamic SQL: Applications, Performance, and Security in Microsoft SQL Server Rating: 0 out of 5 stars0 ratingsBeginning jOOQ: Learn to Write Efficient and Effective Java-Based SQL Database Operations Rating: 0 out of 5 stars0 ratingsPro Azure Administration and Automation: A Comprehensive Guide to Successful Cloud Management Rating: 0 out of 5 stars0 ratingsMySQL Connector/Python Revealed: SQL and NoSQL Data Storage Using MySQL for Python Programmers Rating: 0 out of 5 stars0 ratingsModern API Design with ASP.NET Core 2: Building Cross-Platform Back-End Systems Rating: 0 out of 5 stars0 ratingsORACLE PL/SQL Interview Questions You'll Most Likely Be Asked Rating: 5 out of 5 stars5/5SQL: For Beginners: Your Guide To Easily Learn SQL Programming in 7 Days Rating: 5 out of 5 stars5/5Beginning Oracle SQL for Oracle Database 18c: From Novice to Professional Rating: 0 out of 5 stars0 ratingsBeginning Backup and Restore for SQL Server: Data Loss Management and Prevention Techniques Rating: 0 out of 5 stars0 ratingsSQL Programming & Database Management For Noobee Rating: 0 out of 5 stars0 ratingsSQL Interview Questions: A complete question bank to crack your ANN SQL interview with real-time examples Rating: 0 out of 5 stars0 ratingsSQL Server: Tips and Tricks - 1 Rating: 5 out of 5 stars5/5Introducing Delphi ORM: Object Relational Mapping Using TMS Aurelius Rating: 0 out of 5 stars0 ratingsSQL 101 Crash Course: Comprehensive Guide to SQL Fundamentals and Practical Applications Rating: 5 out of 5 stars5/5Expert Oracle Database Architecture: Techniques and Solutions for High Performance and Productivity Rating: 0 out of 5 stars0 ratingsAdvanced ASP.NET Core 3 Security: Understanding Hacks, Attacks, and Vulnerabilities to Secure Your Website Rating: 0 out of 5 stars0 ratingsSelenium Framework Design in Keyword-Driven Testing: Automate Your Test Using Selenium and Appium Rating: 0 out of 5 stars0 ratings
Databases For You
100+ SQL Queries T-SQL for Microsoft SQL Server Rating: 4 out of 5 stars4/5Practical Data Analysis Rating: 4 out of 5 stars4/5SQL QuickStart Guide: The Simplified Beginner's Guide to Managing, Analyzing, and Manipulating Data With SQL Rating: 4 out of 5 stars4/5Grokking Algorithms: An illustrated guide for programmers and other curious people Rating: 4 out of 5 stars4/5Learn SQL Server Administration in a Month of Lunches Rating: 0 out of 5 stars0 ratingsLearn SQL in 24 Hours Rating: 5 out of 5 stars5/5Blockchain Basics: A Non-Technical Introduction in 25 Steps Rating: 5 out of 5 stars5/5CompTIA DataSys+ Study Guide: Exam DS0-001 Rating: 0 out of 5 stars0 ratingsData Governance: How to Design, Deploy and Sustain an Effective Data Governance Program Rating: 4 out of 5 stars4/5Oracle DBA Mentor: Succeeding as an Oracle Database Administrator Rating: 0 out of 5 stars0 ratingsAccess 2010 All-in-One For Dummies Rating: 4 out of 5 stars4/5Access 2019 For Dummies Rating: 0 out of 5 stars0 ratingsBuilding a Scalable Data Warehouse with Data Vault 2.0 Rating: 4 out of 5 stars4/5Behind Every Good Decision: How Anyone Can Use Business Analytics to Turn Data into Profitable Insight Rating: 5 out of 5 stars5/5The Visual Imperative: Creating a Visual Culture of Data Discovery Rating: 4 out of 5 stars4/5Data Mining: Concepts and Techniques Rating: 4 out of 5 stars4/5Beginning Microsoft SQL Server 2012 Programming Rating: 1 out of 5 stars1/5Relational Database Design and Implementation Rating: 5 out of 5 stars5/5Business Intelligence Guidebook: From Data Integration to Analytics Rating: 4 out of 5 stars4/5The Data and Analytics Playbook: Proven Methods for Governed Data and Analytic Quality Rating: 5 out of 5 stars5/5Data Modeling Essentials Rating: 4 out of 5 stars4/5SQL Clearly Explained Rating: 5 out of 5 stars5/5The SQL Workshop: Learn to create, manipulate and secure data and manage relational databases with SQL Rating: 0 out of 5 stars0 ratingsDatabase Design: Know It All Rating: 5 out of 5 stars5/5Beginning Microsoft Power BI: A Practical Guide to Self-Service Data Analytics Rating: 0 out of 5 stars0 ratingsServerless Architectures on AWS, Second Edition Rating: 5 out of 5 stars5/5Python and SQLite Development Rating: 0 out of 5 stars0 ratings
Reviews for Expert SQL Server Transactions and Locking
0 ratings0 reviews
Book preview
Expert SQL Server Transactions and Locking - Dmitri Korotkevitch
© Dmitri Korotkevitch 2018
Dmitri KorotkevitchExpert SQL Server Transactions and Lockinghttps://doi.org/10.1007/978-1-4842-3957-5_1
1. Data Storage and Access Methods
Dmitri Korotkevitch¹
(1)
Land O Lakes, Florida, USA
It is impossible to grasp the SQL Server concurrency model without understanding how SQL Server stores and accesses the data. This knowledge helps you to comprehend various aspects of locking behavior in the system, and it is also essential when troubleshooting concurrency issues.
Nowadays, SQL Server and Microsoft Azure SQL Databases support three different technologies that dictate how data is stored and manipulated in the system. The classic Storage Engine implements row-based storage. This technology persists the data in disk-based tables, combining all columns from a table together into data rows. The data rows, in turn, reside on 8 KB data pages, each of which may have one or multiple rows.
Starting with SQL Server 2012, you can store data in a columnar format using columnstore indexes. SQL Server splits the data into row groups of up to 1,048,576 rows each. The data in the row group is combined and stored on a per-column rather than a per-row basis. This format is optimized for reporting and analytics queries.
Finally, the In-Memory OLTP Engine, introduced in SQL Server 2014, allows you to define memory-optimized tables, which keep all data entirely in memory. The data rows in memory are linked to the data row chains through the memory pointers. This technology is optimized for heavy OLTP workload.
We will discuss locking behavior in In-Memory OLTP and columnstore indexes later in the book, after we cover the concurrency model of the classic Storage Engine. This knowledge is a cornerstone of understanding how SQL Server behaves in a multi-user environment.
The goal of this chapter is to give a high-level overview of row-based storage in SQL Server. It will explain how SQL Server stores the data in disk-based tables, illustrate the structure of B-Tree indexes, and demonstrate how SQL Server accesses data from them.
You should not consider this chapter as a deep dive into the SQL Server Storage Engine. It should provide, however, enough information to discuss the concurrency model in SQL Server.
Anatomy of a Table
The internal structure of a disk-based table is rather complex and consists of multiple elements and internal objects, as shown in Figure 1-1.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig1_HTML.jpgFigure 1-1
Internal structure of a table
The data in the tables is stored either completely unsorted (those tables are called heap tables or heaps) or sorted according to the value of a clustered index key when a table has such an index defined.
In addition to a single clustered index, every table may have a set of nonclustered indexes. These indexes are separate data structures that store a copy of the data from a table sorted according to index key column(s). For example, if a column was included in three nonclustered indexes, SQL Server would store that data four times—once in a clustered index or heap and in each of the three nonclustered indexes.
You can create either 250 or 999 nonclustered indexes per table, depending on SQL Server version. However, it is clearly not a good idea to create a lot of them due to the overhead they introduce. In addition to storage overhead, SQL Server needs to insert or delete data from each nonclustered index during data modifications. Moreover, the update operation requires SQL Server to modify data in every index in which updated columns were present.
Internally, each index (and heap) consists of one or multiple partitions. Every partition, in a nutshell, is an internal data structure (index or heap) independent from other partitions in the object. SQL Server allows the use of a different partition strategy for every index in the table; however, in most cases, all indexes are partitioned in the same way and aligned with each other.
Note
Every table/index in SQL Server is partitioned. Non-partitioned tables are treated as single-partition tables/indexes internally.
As I already mentioned, the actual data is stored in data rows on 8 KB data pages with 8,060 bytes available to users. The pages that store users’ data may belong to three different categories called allocation units based on the type of data they store.
IN_ROW_DATA allocation unit pages store the main data row objects, which consist of internal attributes and the data from fixed-length columns, such as int, datetime, float, and others. The in-row part of a data row must fit on a single data page and, therefore, cannot exceed 8,060 bytes. The data from variable-length columns, such as (n)varchar(max), (n)varbinary(max), xml, and others, may also be stored in-row in the main row object when it fits into this limit.
In cases when variable-length data does not fit in-row, SQL Server stores it off-row on different data pages, referencing them through in-row pointers. Variable-length data that exceeds 8,000 bytes is stored on LOB_DATA allocation unit data pages (LOB stands for large objects). Otherwise, the data is stored in ROW_OVERFLOW_DATA allocation unit pages.
Let’s look at an example and create a table that contains several fixed- and variable-length columns and insert one row there, as shown in Listing 1-1.
create table dbo.DataRows
(
ID int not null,
ADate datetime not null,
VarCol1 varchar(max),
VarCol2 varchar(5000),
VarCol3 varchar(5000)
);
insert into dbo.DataRows(ID, ADate, VarCol1, VarCol2, VarCol3)
values
(
1
,'1974-08-22'
,replicate(convert(varchar(max),'A'),32000)
,replicate(convert(varchar(max),'B'),5000)
,replicate(convert(varchar(max),'C'),5000)
);
Listing 1-1
Data row storage: Creating the test table
The data from fixed-length columns (ID, ADate) will be stored in-row on an IN_ROW_DATA allocation unit page. The data from VarCol1 column is 32,000 bytes and will be stored on LOB_DATA data pages.
The VarCol2 and VarCol3 columns have 5,000 bytes of data each. SQL Server would keep one of them in-row (it would fit into the 8,060-byte limit) and place the other one on the single ROW_OVERFLOW_DATA page.
Note
Off-row column pointers use 16 or 24 bytes in-row, which counts toward the 8,060 maximum row size. In practice, this may limit the number of columns you can have in a table.
Figure 1-2 illustrates this state.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig2_HTML.jpgFigure 1-2
Data row storage: Data pages after the first INSERT
The sys.dm_db_index_physical_stats data management function is usually used to analyze index fragmentation. It also displays the information about data pages on a per–allocation unit basis.
Listing 1-2 shows the query that returns the information about the dbo.DataRows table.
select
index_id, partition_number, alloc_unit_type_desc
,page_count, record_count, min_record_size_in_bytes
,max_record_size_in_bytes, avg_record_size_in_bytes
from
sys.dm_db_index_physical_stats
(
db_id()
,object_id(N'dbo.DataRows')
,0 /* IndexId = 0 -> Table Heap */
,NULL /* All Partitions */
,'DETAILED'
);
Listing 1-2
Data row storage: Analyzing the table using sys.dm_db_index_physical_stats DMO
Figure 1-3 illustrates the output of the code. As expected, the table has one IN_ROW_DATA, one ROW_OVERFLOW_DATA, and four LOB_DATA pages. The IN_ROW data page has about 2,900 free bytes available.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig3_HTML.jpgFigure 1-3
Data row storage: sys.dm_db_index_physical_stats output after the first INSERT
Let’s insert another row using the code from Listing 1-3.
insert into dbo.DataRows(ID, ADate, VarCol1, VarCol2, VarCol3)
values(2,'2006-09-29','DDDDD','EEEEE','FFFFF');
Listing 1-3
Data row storage: Inserting the second row
All three variable-length columns store five-character strings, and, therefore, the row would fit on the already-allocated IN_ROW_DATA page. Figure 1-4 illustrates data pages at this phase.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig4_HTML.jpgFigure 1-4
Data row storage: Data pages after the second INSERT
You can confirm it by running the code from Listing 1-2 again. Figure 1-5 illustrates the output from the view.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig5_HTML.jpgFigure 1-5
Data row storage: sys.dm_db_index_physical_stats output after the second INSERT
SQL Server logically groups eight pages into 64KB units called extents. There are two types of extents available: mixed extents store data that belongs to different objects, while uniform extents store the data for the same object.
By default, when a new object is created, SQL Server stores the first eight object pages in mixed extents. After that, all subsequent space allocation for that object is done with uniform extents.
Tip
Disabling mixed extents allocation may help to improve tempdb throughput in the system. In SQL Server prior to 2016, you can achieve that by enabling server-level trace flag T1118. This trace flag is not required in SQL Server 2016 and above, where tempdb does not use mixed extents anymore.
SQL Server uses a special kind of pages, called allocation maps, to track extent and page usage in database files. Index Allocation Maps (IAM) pages track extents that belong to an allocation unit on a per-partition basis. Those pages are, in a nutshell, bitmaps, where each bit indicates if the extent belongs to a specific allocation unit from the object partition.
Each IAM page covers about 64,000 extents, or almost 4 GB of data in a data file. For larger files, multiple IAM pages are linked together into IAM chains.
Note
There are many other types of allocation maps used for database management. You can read about them at https://docs.microsoft.com/en-us/sql/relational-databases/pages-and-extents-architecture-guide or in my Pro SQL Server Internals book.
Heap Tables
Heap tables are tables without a clustered index. The data in heap tables is unsorted. SQL Server does not guarantee, nor does it maintain, a sorting order of the data in heap tables.
When you insert data into heap tables, SQL Server tries to fill pages as much as possible, although it does not analyze the actual free space available on a page. It uses another type of allocation map page called Page Free Space (PFS) , which tracks the amount of free space available on the page. This tracking is imprecise, however. SQL Server uses three bits, which indicate if the page is empty, or if it is 1 to 50, 51 to 80, 81 to 95 or above 95 percent full. It is entirely possible that SQL Server would not store a new row on the page even when it has available space.
When you select data from the heap table, SQL Server uses IAM pages to find the pages and extents that belong to the table, processing them based on their order on the IAM pages rather than on the order in which the data was inserted. Figure 1-6 illustrates this point. This operation is shown as Table Scan in the execution plan.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig6_HTML.jpgFigure 1-6
Selecting data from the heap table
When you update the row in the heap table, SQL Server tries to accommodate it on the same page. If there is no free space available, SQL Server moves the new version of the row to another page and replaces the old row with a special 16-byte row called a forwarding pointer. The new version of the row is called a forwarded row. Figure 1-7 illustrates this point.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig7_HTML.jpgFigure 1-7
Forwarding pointers
There are two main reasons why forwarding pointers are used. First, they prevent updates of nonclustered index keys, which reference the row. We will talk about nonclustered indexes in more detail later in this chapter.
In addition, forwarding pointers help minimize the number of duplicated reads; that is, the situation when a single row is read multiple times during the table scan. Let’s look at Figure 1-7 as an example and assume that SQL Server scans the pages in left-to-right order. Let’s further assume that the row in page 3 was modified at the time when SQL Server reads page 4 (after page 3 has already been read). The new version of the row would be moved to page 5, which has yet to be processed. Without forwarding pointers, SQL Server would not know that the old version of the row had already been read, and it would read it again during the page 5 scan. With forwarding pointers, SQL Server skips the forwarded rows—they have a flag in their internal attributes indicating that condition.
Although forwarding pointers help minimize duplicated reads, they introduce additional read operations at the same time. SQL Server follows the forwarding pointers and reads the new versions of the rows at the time it encounters them. That behavior can introduce an excessive number of I/O operations when heap tables are frequently updated and have a large number of forwarded rows.
Note
You can analyze the number of forwarded rows in the table by checking the forwarded_record_count column in the sys.dm_db_index_physical_stats view.
When the size of the forwarded row is reduced by another update, and the data page with the forwarding pointer has enough space to accommodate the updated version of the row, SQL Server may move it back to its original data page and remove the forwarding pointer row. Nevertheless, the only reliable way to get rid of all forwarding pointers is by rebuilding the heap table. You can do that by using an ALTER TABLE REBUILD statement.
Heap tables can be useful in staging environments where you want to import a large amount of data into the system as quickly as possible. Inserting data into heap tables can often be faster than inserting it into tables with clustered indexes. Nevertheless, during a regular workload, tables with clustered indexes usually outperform heap tables as a result of heap tables’ suboptimal space control and extra I/O operations introduced by forwarding pointers.
Note
You can find the scripts that demonstrate forwarding pointers’ overhead and suboptimal space control in heap tables in this book’s companion materials.
Clustered Indexes and B-Trees
A clustered index dictates the physical order of the data in a table, which is sorted according to the clustered index key. The table can have only one clustered index defined.
Let’s assume that you want to create a clustered index on the heap table with the data. As a first step, which is shown in Figure 1-8, SQL Server creates another copy of the data and sorts it based on the value of the clustered key. The data pages are linked in a double-linked list, where every page contains pointers to the next and previous pages in the chain. This list is called the leaf level of the index , and it contains the actual table data.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig8_HTML.jpgFigure 1-8
Clustered index structure: Leaf level
Note
The pages reference each other through page addresses, which consist of two values: file_id in the database and sequential number of the page in the file.
When the leaf level consists of multiple pages, SQL Server starts to build an intermediate level of the index, as shown in Figure 1-9.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig9_HTML.jpgFigure 1-9
Clustered index structure: Intermediate levels
The intermediate level stores one row per each leaf-level page. It stores two pieces of information: the physical address and the minimum value of the index key from the page it references. The only exception is the very first row on the first page, where SQL Server stores NULL rather than the minimum index key value. With such optimization, SQL Server does not need to update non-leaf level rows when you insert the row with the lowest key value in the table.
The pages on the intermediate level are also linked in a double-linked list. SQL Server adds more and more intermediate levels until there is a level that includes just a single page. This level is called the root level, and it becomes the entry point to the index, as shown in Figure 1-10.
Note
This index structure is called a B-Tree Index , which stands for Balanced Tree.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig10_HTML.jpgFigure 1-10
Clustered index structure: Root level
As you can see, the index always has one leaf level, one root level, and zero or more intermediate levels. The only exception is when the index data fits into a single page. In that case, SQL Server does not create the separate root-level page, and the index consists of just the single leaf-level page.
SQL Server always maintains the order of the data in the index, inserting new rows on the data pages to which they belong. In cases when a data page does not have enough free space, SQL Server allocates a new page and places the row there, adjusting pointers in the double-linked page list to maintain a logical sorting order in the index. This operation is called page split and leads to index fragmentation.
Figure 1-11 illustrates this condition. When Original Page does not have enough space to accommodate the new row, SQL Server performs a page split, moving about half of the data from Original Page to New Page, adjusting page pointers afterward.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig11_HTML.jpgFigure 1-11
Leaf-level data pages after page split
A page split may also occur during data modifications. SQL Server does not use forwarding pointers with B-Tree indexes. Instead, when an update cannot be done in-place—for example, during data row increase—SQL Server performs a page split and moves updated and subsequent rows from the page to another page. Nevertheless, the index sorting order is maintained through the page pointers.
SQL Server may read the data from the index in three different ways. The first is an allocation order scan . SQL Server accesses the table data through IAM pages similar to how it does this with heap tables. This method, however, could introduce data consistency phenomena—with page splits, rows may be skipped or read more than once—and, therefore, allocation order scan is rarely used. We will discuss conditions that may lead to allocation order scans later in the book.
The second method is called an ordered scan . Let’s assume that we want to run the SELECT Name FROM dbo.Customers query. All data rows reside on the leaf level of the index, and SQL Server can scan it and return the rows to the client.
SQL Server starts with the root page of the index and reads the first row from there. That row references the intermediate page with the minimum key value from the table. SQL Server reads that page and repeats the process until it finds the first page on the leaf level. Then, SQL Server starts to read rows one by one, moving through the linked list of the pages until all rows have been read. Figure 1-12 illustrates this process.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig12_HTML.jpgFigure 1-12
Ordered index scan
Both allocation order scan and ordered scan are represented as Index Scan operators in the execution plans.
Note
The server can navigate through indexes in both directions, forward and backward. However, SQL Server does not use parallelism during backward index scans.
The last index access method is called index seek . Let’s assume we want to run the following query: SELECT Name FROM dbo.Customers WHERE CustomerId BETWEEN 4 AND 7. Figure 1-13 illustrates how SQL Server may process it.
../images/463519_1_En_1_Chapter/463519_1_En_1_Fig13_HTML.jpgFigure 1-13
Index seek
In order to read the range of rows from the table, SQL Server needs to find the row with the minimum value of the key from the range, which is 4. SQL Server starts with the root page, where the second row references the page with the minimum key value of 350. It is greater than the key value that we are looking for, and SQL Server reads the intermediate-level data page (1:170) referenced by the first row on the root page.
Similarly, the intermediate page leads SQL Server to the first leaf-level page (1:176). SQL Server reads that page, then it reads the rows with CustomerId equal to 4 and 5, and, finally, it reads the two remaining rows from the second page.
Technically speaking, there are two kinds of index seek operations. The first is called a point-lookup (or, sometimes, singleton lookup), where SQL Server seeks and returns a single row. You can think about the WHERE CustomerId = 2 predicate as an example.
The other type is called a range scan , and it requires SQL Server to find the lowest or highest value of the key and scan (either forward or backward) the set of rows until it reaches the end of scan range. The predicate WHERE CustomerId BETWEEN 4 AND 7 leads to the range scan. Both cases are shown as Index Seek operators in the execution plans.
As you can guess, index seek is more efficient than index scan because SQL Server processes just the subset of rows and data pages rather