Tag Archives: Sql Server

Page allocation changes in Sql Server 2016

This article explains the changes in the page allocation mechanism in Sql Server 2016 with extensive list of examples

Comparision of Page allocation in TempDB

Introduction to Page and Extent in Sql Server

In Sql Server database object (i.e. Table/Index) data is stored on an 8KB data pages and a set of 8KB contiguous data pages form an extent of 64KB. In Sql Server memory management happens at extent level. There are two types of extents in Sql Server:

  • Uniform Extent: An extent whose all 8 pages are reserved or filled with one Table data is referred as uniform extent
  • Mixed Extent: An extent whose 8 pages are shared by multiple objects is referred as a mixed extent. At max a mixed extent can have pages belonging to 8 different tables

[ALSO READ] Live Query Statistics in Sql Server 2016

Page allocation mechanism in the versions of Sql Server prior to Sql Server 2016

Prior to Sql Server 2016 by default first 8 pages for the tables were from a mixed extent and subsequent pages were from an uniform extent. Microsoft in the versions of Sql Server prior to 2016 provided an option to override this behavior by means of Trace Flag 1118. If this trace flag is enabled, the first 8 data pages for the tables were also from Uniform Extent instead of Mixed Extent. This flag was helpful to avoid the resource contention issues, especially in the TempDB in the scenario’s where huge number of temp tables were created.

[ALSO READ] STRING_SPLIT function in Sql Server 2016

Page allocation mechanism in Sql Server 2016

Below is the page allocation mechanism in the TempDb and User Databases of Sql Server 2016

  • TempDB Database
    With Sql Server 2016, TempDb database objects by default will get the pages from the Uniform Extent.And there is no option to over-ride this behaviour. There is no effect of the Trace Flag 1118 on the page allocation behavior.
  • User Databases
    With Sql Server 2016, even the user databases objects by default will get the pages from the Uniform Extent. We can change this behavior by setting the database property MIXED_PAGE_ALLOCATION by using the ALTER DATABASE statement.

[ALSO READ] GZIP COMPRESS and DECOMPRESS functions in Sql Server 2016

Sys.Databases catalog view changes

In Sql Server 2016, the Sys.Databases catalog view has an additional column is_mixed_page_allocation_on. This column value 1 means the database table will get the first 8 pages from the mixed extent and subsequent pages from the uniform extent. And a value 0 means all the pages for the table are from the uniform extent.

Execute the following query to check the page allocation mechanism for the user and system databases

SELECT name, is_mixed_page_allocation_on 
FROM Sys.Databases

RESULT:
Default Page Allocation Setting for TempDB and User Database

From the result we can see that for the TempDB and user databases the mixed page allocation is OFF. It means TempDB and user databases in Sql Server 2016 by default will have the page allocated from uniform extent.

[ALSO READ] Dynamic Data Masking in Sql Server 2016

EXAMPLES

Let us understand these page allocation changes with below examples

EXAMPLE 1: This example compares the page allocation mechanism in user database between Sql Server 2016 and the previous versions of the Sql Server.

Page allocation mechanism in Sql Server 2016 user databases

Create a demo database SqlhintsPageAllocation with a DemoTable table by executing the following script in Sql Server 2016:

--Create a Demo database
CREATE DATABASE SqlhintsPageAllocation 
GO
USE SqlhintsPageAllocation
GO
--Create a demo Table
CREATE TABLE DemoTable
(
	DemoColumn CHAR(8000)
)
GO
--Check the space usage of the DemoTable
SP_SPACEUSED DemoTable

RESULT:
Initial Space Usage

In one 8KB data page for data 8060 bytes is reserved. In the DemoTable, the DemoColumn is of type CHAR(8000) it means even when we store just one character in this column it will take 8000 bytes which is almost equivalent to 1 data page. So, we can consider 1 row in the DemoTable require 1 data page. From the above result we can see that when table is created initially no space is reserved for it.

Execute the following statement to insert 9 rows in to the DemoTable and display the space used after inserting each record:

INSERT INTO DemoTable VALUES ('A')
EXECUTE SP_SPACEUSED DemoTable
GO 9 

RESULT:
User Database Uniform Extent Allocation
[ALSO READ] GO Statement can also be used to excute batch of T-Sql statement multiple times

From the result we can see that insertion of the first record resulted in reserving 72KB space (i.e. 1 Uniform 64 KB extent + 1 8KB Index Allocation MAP(IAM) Page from the mixed extent). Each table will have at the least one IAM page, IAM page tracks all the pages and extents associated with a table. A table will have separate IAM page and it’s IAM page chain for the In-Row data, row overflow data and lob data. IAM pages will always be from the mixed extents.

Insertion of the 9th record resulted in the allocation of one more uniform extent, because that after the 9th record insertion we can see that reserved space is 136 KB( 72 KB + one more 64 KB uniform extent).

Execute the following un-document statement to get the all the pages associated with the DemoTable. Please don’t experiment any of such un-documented statements in the production environment.

 
DBCC IND ('SqlhintsPageAllocation','DemoTable',-1)

RESULT:
Pages of the DemoTable

From the result we can see that DemoTable has 10 pages (1 IAM Page from mixed extent + 9 Data pages from the Uniform extents).

Page allocation mechanism in the older versions of Sql Server user databases

Execute the following statement to Create a demo database SqlhintsPageAllocation with a DemoTable table by executing the following script in the versions of the Sql Server older than Sql Server 2016. I am executing it on Sql Server 2012 instance.

--Create a Demo database in the version of Sql Sever
--which is older than Sql Server 2016
CREATE DATABASE SqlhintsPageAllocation 
GO
USE SqlhintsPageAllocation
GO
--Create a demo Table
CREATE TABLE DemoTable
(
	DemoColumn CHAR(8000)
)
GO
--Check the space usage of the DemoTable
SP_SPACEUSED DemoTable

RESULT:
Initial Space Usage in Older Sql Versions

Execute the following statement to insert 9 rows in to the DemoTable and display the space used after inserting each record:

User Database Mixed Extent Allocation Sql Server 2012

From the result we can see that insertion of the first record resulted in reserving 16KB space (i.e. 1 8KB data page from mixed extent + 1 8KB Index Allocation MAP(IAM) Page from the mixed extent). For each record insertion till the 8th record one 8KB data page is allocated from the mixed extent.

Insertion of the 9th record resulted in the allocation of one more uniform extent, after the 9th record insertion we can see that reserved space is 136 KB( 72 KB (previously alloated space) + one more 64 KB uniform extent).

Execute the following un-document statement to get the all the pages associated with the DemoTable. Please don’t experiment any of such un-documented statements in the production environment.

DBCC IND ('SqlhintsPageAllocation','DemoTable',-1)

Pages of the DemoTable in older versions

From the result we can see that DemoTable has 10 pages, one 8KB IAM Page from mixed extent and 9 Data pages (8 Pages from mixed extent and 1 page from uniform extent).

Summary: For user databases in the versions of Sql Server older than 2016, the first 8 data pages were allocated from the mixed extent and next pages from uniform extent. In Sql Server 2016 even the first eight data pages were allocated from the uniform extent. Below image summaries this page allocation mechanism comparison between Sql Server 2016 and older versions of Sql Server

Comparision of Page allocation in User Database
[ALSO READ] Row level security in Sql Server 2016

EXAMPLE 2: This example compares the page allocation mechanism in the TempDB database between Sql Server 2016 and the previous versions of the Sql Server.

Page allocation mechanism in Sql Server 2016 TempDB database

Create a temporary table #DemoTable table by executing the following script in Sql Server 2016:

USE TEMPDB
GO
--Create a demo Temp Table
CREATE TABLE #DemoTable
(
	DemoColumn CHAR(8000)
)
GO
--Check the space usage of the temp table: #DemoTable
SP_SPACEUSED #DemoTable
GO

RESULT:
TempDB12016

In one 8KB data page for data 8060 bytes is reserved. In the #DemoTable, the DemoColumn is of type CHAR(8000) it means even when we store just one character in this column it will take 8000 bytes which is almost equivalent to 1 data page. So, we can consider 1 row in the #DemoTable require 1 data page. From the above result we can see that when table is created initially no space is reserved for it.

Execute the following statement to insert 8 rows in to the #DemoTable temp table and display the space used after inserting each record:

INSERT INTO #DemoTable VALUES ('A')
EXECUTE SP_SPACEUSED #DemoTable
GO 8 -- loop 8 times

RESULT:
Uniform Extent Allocation for TempDB

From the result we can see that insertion of the first record resulted in reserving 72KB space (i.e. 1 Uniform 64 KB extent + 1 8KB Index Allocation MAP(IAM) Page from the mixed extent). Each table will have at the least one IAM page, IAM page tracks all the pages and extents associated with a table. A table will have separate IAM page and it’s IAM page chain for the In-Row data, row overflow data and lob data. IAM pages will always be from the mixed extents.

Page allocation mechanism in the older versions of Sql Server TempDB

Create a temporary table #DemoTable table by executing the following script in the versions of the Sql Server older than Sql Server 2016. I am executing it on Sql Server 2012 instance.

USE TEMPDB
GO
--Create a demo Temp Table
CREATE TABLE #DemoTable
(
	DemoColumn CHAR(8000)
)
GO
--Check the space usage of the temp table: #DemoTable
SP_SPACEUSED #DemoTable
GO

RESULT:
TempDB12012

Execute the following statement to insert 8 rows in to the temp table #DemoTable and display the space used after inserting each record:

INSERT INTO #DemoTable VALUES ('A')
EXECUTE SP_SPACEUSED #DemoTable
GO 8 -- loop 8 times

RESULT:
Mixed Extent Allocation for TempDB

From the result we can see that insertion of the first record resulted in reserving 16KB space (i.e. 1 8KB data page from mixed extent + 1 8KB Index Allocation MAP(IAM) Page from the mixed extent). For each record insertion till the 8th record one 8KB data page is allocated from the mixed extent.

Summary: In the versions of Sql Server older than 2016 for TempDB, by default the first 8 data pages were allocated from the mixed extent and next pages were from uniform extent. In Sql Server 2016 even the first eight data pages were allocated from the uniform extent. Below image summaries this page allocation mechanism comparison between Sql Server 2016 and older versions of Sql Server

Comparision of Page allocation in TempDB
[ALSO READ] DROP IF EXISTS Statement in Sql Server 2016

Changing Page allocation mechanism at database level

By default in Sql Server 2016 both TempDB and User Database objects will get all the pages are allocated from the uniform extent. Sql Server 2016 provides a mechanism where we can change this behavior in the user database by the ALTER DATABASE statement. We an execute a script like the below one which is changing the SqlhintsPageAllocation database default seeting to alloate first 8 pages for the data base objects from uniform extent to mixed extent

SELECT name, is_mixed_page_allocation_on 
FROM Sys.Databases
WHERE name = 'SqlhintsPageAllocation'
GO
--Change SqlhintsPageAllocation page alloation setting
--to allocate first 8 pages from mixed extent
ALTER DATABASE SqlhintsPageAllocation 
SET MIXED_PAGE_ALLOCATION  ON
GO
SELECT name, is_mixed_page_allocation_on 
FROM Sys.Databases
WHERE name = 'SqlhintsPageAllocation'
GO

RESULT:
Changing MIXED_PAGE_ALLOCATION Setting

Let us execute the following statement to change TempDB database page allocation mechanism

ALTER DATABASE TempDB
SET MIXED_PAGE_ALLOCATION  ON

RESULT:

Msg 5058, Level 16, State 9, Line 1
Option ‘MIXED_PAGE_ALLOCATION’ cannot be set in database ‘tempdb’.

From the result we can see that TempDB database default page allocation mechanism can’t be changed. Where as user databases page allocation mechanism can be changed

[ALSO READ]:

New features in SQL SERVER 2016

Query Store in Sql Server 2016

Introduction to Query Store

Many a time we come across a scenario where suddenly in production without any release or changes some query which was working perfectly alright till yesterday is taking too long to execute or consuming lot of resources or timing out.

Most of the times such issue are related to execution plan change (commonly referred as Plan Regression). Till yesterday the query was running fine as it was running with good cached execution plan and today a bad plan is generated and cached. Because of this bad cached plan the query which was executing perfectly alright suddenly starts misbehaving.

To identify and fix such performance problems due to the execution plan change the Query Store feature introduced in Sql Server 2016 will be very handy.

Query Store basically captures and stores the history of query execution plans and its performance data. And provides the facility to force the old execution plan if the new execution plan generated was not performing well.

To understand Query Store with an example let us create a demo database SqlhintsQSDemo and a Customer table having sample data as shown in the following image by executing the following statement:

Customer Table for Query Store Demo

Script:

--------Create Demo database--------
CREATE DATABASE SqlhintsQSDemo
GO
USE SqlhintsQSDemo
GO
--------Create a Customer Table------
CREATE TABLE dbo.Customer( 
 Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
 FirstName NVARCHAR(50), LastName NVARCHAR(50))
GO
--Populate 100,000 customers with unique FirstName 
INSERT INTO dbo.Customer (FirstName, LastName)
SELECT TOP 100000 NEWID(), NEWID()
FROM SYS.all_columns SC1 
	CROSS JOIN SYS.all_columns SC2
GO 
--Populate 15000 customers with FirstName as Basavaraj
INSERT INTO dbo.Customer (FirstName, LastName)
SELECT TOP 15000 'Basavaraj', NEWID()
FROM SYS.all_columns SC1
        CROSS JOIN SYS.all_columns SC2

Execute the following statement to create a non-clustered index on the FirstName column of the customer table and the stored procedure GetCustomersByFirstName to get the customer details by FirstName

-- Create non-clustered index on the FirstName column
CREATE INDEX IX_Customer_FirstName on dbo.Customer (FirstName)
GO
-- Create stored procedure to get customer details by FirstName
CREATE PROCEDURE dbo.GetCustomersByFirstName
(@FirstName AS NVARCHAR(50))
AS
BEGIN
	SELECT * FROM dbo.Customer 
	WHERE FirstName = @FirstName
END

Enabling Query Store

Query store is a database level feature, which is disabled by default. Follow the following steps to enable it by using Management Studio

Step 1: Right click on the demo database SqlHintsQSDemo and select the Properties menu option as shown in the below image

Enable Query Store DB Properties window

Step 2: Step 1 Pops-up the properties window. In the properties window from the left navigation options select the new option Query Store. Below is the view of the properties window after selecting the Query Store option.

Query Store Properties window

Step 3: From the Query Store option window of Step 2 we can see that the Operation Mode (Requested) setting has three different options. Selecting the ReadWrite option will enable the Query Store and starts capturing the query execution data such as execution plan and it’s performance stats. Selecting Read option will only allow the data to read from the query store but no new query data is captured.
Below image shows the various query store options after selecting the ReadWrite Operation Mode. To know each of these options select the option it will display the details about it below.

Query Store Properties window Post Selecting ReadWrite

Query store captured data is stored in the respective query store enabled database. As we can see from the above image 100 MB is reserved for query store data in the SqlhintsQSDemo database. This value can be changed by changing the Max Size (MB) option value.

Click OK after selecting the desired query store options, it will enable the query store. And if we refresh the SqlhintsQSDemo database in the object explorer we can see the Query Store folder as shown in the below image

Query Store folder in the object explorer

Now query store feature is enabled let us understand how it comes handy in resolving performance issues

Example Usage of Query Store feature to resolve Performance issue

This example explains the scenario where suddenly in production without any release or changes some query which was working perfectly alright, starts taking too long to execute or consuming lot of resources or timing out. And how the query store comes handy in such situation

To explain this we will use the stored procedure GetCustomersByFirstName to fetch the records from the Customer table based on FirstName. And the Customer table has unique FirstName for all the customers except for the FirstName ‘Basavaraj’. There are 15,000 customers with FirstName as ‘Basavaraj’, so fetching a record where CustomerName is ‘Basavaraj’ will be better with table scan (i.e. clustered index scan) than using the non-clustered index on FirstName. But fetching the Customer records where FirstName value is anything other than ‘Basavaraj’, it makes sense to use the non-clustered index on FirstName. In this case where the majority queries will be better of executing by using the Non-clustered index on FirstName

Let us execute the following statement to get the details of the Customer whose FirstName is Xyz. While executing the below statement select the “Include Actual Execution Plan” option.

EXEC dbo.GetCustomersByFirstName @FirstName = N'Xyz'

Result:
Query doing Non Clustered Index Seek

From the execution plan we can see that the Stored Procedure is using the Non-clustered index seek on the FirstName column to fetch the Customer details, which is the optimal option too.

From the object explorer select the “Top Resource Consuming Queries” option as shown in the below image to view the query plan and the performance stats capture for the above SP execution in the Query Store

Top Resource Consuming Queries Option

Below is the Query Store view of the “Top Resource Consuming Queries”. Here we can see that Plan Id 6 is corresponding to the above stored procedure execution which was using Non-Clustered Index Seek. Mouse over on the bar corresponding to the query which got executed by the SP GetCustomersByFirstName shows that it is executed ones, has one execution plan, it took 16129 micro seconds and query id is 6.

Qury Store After First Execution of the Query

Now the execution of the GetCustomersByFirstName will always use the Non-Clustered index on the FirstName column, as the first time execution of it cached this Non-Clustered Index Seek plan. So as long as this plan is cached the query where we need to get the Customer data by FirstName will always execute optimally. The only way this cached query plan is removed from the cache if the server is re-started or the memory pressure on the server etc. We can also execute the following DBCC statement to remove all the cached plans from the database server

DBCC FREEPROCCACHE

After executing the above DBCC statement all the cached plans are removed from the cache. Now try executing the same stored procedure i.e. GetCustomersByFirstName but this time pass the @FirstName parameter value as Basavaraj instead of Xyz. While executing the following statement select the “Include Actual Execution Plan” option.

EXEC dbo.GetCustomersByFirstName @FirstName = N'Basavaraj'

Result:
Query doing Clustered Index San

From the result we can see that instead of using Non-Clustered Index on FirstName, it is doing the Clustered Index Scan. The reason it is doing clustered index scan is, there are 15,000 records whose FirstName is ‘Basavaraj’ and there are no previously cached plans. It makes sense for the Sql Server to use Clustered Index scan, instead of using a Non-Clustered index seek which uses bookmark look-up for each of the record. Now this plan is cached and each time the SP: GetCustomersByFirstName is executed it will use the clustered index scan

Below is the view of the Query Store after executing the SP: GetCustomersByFirstName with @FirstName parameter value as ‘Basavaraj’. You need to refresh the query store if it is already open. Here we can see a new plan with plan id 11 is generated which corresponds to the clustered index scan. So we can see that now we have two query plans corresponding to the query executed by the stored procedure GetCustomersByFirstName.

Query Store View with two plans for the Same query 1

Let us again execute the following statement to get the details of the Customer whose FirstName is Xyz. While executing the below statement select the “Include Actual Execution Plan” option.

EXEC dbo.GetCustomersByFirstName @FirstName = N'Xyz'

Result:
Using worst Cached Plan

From the result we can see that this time the stored procedure execution is using the Clustered index scan instead of the non-clustered index scan which it was doing previously. To get the customer details whose FirstName is ‘Xyz’, from the result we can see it is using the cached plan which is optimized/compiled for the @FirstName parameter value ‘Basavaraj’. This is how a perfectly working query starts misbehaving without any release or changes.

Below is the view of the Query Store after executing the SP: GetCustomersByFirstName with @FirstName parameter value as ‘Xyz’. You need to refresh the query store if it is already open. Mouse over on the bar corresponding to the query which got executed by the SP GetCustomersByFirstName shows that it is executed 3 times and has two execution plans i.e. 6 and 11 and plan 11 which is a clustered index scan is the current plan. In the below image we can clearly see than plan 11 is taking more time to execute compared to plan 6. So, in this case it is optimal to execute the query with plan 6 compared to plan 11.

Query Store View after  third execution

From the above query store view it is clear that plan 6 is the optimal plan for the execution of the stored procedure GetCustomersByFirstName compared to plan 11 as majority of the time the stored procedure is executed with a parameter value other than ‘Basavaraj’.

As shown in the below image query store gives an option to force the execution plan for the future execution of the queery. To force the plan select the plan 6 (i.e. light blue color circle) and then click on the Force Plan option.

Query Store force the execution plan 1

After forcing the plan 6 as shown in the above image. Now execute the GetCustomersByFirstName one more time with @FirstName parameter value as ‘Xyz’.

EXEC dbo.GetCustomersByFirstName @FirstName = N'Xyz'

Result:
Execution plan after forcing it by the query store

From the above result we can see that execution of the SP: GetCustomersByFirstName with @FirstName parameter value as ‘Xyz’ is doing a non-clustered index scan compared to clustered index which it was doing previously.

Below is the view of the Query Store after forcing the execution plan 6 and executing the SP: GetCustomersByFirstName with @FirstName parameter value as ‘Xyz’. We can see that plan 6 circle has a right tick mark, which means this is the force plan which now will not change even after server re-start. As shown in the below image adjacent to Force Plan option we have an Un-force Plan which an be used to un-force this forced plan.

Query store view post forcing the plan

New features in SQL SERVER 2016

INSERTED and DELETED Logical Tables in Sql Server

Data Manipulation Language (DML) Triggers have access to the two special logical Tables named INSERTED and DELETED. These are the temporary tables managed by Sql Server. Structure of these tables will be same as that of the table on which the DML action is fired and holds the old and new values of the rows which are modified by the DML statement.

DELETED logical table will hold the rows which are deleted from the trigger table (i.e. the table on which the trigger is defined) by the DELETE or UPDATE operation. An UPDATE DML operation is internally executed as first the deletion of the existing record and then insertion of the new record.

INSERTED logical table will hold the rows which are inserted by the INSERT and UPDATE statement.

Below table depicts the contents of the INSERTED and DELETED logical tables for three DML operations

INSERTED and DELETED Logical Tables

To understand INSERTED and DELETED logical tables with extensive list of examples let us create a Customer Table as shown in the following image by the following script:

Customer Table
SCRIPT:

--Create Demo Database
CREATE DATABASE SqlHintsLogicalTablesDemo
GO
USE SqlHintsLogicalTablesDemo
GO
--Create Customer Table
CREATE TABLE Customer 
( CustomerId INT IDENTITY (1, 1) NOT NULL ,
  FirstName NVARCHAR(50), LastName NVARCHAR(50))
GO
--Add sample records
INSERT INTO dbo.Customer ( FirstName, LastName )
VALUES('Basavaraj','Biradar'),
      ('Kalpana','Patil')

Let us create a AFTER trigger INSERTEDAndDELETEDTableExample on the Customer table for the triggering DML actions INSERT, UPDATE and DELETE by executing the following statement. This AFTER trigger is displaying the content of the INSERTED and DELETED logical tables

CREATE TRIGGER INSERTEDAndDELETEDTableExample
ON Customer
FOR INSERT, UPDATE, DELETE
AS
BEGIN
     PRINT '**********DELETED Table***************'
     SELECT * FROM DELETED
     PRINT '*********INSERTED Table***************'
     SELECT * FROM INSERTED
END

INSERTED and DELETED Logical Tables behavior in-case of INSERT DML OPERATION

Execute the following INSERT statement to insert the record into the Customer table.

INSERT INTO dbo.Customer ( FirstName, LastName )
VALUES('Shreeganesh','Biradar')

RESULT:
INSERTED DELETED TABLE ENTRIES IN CASE OF INSERT

From the result we can see that for the INSERT DML operation on the Customer table, no record is added to the DELETED logical table but the inserted record is available in the INSERTED logical table

Let us check the records in the Customer table post the execution of the above INSERT statement

AFTER INSERT Trigger Example

INSERTED and DELETED Logical Tables behavior in-case of DELETE DML OPERATION

Execute the following DELETE statement to DELETE a record from the Customer table.

DELETE FROM Customer WHERE CustomerId = 2

RESULT:
AFTER DELETE Trigger Example

From the result we can see that the record which is deleted from the Customer table from DELETE DML operation is available in the DELETED logical table and the INSERTED logical table is empty

Let us check the records in the Customer table post the execution of the above DELETE statement

Post DELETE

INSERTED and DELETED Logical Tables behavior in-case of UPDATE DML OPERATION

Execute the following UPDATE statement to update all the records from the Customer table.

UPDATE Customer
SET FirstName = 'Mr. ' + FirstName

RESULT:
AFTER UPDATE Trigger Example

From the above result we can see that INSERTED table will have the records with values post update and the DELETED table will have the the records with a state prior to the update

Let us check the records in the Customer table post the execution of the above UPDATE statement

Post Update

Drop the trigger INSERTEDAndDELETEDTableExample by executing the following statement

DROP TRIGGER INSERTEDAndDELETEDTableExample