SQLCAT's Guide To Relational Engine
SQLCAT's Guide To Relational Engine
CREATE FUNCTION Person.USR_GetTopDuplicateCustomer (@base int)
RETURNS @retval TABLE
(
AddressLine1 NCHAR(600) NOT NULL,
AddressLine2 NCHAR(600) NULL,
City NVARCHAR(30) NOT NULL,
StateProvinceID INT NOT NULL,
CT INT NOT NULL
)
AS
BEGIN
INSERT INTO @retval
select AddressLine1, AddressLine2, City, StateProvinceID, ct = COUNT(*)
from [Link] o
WHERE AddressID >= @base
GROUP BY AddressLine1, AddressLine2, StateProvinceID, City
HAVING COUNT(*) > 1
return
end
The specific line in the function is the text of the STMT column.
INSERT INTO @retval
select AddressLine1, AddressLine2, City, StateProvinceID, ct = COUNT(*)
from [Link] o
WHERE AddressID >= @base
GROUP BY PersonID, AddressLine1, AddressLine2, StateProvinceID, City
HAVING COUNT(*) > 1
Another statement within another batch is also running among the contending statements.
SELECT DISTINCT
(Select top 1 AddressLine1
from Person.USR_GetTopDuplicateCustomer(AddressID)) as
AddressLine1,
(Select top 1 ISNULL(AddressLine2, '')
from Person.USR_GetTopDuplicateCustomer(AddressID)) as
AddressLine2,
(SELECT TOP 1 City
FROM Person.USR_GetTopDuplicateCustomer(AddressID)) as City,
(SELECT TOP 1 StateProvinceID
FROM Person.USR_GetTopDuplicateCustomer(AddressID)) as
StateProvinceID,
(Select TOP 1 ct
FROM Person.USR_GetTopDuplicateCustomer(AddressID)) as ct
FROM [Link] a
WHERE AddressID IN
(SELECT AddressID FROM Person.USR_IsInTop1000Customers(@base))
The TEXT column shows the full batch text and indicates that this statement is found in a stored
procedure named [Link].
At this point in the example, you can see the call stack by querying sys.dm_exec_requests. The stored
procedure [Link] contains a SELECT query that calls the function
Person.USR_IsInTop1000Customers once for each row evaluated in the SELECT query. If the name of
the function is correct, you expect this to result in a maximum of 1,000 rows. The
Person.USR_GetTopDuplicateCustomer function is then called five times for each of those 1,000 rows
one call for each column returned by the correlated subqueries in the column list. Although the
Person.USR_GetTopDuplicateCustomer TVF is simple, it is not constructed as an inline TVF.
Each call to a multi-statement TVF requires SQL Server to create a table variable to hold the return
values, allocate pages to that table as required, search for free space, and update the corresponding PFS
page as data is added. When the table variable goes out of scope, SQL Server must deallocate the space
used by that variable. All of this work on table variables occurs in tempdb. As larger numbers of users
begin to use the system, and as the size of the data contained in the table variables increases,
contention develops in the SGAM and PFS pages used to track allocation and space available for these
table variables.
This tempdb contention causes SQL Server to be unable to use its available resources efficiently. To
improve the throughput and to scale the application further, you must eliminate this hot spot
contention.
NOTE: Although there are calls to TVFs in the column list in this function, there are cases of tempdb
contention caused by queries with calls to TVFs only in the WHERE clause.
When Did Contention Start?
In the earlier example, contention appeared with as few as three concurrent users, and average wait
times for latches became higher than 10 ms with as few as 25 concurrent users. As the number of
concurrent users increased, both the number of processes waiting for latches and the average time each
process waited for a latch increased linearly. Changing the amount of data being stored in the table
variables also had a linear effect on latch wait time for any observed load level.
Reducing or Eliminating Contention
When you have identified the cause of the contention, you can start to determine how to reduce or
eliminate it.
In the earlier example, there is nothing further a DBA can do with configuration to alleviate the
contention. Changes must be made to the queries and to the method used to build the desired result
set. Assuming that the existing queries result in the correct data set, you should concentrate on pulling
this data set more efficiently.
The options to eliminate the contention are:
Take advantage of the new syntax (as of Microsoft SQL Server 2005) to eliminate the need for
the correlated subqueries and to lower the number of times the TVFs are called.
Use a JOIN clause to replace the correlated subquery in the WHERE clause.
Eliminate the use of TVFs for the data in the SELECT list.
Each of these solutions may work in some scenarios.
Eliminating the Correlated Subqueries in the Selected Columns
Depending on the query, you can eliminate calls to TVFs in a SELECT list by doing any of the following:
Use an APPLY operator in the FROM clause instead of individual correlated subqueries for each
column.
Pull the data into a temporary or staging table and performing an appropriate JOIN to that
table.
Rewrite the query so that temporary objects are not necessary to pull the appropriate data.
Using an APPLY Operator
In queries such as that in the earlier example, a single TVF is called multiple times in a series of
correlated subqueries, and each call to that function uses the same parameter. Prior to SQL Server 2005,
this was the only way to use this TVF to retrieve the data. SQL Server 2005, however, introduced the
APPLY operator, and this provides another option.
In the example, the query is constructed such that it uses the TVF in correlated subqueries on each
column: The table variable is built and dropped once for each column when the function is called on
each row. A CROSS APPLY or OUTER APPLY clause offers an advantage over the multiple correlated sub-
queries in the SELECT list because one table variable is returned for each row. In the example, the
function is called for each of the five columns, so it is possible to reduce the number of times the table
variable is returned for each row from five to one by using an APPLY operator.
Following is an example of writing a query to use the APPLY operator.
NOTE: You may need to modify the TVF to ensure that it only returns one row when only the first row of
the results set is used.
SELECT DISTINCT
d.AddressLine1
, d.AddressLine2
, [Link]
, [Link]
, [Link]
FROM [Link] a
CROSS APPLY Person.USR_GetTopDuplicateCustomer(AddressID) d
WHERE AddressID IN
(SELECT AddressID FROM Person.USR_IsInTop1000Customers(@base))
This query replaces a call to Person.USR_GetTopDuplicateCustomer for each column in each row with a
single call to Person.USR_GetTopDuplicateCustomer for the row. This reduces the number of calls to
that stored procedure by 80 percent.
Note, however, that Person.USR_IsInTop1000Customers still gets called for each row of the results set,
and because the argument used to call it never changes, the same result set is returned every time. You
can therefore further reduce the potential for contention by obtaining this result set only once, as
follows.
-- retrieve the list of top 1,000 customers first
SELECT AddressID INTO #top1000Customers
FROM Person.USR_IsInTop1000Customers(@base)
-- use the temp table
-- instead of calling the TVF for every row
SELECT DISTINCT
d.AddressLine1
, d.AddressLine2
, [Link]
, [Link]
, [Link]
FROM [Link] a
CROSS APPLY Person.USR_GetTopDuplicateCustomer(AddressID) d
WHERE AddressID IN
(SELECT AddressID FROM #top1000Customers)
NOTE: Multi-statement TVFs are very different from inline TVFs. Temporary objects are not created by
inline TVFs as they are with multi-statement TVFs. Inline TVFs may be another option to consider, but
note that even when using inline TVFs, using the correlated subqueries in the column list of the SELECT
is inefficient. You should therefore use the CROSS APPLY clause when possible with inline TVFs.
Using Temporary Tables with JOINS
Another option is to rewrite the procedure so that the multi-statement TVF is not called for each
column, or even for each row. Sometimes queries using multi-statement TVFs are written to reuse the
logic in a TVF; with SQL Server, however, you must consider how the data is retrieved.
In the earlier example, the purpose of the query is to determine which addresses out of the next 1,000
addresses appear more than once in the table. Although the user-defined function makes it easy to
reuse logic, the extra overhead of allocating and deallocating space for the table variable being returned
by the function puts an additional load on SQL Server and causes a bottleneck in tempdb where the
TVFs are created.
A JOIN clause can often replace the logic of the correlated subqueries in the column list of the SELECT
query. In the earlier example, a JOIN on a single object eliminates the need to make calls to the TVF in
the column list. If the data used in the SELECT lists can be pulled into a temporary table once per
execution of the stored procedure, a JOIN can then be used to include this data in the final result set
rather than using the TVFs in the query.
Using temporary staging tables provides a way to break down the logic if it is too complex to be
efficiently performed in a single query. Using temporary tables also lets temporary objects be created
only once per execution, and this reduces the potential for tempdb contention even more than using
CROSS APPLY does.
Following is an example of how you can use temporary tables with joins for this particular example.
-- get the next 1,000 addresses
SELECT TOP 1000 AddressID
INTO #top1000Customers
FROM [Link] WHERE AddressID >= @base
ORDER BY AddressID
-- get the list of duplicate addresses
select AddressLine1, AddressLine2, City, StateProvinceID, ct = COUNT(*)
INTO #duplicateAddresses
FROM [Link]
WHERE AddressID IN (SELECT AddressID FROM #top1000Customers)
GROUP BY AddressLine1, AddressLine2, StateProvinceID, City
HAVING COUNT(*) > 1
SELECT [Link], a.AddressLine1, ISNULL(a.AddressLine2, '') AddressLine2,
[Link], [Link], [Link]
FROM [Link] a JOIN
#top1000Customers ta ON [Link] = [Link]
JOIN
#duplicateAddresses b ON a.AddressLine1 = b.AddressLine1
AND ((a.AddressLine2 = b.AddressLine2)
OR (a.AddressLine2 IS NULL AND b.AddressLine2 IS NULL))
AND [Link] = [Link] AND [Link] = [Link]
Rewriting the Query to Eliminate the Need for Temporary Objects
When steps that perform significant transformations produce the query results, using a temporary table
gives SQL Server a way to calculate statistics on intermediate result sets and makes query performance
more predictable. However, with a simple query, the results can be efficiently limited within the query
without creating a temporary object.
In this example, the logic of the query can be expressed as follows.
/*
get the list from this set of AddressIDs where duplicates exist,
and give the count of the occurrences of this in the
next 1,000 addresses
*/
SELECT [Link], a.AddressLine1, ISNULL(a.AddressLine2, '') AddressLine2,
[Link], [Link], [Link]
FROM [Link] a JOIN
(
SELECT TOP 1000 AddressID FROM [Link] WHERE AddressID >= @base
ORDER BY AddressID
) ta ON [Link] = [Link] -- limit to 1,000
JOIN
(
select AddressLine1, AddressLine2, City, StateProvinceID, ct = COUNT(*)
from [Link]
WHERE AddressID >= @base
GROUP BY AddressLine1, AddressLine2, StateProvinceID, City
HAVING COUNT(*) > 1
) b ON a.AddressLine1 = b.AddressLine1
AND ((a.AddressLine2 = b.AddressLine2)
OR (a.AddressLine2 IS NULL AND b.AddressLine2 IS NULL))
AND [Link] = [Link] AND [Link] = [Link]
In this example, the additional work created by multiple calls to the multi-statement TVF is eliminated
by rewriting the query. This significantly increases the performance of each individual execution, and
more importantly, it removes the hotspot on tempdb.
No contention develops on tempdb when the same load is applied with the rewritten stored procedure,
which returns the same data. Contention does not develop on tempdb even when the load is increased
to 100 times as many users as the original load. Query performance improves from about 45 seconds
per execution with the original query to less than 1 second per execution with the rewritten query.
However, as frequently occurs with performance tuning, a bottleneck on a different resource develops
at the higher level of throughput.
Conclusion
Transact-SQL programming or querying practices that rapidly create and drop temporary objects can
create significant bottlenecks on tempdb, reducing SQL Server throughput even when best practices for
configuration of tempdb are followed.
Multi-statement TVFs create table variables as return values. Using multi-statement TVFs that are called
once or more for each row processed in a query can lead to tempdb contention because of the number
of times the table variables must be created and dropped in rapid succession.
In the examples described in this white paper, the contention tended to appear on one file at a time, but
contention migrated periodically from file to file. Contention was most often observed on the PFS page,
but contention occasionally appeared on the SGAM page also.
tempdb contention can be reduced or eliminated by altering the Transact-SQL programming so that
these TVFs are not called on each row. Alternately, you can create a temporary table once for each
execution if statistics are needed on an intermediate result set, or you can perform the entire operation
in a single query, removing the function logic to this query. The single-query alternative was
demonstrated in this white paper, eliminating the use of tempdb and therefore any contention on
tempdb. Avoiding the multi-line TVF produced the best query performance and eliminated the tempdb
contention.
Because of the tempdb contention that multi-statement TVFs can cause, you should avoid using multi-
statement TVFs when other ways of deriving the result set are available.
Maximizing Throughput with TVPs
Introduction
This technical note looks at considerations of whether to use the SqlBulkCopy, or Table Valued
Parameters (TVPs) in a customer scenario encountered as part of a CAT engagement. The decision of
which is better depends on several considerations which will be discussed. TVPs offer several
performance optimization possibilities that other bulk operations do not allow, and these operations
may allow for TVP performance to exceed other bulk operations by an order of magnitude, especially for
a pattern where subsets of the data are frequently updated.
Executive Summary
TVPs and MERGE operations make a powerful combination to minimize round trips and batch insert and
update data with high throughput. Parallel operation on naturally defined independent sets of data can
be performed efficiently like this. The TVP makes optimizations possible that are not possible with bulk
insert or other operations types. To get the most out of the operation, you must optimize your
underlying table as well as your method for inserting and updating the data. The principles followed in
this case emphasize these points:
Do not create artificial keys with IDENTITY when it is not necessary. This creates a point of
contention on heavy, parallel insert operations.
If old data key values will not expire, use a MERGE operation instead of DELETE and INSERT. This
minimizes data operations; rebalancing and page splits, and the amount of data that must be
replicated. If old data key values will expire, then test two operations of MERGE followed by a
deletion of only the expired keys rather than a DELETE and INSERT of the full data set.
If not all the data will be changed, modify the WHEN MATCHED portion of the MERGE
statement to also check that the data that may change has changed, and only update the data
that is actually changed. This minimizes the number of rows of data that are actually modified,
and thus minimizes the amount of data that must be replicated to secondaries in Windows
Azure SQL Database environments.
Although these are best practices in any environment they become increasingly important in a shared
environment such as Windows Azure SQL Database.
Scenario
In a recent engagement, a problem was encountered in performance of inserting and updating data in
Windows Azure SQL Database. The scenario was:
Data from hundreds of thousands of devices needs to be stored in Windows Azure SQL Database
Each device stores approximately 8000 rows of configuration data across three tables in the
database
Data for each device is updated approximately once per day
Only the most current data is stored in Windows Azure SQL Database
The data must be processed at a sustained rate of six devices per second (Approximately 48,000
rows per second)
The first concept tried was to delete the data for each device first, then use the BulkCopy API to insert
the new rows. Several worker role instances were used to scale out the processing of the data into the
database. However; when running this against an Azure SQL Database, this did not give the performance
the scenario demanded.
The second approach was to use Table Valued Parameters (TVPs) with stored procedures to do the
processing. In the stored procedures, the data was validated first. Next, all existing records were deleted
for the device being processed, and then the new data was inserted. This did not perform better than
the previous bulk insert option.
We were able to improve the process to meet the performance demands by making optimizations to
the tables themselves, and to the stored procedures in order to minimize the lock, latch, and Windows
Azure SQL Database specific contention the process initially encountered.
Optimizing the Process
Several optimizations were made to this process.
First, the underlying tables contained identity columns, and data needed to be inserted in sets from
several different processes. This created both latch, and lock contention. Latch contention was created
because each insert is performed only on the last page of the index, and several processes were trying
to insert to the last page simultaneously. Lock contention is created because the identity column was
the primary key and clustered index key, so all processes had to go through the process of having the
identity value created, then only one at a time could insert. To remedy this type of contention, other
values within the data were used as a primary key. In our example, we found composite keys of DeviceID
and SubCondition in one table, and a combination of three columns in the second that third tables that
could be used to maintain entity integrity. Since the IDENTITY column was not really necessary, it was
dropped.
An example of the optimization of the table is
Original Table Definition:
CREATE TABLE dbo.Table1
(
RecordID BIGINT NOT NULL IDENTITY(1, 1),
DeviceID BIGINT NOT NULL,
SubCondition NVARCHAR(4000) NOT NULL,
Value NVARCHAR(MAX) NOT NULL,
SubValue TINYINT NOT NULL,
CONSTRAINT [pk_Table1] PRIMARY KEY CLUSTERED
(
RecordID ASC
)
)
Optimized Table Definition:
CREATE TABLE dbo.Table1
(
DeviceID BIGINT NOT NULL,
SubCondition NVARCHAR(4000) NOT NULL,
Value NVARCHAR(MAX) NOT NULL,
SubValue TINYINT NOT NULL,
CONSTRAINT [pk_Table1] PRIMARY KEY CLUSTERED
(
DeviceID ASC,
SubCondition ASC
)
)
The second suboptimal part of the described process is that the stored procedure deleted the old data
for a device first, then re-inserted the new data for the device. This is additional maintenance of a data
structure as deletes can trigger re-balancing operations on an index, and inserts can result in page splits
as pages are filled. Making two data modifications, each with implicit maintenance work that must be
done, should be avoided when the data operation can be done with one operation. A MERGE
operation can be used in place of a DELETE then INSERT provided the updated set of data will not omit
previous rows of data. In other words, this works if no SubConditions for any DeviceID in the example
table will expire.
Any time data is modified in Windows Azure SQL Database, it must be replicated to two replicas. The
DELETE then INSERT method was inefficient in this as well since both the delete and the insert operation
must be replicated to the Azure SQL Database replicas. Using a MERGE with only the WHEN MATCHED
and WHEN NOT MATCHED conditions will eliminate this double operation, and thus eliminates half of
the data replication, but it still modifies every row of data. In the case of this scenario, at most, 10% of
the incoming data would actually be different from existing data. By adding an additional condition to
the MATCHED condition so that it reads WHEN MATCHED AND ([Link] <> [Link]) only the
rows that contained actual data differences were modified, which means that only the data that was
actually changed in the incoming data needed to be replicated to secondaries. Making this modification
minimized SE_REPL_SLOW_SECONDARY_THROTTLE, SE_REPL_ACK, and other SE_REPL_* wait types.
The last area of optimization we took was to ensure efficient joining with minimal chance for contention
among processes. This action was taken because the optimizer tended to want to scan both source and
target tables to perform a merge join when processing the MERGE operation. This was not only
inefficient, but caused significant lock contention. To eliminate this contention, the query was hinted
with OPTION (LOOP JOIN).
An example of the MERGE written to minimize the amount of data that must be processed into the
tables is:
CREATE PROCEDURE [dbo].[TVPInsert_test]
@TableParam TVPInsertType_test READONLY
AS
BEGIN
MERGE dbo.Table1 AS target
USING @TableParam AS source
ON [Link] = [Link]
and [Link] = [Link]
WHEN MATCHED AND
([Link] != [Link]
OR [Link] != [Link])
THEN
UPDATE SET Value = [Link], SubValue = [Link]
WHEN NOT MATCHED THEN
INSERT (DeviceID, SubCondition, Value, SubValue)
VALUES ([Link], [Link]
, [Link], [Link])
OPTION (LOOP JOIN)
END
The Table Value Type definition created for use with this stored procedure:
CREATE TYPE dbo.TVPInsertType_test AS TABLE
(
DeviceID BIGINT NOT NULL,
SubCondition NVARCHAR(4000) NOT NULL,
Value NVARCHAR(MAX) NOT NULL,
SubValue TINYINT NOT NULL,
PRIMARY KEY CLUSTERED
(
DeviceID ASC,
XPath ASC
)
)
NOTE: Check the properties of joins before using join hints. Loop joins can explode in cost when scans,
including range scans, are performed on the inner table (the table accessed second). However; the join
in the MERGE is primary key to primary key. In this case, there is no chance for range scans on the inner
table, and therefore, the risk of cost explosion on the loop join is eliminated.
Testing the Optimization
Performance was tested by running multiple concurrent processes to process data. Elapsed time was
measured as only the time it took to execute the three stored procedures for the in-processing of the
new data. In different tests, the amount of data changed in each incoming data set varied so that
measurements could be taken with 10%, 18%, 25%, or 100% modified data. Since 10% was determined
to be the most that would ever be seen on any particular processing day, the changed percentage of
10% was used as the main indicator of the amount of improvement the optimizations would yield, and
other percentages were used to give an indication of what might happen should an exceptional day
produce much different data.
To test headroom, the tests were run with 4, 8, 12, and 16 concurrent processes. 8 was considered to be
the number of worker roles that would normally be processing data, so this was the main test of record.
Testing with 12 and 16 concurrent processes allowed us to determine if it was likely that adding worker
roles improved or hurt throughput, and thus evaluate whether bursts above the normal level of
processing could be handled by scaling out the worker role tier.
In the tests, data was processed with no delay between data sets, and the elapsed time to process the
data into the database was recorded. Initially, 8000 tests were run and statistics taken on it to give the
indication. However; the number of tests was reduced to 1000 when comparing just the original stored
procedures with the optimized stored procedures because the original method produced so much
contention that it became obvious with the lower number of tests that the optimizations were
worthwhile.
The comparison between stored procedures with 8 concurrent processes was:
Milliseconds Original Stored Procedures and Tables Optimized Stored Procedures and Tables
AVG 13095.27 422.91
Median 12553.00 356.00
Standard Deviation 3489.15 255.88
Max 32306 2750
Min 4460 210
MS/Device* 1636.91 52.86
* MS/Device was calculated as the average time/number of concurrent processes. It can be read as On average, one device was
processed every ____ milliseconds. One device every 53 milliseconds is well within the requirement of 6 devices per second.
Conclusion
The question of whether to use SqlBulkCopy or TVP is not always a question of which operates faster.
When not all the data that is received actually changes data in the table, using a TVP as a parameter for
a stored procedure, and optimizing appropriately can lead to very significant performance advantages
over other methods that must delete and insert full sets only.
Additionally, ensuring the underlying tables do not make use of IDENTITY columns or other order-forcing
mechanism allows for data modifications to be spread over multiple database pages, thus removing the
potential for contention for the single, last-page where ordered writes of new data will be performed.
Resolving PAGELATCH Contention on Highly Concurrent
INSERT Workloads
Introduction
Recently, we performed a lab test that had a large OLTP workload in the Microsoft Enterprise
Engineering Center. The purpose of this lab was to take an intensive Microsoft SQL Server workload and
see what happened when we scaled it up from 64 processors to 128 processors. (Note: This
configuration is supported as part of the Microsoft SQL Server 2008 R2 release.). The workload had
highly concurrent insert operations going to a few large tables.
As we began to scale this workload up to 128 cores, the wait stats captured were dominated by
PAGELATCH_UP and PAGELATCH_EX. The average wait times were tens of milliseconds, and there were
a lot of waits. These waits were not expected, or they were expected to be a few milliseconds only.
In this TechNote we will describe how we first diagnosed the problem and how we then used table
partitioning to work around it.
Diagnosing the Problem
When you see large waits for PAGELATCH in sys.dm_os_wait_stats, you will want to do the following.
Start your investigation with sys.dm_os_waiting_tasks and locate a task waiting for PAGELATCH, like
this:
SELECT session_id, wait_type, resource_description
FROM sys.dm_os_waiting_tasks
WHERE wait_type LIKE 'PAGELATCH%'
Example Output:
The resource_description column lists the exact page being waited for in the format:
<database_id>:<file_id>:<page_id>.
Using the resource_description column, you can now write this rather complex query that looks up all
these waiting pages:
SELECT wt.session_id, wt.wait_type, wt.wait_duration_ms
, [Link] AS schema_name
, [Link] AS object_name
, [Link] AS index_name
FROM sys.dm_os_buffer_descriptors bd
JOIN (
SELECT *
, CHARINDEX(':', resource_description) AS file_index
, CHARINDEX(':', resource_description
, CHARINDEX(':', resource_description)) AS page_index
, resource_description AS rd
FROM sys.dm_os_waiting_tasks wt
WHERE wait_type LIKE 'PAGELATCH%'
) AS wt
ON bd.database_id = SUBSTRING([Link], 0, wt.file_index)
AND bd.file_id = SUBSTRING([Link], wt.file_index, wt.page_index)
AND bd.page_id = SUBSTRING([Link], wt.page_index, LEN([Link]))
JOIN sys.allocation_units au ON bd.allocation_unit_id = au.allocation_unit_id
JOIN [Link] p ON au.container_id = p.partition_id
JOIN [Link] i ON p.index_id = i.index_id AND p.object_id = i.object_id
JOIN [Link] o ON i.object_id = o.object_id
JOIN [Link] s ON o.schema_id = s.schema_id
The query shows that the page we are waiting for is in a clustered index, enforcing the primary key, of a
table with this structure:
CREATE TABLE HeavyInsert (
ID INT PRIMARY KEY CLUSTERED
, col1 VARCHAR(50)
) ON [PRIMARY]
What is going on here, why are we waiting to access a data page in the index?
Background Information
To diagnose what was happening in our large OLTP workload, its important to understand how SQL
Server handles the insertion of a new row into an index. When a new row is inserted into an index, SQL
Server will use the following algorithm to execute the modification:
1. Record a log entry that row has been modified.
2. Traverse the B-tree to locate the correct page to hold the new record.
3. Latch the page with PAGELATCH_EX, preventing others from modifying it.
4. Add the row to the page and, if needed, mark the page as dirty.
5. Unlatch the page.
Eventually, the page will also have to be flushed to disk by a checkpoint or lazy write operation.
However, what happens if all the inserted rows go to the same page? In that case, you can see a queue
building up on that page. Even though a latch is a very lightweight semaphore, it can still be a contention
point if the workload is highly concurrent. In this customer case, the first, and only, column in the index
was a continuously increasing key. Because of this, every new insert went to the same page at the end
of the B-tree, until that page was full. Workloads that use IDENTITY or other sequentially increasing
value columns as primary keys may run into this same issue at high concurrency too.
Solution
Whenever many threads need synchronized access to a single resource, contention can occur. The
solution is typically to create more of the contended resource. In this case, the contended resource is
the last page in the B-tree.
One way to avoid contention on a single page is to choose a leading column in the index that is not
continually increasing. However, this would have required an application change in the customers
system. We had to look for a solution that could be implemented within in the database.
Remember that the contention point is a single page in a B-tree. If only there was a way to get more B-
trees in the table. Fortunately, there IS a way to get this: Partition the table. The table can be partitioned
in such a way that the new rows get spread over multiple partitions.
First, create the partition function and scheme:
CREATE PARTITION FUNCTION pf_hash (TINYNT) AS RANGE LEFT FOR VALUES (0,1,2)
CREATE PARTITION SCHEME ps_hash AS PARTITION pf_hash ALL TO ([PRIMARY])
This example uses four partitions. The number of partitions you need depends on the amount
of INSERT activity happening on the table. There is a drawback to hash-partitioning the table
like this: Whenever you select rows from the table, you have to touch all partitions. This means
that you need to access more than one B-tree you will not get partition elimination. There is a
CPU cost and latency cost to this, so keep the number of partitions as small as possible (while
still avoiding PAGELATCH). In our particular customer case, we had plenty of spare CPU cycles,
so we could afford to sacrifice some time on SELECT statements, as long as it helped us increase
the INSERT rate.
Second, you need a column to partition on, one that spreads the inserts over the four partitions. There
was no column available in the table for this in the Microsoft Enterprise Engineering Center scenario.
However, it is easy to create one. Taking advantage of the fact that the ID column is constantly
increasing in increments of one, here is a simple hash function of the row:
CREATE TABLE HeavyInsert_Hash(
ID INT NOT NULL
, col1 VARCHAR(50)
, HashID AS CAST(ABS(ID % 4) AS TINYINT) PERSISTED NOT NULL)
With the HashID column, you can cycle the inserts between the four partitions. Create the
clustering index in this way:
CREATE UNIQUE CLUSTERED INDEX CIX_Hash
ON HeavyInsert_Hash (ID, HashID) ON ps_hash(HashID)
By using this new, partitioned table instead of the original table, we managed to get rid of the
PAGELATCH contention and increase the insertion rate, because we spread out the high
concurrency across many pages and across several partitions, each having its own B-tree
structure. We managed to increase the INSERT rate by 15 percent for this customer, with the
PAGELATCH waits going away on the hot index in one table. But even then, we had CPU cycles
to spare, so we could have optimized further by applying a similar trick to other table with high
insert rates.
Strictly speaking, this optimization trick is a logical change in the primary key of the table. However,
because the new key is just extended with the hash value of the original key, duplicates in the ID column
are avoided.
The single column unique indexes on a table are typically the worst offender if you are experiencing
PAGELATCH contention. But even if you eliminate this, there may be other, nonclustered indexes on the
table that suffer from the same problem. Typically, the problem occurs with single column unique keys,
where every insert ends up on the same page. If you have other indexes in the table that suffer from
PAGELATCH contention, you can apply this partition trick to them too, using the same hash key as the
primary key.
Not all applications can be modified, something that is a challenge for ISVs. However, if you DO have the
option of modifying the queries in the system, you can add an additional filter to queries seeking on the
primary key.
Example: To get partition elimination, change this:
SELECT * FROM HeavyInsert_Hash
WHERE ID = 42
To this:
SELECT * FROM HeavyInsert_Hash
WHERE ID = 42 AND HashID = CAST(ABS(42 % 4) AS TINYINT)
With partition elimination, the hash partitioning trick is almost a free treat. You will still add one byte to
each row of the clustered index.
Bulk Loading Data into a Table with Concurrent Queries
Introduction
This article describes load scenarios for the common data warehouse scenario in which many queries
read data from a table while the table is loaded. The key question that administrator/developer has to
answer before deciding on a strategy is whether the read queries can afford to wait. If they can wait,
you wont want to complicate the loading process by running queries simultaneously with the load.
However, if you have a business need to run queries while you load data, read on.
For these tests, we used Microsoft SQL Server 2008. We created two identical tables, each containing
18 million rows totaling 2.6 GB. One table was organized as a heap, and the other was organized as a
clustered index. These tables were used as targets into which data was bulk inserted. The input data file
for the bulk insert had 65,535 rows. We were not concerned about performance differences between
loading heaps or indexes; we were looking instead at the impact of queries concurrent with loads.
One of the tests goals was to understand whether read committed snapshot isolation (RCSI) makes any
difference in query concurrency during loads. Does it help keep readers unblocked from the loading
process? And how does the WITH TABLOCK option affect the bulk loading process?
Bulk Insert into Heap Table
For our first test, we loaded data into the heap with RCSI turned off (that is, with the default READ
COMMITTED isolation level).
BULK INSERT Without TABLOCK hint, Read Committed
To imitate a data warehouse workload, we had five concurrent connections executing random SELECT
statements on the table. While the SELECT statements were running, we started the bulk load
operation. We used the sys.dm_exec_requests dynamic management view (DMV) to monitor requests
on the server, and we observed that the BULK INSERT statement was waiting in the queue with
LCK_M_IX wait type. Figure 1. shows the output of sys.dm_exec_requests while the BULK INSERT
statement was being blocked by the SELECT statement. As soon as the BULK INSERT operation started
loading data (Figure 2), all SELECT statements were blocked by the BULK INSERT statement until it
completed.
Figure 1 : BULK INSERT is waiting for the SELECT statement to complete
Figure 2 : BULK INSERT is loading data, and SELECT statements are blocked
BULK INSERT With TABLOCK hint, Read Committed
For this test, the same five SELECT statements were running concurrently when we started the bulk
insert, but this time we specified the WITH TABLOCK option. This time the sys.dm_exec_requests DMV
indicated that the BULK INSERT operation was waiting with the LCK_M_BU wait type (Figure 3). The
LCK_M_BU wait type occurs in SQL Server when a task is waiting to acquire a Bulk Update (BU) lock. Just
as with our first test, as soon as BULK INSERT started loading data, we saw that the SELECT statements
(Figure 4) were blocked by the bulk insert operation.
Figure 3: BULK INSERT with TABLOCK waiting for the SELECT statement to complete
Figure 4: SELECT statements waiting for the BULK INSERT statement with TABLOCK to complete
For our second test, we did the same test but with RCSI enabled. We wanted to see whether it would
produce different results.
BULK INSERT Without TABLOCK Hint, Read Committed Snapshot Isolation
When we loaded data into the heap table without using the TABLOCK hint, we did not observe any
waits. We concluded that option provides the maximum amount of concurrency, if you need to load
data into a table while you run SELECT queries.
Figure 5: BULK INSERT into HEAP table with no TABLOCK hint
BULK INSERT with TABLOCK Hint, Read Committed Snapshot Isolation
Despite the fact that we were using RCSI while we were loading data into the table, the
sys.dm_exec_requests DMV indicated that the BULK INSERT operation was waiting for a LCK_M_IX lock
and that it was being blocked by an active SELECT statement.
Two other SELECT queries, which were issued after the BULK INSERT command started, were observed
waiting on the LCK_M_S lock type.
Figure 6: BULK INSERT into HEAP with the TABLOCK hint
As soon as the SELECT statement with session_id 59 (in Figure 6) was completed, BULK INSERT started
loading data, with the selects in sessions 55 and 60 continuing to wait until the BULK INSERT command
completed.
We did observe another interesting effect. So far we were dealing only with the BULK INSERT command.
But SQL Server 2008 introduced bulk optimization the INSERT INTO SELECT statement. This operation
behaved like BULK INSERT in all the tests on the heap table. The only exception was that if we loaded
data into the table using INSERT INTO SELECT with the TABLOCK hint under RCSI mode, none of the
readers was blocked.
Isolation level TABLOCK specified?
TIME to load data
(min:sec)
WAIT TYPE
Read Committed NO TABLOCK 3:34 LCK_M_IX
Read Committed WITH TABLOCK 1:28
LCK_M_BU*
Read Committed Snapshot Isolation NO TABLOCK 0:04 NONE
Read Committed Snapshot Isolation WITH TABLOCK 1:03 LCK_M_IX
*Very short; difficult to measure how long it was taken
Table 1: Duration of data loading into the heap table
BULK INSERT into a Table with Clustered Index
Next, we loaded data into the table with a clustered index and with RCSI turned off. This test didnt
show any surprises and behaved as expected: All read operations were blocked while BULK INSERT was
loading data into the table.
If RCSI was enabled, no blocking was observed with BULK INSERT running with concurrent read
operations. As we expected, enabling RCSI eliminated locks for read operations that allowed
simultaneous loading of the data into the table.
Figure 7: BULK INSERT into a table with a clustered index, no TABLOCK hint, and RCSI turned off
Figure 8: BULK INSERT into a table with a clustered index, a TABLOCK hint, and RCSI turned off
Figure 9: BULK INSERT into a table with a clustered index and RCSI enabled
Figure 9 shows SELECT statements reading from the table while a BULK INSERT is running. BULK INSERT
behaved similarly both with and without the TABLOCK hint. This helped us conclude that if you are
loading data with RSCI enabled, the TABLOCK hint in the BULK INSERT statement doesnt play a
significant role, as far as concurrency is concerned.
Isolation level TABLOCK specified?
TIME to load data
(min:sec)
WAIT TYPE
Read Committed WITH TABLOCK 6:49 LCK_M_IX
Read Committed NO TABLOCK 10:18 LCK_M_X
Read Committed Snapshot Isolation WITH TABLOCK 01:01 NONE
Read Committed Snapshot Isolation NO TABLOCK 0:48 NONE
Table 2: Duration of data loading into the table with a clustered index
The only time that readers were blocked by bulk operations was when data was loaded into an empty
table with the clustered index built on it. In that special case only, we observed that read operations
were blocked by bulk load operations and read operations if they had the LCK_M_SCH_S wait type. This
lock was released only after the loading batch was completed.
Conclusion
RSCI can provide great benefits, if you are bulk loading into the table with concurrent readers working
on it . In most cases, it provides you with the ability to read while bulk loads are performed. Bulk loading
does not affect the size of the version store in tempdb under RCSI; however, RCSI cannot be enabled at
the table level. It can be enabled on the entire database only. Therefore you should carefully analyze
your workload to ensure that other operations on your database will not cause the tempdb size to
explode.
Note that RCSI will introduce 14 noncompressible bytes into every row in every table in the database
([Link]
An alternative strategy for concurrent readers is to execute queries using the READ UNCOMMITTED
isolation level (also known as a dirty read), but this requires application changes, and it can deliver
transitionally inconsistent results. RCSI requires no application change, and it guarantees consistent
results.
For more information about data loading strategies and scenarios, see the Data Loading Performance
Guide at [Link]
Section 5: Real World Scenarios
Lessons Learned from Benchmarking a Tier 1 Core Banking
ISV Solution - Temenos T24
Background
TEMENOS T24 is a complete banking solution designed to meet the challenges faced by financial
institutions in todays competitive market. By working with Microsoft, Temenos was able to take
advantage of the latest Windows and SQL Server technologies to tune T24 and run it well on Microsoft
platform. The lessons learned in the tech note were derived from T24 solution tuning engagement, but
most of them apply to other typical OLTP workloads as well.
Benchmark Overview
The benchmark environment, created to reflect real-world retail banking activity volumes, was
made up of 25 million accounts and 15 million customers across 2,000 branches. At peak
performance, the system processed 3,437 transactions per second (TPS) in online business
testing and averaged a record-breaking 5,203 interest accrual and capitalizations per second
during COB testing, processing 25 million accounts in less than two hours. The maximum CPU
utilization of the database server during the peak hour did not exceed 70%, providing
considerable additional capacity. In addition, the testing demonstrated near linear scalability (95
percent) in building up toward the final the hardware configuration.
T24 Architecture
The T24 solution consists of several layers, as shown below, including:
User access
Presentation (clients)
Messaging/connectivity (web servers)
Application (application servers)
Database (database servers)
The application layer accepts messages in a Temenos-specific format called Open Financial Services
(OFS). All requests, from a web browser or from a non-web client, are translated into the OFS format
and then submitted to the application layer. The communication between the messaging/connectivity
layer and the application layer depends on the specific deployment and can use various channels,
including message queues, web services, and a native direct connection between the two layers.
T24 was originally designed to use jBASE, a multidimensional database that uses records consisting of
fields, multi-values (multi-valued lists), and sub-values. OFS messages are transformed into the internal
record format and processed by the application layer; the records are then stored in a jBASE database.
When SQL Server, a supported database system, is used, the jBASE records are transformed into XML
format (or in some cases left as BLOBs) and are stored in the database.
1. SQL Server File Configuration
1.1 Configure Data Files
The filegroup used for the T24 data should be composed of multiple files. Best practice is to use one file
for every two CPU cores on computer systems with 32 or more cores. On computer systems with less
than 32 cores, use the same number of files as the number of CPU cores (the ratio should be 1:1). The
data files should be equal in size. Note that the out-of-the-box configuration uses only one file in the
primary filegroup, so you need to add additional files for optimal configuration.
Pre-allocate enough space in the data files based on the initial size of the computer system. Monitor the
database free space and if necessary extend each file simultaneously so that all of the files have the
same amount of free space. SQL Server optimizes writes by spreading its write operations across the
files based on the ratio of free space among the files, so extending all files at once maintains this
optimization.
Leave the autogrowth setting on as an insurance policy so that SQL Server does not stop when it runs
out of space; however, do not rely on autogrowth to extend the database files as a standard way of
operating. While you should not allocate space for the data files in small units, if you allocate in very
large units during autogrowth, the application must wait (possibly several minutes) while the space is
allocated. Since you cannot control when autogrowth engages, allocate only by the space needed for a
few days of operations.
1.2 Configure Log File
The transaction log file, generally a sequentially written file, must be written as quickly as possible
even before the data is written to the data files (the data portion can be rebuilt from the log if
necessary). While there is no performance benefit from using more than one file, multiple files can be
beneficial for maintenance purposes (for example, if you are running out of space on the log drive).
Adding physical devices to support the LUN can benefit performance.
1.3 Configure tempdb Files
SQL Server tempdb files are used for the storage of temporary data structures. The tempdb files are
responsible for managing temporary objects, row versioning, and online index rebuilds. T24 uses a read-
committed snapshot isolation level as its default isolation level, which uses row versioning. For more
information, see Isolation Levels in the Database Engine.
To ensure efficient tempdb operation:
Create one tempdb file per physical CPU core.
This reduces page free space (PFS) contention.
Pre-size the tempdb files, and make the files equal in size.
Do not rely on autogrow.
Use startup trace flag 1118.
For more information about this SQL Server trace flag, see the article Concurrency
Enhancements for the tempdb Database.
For information on how to set startup settings for SQL Server, see the article Configure Server Startup
Options (SQL Server Configuration Manager.
For further information, see the MSDN article Optimizing tempdb Performance.
2. SQL Server Memory Configuration
2.1 SQL Server Memory Settings
Configure the SQL Server max server memory (MB) setting by taking the amount of memory allocated
to the database system and subtracting one GB for every four cores (round up). This leaves the
operating system with enough memory to work efficiently without having to grab memory back from
SQL Server. For example, if the server has 64 GB of RAM and 24 cores, set the maximum memory to 58
GB (64 GB minus 6 [24 cores divided by 4]).
2.2 Lock Pages in Memory
To reduce SQL Server paging, you can grant the SQL Server service account Lock Pages in Memory
privilege through the Windows Group Policy editor.
For detailed instructions, see How to reduce paging of buffer pool memory in the 64-bit version of SQL
Server on the Microsoft Support site.
3. Recovery Interval Change
Increasing the recovery interval server configuration option causes the checkpoint process to occur less
often. This can reduce the I/O load driven by checkpoints and improve the overall performance. During
lab testing, a recovery interval of 510 minutes has been determined to be the best setting for T24.
Before changing the recovery interval, you should consider its implication on the mean time to recovery
and recovery point objectives. Note that when using failover clustering, a longer recovery interval also
influences the failover time of the database instance.
For more information about the recovery interval option, see the article Recovery Interval Option.
4. Use Trace Flag 834
On computer systems with 64 or more CPU cores, use startup trace flag 834. When this trace flag is set,
SQL Server uses Windows large-page memory allocations for the buffer pool. Allocating buffer pages is
expensive, and turning on trace flag 834 boosts performance.
For more information about this SQL Server trace flag, see Microsoft Support Article 920093
5. Enable Receive-Side Scaling
You should enable Receive-Side Scaling (RSS) on the SQL Server network interface card (NIC) that is
serving the application servers. This setting is found on the Advanced Property tab of the network card.
Also, be sure that offloading options are enabled. See the Microsoft Developer Network (MSDN)
articles Introduction to Receive-Side Scaling and Receive-Side Scaling Enhancements in Windows Server
2008 for more information. If your NIC does not support these options, consider replacing it with one
that does.
You should configure the maximum number of RSS processors by setting the MaxNumRssCpus registry
key value to 8 on a computer system with 32 or more CPU cores. For computer systems with less than
32 cores, use the default setting.
The RSS base CPU number (RssBaseCpu) is the CPU number of the first CPU that RSS can use. RSS cannot
use the CPUs that are numbered below the base CPU number. You should set RssBaseCpu carefully so it
does not overlap with the starting CPU.
Lab testing has shown good results with setting both registry key values to 8 (on a computer system with
more than 32 cores); this means that 8 RSS processors are used starting with core number 8 to process
network traffic.
Note: You should use the Windows RSS registry keys to configure these values instead of NIC settings
because NIC settings can be overridden by the Windows registry keys.
6. Index Fill-factor Change
In high-volume deployments (installations with 10 million accounts and more) of T24, you should
consider a lower fill factor with PAD_INDEX on for indexes on hot tables with high latch contention.
Consider a lower fill factor only if there is need to improve the performance and if excessive latch
contention has been observed. Lab testing has shown good results using a fill factor of 50% for hot
tables.
Page latch contention can be identified by examining the SQL Server: Wait Statistics Page Latch waits
performance counter and querying the dynamic management view sys.dm_os_wait_stats using this query:
SELECT * FROM sys.dm_os_wait_stats
WHERE wait_type LIKE 'PAGELATCH%'
To identify which tables and which pages experience latch contention, you can use the following
queries:
SELECT *
FROM sys.dm_db_index_operational_stats (DB_ID('T24'), NULL, NULL, NULL)
ORDER BY [page_latch_wait_in_ms] DESC, tree_page_latch_wait_in_ms DESC
and
SELECT * FROM sys.dm_os_waiting_tasks
WHERE wait_type LIKE 'PAGELATCH%'
For more information on the fill-factor option for indexes, see the article Fill Factor.
7. Optimizing T24 XQueries Promote Key Attributes/Elements to Relational
Columns
To improve query performance, start by identifying slow-running queries. The following query selects
the top 50 SQL Server statements ordered by the total CPU time (i.e., total amount of CPU time, in
microseconds, for all executions of each statement):
SELECT TOP 50
SUM(query_stats.total_worker_time) AS "total CPU time",
SUM(query_stats.total_worker_time)/SUM(query_stats.execution_count) AS "avg CPU Time",
SUM(query_stats.execution_count) AS "executes",
SUM(query_stats.total_logical_reads) AS "total logical reads",
SUM(query_stats.total_logical_reads)/SUM(query_stats.execution_count) AS "avg logical reads",
SUM(query_Stats.total_logical_writes) AS "total logical writes",
SUM(query_Stats.total_logical_writes)/SUM(query_stats.execution_count) AS "avg logical writes",
MIN(query_stats.statement_text) AS "statement text"
FROM
(SELECT QS.*,
SUBSTRING([Link], (QS.statement_start_offset/2) + 1,
((CASE statement_end_offset
WHEN -1 THEN DATALENGTH([Link])
ELSE QS.statement_end_offset END
- QS.statement_start_offset)/2) + 1) AS statement_text
FROM sys.dm_exec_query_stats AS QS
CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) AS query_stats
GROUP BY query_stats.query_hash
ORDER BY 1 DESC
For T24, we have identified some of the XQueries on the list of the top list. An example of T24 XQuery is:
SELECT [Link],[Link]
FROM F_HOLD_CONTROL t
WHERE [Link](N'/row/c2[.="[Link]"]') = 1
For XQuery like this, we can use scalar promotion to reduce the query runtime. A single-value field (or
even a specific value of a multi-valued field) that is part of the XMLRECORD can be promoted as
computed column of the table and be used in relational search conditions. Further, a relational index
can be created on the computed column to improve the query performance. The detailed steps to
promote a single-value XML field are as follows:
1.) Create a persisted computed column for the specific field.
Create a user-defined function that evaluates the value of the field. The return value of
the function should be a single scalar value. Using this function, the computed column
should be added to the table and persisted.
-- scalar promotion of single valued field
CREATE FUNCTION udf_HOLD_CONTROL_C2(@xmlrecord XML)
RETURNS nvarchar(35)
WITH SCHEMABINDING
BEGIN
RETURN @[Link]('(/row/c2/text())[1]', 'nvarchar(35)')
END
ALTER TABLE F_HOLD_CONTROL
ADD C2 AS dbo.udf_HOLD_CONTROL_C2(XMLRECORD) PERSISTED
2.) Create non-clustered index on the computed column.
After creating the persisted computed column, create an index for this column:
-- example 1
CREATE INDEX ix_HOLD_CONTROL_C2 ON F_HOLD_CONTROL(C2)
Verify optimizations
Verify that the changes are successful and measure the impact of the optimizations.
For scalar promotion (promoted and indexed fields):
Verify the query translation.
Without scalar promotion, T24 uses a query syntax such as:
SELECT [Link],[Link]
FROM F_HOLD_CONTROL t
WHERE [Link](N'/row/c2[.="[Link]"]') = 1
The execution of this query usually uses a table scan to retrieve the results.
After promoting the field c2, query should become:
SELECT [Link],[Link]
FROM F_HOLD_CONTROL t
WHERE t.c2 = '[Link]'
In this case, index lookup on ix_HOLD_CONTROL_C2 is used.
Prove that the index is used by reproducing the query and verifying the actual
execution plan. You can run the query in SQL Server Management Studio and activate
the icon Include Actual Execution Plan on the SQL Editor toolbar.
Alternatively, you can use the SET STATISTICS PROFILE ON statement to display execution
plan information.
Verify the performance of the query has improved.
After using the application for a period of time (e.g., couple of hours or days), use
the sys.dm_db_index_usage_stats dynamic management view to verify the index usage.
Consider the ratio between index reads and index writes, keeping in mind that an index
usually improves the performance for read operations but slows down modifications
(i.e., inserts, updates, deletes) at the same time.
Consider the number of promoted columns and indexes per table. Too many
indexes may degrade the overall performance. As a general rule, you should avoid
creating more than seven indexes on a table for T24.
Do not create XML indexes on T24 XMLRECORD fields. The impact on transaction
latency is too high, and the benefit in query performance is usually not significant.
Section 6: Replication
Initializing a Transactional Replication Subscriber from an
Array-Based Snapshot
Overview
This article describes how to initialize a transactional replication Subscriber from an array-based snapshot rather than
using the native SQL Server snapshot mechanism. Initializing the Subscriber using a SAN-based restore solution is
particularly beneficial for very large databases. In this context, I use the term VLDB to mean a database that is typically
multi-terabyte and requires specialized administration and management This is primarily because the standard
transactional replication initialization process, which is typically restricted by either the network or storage I/O
bandwidth, could take longer than the business service-level agreement (SLA) permits because of the time needed to
initialize or recover the Subscriber. In contrast, initializing a Subscriber using an array-based snapshot utilizes the
Virtual Device Interface (VDI) freeze and thaw mechanism, thereby minimizing recovery time. This procedure is also
particularly beneficial in non-production environments that use transactional replication and require repeatable tests
with large volumes of data.
Scope
This procedure was performed using Microsoft SQL Server 2005 Enterprise Edition IA64 with Service Pack 2 (SP2) and
cumulative update 9 running on Windows Server 2003 Datacenter Edition IA64. The procedure is expected to be
identical in SQL Server 2008; however, this was not tested during the exercise. The storage array was provided by
Hitachi Data Systems (HDS), and HDS Split Second was used to manage the array-based snapshots (backup and
restore of the databases). VERITAS Storage Foundation HA software was used for volume management.
It should be emphasized that even though the hardware listed above was used, the principle of initializing a
Subscriber from an array-based snapshot can be performed using other storage array network (SAN) vendor
technologies specific implementation details will vary. Microsoft recommends that customers attempting this
procedure work closely with an engineer from the storage vendor to ensure the solution are implemented correctly.
When Is This Technique Useful?
There may be situations where the Publisher, Distributor, and Subscriber need to be restored after data loss. This
technique:
Minimizes the transactional replication setup time for the Subscriber through the use of the underlying Virtual Device
Interface (VDI) storage mechanisms, which reduce the time required to back up and restore large volumes of data.
Supports repeated benchmark tests to re-establish a test baseline.
Provides rapid recovery of the Subscriber if data loss has occurred and the database(s) need to be recovered from a
point outside of the distribution retention period.
Background
Transactional replication has been available as a feature in SQL Server since version 6.0. Available functionality has
grown since this time to include tracer tokens to measure latency, concurrent snapshot processing, and peer-to-peer
replication. However, the general premise has remained the same: to replicate a copy or subset of the data to another
database. The Publisher, Distributor, and Subscriber terminology is used to describe the nodes within the topology.
For more information about replication, see SQL Server Books Online.
A transactional replication Subscriber can be initialized using one of the following mechanisms:
a) Transactional replication concurrent snapshot processing
b) Database snapshot (this requires SQL Server 2005 Enterprise Edition with Service Pack 2)
c) Initialize from log sequence number (LSN) (SQL Server 2008 only)
d) SQL Server backup (initialize from backup)
e) Copy of the data or array-based restore
A summary of the pros and cons for each technique is presented in a table below.
Option (a) Transactional replication concurrent snapshot processing does not require an outage, and it allows
production activity to occur on the Publisher while the initialization process is copying the schema and data to the
Subscriber. Concurrent snapshot processing does not hold shared locks during snapshot generation, thereby allowing
production activity to continue. However, a schema modification lock (Sch-M) is taken for a brief period. In contrast to
option (d) and (e), this procedure also has the benefit of only copying the schema and data that is required rather
than making a complete copy of the database.
Option (b) SQL Server 2005 Enterprise Edition with Service Pack 2 introduced a new snapshot mechanism that
permits the initialization of the Subscriber from a database snapshot. The database snapshot functionality was
introduced in SQL Server 2005 to provide a read-only static view of the database using sparse files. Before a page is
updated in the source database, the original page is copied to the sparse file. Subsequent updates to the same
modified page do not prompt a repeat of this procedure. That is, the pre-change copy of a particular page only
pushes into the database snapshot once after the database snapshot is created. It is possible to initialize a
transactional replication Subscriber from a database snapshot as described above. This procedure is similar to the
concurrent snapshot processing described in option (a) in that it permits transactional activity during the initialization
procedure. However, although a database snapshot utilizes the sparse file capability of NTFS, with a lot of concurrent
updates, the database snapshot may grow. It is important to ensure that sufficient storage space is allocated for the
database snapshot sparse files to store data pages that have been updated during the Subscriber initialization period,
because the database snapshot stores the pre-updated page images.
Option (c) Initialize from LSN was introduced in SQL Server 2008 to aid the configuration of peer-to-peer
transactional replication topologies. Initializing from an LSN can also be used in disaster recovery scenarios; instead of
performing a full re-initialization of the Subscriber database, you can initialize a Subscriber from an LSN. This means
that the Distribution Agent will apply transactions (after the supplied LSN), from the distribution database to the
Subscriber, as long as the distribution retention period has been set appropriately.
Option (d) - A Subscriber can also be initialized from a SQL Server backup. For more information, see Initializing a
Transactional Subscription without a Snapshot in SQL Server Books Online ([Link]
us/library/[Link]). This approach restores the complete dataset on the Subscriber and does not require an
outage for the initialization, as long as the distribution database stores the in-flight transactions that were taken after
the backup. Post-configuration administrative procedures may be required to remove unwanted objects and data on
the Subscriber database.
Option (e) - It is also possible to initialize the Subscriber using a copy of the data. The database can be provisioned
using any mechanism that will copy the Publisher schema and data to the Subscriber, such as a manual network file
copy of the data and log files, a native SQL Server restore, or alternatively, an array-based restore. While this approach
does require an outage during the initialization process, an array-based restore is particularly beneficial for very large
databases (VLDBs) in either production or benchmark environments where the setup time can be minimized by using
the VDI mechanisms. This technique was used to initialize the Subscriber from a HDS-array based snapshot, which is
the focus of this article, and is discussed in more detail in the rest of this article. For more information about VDI, see
SQL Server 2005 Virtual Backup Device Interface (VDI) Specification
([Link]
adfe15e850fc&displaylang=en
The table below summarizes the pros and cons of the transactional replication snapshot techniques.
Initialization technique
Online
Pros
Cons
Use when
Concurrent snapshot
processing
Yes
Available in Workgroup,
Standard, and
Enterprise editions,
allows online
processing, generates a
snapshot only for
published objects.
Duration of Subscriber
initialization is
impacted by size of
database, network, and
so on. DML statements
can be expensive.
Online processing is a requirement,
storage space is limited on the
Subscriber.
Database snapshot
Yes
No locking on the
Publisher database, in
comparison to
concurrent snapshot
processing.
Enterprise Edition only.
Requires sufficient
storage for the
snapshot sparse files.
Online processing is a requirement,
concurrency is essential, sufficient
storage exists for the sparse file.
Initialize from LSN
Yes
Full snapshot not
required, allows online
processing.
SQL Server 2008
Enterprise only, must
be able to determine
correct LSN.
Only consider if a full snapshot is
not feasible and not necessary.
May also be suitable for
environments that use database
mirroring and transactional
replication.
SQL Server Backup
(initialize with backup)
Yes
Full snapshot not
required, allows online
processing.
Restores a complete
copy of the database on
the Subscriber (for
example, requires more
storage space) and
persists copies of all
objects unless
removed.
Data volume is manageable and
offline initialization is not an
option. Speed of backup and
restore is preferable to use of the
BCP utility to copy data.
Copy of data /
array-based restore
No
Rapid backup/restore
of large data volumes,
replication snapshot is
not required.
Offline processing,
must stop transactional
activity, requires a
complete copy of the
data on the Subscriber.
Volume of data is in the terabyte
range and backup and restore
times are a priority.
Pros and Cons for Transactional Replication Initialization Techniques
Setup Procedure Initializing the Subscriber from an Array-Based Snapshot
The procedure to initialize the Subscriber from a copy of the data is similar to using the native SQL Server restore
mechanism. However, the array-based approach is more useful for large databases, because hardware
implementations of the VDI freeze and thaw mechanism allow large amounts of data to be copied quickly and in a
transactionally consistent manner, thereby reducing the restore time.
High-Level Steps
The following procedure was used to initialize the Subscriber during a high-end benchmark with a multi-terabyte
database. The backup and restore of the database was performed using the HDS array capabilities. This includes the
use of HDS Split Second, which is a command-line utility developed by Hitachi Consultancy Services. We also tested a
full database backup operation with SQL Server 2005. However, this was simply to benchmark the operation for a 17-
terabyte database.
It is important to note that application activity must be paused during this operation, because any change in data at
the Publisher will result in data inconsistency. This inconsistency will be raised as an alert in Replication Monitor. One
can use tablediff, a command line utility that returns detailed information about the differences between two tables.
It can also generate a Transact-SQL script to bring a subscription into convergence with data at the Publisher. This
utility can be used to correct data inconsistencies; however, it is recommended that precautionary steps be put in
place to ensure that the application or any other activity cannot write to the Publisher or Subscriber databases during
this procedure. These steps are discussed later. Alternatives to this approach that allow transactional activity during
the setup procedure include concurrent snapshot processing, initialization from backup, and initialization from a
database snapshot. Generating a replication snapshot for this volume of data would take many hours, so we elected
to initialize the Subscriber using an array-based restore in order to minimize the movement of data.
Initializing the Subscriber
The following steps were used to initialize the Subscriber from the array-based restore. The majority of the time was
consumed by the backup and restore procedures using HDS Split Second. This was approximately two hours. The
other activities were DBA operations to modify and validate the replication metadata.
1. Pause application activity.
2. Disable the application login(s) using ALTER LOGIN <login> DISABLE.
3. Ensure the database is in RESTRICTED_USER WITH ROLLBACK IMMEDIATE mode to clear user connections that may
still be in the database. Please note that RESTRICTED_USER does not prevent access to applications run under the
context of sysadmin, dbcreater, or sysadmin.
4. Back up the primary (Publisher) database using a storage array-based mechanism.
5. Restore the primary (Publisher) database on a separate instance using the storage array-based mechanism. This
database will become the Subscriber.
6. Recover the Subscriber database and check the SQL Server error log to verify completion of the recovery operation.
7. Set the RESTRICTED_USER mode on the subscription database to prevent any user activity. This is simply an
additional precautionary step to ensure that applications or users cannot access the database. Please refer to step 3
above, because the same caveat applies.
8. Enable the NOT FOR REPLICATION value on the Subscriber tables (and any other objects that will be published for
replication). The NOT FOR REPLICATION option enables you to specify which database objects are treated differently
if a replication agent performs a transactional operation. For example, the identity column value is not incremented if
a replication agent performs an insert operation. For more information, see Controlling Constraints, Identities, and
Triggers with NOT FOR REPLICATION in SQL Server 2005 Books Online ([Link]
us/library/ms152529(SQL.90).aspx) or SQL Server 2008 Books Online ([Link]
us/library/[Link]).
9. Create the publication(s) on the Publisher.
10. Drop any redundant columns on the subscription database using ALTER TABLE DROP COLUMN. For our tests,
binary large object (BLOB) columns were dropped, because we did not replicate columns of this data type (primarily
to reduce the storage requirement on the Subscriber). We did not drop any other objects, because there were
scenarios where we wanted to be able to view or query copies of nonpublished tables on the Subscriber.
11. Optional: Reclaim storage space on the Subscriber by using DBCC CLEANTABLE in the subscription database and
by specifying a batch size to reduce the impact on the transaction log. In previous smaller volume tests, we also
reclaimed the BLOB storage space, using DBCC CLEANTABLE with a batch size of 100,000. If a batch size is not
specified, DBCC CLEANTABLE processes the whole table in one transaction and the table is exclusively locked during
the operation. This can require considerable transaction log space for very large tables.
12. Create the subscriptions using the replication support only sync_type of sp_addsubscription. This is a key
component of the process, because it indicates that the Subscriber already has a copy of the schema and data and
does not require initialization with a replication snapshot.
13. Perform DBA checks. For this exercise, we used Transact-SQL scripts to ensure that the number of objects marked
for publication was consistent with the number of objects marked for replication at the Subscriber. For example:
-- Count the number of columns with the NOT FOR REPLICATION option set to 1.
-- Execute this on the Publisher and Subscriber to ensure the count is consistent.
-- Investigate further if there is a difference.
USE <Publisher or Subscriber>
GO
SELECT COUNT(*) NOT_FOR_REPL_IDENT_Tables
FROM sys.identity_columns
WHERE is_not_for_replication = 1
AND OBJECTPROPERTY(OBJECT_ID, 'IsMSShipped') = 0;
Similar statements can also be executed for foreign keys, triggers, and constraints that may require this property to be
set.
14. Enable the publication and subscription databases for MULTI_USER activity.
15. Enable the application login(s); for example, use ALTER LOGIN <login> ENABLE.
It is advisable to launch Replication Monitor ([Link]) to verify that the publications are healthy and that the
Log Reader and Distribution agents are not reporting any problems. Similarly, posting a tracer token will also provide
a good idea of latency between the databases.
Restoring the Databases for Repeated Benchmark Tests Initializing the Subscriber from an
Array-Based Snapshot
There may be situations where the transactional replication databases need to be restored to a previous point in time,
for example, in a benchmark environment to re-establish the baseline for repeated tests. This eliminates the need to
re-create the replication objects and also synchronise the Publisher and Subscriber, which can be time-consuming for
large databases.
The procedure is similar to that shown above, with the added advantage that SQL Server transactional replication has
already been configured and is functioning correctly.
The following steps were used to initialize the transactional replication Publisher, Distributor, and Subscriber from the
array-based restore prior to each test cycle.
1. Pause application activity.
2. Disable the application login(s) by using ALTER LOGIN <login> DISABLE; on both the publication and subscription
databases.
3. Ensure that the Publisher database is in RESTRICTED_USER WITH ROLLBACK IMMEDIATE mode to clear user
connections that may still be in the database. Connections under the context of sysadmin, dbcreator, or db_owner
will still be allowed.
4. Stop the SQL Server Agent services on both the Publisher and Subscriber, or disable the Log Reader and
Distribution agent jobs to ensure that the array-based backup and restore mechanisms have exclusive access to the
database.
5. Set RESTRICTED_USER mode on the subscription database to prevent any user activity.
6. Use the HDS array-based restore mechanisms to:
a. Restore the Publisher database.
b. Restore the distribution database.
c. Restore the Subscriber database.
Note: For large OLTP databases with high transaction volume, you may observe intermittent I/O in System Monitor or
Performance Monitor ([Link]) after the restore and recovery. This is typically due to the GHOST_CLEANUP
process, which is removing records that have been marked for deletion. This process can be observed in the
sys.dm_exec_requests catalog view.
7. Start the SQL Agent Service on the Publisher, Distributor, and Subscriber.
8. Enable the Publisher and Subscriber databases for MULTI_USER activity.
9. Enable the application login(s); for example, use ALTER LOGIN <login> ENABLE.
As with the previous procedure, it is recommended that Replication Monitor be launched to verify the health of the
publications. The Log Reader and Distribution agents should also be launched before the application logins are
enabled.
The restore of the Publisher, Distributor, and Subscriber databases can be performed in parallel using the HDS array-
based restore mechanism. This approach was used to re-establish the baseline for repeated benchmark tests.
Observations and Data Points
A number of observations were documented during the initialization of the Subscriber during the benchmark. This
relates to both the initial setup as described above and also to repeated tests conducted during the benchmark.
Figure 1.0 below provides a graphical illustration to accompany the following data points. Numbers denote phases in
figure 1.0.
1. The data was first loaded into the primary OLTP database without using transactional replication.
2. During this data load phase, the data was shadowed to a second set of volumes. Owing to the volume of data, this
was deemed the most appropriate mechanism to provision the Subscriber database due to the volume of data.
Loading data with transactional replication enabled was not appropriate, because the operation is fully logged
regardless of the database recovery model.
3. After the data load had completed, the volumes were dismounted from the Publisher and mounted on the
Subscriber. Transactional replication was then configured using the replication support only sync_type parameter to
avoid the need for a replication snapshot.
4. The 17-terabyte Subscriber database was then paired with a set of volumes as a backup. This process took
approximately 17 hours.
A full backup of the 17-terabyte OLTP database using the native SQL Server 2005 backup mechanism took
approximately four hours using eight streams. Please note that this was conducted on SQL Server 2005 Enterprise
Edition. SQL Server 2008 may offer additional gains due to the native backup compression. For the native SQL Server
2005 backup procedure, the following Transact-SQL script was executed.
USE [master]
GO
DECLARE
@DateStr nvarchar(50),
@Stmt nvarchar(1000),
@Filepath nvarchar(50),
@Quote nvarchar(1),
@Databasename nvarchar(50)
SET @Quote = char(39)
SET @Filepath = 'DISK=' + @Quote + '<directory>'
SET @Databasename = '<database>'
SELECT @DateStr = N'_' +
CONVERT(nchar(8),
getdate(), 112) +
N'_' +
RIGHT(N'0' + rtrim(CONVERT
(nchar(2), datepart(hh, getdate()))), 2) +
RIGHT(N'0' + rtrim(CONVERT
(nchar(2), datepart(mi, getdate()))), 2) +
RIGHT(N'0' + rtrim(CONVERT
(nchar(2), datepart(ss, getdate()))), 2)
SELECT @Stmt =
'BACKUP DATABASE ' + @databasename + ' TO ' +
@Filepath + @databasename + @DateStr + '_1.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_2.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_3.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_4.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_5.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_6.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_7.BAK' + @Quote + ', ' +
@Filepath + @databasename + @DateStr + '_8.BAK' + @Quote +
' WITH NOFORMAT, NOINIT, SKIP, NOREWIND, NOUNLOAD, STATS = 5'
EXEC (@Stmt)
You should modify the @Filepath and @Databasename variables if script reuse is intended.
5. New gold (baseline) backups were occasionally required due to schema changes like indexes or partitioning for
performance reasons. This HDS Split Second backup took approximately 30 minutes to complete; this was also a
function of how much data had changed since the last restore, because the data had changed during the test cycle.
While this backup process typically occurs almost instantaneously in the background, we opted to wait for the
completion signal from the array before commencing a new test. SQL Server 2005 differential database backup
timings were comparable and took approximately the same amount of time.
6. Following a test run, a restore of the Publisher, Distributor, and Subscriber databases took approximately two hours.
This was also a symptom of the data that had changed during the test. However, approximately one hour and thirty
minutes of this duration was due to the import and export of the VERITAS volumes, which was sequential in nature.
The Publisher and Subscriber databases were restored in parallel, thereby reducing the restore time for the repeated
benchmark tests. The Distributor could be restored in parallel; however, we opted to conduct this step separately,
because we wanted more control over the procedure.
Figure 1.0. Benchmark Data Points
As previously mentioned, before commencing a test, ensure that the GHOST_CLEANUP process has completed on
both the Publisher and Subscriber databases (following a restore), because it may affect I/O measurements taken
during the test.
Summary
Initializing a transactional replication Subscriber from an array-based restore is beneficial when the data volume is
very large and the restore and configuration time need to be minimized. Though this can still be achieved using any
other restore or data copy mechanism, the benefit of an array-based VDI freeze and thaw, which allows large amounts
of data to be copied quickly and in a transactionally consistent manner, significantly reduces backup and restore time.
Upgrading Replication from SQL Server 2000 32-Bit to SQL
Server 2008 64-Bit without re-initialization
Summary
In this article we summarized the experiences we gathered during the planning and upgrade
of a customer's project. The customer is one of the largest retail shops in its area. Their
architecture is made up of a main server, which is located at headquarters and hosts several
hundred publications containing over a thousand articles. Regional stores are located all over
the country acting as subscribers for the publications hosted on the main server. Based on the
business needs and infrastructure limitations the customer set the following goals for the
project:
Install all SQL Server instances on the Windows Server 2003 x64 platform.
Upgrade the publisher and distributor from the 32-bit edition of SQL Server 2000 to the 64-bit
edition of SQL Server 2008. (Upgrading subscribers was not a goal of this phase of the project.)
Avoid replication re-initialization.
This paper describes how to upgrade replication members such as Publishers and Distributors
from the 32-bit edition of SQL Server 2000 to the 64-bit edition of SQL Server 2008. Both
clustered and nonclustered environments are discussed.
Section 7: Service Broker
SQL Server Service Broker: Maintaining Identity
Uniqueness Across Database Copies
A Deployment Method: Copying Databases
When dealing with many large customers, they often develop new and interesting ways of using technology and
deploying it. One such case concerning service broker is with large, scale-out deployments consisting of hundreds of
servers where the same database will be copied and deployed many times over. This database will contain all of this
particular customer's service broker standards and custom built pieces for how broker will operate in their
environment. As a package this database can be easily copied and deployed across many instances and servers
through 1) "copy & attach," or 2) backup & restore. The caveat for this approach is that each of these databases will
contain the same service broker identity. Therefore, we need to reset it. For more details on Managing Service
Broker Identities see Books Online (BOL):
[Link]
We will get to the relevance of having an actual globally unique service broker identity shortly. However, it's
important to recognize the database properties that are modified when a database is attached or restored. For our
discussion relevant to service broker, I'll point out that the following values are set to 0 when the database is attached
or restored:
is_broker_enabled
is_honor_broker_priority_on
is_trustworthy_on
You can view database properties by querying [Link], see BOL:
[Link]
SELECT
name
,is_broker_enabled
,is_honor_broker_priority_on
,is_trustworthy_on
,service_broker_guid
FROM [Link]
Just after detaching, making a couple copies and attaching the 3 databases, youll notice that all of my databases
have the same service_broker_guid. There is no way to know the purpose, function or environment in which the
database is attached or restored. This is why service broker is disabled by default. Now that the database has been
duplicated, we need to reset the service broker identity as follows:
ALTER DATABASE [MySSBDBCopy1] SET NEW_BROKER
ALTER DATABASE [MySSBDBCopy2] SET NEW_BROKER
GO
SELECT
name
,is_broker_enabled
,is_honor_broker_priority_on
,is_trustworthy_on
,service_broker_guid
FROM [Link]
Giving the database a new service broker identity will enable service broker (is_broker_enabled=1) and will remove all
service broker messages and conversations in the database with sending end dialog messages. Again, in our scenario
since we are deploying a "standards" database we will assume there are no messages or conversations in the
database.
Service Broker Diagnostic Utility
Having 1 or more databases with the same service broker guid may not seem problematic. However, service broker
does assume that this guid is globally unique across "time and space" independent of the location, instance and
server. When messages are sent and received i.e. the initiator and target are the same database, having multiple
identical guids may not pose a problem. Unless you'd like to use the new service broker diagnostic tool called the
ssbdiagnose Utility. See BOL for more detail: [Link]
SSBDiagnose is new in SQL Server 2008 however it can be used in SQL Server 2005 only environments or mixed. The
utility will error when used against an instance where there are 2 or more databases having the same service broker
guid. Yes, it is a problem even if the database being diagnosed has a guid that's not duplicated. The Service Broker
Diagnostic Utility will generate output similar to:
Microsoft SQL Server 10.0.1442.23
Service Broker Diagnostic Utility
D 29997 MYSERVER\INST2 SSBReceiver1 Service Broker GUID is identical to that of database SSBReceiver
on server MYSERVER\INST2
D 29997 MYSERVER\INST2 SSBReceiver2 Service Broker GUID is identical to that of database SSBReceiver
on server MYSERVER\INST2
D 29905 MYSERVER\INST2 SSBReceiver Service Broker is not enabled in the database
3 Errors, 0 Warnings
To summarize, when making a copy of a database establish a new service broker identity. This will avoid any potential
future conflicts and will guarantee that your service broker identity will truly be a G.U.I.D.!
Section 8: Troubleshooting
Diagnosing Transaction Log Performance Issues and Limits
of the Log Manager
Overview
For transactional workloads I/O performance of the writes to the SQL Server transaction log is critical to both
throughput and application response time. This document discusses briefly how to determine if I/O to the transaction
log file is a performance bottleneck and how to determine if this is storage related; a limitation is due to log manager
itself or a combination of the two. Concepts and topics described in this paper apply mainly to SQL Server 2005 and
SQL Server 2008.
Monitoring Transaction Log Performance
To determine if I/O performance of transaction log writes is a problem there are several tools which can help quickly
isolate bottlenecks related to the log writes. These are:
1. SQL Server Dynamic Management Views (DMVs).
a. sys.dm_os_wait_stats: This DMV exposes a number of wait types documented here. The wait type most relevant
the current discussion is WRITELOG. WRITELOG waits represent the time waiting on a log I/O to complete after a
COMMIT has been issued by the transaction. When observed these should be an indication I/O performance and
characteristics of the log writes should be pursued.
b. sys.dm_io_pending_io_requests: This DMV exposes outstanding I/O requests at the individual I/O level.
Documentation for this DMV can be found here. In scenarios where the SQL Server transaction log file is not on a
dedicated volume this DMV can be used to track the number of outstanding I/Os at the file level. If the transaction
log is on a dedicated logical volume this information can be obtained using Performance Monitor counters. More
details on both are given below.
2. Window Performance Monitor SQL Server:Databases Object. This performance
monitor object contains several counters specific to performance of a transaction log for a specific database. In many
cases these can provide more detailed information about log performance as the granularity is at the log level
regardless of the logical storage configuration. The specific counters are:
a. Log Bytes Flushed/sec
b. Log Flushes/sec - (i.e. I/O operation to flush a log record to the transaction log)
c. Log Flush Wait Time
3. Windows Performance Monitor Logical or Physical Disk Objects. There are five
counters that should be considered in any I/O analysis:
a. Current Disk Queue Length
b. Avg. Disk/sec Read, Avg. Disk/Write
c. Avg. Disk Bytes/Read, Avg. Disk Bytes/Write
d. Disk Reads/sec, Disk Writes/sec
e. Disk Read Bytes/sec, Disk Write Bytes/sec
The key counters to monitor with respect to log performance are the Current Disk Queue Length and Avg. Disk/sec
Write.
These are the primary tools which are used to monitor I/O performance of the transaction log and diagnose any
bottlenecks related to transaction log throughput. When troubleshooting any performance issue it is critical to look
first at the complete overall system performance before focusing on any individual item. For the purpose of this
discussion we will focus on diagnosing transaction log performance.
How Do I Determine if I Have a Log Bottleneck?
The quickest way to determine if you think performance issues are related to transaction log performance is to
monitor sys.dm_os_wait_stats for high waits on WRITELOG. It is important to understand that this counter is
cumulative since SQL Server was last restarted so monitoring deltas of these values over specific time periods is
necessary to provide meaningful details.
There are two primary questions to answer when investigating a performance issue which is suspected to be log
related.
1. Is the performance of the I/O subsystem adequate to provide healthy response times to I/O issued against the log?
2. Am I hitting any limits imposed by SQL Server related to transaction log behavior?
In the majority of experiences observed related to I/O performance issues, improperly sized or poorly configured
storage is the primary contributor to I/O issues. This can be made worse by queries which are not tuned and issue
more I/O than necessary affecting everyone using the I/O subsystem. In addition, there are other factors that will have
some impact on log including things such as transactional replication, log backups, mirroring, storage level replication
etc...
With respect to #1 our recommendation for response time on the log device should be in the range of <1ms to 5ms.
It is important to keep I/O response time on log devices as low as possible which is the primary reason we
recommend isolating logs from data files on separate physical spindles. Writes to the transaction log are sequential in
nature and benefit by being isolated on to separate physical devices. In todays modern storage environments there
are many considerations which may make this impractical so in the absence of the ability to do this focus on keeping
response times in the healthy range.
Limits of the Log Manager
Within the SQL Server engine there are a couple limits related to the amount of I/O that can be in-flight at any given
time; in-flight meaning log data for which the Log Manager has issued a write and not yet received an
acknowledgement that the write has completed. Once these limits are reached the Log Manager will wait for
outstanding I/Os to be acknowledged before issuing any more I/O to the log. These are hard limits and cannot be
adjusted by a DBA. The limits imposed by the log manager are based on conscious design decisions founded in
providing a balance between data integrity and performance.
There are two specific limits, both of which are per database.
1. Amount of outstanding log I/O Limit.
a. SQL Server 2008: limit of 3840K at any given time
b. Prior to SQL Server 2008: limit of 480K at any given time
c. Prior to SQL Server 2005 SP1: based on the number of outstanding requests (noted below)
2. Amount of Outstanding I/O limit.
a. SQL Server 2005 SP1 or later (including SQL Server 2008 ):
i. 64-bit: Limit of 32 outstanding I/Os
ii. 32-bit: Limit of 8 outstanding I/Os
b. Prior to SQL Server 2005 SP1: Limit of 8 outstanding I/Os (32-bit or 64-bit)
Either of the above limits being reached will cause the suspension of the Log I/O until acknowledgments are received.
Below are two examples which illustrate the above and provide guidance on how to determine how isolate whether or
not these limits are being reached.
Example 1
This example illustrates the limit of 32 outstanding I/Os on the 64-bit edition of SQL Server 2008. The Current Disk
Queue Length counter is represented by the black line in the graph. The workload was executing approximately
14,000 inserts per second with each insert being a single transaction. The workload was generated using a multi-
threaded client. The disk response time in this example is averaging 2ms. High wait times on WRITELOG were
observed as this workload throughput level was approached suggesting investigation of disk throughput on the log
device. This led us to investigate if the 32 outstanding I/O limit was the cause.
In Figure 1 below we can see the limit of 32 outstanding I/Os clearly being reached. We can also determine it is the
limit of outstanding I/Os vs. the limit of 480K outstanding log I/O based on the following calculations (averages
over this period of time as observed via Performance Monitor).
(9.6MB Log Bytes Flushed/sec)/(5000 Log Flushes/sec) = ~1.9K per Flush
(32 Outstanding I/Os)*(1.9K per Flush) = ~60K in-flight at any given time (far below the limit)
Figure 1: Performance Monitor graph illustrating the limit of 32 outstanding I/Os
In this particular scenario the Current Disk Queue Length could be utilized to diagnose the bottleneck because the log
resided on its own logical volume (in this case H:\) . As discussed above, if the log did not reside on a dedicated
logical volume, an alternative approach to diagnosing the limit being encountered would have been to use the
sys.dm_io_pending_io_requests DMV discussed previously.
The following is a sample query that could be used to monitor a particular log file. This query needs to be run in the
context of the database being monitored. It is worth noting that the results returned by this query are transient in
nature and polling at short intervals would likely be needed for the data to be useful much in the same way
performance monitor samples the disk queue.
select vfs.database_id, [Link], df.physical_name
,vfs.file_id, ior.io_pending
,ior.io_handle, vfs.file_handle
from sys.dm_io_pending_io_requests ior
inner join sys.dm_io_virtual_file_stats (DB_ID(), NULL) vfs on (vfs.file_handle = ior.io_handle)
inner join sys.database_files df on (df.file_id = vfs.file_id)
where [Link] = '[Logical Log File Name]'
For this example, the best strategy to resolve or optimize the transaction log performance would be to isolate the
transaction log file on a storage device which was not impacted by the checkpoint operations. In our storage
configuration this was not possible and shared components (specifically array controllers) were the primary source of
the problem.
Example 2
This example is a little more complicated but illustrates the limit of 480K in-flight data running SQL Server 2005 SP2
(64-bit edition). This was observed during a benchmark of a high throughput OLTP application supporting
approximately 15,000 concurrent users. Characteristics of this application were a bit different in that the log flush sizes
were much larger due to logging of larger transactions (including BLOB data). Again, high waits on the WRITELOG
wait type were observed pointing in the direction of investigating I/O performance of the log device.
One important item to note when considering this data is that the log behavior over this period of time is spiky in
nature and it is during the spikes the limits are encountered. If we look at averages over the problem time period we
see the following
(4,858,000 Log Bytes Flushed/sec)/(124 Log Flushes/sec) = 39,177 bytes per flush
5.4 ms per log flush but there are spikes. This is on the edge of health latency for the log.
Current Disk Queue Length shows spikes and when Current Disk Queue Length * Bytes per flush is close to 480KB
then we will start to see the Log Manager throttle the writes (observed as higher wait times on WRITELOG).
Figure 2 illustrates this although it is not as obvious to see as the pervious example. The correlation to watch for is
(Average Bytes per Flush) * (Current Disk Queue Length) near the 480K limit.
Figure 2: Performance Monitor graph illustrating the limit of 480K outstanding log I/O data
A strategy for resolving this problem would be to work on the storage configuration to bring the latency on the log
writes to a lower average. In this particular scenario, log latency was impacted by the fact that synchronous storage
replication was occurring at the block level from the primary storage array to a second array. Disabling storage level
replication reduces the log latency from the 5ms range to the range of <1ms however disaster recovery requirements
for zero data loss made this not an option for the production environment.
It is worth noting that this was observed on SQL Server 2005 SP2 and migrating to SQL Server 2008 resolved the
bottleneck since the limit in SQL 2008 is 8 times that of SQL Server 2005.
In either example reducing log latency is critical to increasing the transactional throughput. In very high throughput
scenarios at the extreme end we have observed customers approaching this by alternative methods including 1)
utilizing minimal logging capabilities of SQL Server when possible and 2) scaling out the transaction log through
partitioning data into multiple databases.
Summary
The above provides information on limits imposed by the SQL Server Log Manager. These limits are based on
conscious design decisions founded in providing a balance between data integrity and performance. When
troubleshooting performance issues related to log performance, always consider optimizing the storage configuration
to provide the best response time possible to the log device. This is critical to transactional performance in OLTP
workloads.
Eliminating Deadlocks Caused By Foreign Keys with Large
Transactions
Executive Summary
Validating foreign keys requires additional work for data modification operations and can lead to deadlocks in certain
scenarios. There are, however, ways you can work around this contention and eliminate the possibility of deadlocks
while checking referential integrity.
In this technical note, we describe a recent test lab in which we were able to observe how SQL Server behaves at scale
with tables that use foreign keys. We discuss what we learned about how SQL Server locking and access strategies can
change as the data size changes and we describe the effects these changes can have. We also describe how the
deadlocking was resolved in our case by preventing different transactions from requesting locks on the same rows or
pages, forcing a direct access of the rows and forcing all locks to be taken at the lowest possible granularity. This
allowed for very high levels of concurrent imports on tables that use foreign keys.
Introduction
In a typical parent-child modification operation, one table is modified before the related table or tables are modified.
In the case of inserts, the parent table is modified first; when the child table receives the corresponding insert, a
verification step is initiated. In the case of deletes, the child table is removed first; when the parent table is deleted, a
verification step is carried out to ensure that no child records exist for the parent record. It is in the verification steps
that deadlocking can occur.
When Microsoft SQL Server operates with large INSERT SELECT combo statements or with large DELETE statements,
SQL Server might choose verification methods that increase the chances of deadlocking. Disabling page locks and
lock escalation on certain objects involved in these transactions and specifying OPTION (LOOP JOIN) might be
necessary to prevent deadlocking in systems that have multiple concurrent large transactions on tables that use
foreign keys.
In a recent test lab, we were able to observe how SQL Server behaves at scale with tables that use foreign keys and we
tested these locking and access strategies that can prevent deadlocking. In our case, 24 or 48 processes imported
data concurrently. Each of these processes performed a bulk insert into a local temporary table where the data was
scrubbed and converted. Each process then used an INSERT SELECT combo statement to move the scrubbed data
into the permanent tables.
Our system performed well with low volumes of data in the tables and with low volumes of data in the import
transactions. However, severe deadlocking surfaced that effectively prevented concurrent imports as the tables grew
and as the size of the data imported from the temporary staging tables expanded. We observed that this deadlocking
occurred even though different import processes dealt strictly with different sets of data that were loaded into the
same tables.
To resolve deadlocking, we typically resulted had to rollback most of the concurrent transactions, a major setback
when processing these large amounts of data. Moreover, retrying the rolled-back transactions often resulted in
additional deadlocks.
Validation of Foreign Keys
In our benchmark, foreign keys were a critical part of the database design. Dropping foreign keys was not considered
a viable option. Everyone understood that foreign keys would require additional work for data manipulation
operations, but this was deemed to be an equitable tradeoff for maintaining the data integrity, regardless of the
source of the data manipulation operation.
SQL Server places the validation of foreign keys into the query plan after the data is inserted into the target table. You
see the validation of foreign keys as a join to the table against which the data must be validated.
Figure 1 shows a query plan with foreign key validation.
Figure 1: Execution plan showing SQL Server using a loop join to verify referential integrity
In Figure 1, an INSERT SELECT statement is used to insert data into [Link] from #detailData. The table
[Link] has a foreign key that references [Link].
Starting from the upper right, you can see the flow of the data as SQL Server first scans the #detailData table for the
data to be inserted and then inserts the data into [Link]. After inserting the data into the base table, SQL
Server sorts the data so that the rows can also be inserted into the non-clustered index ncl_referringTable. This sort
makes the insert into that non-clustered index most efficient, but note that an estimated 42% of the total query cost
is spent on sorting the data in the order of the non-clustered index.
After the data is inserted into [Link] and all of its associated indexes, SQL Server verifies that referential
integrity is maintained by using a loop join operation to perform a left semi-join to [Link]. Note that the
row count spool operation that is used to support this join accounts for an estimated 40% of the total query cost.
More importantly, the verification does not take place until after the data is inserted into the target table
([Link], in this case). It is only at this point that SQL Server can verify whether the data inserted into the
target table violates the foreign key constraint, so it is at this time that SQL Server determines whether the transaction
can continue or if it must be rolled back.
Note SQL Server acquires shared locks when validating foreign keys, even if the transaction is using read committed
snapshot (read committed using row versioning) or snapshot isolation level. Be mindful of this when examining
deadlock graphs from transactions when these transaction isolation levels are used. If you see shared locks, check to
see whether the locks are taken on an object that is referenced by a foreign key.
Contention Issues as Transactions Grow
As the amount of data grows, the query plan is likely to change, typically when statistics get updated (automatically or
manually) or when the SQL Server Query engine determines that a significant change in table size necessitates a
different query plan. Additionally, when an index is created, the statistics are updated with fullscan, also causing a
potential change in the query plan.
For large datasets, when the clustered index is added (whether the index is added before or after the data is inserted)
can determine whether SQL Server uses a nested loop join or a merge join during the foreign key validation.
In the query plan shown in Figure 1, the temporary table from which the data to be inserted is selected has no
clustered index key. The statistics have probably been updated, but only by auto-create and auto-update statistics.
This means that the table might contain significantly more data than the statistics indicate. Therefore, we added a
clustered index to the #detailData temporary table after data was added in an effort to improve join efficiencies
(resulting in up-to-date statistics on #detailDatas new index).
Figure 2 shows the query plan for the same INSERT SELECT statement.
Figure 2: Execution plan showing SQL Server using a merge join to validate referential integrity
Figure 2 and Figure 1 show query plans for the same query, but because SQL Server recognizes that a larger dataset is
being inserted in Figure 2, SQL Server replaces the loop join to validate referential integrity in Figure 1 with a merge
join in Figure 2. This change eliminates the row count spool operation, but the most significant effect it has, from a
concurrency standpoint, is that the clustered index seek on HeaderTable.PK_HeaderTable is replaced by a clustered
index scan on HeaderTable.PK_HeaderTable.
Understanding the potential deadlocking
The new query plan in Figure 2 sets up potential deadlocks in transactions. Consider the following progression with
two transactions, T1 and T2:
1. T1 opens a transaction and inserts a few rows into HeaderTable. Because only a few rows are inserted, an intent
exclusive (IX) lock is taken on HeaderTable and on the pages into which the rows will be inserted. Exclusive key (X)
locks are taken on the rows that are actually inserted.
2. T2 opens a transaction and inserts a few rows into HeaderTable. Because only a few rows are inserted, an IX lock is
taken on HeaderTable and on the pages into which the rows will be inserted. Exclusive key locks are taken on the rows
that are actually inserted. No blocking has occurred at this point.
3. T1 begins inserting data into ReferringTable. Locks are taken as appropriate on this table.
4. T2 begins inserting data into ReferringTable. Locks are taken as appropriate on this table. Lock escalation is
prevented because multiple transactions concurrently hold locks on this table.
5. When T1 data is inserted into ReferringTable, T1 begins the scan of HeaderTable.PK_HeaderTable to verify
referential integrity. Before the scan is complete, T1 is blocked in its attempt to read the rows that have been inserted
into HeaderTable by T2.
6. When T2 data is inserted into ReferringTable, T2 begins the scan of HeaderTable.PK_HeaderTable to verify
referential integrity. Before the scan is complete, T2 is blocked in its attempt to read the rows that have been inserted
into HeaderTable by T1. This is now a deadlock.
Blocking occurs on HeaderTable when referential integrity is validated because shared locks are taken for this
validation, even if the transaction itself is using read committed snapshot isolation or snapshot isolation levels.
We also encountered deadlocking because, in some instances, a row locking strategy was chosen for inserting rows,
but a page locking strategy was chosen for validation of referential integrity. In this case, T1 and T2 could deadlock
when trying to validate referential integrity because a shared lock on the page was requested by one transaction to
validate the referential integrity, but the other transaction had an IX lock on the page because of X locks on rows
inserted into that page.
Eliminating the Contention
To work around the contention, it is necessary to prevent different transactions from requesting locks on the same
rows or pages. The general idea is to force a direct access of the rows and to force all locks to be taken at the lowest
possible granularity.
In our benchmark, we used the following steps:
1. Ensure that different transactions process different data.
2. Disable lock escalation on HeaderTable.
3. Disable page locking on indexes on HeaderTable.
4. Hint the INSERT SELECT query with OPTION (LOOP JOIN).
Ensure that different transactions process different data
In the case of our benchmark, there were 24 or 48 processes running concurrently to import data, and each processed
a different dataset. For inserts, data must first be inserted into HeaderTable and then the details that reference those
header records are inserted into detailTable. Mutually exclusive datasets were an integral part of the process; if the
datasets were not mutually exclusive, there would have been primary key violations when inserting data into the
HeaderTable. Note that it might be possible for multiple processes that all refer to the same row in the HeaderTable to
insert data into detailTable; however, the processes must address the situation to prevent accessing the same rows.
Disable lock escalation on HeaderTable
If locks are allowed to escalate on HeaderTable, different types of deadlocking might occur. In our case, lock
escalation on HeaderTable would result in severe contention, preventing the required level of concurrency. Because
we also planned to disable page locking, we would have forced inserts into HeaderTable to take row locks, and single
statements could reach the escalation threshold much faster than if the statement were able to take page locks. For
the sake of concurrency, therefore, we disabled lock escalation.
Lock escalation can be enabled or disabled on a table by using the ALTER TABLE syntax. To disable lock escalation on
HeaderTable, execute the following query:
ALTER TABLE HeaderTable SET (LOCK_ESCALATION = DISABLE)
Note Disabling lock escalation is not appropriate for all scenarios; in some cases, disabling lock escalation can cause
out-of-memory errors in SQL Server. We recommend disabling lock escalation only for dealing with specific
contention issues and only after extensive testing. In some cases, lock escalation can be disabled temporarily for
processes that are known to have contention issues.
Note that you can determine whether lock escalation is allowed or disabled on a table by querying [Link].
Disable page locking on indexes on HeaderTable
You can disable page locking on indexes to keep different granularities of locks from causing deadlocks. Disabling
page locking on indexes eliminates the possibility of contention that is caused by the inserts holding key locks (which
implies IX locks are held on the containing pages) and by the validation of referential integrity taking shared page
locks. If page locking is disaabled, the process of checking foreign keys takes row locks, even if a scan is performed.
To disable page locking on HeaderTable.PK_HeaderTable, you can execute the following query:
ALTER INDEX PK_HeaderTable ON HeaderTable SET (ALLOW_PAGE_LOCKS = OFF)
Note Disabling page locking is not appropriate for all scenarios; in some cases, disabling page locking can cause
additional memory contention or can lock escalation, which can increase lock contention. We recommend disabling
page locking on an index only for dealing with specific contention issues and only after extensive testing.
Note that you can determine whether page locking and row locking are enabled by querying [Link].
Hint the INSERT SELECT query with OPTION (LOOP JOIN)
If a scan is performed to support a merge or hash join, every row in that index or table must be read. If another
transaction holds a lock on a row, the scan cannot complete until the lock on that row is released. This sets up the
possibility of deadlock. To eliminate this possibility, you can force an access strategy that avoids touching rows locked
by other transactions. We can use the OPTION (LOOP JOIN) when we know that the query must validate foreign keys
to prevent the deadlock.
If an index exists to support a seek on the inner table, SQL Server can navigate from the root of that index directly to
the leaf page where the row that needs to be accessed exists when a loop join is used. No other rows need be
checked. This eliminates the possibility of a query being blocked by locks on rows in the table that are locked by other
transactions. (We eliminate the scan by hinting for a loop join to be used, and this prevents the blocking and
deadlocking.)
With normal joins, for example, you can use the INNER LOOP JOIN or the LEFT OUTER LOOP JOIN syntax in the FROM
clause of the query to specify a join type. However, the table that is referenced by a foreign key is not explicitly stated
in the query. Because SQL Server uses a join to validate foreign keys, you can still bring about the chosen join strategy
by using the OPTION clause in the query.
In the query plans shown in Figures 1 and 2, the query text was as follows:
INSERT INTO referringTable SELECT * FROM #detailData
This query allowed SQL Server to choose the join type that was used to validate the referential integrity. In the two
query plans, SQL Server chose two different join strategies. To force SQL Server to use a loop join to validate the
referential integrity, the query should read as follows:
INSERT INTO referringTable SELECT * FROM #detailData
OPTION (LOOP JOIN)
When looking at the resulting query plan, you will notice that the overall estimated subtree cost in the execution plan
is higher with the OPTION (LOOP JOIN) hint than it was when SQL Server chose the scan and merge join. This cost
difference is the reason SQL Server chose the scan and merge join. However, in our case, the contention prevented
concurrency and therefore limited the overall throughput. It was worthwhile to choose a query that had a higher
estimated cost to gain the concurrency and to allow for a higher level of overall throughput. You can determine
whether this is true in your environment only by testing.
Challenges to using the OPTION (LOOP JOIN) hint
Using the OPTION (LOOP JOIN) hint causes SQL Server to use a loop join to perform all joins in the query, not just
joins that are used to validate foreign keys. If SQL Server previously found another join strategy to be more efficient
on other joins, it will now use a loop join operator to perform those joins as well.
In some cases, this can cause a cost explosion. You cannot hint those other joins specifically to use a different join
type, because this will result in an error informing you that you have conflicting join hints. If you must use the OPTION
(LOOP JOIN) hint, consider the following tips:
Look at the execution plan on the other joins. Ensure that the inner table (the table that appears on the bottom on
the execution plan) has an index to support the loop join without scanning. For example, consider the following
query:
SELECT l.col1, l.col2, r.col3, r.col4
FROM leftTable l
JOIN rightTable r ON l.col1 = r.col1 AND l.col2 = r.col2
OPTION (LOOP JOIN)
If the execution plan shows that rightTable is the inner table, ensure that rightTable has an index on (col1, col2) or on
(col2, col1), whichever is appropriate in the situation, to avoid scanning on the inner table. A loop join that has a scan
on the inner table is very inefficient and can result in high CPU utilization or other resource contention.
Loop joins for which the seek on the inner table results in a range scan can also be very inefficient and might result
in high CPU utilization or other resource contention. Try to avoid situations in which many rows can be returned from
the inner table on any one join value combination.
As with any possible solution, hinting a loop join results in a tradeoff. In this case, we are trading available CPU
resources for better concurrency. Be sure to test this solution thoroughly in your environment to verify that the
tradeoff is worthwhile.
Summary
Using foreign keys results in additional work for data manipulation operations in which referential integrity is
enforced. The validation of the referential integrity is verified with a join. In cases in which the data being inserted is
small enough, SQL Server defaults to the use of a loop join. In cases in which the dataset being inserted is known to
be large, SQL Server defaults to the use of another join strategy that necessitates a scan of the referenced table.
Scanning can result in deadlocking with concurrent insert operations.
In some cases, transactions may use a key locking strategy when inserting into the referenced table and then use a
page locking strategy when verifying referential integrity. This different granularity can also result in deadlocking.
SQL Server always uses shared locks when validating referential integrity. This is true even if the transactions are using
read committed snapshot (read committed using row versioning) or snapshot isolation levels.
To eliminate the deadlocking when checking referential integrity, you can disable lock escalation on the referenced
table, disable page locking on the referenced table, and hint OPTION (LOOP JOIN) on transactions that operate on
mutually exclusive datasets. This forces SQL Server to lock the minimal amount of data and to use a more direct seek
operation to access the referenced rows while checking referential integrity.
Resolving scheduler contention for concurrent BULK
INSERT
Background information
As you may be aware, SQL Server, through SQLOS, implements its own scheduling mechanism
on top of the Windows operating system. This is done to spend the maximum amount of CPU
time in user mode by using yielding instead of preemptive scheduling. Also, SQLOS can
exercise very fine control over the threads by providing an abstraction on top of Windows
threads.
Central to the SQLOS scheduling mechanism is the scheduler object. A SQLOS scheduler is an
abstraction of a CPU or, in the case of multi-core machines, a CPU-core. Schedulers are grouped
into nodes; a node corresponds either to the hardware NUMA nodes on the host machine or to
the soft NUMA configuration of SQL Server. For example, an 8 CPU dual core machine with 2
hardware NUMA nodes and no soft NUMA configured has 2 nodes with 8 schedulers in each
node.
You can view the SQLOS schedulers and nodes by using the DMV sys.dm_os_schedulers.
Notice that there are some extra, special schedulers in this DMV; these are for SQL Server
internal use. Also, you will see the dedicated admin connection (DAC) scheduler here
(scheduler_id = 255). The schedulers that do the actual query execution work are marked as
VISIBLE ONLINE in the status column.
At connection time, the users session is assigned to a specific node. SQL Server uses a round-
robin assignment mechanism to assign the connection to the node. Once a session is on a specific
node, it will not move from it for the duration of the connection.
Whenever a session sends a batch request or RPC to the server, a scheduler is assigned to handle
the full execution of the request. In SQLOS, this call is known as a task. The assignment of a
scheduler is done by identifying the least busy scheduler within the sessions node, although the
scheduler that was used for a prior task on the same connection is somewhat favored. The
assigned scheduler will be used for the duration of the taskit is not possible for SQLOS to do a
scheduler switch inside a task.
BULK INSERT commands
There are cases when SQL Server assignment of schedulers is less than optimal. Remember that
the scheduler is an SQLOS internal abstraction of a CPU Core. So, if two long-running, CPU-
bound queries end up on the same scheduler, they will compete for the same CPU resources. At
the same time, another idle scheduler may have CPU time available. Ideally, the idle schedulers
would take some of the load from the scheduler that is executing two long-running queries. But
remember, the scheduler switch cannot happen in the middle of a task.
Multiple BULK INSERT tasks fit this scenario precisely: a bulk insert, assuming good I/O, is
CPU bound and typically runs for a long time. If you execute more than one of them
concurrently, you can end up in a situation where two bulk inserts run on the same scheduler,
while another scheduler is idle.
If the above situation occurs, your wait stats will show signal waits, even though the system is
not actually under CPU pressure. If two CPU-bound queries share a scheduler, each one will
periodically yield to the other. These yields show up in sys.dm_os_wait_stats as waits for
SOS_SCHEDULER_YIELD.
Summing up, if you see the following pattern on your server:
Total CPU load less than 100%
More than one long-running, CPU-bound query is executing on the same scheduler
Your queries are individually CPU bound, but you are still not able to push CPU load to 100%
Many signal waits in sys.dm_os_wait_stats
Many waits for SOS_SCHEDULER_YIELD in sys.dm_os_wait_stats
You may have less than optimal scheduler distribution.
The solution: terminate and reconnect
In a well-planned, long-running BULK INSERT workload you may be able to do better than the
SQL Server scheduler assignment method. You can actually leverage the fact that a task stays on
the same scheduler during its run. The DMV sys.dm_exec_requests contains information about
all sessions, including the scheduler that is currently executing your connection. Using this view,
you can check to see if the current scheduler is busy. If it is, you can have your client application
retry the connection.
Using this terminate and reconnect trick we were able to increase the throughput of a CPU-
bound batch run by more than 25% on a large 64-core computer. Before we applied this trick, we
might have 16 cores almost idle and still we observed SOS_SCHEDULER_YIELD waits. Your
mileage may vary depending on the type of work you do and how lucky you get with the
assignment of schedulers.
The following code snippet can be used to build a "terminate and reconnect" wrapper:
CREATE PROCEDURE Batch.Wrapper_DoWork
AS /* Get my scheduler */
DECLARE @my_scheduler_id INT
SELECT @my_scheduler_id = scheduler_id
FROM sys.dm_exec_requestsWHERE session_id = @@SPID
/* Check if someone else is doing long running work on my scheduler */
IF EXISTS (SELECT *
FROM sys.dm_exec_requests
WHERE scheduler_id <> @my_scheduler_id
AND command LIKE 'BULK%' /* replace with your specific check for long running
query*/
)
BEGIN
RETURN 0 /* Failed to get a non busy scheduler, let client try again */
END
ELSE BEGIN
/* Do long running query here */
RETURN 1
END
Remember that your client application must perform a reconnect if the wrapper returns 0.
The above stored procedure is subject to some race conditions. You can end up in a situation
where the scheduler check shows that your scheduler is not busy but in the meantime, before you
start your long-running query, another query connects to your scheduler and starts its long
running work.
One way to avoid this race condition is to add a semaphore to your batch control system. This
semaphore can then be used to ensure exclusive access to a scheduler.
An example of this implementation is:
/* Create table to act as semaphore */
CREATE TABLE [Link](
scheduler_id INT PRIMARY KEY
, session_id INT
)
CREATE PROCEDURE Batch.DoWork_WithClaim
AS
/* Get my scheduler */
DECLARE @my_scheduler_id INT
SELECT @my_scheduler_id = scheduler_id
FROM sys.dm_exec_requests
WHERE session_id = @@SPID
DECLARE @RC INT /* hold the row count of inserted data */
/* Claim the scheduler as my own */
INSERT INTO [Link] (scheduler_id, session_id)
SELECT @my_scheduler_id, @@SPID
FROM sys.dm_os_schedulers s
LEFT JOIN [Link] cs WITH (TABLOCKX) /* ensure serialization */
ON cs.scheduler_id = s.scheduler_id
WHERE cs.scheduler_id IS NULL
AND [Link] = 'VISIBLE ONLINE'
AND s.scheduler_id = @my_scheduler_id
SET @RC = @@ROWCOUNT/* Did my scheduler claim fail? (no row will be inserted) */
IF @RC = 0 BEGIN
RETURN 0 /* Could not get a non busy scheduler, let client try again */
END
ELSE BEGIN
/* DO LONG RUNNING QUERY HERE */
/* Release my scheduler again */
DELETE FROM [Link]
WHERE session_id = @@SPID
RETURN 1
END
Response Time Analysis using Extended Events
This tool demonstrates response time analysis at the session or statement level including waitstats using the new
Extended Events infrastructure in SQL Server 2008. This tool is based on the simple principle:
Response time = service time + wait time
This tool allows you to drill down on the time spent in serving the user requests and the time spent in waiting for
resources.
Download the application and documentation from
[Link] Follow the User Guide to install and use
the tool. The download also contains the source code for the project.
Memory Error Recovery in SQL Server 2012
SQL Server 2012 has many hidden gems, one of them is the capability of recovering from memory
corruption error. We will tell you how it works in this article.
Contents
1. Memory Error Recovery in SQL Server 2012
2. Hardware errors and taxonomy
3. Soft error vs. hard error
4. Corrected error vs. uncorrected error
5. Fatal error vs. non-fatal error
6. What is memory scrubbing?
7. How To: Find if this feature is available
8. How To: Detect that a page has been repaired
9. How to: Detect uncorrected hardware memory corruption
10. How To: Monitor the system
SQL Server is able to recover from memory corruption when hardware support is available. Platforms that
support hardware memory scrubber can send notification to applications when memory corruption is
detected. SQL Server responds to these notifications and attempts to repair the memory. Clean database
pages in buffer pool are restored by reading the page again from disk. This new feature helps SQL Server
to remain running even when there are hardware memory errors.
Hardware errors and taxonomy
Please see the Hardware Errors and Error Sources on MSDN for an overview of hardware errors and their
definitions.
Soft error vs. hard error
A soft error is an error in a signal or datum which is wrong. After a soft error is observed , there is no
implication that the system is any less reliable than before. If detected, a soft error may be corrected by
rewriting correct data in place of erroneous data. An example of a soft error is a single bit flip.
Unlike soft error, a hard error is an error that occurs because of a physical hardware issue such as a defect,
a mistake in design or construction, or a broken component. These errors require that the hardware
causing the error be replaced. Rewriting the data does not correct the error.
Corrected error vs. uncorrected error
A corrected error is a hardware error condition that has been corrected by the hardware or the firmware
by the time that the operating system is notified about the presence of the error condition.
An uncorrected error is a hardware error condition that cannot be corrected by the hardware or the
firmware. Uncorrected errors are classified as either fatal or nonfatal.
Fatal error vs. non-fatal error
A fatal hardware error is an uncorrected or uncontained error condition that is determined to be
unrecoverable by the hardware. When a fatal uncorrected error occurs, the operating system generates a
bug check to contain the error.
A nonfatal hardware error is an uncorrected error condition from which the operating system can attempt
recovery by trying to correct the error. If the operating system cannot correct the error, it generates a bug
check to contain the error.
What is memory scrubbing?
Memory scrubbing is the process of detecting and correcting bit errors in computer memory by using
error-detecting codes like ECC. Memory scrubbing can detect and correct soft, correctable errors. For
certain soft, uncorrected non-fatal errors, SQL Server captures of this information and checks whether the
corrupted memory is part of a clean database page that is in Buffer Pool. If that is the case, this page is
tossed and the memory is de-allocated. If corruption is in another region of memory that can not be
repaired, only logging is done to notify of the event and no action is taken.
How To: Find if this feature is available
If the memory error recovery feature is available, you will be able to see the following by looking at SQL
errorlog.
Machine supports memory error recovery. SQL memory protection is enabled to recover
from memory corruption.
How To: Detect that a page has been repaired
If the memory corruption is associated with a clean page in Buffer Pool, SQL Server is able to recover from
it and the following message will be logged in the errorlog.
SQL Server has detected hardware memory corruption in database '%ls', file ID: %u,
page ID; %u, memory address: 0x%x and has successfully recovered the page.
Also you can monitor the detection and repair of the memory corruption with the following Extended
Events (XEvents):
- bad_memory_detected: corrupted memory has been detected and reported to SQL. The memory may or
may not belong to a database page.
- bad_memory_fixed: memory corruption has been detected and fixed. Fields of then event contain more
details regarding the exact page affected
How to: Detect uncorrected hardware memory corruption
SQL Server is able to recover from memory corruption of clean pages in buffer pool. It cannot recover
from memory errors associated with dirty pages or outside Buffer Pool. Upon detection of such
uncorrectable hardware errors, the following entry can be observed from errorlog.
Uncorrectable hardware memory corruption detected. Your system may become unstable.
Please check the Windows event log for more details.
How To: Monitor the system
The user can monitor the detection of memory corruption using sp_server_diagnostics. The system
component state will be set to warning upon detection of a memory corruption. In addition, the data
column logs information about count of bad page detected, count of bad page fixed as well as the virtual
address of the bad page last encountered.
Section 9: SQL Top 10
Storage Top 10 Best Practices
Proper configuration of IO subsystems is critical to the optimal performance and operation of SQL Server systems.
Below are some of the most common best practices that the SQL Server team recommends with respect to storage
configuration for SQL Server.
1 - Understand the IO characteristics of SQL Server and the specific IO requirements / characteristics
of your application.
In order to be successful in designing and deploying storage for your SQL Server application, you need to have an
understanding of your applications IO characteristics and a basic understanding of SQL Server IO patterns.
Performance monitor is the best place to capture this information for an existing application. Some of the questions
you should ask yourself here are:
What is the read vs. write ratio of the application?
What are the typical IO rates (IO per second, MB/s & size of the IOs)? Monitor the perfmon counters:
1. Average read bytes/sec, average write bytes/sec
2. Reads/sec, writes/sec
3. Disk read bytes/sec, disk write bytes/sec
4. Average disk sec/read, average disk sec/write
5. Average disk queue length
How much IO is sequential in nature, and how much IO is random in nature? Is this primarily an OLTP
application or a Relational Data Warehouse application?
To understand the core characteristics of SQL Server IO, refer to SQL Server 2000 I/O Basics.
2 - More / faster spindles are better for performance
Ensure that you have an adequate number of spindles to support your IO requirements with an acceptable
latency.
Use filegroups for administration requirements such as backup / restore, partial database availability, etc.
Use data files to stripe the database across your specific IO configuration (physical disks, LUNs, etc.).
3 - Try not to over optimize the design of the storage; simpler designs generally offer good
performance and more flexibility.
Unless you understand the application very well avoid trying to over optimize the IO by selectively placing
objects on separate spindles.
Make sure to give thought to the growth strategy up front. As your data size grows, how will you manage
growth of data files / LUNs / RAID groups? It is much better to design for this up front than to rebalance
data files or LUN(s) later in a production deployment.
4 - Validate configurations prior to deployment
Do basic throughput testing of the IO subsystem prior to deploying SQL Server. Make sure these tests are
able to achieve your IO requirements with an acceptable latency. SQLIO is one such tool which can be used
for this. A document is included with the tool with basics of testing an IO subsystem. Download the SQLIO
Disk Subsystem Benchmark Tool.
Understand that the of purpose running the SQLIO tests is not to simulate SQL Servers exact IO
characteristics but rather to test maximum throughput achievable by the IO subsystem for common SQL
Server IO types.
IOMETER can be used as an alternative to SQLIO.
5 - Always place log files on RAID 1+0 (or RAID 1) disks. This provides:
Better protection from hardware failure, and
Better write performance.
Note: In general RAID 1+0 will provide better throughput for write-intensive applications. The amount of
performance gained will vary based on the HW vendors RAID implementations. Most common alternative to
RAID 1+0 is RAID 5. Generally, RAID 1+0 provides better write performance than any other RAID level
providing data protection, including RAID 5.
6 - Isolate log from data at the physical disk level
When this is not possible (e.g., consolidated SQL environments) consider I/O characteristics and group
similar I/O characteristics (i.e. all logs) on common spindles.
Combining heterogeneous workloads (workloads with very different IO and latency characteristics) can have
negative effects on overall performance (e.g., placing Exchange and SQL data on the same physical spindles).
7 - Consider configuration of TEMPDB database
Make sure to move TEMPDB to adequate storage and pre-size after installing SQL Server.
Performance may benefit if TEMPDB is placed on RAID 1+0 (dependent on TEMPDB usage).
For the TEMPDB database, create 1 data file per CPU, as described in #8 below.
8 - Lining up the number of data files with CPUs has scalability advantages for allocation intensive
workloads.
It is recommended to have .25 to 1 data files (per filegroup) for each CPU on the host server.
This is especially true for TEMPDB where the recommendation is 1 data file per CPU.
Dual core counts as 2 CPUs; logical procs (hyperthreading) do not.
9 - Dont overlook some of SQL Server basics
Data files should be of equal size SQL Server uses a proportional fill algorithm that favors allocations in
files with more free space.
Pre-size data and log files.
Do not rely on AUTOGROW, instead manage the growth of these files manually. You may leave AUTOGROW
ON for safety reasons, but you should proactively manage the growth of the data files.
10 - Dont overlook storage configuration bases
Use up-to-date HBA drivers recommended by the storage vendor
Utilize storage vendor specific drivers from the HBA manufactures website
Tune HBA driver settings as needed for your IO volumes. In general driver specific settings should come
from the storage vendor. However we have found that Queue Depth defaults are usually not deep enough
to support SQL Server IO volumes.
Ensure that the storage array firmware is up to the latest recommended level.
Use multipath software to achieve balancing across HBAs and LUNs and ensure this is functioning properly
Simplifies configuration & offers advantages for availability
Microsoft Multipath I/O (MPIO): Vendors build Device Specific Modules (DSM) on top of Driver Development
Kit provided by Microsoft.
Top 10 Hidden Gems in SQL Server 2005
By Cihan Biyikoglu
Technical Reviewers: Lindsey Allen, Peter Scharlock, Burzin Patel, Eric Hanson, Mark Souza, Sanjay Mishra, Michael
Thomassy
SQL Server 2005 has hundreds of new and improved components. Some of these improvements get a lot of the
spotlight. However there is another set that are the hidden gems that help us improve performance, availability or
greatly simplify some challenging scenarios. This paper lists the top 10 such features in SQL Server 2005 that we have
discovered through the implementation with some of our top customers and partners.
The order in the list does not have much significance except the specific instances we used them and the impact we
saw. I will use a practical analogy; I started with the utility-knife size features that can help make life very easy at the
right moment and build up to chain-saw size features that can help you implement a full scenario.
[Link]
Table Difference tool allows you to discover and reconcile differences between a source and destination table or a
view. Tablediff Utility can report differences on schema and data. The most popular feature of tablediff is the fact that
it can generate a script that you can run on the destination that will reconcile differences between the tables.
[Link] takes 2 sets of input;
Connectivity - Provide source and destination objects and connectivity information.
Compare Options - Select one of the compare options
Compare schemas: Regular or Strict
Compare using Rowcounts, Hashes or Column comparisons
Generate difference scripts with I/U/D statements to synchronize destination to the source.
TableDiff was intended for replication but can easily apply to any scenario where you need to compare data and
schema.
You can find more information about command line utilities and the Tablediff Utility in Books Online for SQL Server
2005.
Triggers for Logon Events (New in Service Pack 2)
With SP2, triggers can now fire on Logon events as well as DML or DDL events.
Logon triggers can help complement auditing and compliance. For example, logon events can be used for
enforcing rules on connections (for example limiting connection through a specific username or limiting
connections through a username to a specific time periods) or simply for tracking and recording general
connection activity. Just like in any trigger, ROLLBACK cancels the operation that is in execution. In the case
of logon event that means canceling the connection establishment. Logon events do not fire when the server
is started in the minimal configuration mode or when a connection is established through dedicated admin
connection (DAC).
The following code snippet provides an example of a logon trigger that records the information about the
client connection.
CREATE TRIGGER connection_limit_trigger
ON ALL SERVER FOR LOGON
AS
BEGIN
INSERT INTO logon_info_tbl SELECT EVENTDATA()
END;
You can find more information about this feature in updated Books Online for SQL Server Services Pack 2 un the
heading Logon Triggers.
Boosting performance with persisted-computed-columns (pcc).
Btree Indexes provide great compromise for tuning queries vs redundant storage of data and added cost of
modifying data (insert/update/delete). A less known capability for tuning in SQL Server 2005 is persisted
computed columns (PCC). Computed columns can help you shift the runtime computation cost to data
modification phase. The computed column is stored with the rest of the row and is transparently utilized
when the expression on the computed columns and the query matches. You can also build indexes on the
PCCs to speed up filtrations and range scans on the expression.
The following sample can demonstrate the benefits of a persisted computed column applied to a complex
expression. The same TSQL query run against the following table schema with and without the DayType
column will demonstrate the effect of the transparent expression matching with persisted computed
columns. The output from the sys.dm_exec_query_stats DMV also shows the difference in the IO and CPU
characteristics of the query.
Query
SELECT [Ticker] ,[Date] , [DayHigh] ,[DayLow] ,[DayOpen] ,[Volume]
,[DayClose] ,[DayAdjustedClose],
CASE
WHEN volume > 200000000 and dayhigh-daylow /daylow > .05 THEN 'heavy
volatility'
WHEN volume > 100000000 and dayhigh-daylow /daylow > .03 THEN 'volatile'
WHEN volume > 50000000 and dayhigh-daylow /daylow > .01 THEN 'fair'
ELSE 'light'
END as [DayType]
FROM [Link]
WHERE
CASE
WHEN volume > 200000000 and dayhigh-daylow /daylow > .05 THEN 'heavy
volatility'
WHEN volume > 100000000 and dayhigh-daylow /daylow > .03 THEN 'volatile'
WHEN volume > 50000000 and dayhigh-daylow /daylow > .01 THEN 'fair'
ELSE 'light'
END = 'heavy volatility'
Table Schema
CREATE TABLE [dbo].[MarketData](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[Ticker] [nvarchar](5) NOT NULL,
[Date] [datetime] NOT NULL,
[DayHigh] [decimal](38, 6) NOT NULL,
[DayLow] [decimal](38, 6) NOT NULL,
[DayOpen] [decimal](38, 6) NOT NULL,
[Volume] [bigint] NOT NULL,
[DayClose] [decimal](38, 6) NOT NULL,
[DayAdjustedClose] [decimal](38, 6) NOT NULL,
-- PERSISTED COMPUTED COLUMN --
[DayType] AS (
CASE
WHEN volume > 200000000 and dayhigh-daylow /daylow > .05 THEN 'heavy
volatility'
WHEN volume > 100000000 and dayhigh-daylow /daylow > .03 THEN 'volatile'
WHEN volume > 50000000 and dayhigh-daylow /daylow > .01 THEN 'fair'
ELSE 'light'
END) PERSISTED NOT NULL
) ON [PRIMARY]
Output From The Sys.Dm_Exec_Query_Stats Dynamic Management View (DMV)
See full-sized image.
In the above picture, the output from sys.dm_exec_query_stats dynamic management view shows the difference in
CPU and IO statistics between the same query hitting MarketData_Computed and MarketData tables. Line 1
represents the query run against the table with the persisted computed column. Line 2 is the table without the
persisted computed column. With the complex expression pre-calculated in the DayType column, total worker time
and overall elapsed time is lower compared to the table without the DayType persisted computed column.
Another way to verify that the persisted computed column is utilized, is to use the execution plan and look at the scan
or the seek operator for the table with the computed column and check the output list, which should contain the
column. In the example below you can see the DayType, the name for the PCC, in the output list under #9.
See full-sized image.
DEFAULT_SCHEMA setting in sys.database_principles
SQL Server provides great flexibility with name resolution. However name resolution comes at a cost and can
get noticeably expensive in adhoc workloads that do not fully qualify object references. SQL Server 2005
allows a new setting of DEFEAULT_SCHEMA for each database principle (also known as user) which can
eliminate this overhead without changing your TSQL code. Here is an example:
In SQL Server 2005, the following query when executed by user1 that has a DEFAULT_SCHEMA of dbo will directly
resolve to dbo.tab1, instead of the extra search for user1.tab1.
SELECT * FROM tab1
Whereas the same query will search for user1.tab1 in SQL Server 2000 and if that does not exist it will resolve to
dbo.tab1.
This setting can be especially useful for databases upgraded from SQL Server 2000 to SQL Server 2005. To
preserve the original behavior, databases upgraded from SQL Server 2000 will get the username as the
DEFAULT_SCHEMA for each database principle. That means, in a database upgraded from a previous version
to SQL Server 2005, user1 will get a DEFAULT_SCHEMA values of user1. To take advantage of the
performance benefits, administrators can set the DEFAULT_SCHEMA through ALTER USER command and
change it to the schema that most of the of the objects reside. Be aware this may break queries that may be
utilizing objects in other schemas than the one set in the DEFAULT_SCHEMA setting and has not qualified
the object names.
DEFULT_SCHEMA is documented in Book Online under the CREATE USER (Transact-SQL) heading.
Forced Parameterization
Parameterization allows SQL Server to take advantage of query plan reuse and avoid compilation and
optimization overheads on subsequent executions of similar queries. However there are many applications
out there that, for one reason or another, still suffer from ad-hoc query compilation overhead. For those
cases with high number of query compilation and where lowering CPU utilization and response time is
critical for your workload, force parameterization can help.
Force parameterization forces most queries to be parameterized and cached for reuse in subsequent
submissions. Forced parameterization will remove the literal values and replaces them with parameters. This
minimizes the compilation overhead for queries that are the same except the literal values in the query text.
Forced parameterization is typically enabled at the database level. However it is also possible to hint FORCED
PARAMETERIZATION on individual queries.
In a number of cases, we have witnessed improvements in performance up to 30% due to forced
parameterization. However forced parameterization can cause inappropriate plan sharing in cases where a
single execution plan does not make sense. For those cases, you can utilize features like plan guides or query
hints.
You can find more information on Forced Parameterization in Books Online.
Vardecimal Storage Format
In Service Pack 2, SQL Server 2005 adds a new storage format for numeric and decimal datatypes called
vardecimal. Vardecimal is a variable-length representation for decimal types that can save unused bytes in
every instance of the row. The biggest amount of savings come from cases where the decimal definition is
large (like decimal(38,6)) but the values stored are small (like a value of 0.0) or there is a large number of
repeated values or data is sparsely populated.
SQL Server 2005 also includes a stored procedure that can estimate the savings before you enable the new
storage format.
[Link].sp_estimate_rowsize_reduction_for_vardecimal tablename
To enable vardecimal storage format, you need to first allow vardecimal storage on the database;
exec sys.sp_db_vardecimal_storage_format N'databasename', N'ON'
Once the database option is enabled, you can then turn on vardecimal storage at a table level using the
following procedure;
exec sp_tableoption 'tablename', 'vardecimal storage format', 1
Vardecimal storage format presents an overhead due to the complexity inherent in variable length data
processing. However in IO bound workloads, savings on IO bandwidth due to efficient storage can far
exceed this processing overhead.
If you would like more information on this topic, updated SQL Server 2005 Books Online for Service Pack 2
contains extensive information on the new vardecimal format.
Indexing made easier with SQL Server 2005
The new Dynamic Management Views have improved monitoring and trouble shooting greatly. A few of the
dynamic management views (DMVs) deserve special attention.
Through sys.dm_index_usage_stats you can find out how much maintenance and traversal you have for each
index. Indexes with high maintenance numbers and low traversal numbers can be considered as good
candidates for dropping.
Through sys.dm_db_missing_index_* collection of DMVs, you can get recommendations on what new
indexes could benefit the queries running on your server. The recommendations come with a estimate on
how much improvement you can expect from the new index.
If youd like to automate creation and dropping of indexes, SQL Server Query Optimization Team has
blogged about how to automate index recommendations into actions:
[Link]
Figuring out the most popular queries in seconds
Another great DMV that can help save you a lot of work is sys.dm_exec_query_stats. In previous version of
SQL Server to find out the highest impact queries on CPU or IO in system, you had to walk through a long
set of analyses steps including getting aggregated information out of the data you collected from profiler.
With sys.dm_exec_query_stats, you can figure out many combinations of query analyses by a single query.
Here are some of the examples;
Find queries suffering most from blocking
(total_elapsed_time total_worker_time)
Find queries with most CPU cycles (total_worker_time)
Find queries with most IO cycles
(total_physical_reads + total_logical_reads + total_logical_writes)
Find most frequently executed queries
(execution_count)
You can find more information on how to use dynamic management views for performance troubleshooting
in the SQL Server 2005 Waits and Queues whitepaper located at:
[Link]
Scalable Shared Databases
Scalable Shared Databases provide an alternative scale out mechanism for Read-Only environments.
Through Scalable Shared Databases one can mount the same physical drives on commodity machines and
allow multiple instances of SQL Server 2005 to work off of the same set of data files. The setup does not
require duplicate storage for every instance of SQL Server and allows additional processing power through
multiple SQL Server instances that have their own local resources like cpu, memory, tempdb and potentially
other local databases. However this type of setup does limit the IO bandwidth since all instances point to the
physical set of files.
See full-sized image.
Book Online for SQL server 2005 contains details on Scalable Shared Databases.
Steps to setup:
[Link]
Soft-NUMA
Highly concurrent workloads hit a contention point around global state they maintain at some point. That
point in many cases happen to be 8. One way around this contention has been to eliminate the global state
and create hierarchies. NUMA architectures allow us to eliminate the contention around global resources by
moving main resources closer to each other and forming nodes. SQL Server 2005 recognizes the NUMA
architecture and self manages allocation of resources to adhere and take advantage of the hardware NUMA
setup at the time of startup. By aligning with the HW setup SQL Server partitions its internal management to
improve throughput.
Some workloads benefit greatly from the partitioning concept, especially mixed workloads that have to run
varying characteristics of data access concurrently (example: OLTP and Reporting). Soft-NUMA allows
partitioning configuration to be extended into the software level and defined either on top of a NUMA
enabled environment to further divide the hardware partitions into smaller chunks or on a machine that does
not utilize NUMA concepts to enable partitioning for the configuration. By configuring partitions through
Soft-NUMA, the administrator can control the allocation of schedulers and memory managers for each node
and can configure specific TCP/IP ports for the nodes. Then, clients can be configured to connect using the
specific ports to access specific partitions.
See full-sized image.
Soft-NUMA topic is extensively covered in Book Online. You can also read more about the details of Soft-
NUMA at Slava Okss Weblog at [Link]
Top 10 Hidden Gems in SQL 2008 R2
With all the new features in SQL 2008 R2, here are the major ones getting all the press:
PowerPivot
Parallel Data Warehouse
Application and Multi-Server Management
StreamInsight
256 core support
There is so much written on the ones above, I wanted to concentrate on talking about other new
features in SQL 2008 R2. So, in no particular order:
1. SMB support SMB stands for Server Message Block and this protocol is now officially supported by
SQL Server 2008 R2 and beyond. This improvement has formalized the support status of placing SQL
database files on SMB network file shares. From Kevin Farlee, the owner of this feature in SQL
Server: This presents a better-together story with the work that Windows has done in
Windows7/Server 2008 R2 to make the Windows SMB stack far more performant and resilient than
in the past. It is also a recognition that with the increasing acceptance of iSCSI, customers are
viewing Ethernet as a viable way to connect to their storage. Finally, it gives customers in
consolidation environments a very simple to manage method for moving databases between servers
without investing in a large SAN infrastructure.
2. Increased Performance there are very nice performance improvements, especially with the
combination of Windows 2008 R2 and SQL 2008 R2. The actual TPC-E measurements on have been
audited and published.
3. SYSPREP Finally! We can now create Sysprep versions of SQL Server environments, starting with
SQL Server 2008 R2, but only for the relational engine. My favorite thing about this piece is that it
even works with HyperV images containing SQL Server 2008 R2.
4. Report Builder 3.0 and Reporting Services Too many great new features to talk about in a blog
and the development team already has a great blog. But my favorite is the Report Part feature where
you can take an existing report and designate report items and data regions to save and reuse in other
reports. This can amount to a huge time savings for developing new reports. Other customers tell me
they like the improved Sharepoint integration and the performance improvements in Sharepoint. But
that is not all, there is Bing map support, spark lines, and shared data sets.
5. Master Data Services for data consistency across heterogeneous applications. BOL link.
6. SSIS - Bulk Inserts with [Link] provider are now possible, which is extremely nice because it
used to do it a row at a time. Now, if you check the box to Use Bulk Insert when possible then you
can see vastly improved performance when it kicks in.
7. Setup integrated Sharepoint mode setup is vastly improved for both Reporting Services and
Analysis Services. See the link for Powerpivot for Sharepoint to get the instructions.
8. Excel 2010 new additions for databases: slicers, data cleansing, AJAX data feeds, Odata feeds and
named set improvements. To create a Named Set in Excel, once youve created a PivotTable against
an OLAP source go to the Options tab under PivotTable Tools, and select Fields, Items, & Sets
Manage Sets New Create Set Using MDX. Another of my new favorites within
PowerPivot in Excel is the new Data Cleansing ribbon. This allows the users to do their own clean
up, which will be essential when they combine data from disparate sources. And you can also get an
OData feed from a Reporting Services report.
9. Database Compression - now supports Unicode. If you have Unicode date types, like nchar and
nvarchar, but the data contained within is normally single byte character sets, you will see
significant space savings.
10. PHP 5 Driver Version 1.1 of the PHP 5 driver has a list of new capabilities, allowing access to SQL
Server 2005 and SQL Server 2008.
New and changed Editions (more details and pricing)
Data Center Edition needed for machine with more than 8 physical CPU sockets plus other
improvements needed for the top SQL Server projects.
Parallel Data Warehouse Edition Massively Parallel Processing (MPP) Edition of SQL Server
targeted at data warehouses in the 10s to 100s of terabytes. It is an appliance where you
order the hardware and software together and it comes preinstalled and preconfigured. The
minimum installation is one rack so no, you cannot install it on your laptop to play with it.
Standard Edition now has the capability to do backup compression.
Top 10 SQL Server 2008 Features for the Database
Administrator (DBA)
Microsoft SQL Server 2008 provides a number of enhancements and new functionality, building on previous versions.
Administration, database maintenance, manageability, availability, security, and performance, among others, all fall
into the roles and responsibilities of the database administrator. This article provides the top ten new features of SQL
Server 2008 (referenced in alphabetical order) that can help DBAs fulfill their responsibilities. In addition to a brief
description of each feature, we include how this feature can help and some important use considerations.
1 - Activity Monitor
When troubleshooting a performance issue or monitoring a server in real time, it is common for the DBA to execute a
number of scripts or check a number of sources to collect general information about what processes are executing
and where the problem may be. SQL Server 2008 Activity Monitor consolidates this information by detailing running
and recently executed processes, graphically. The display gives the DBA a high-level view and the ability to drill down
on processes and view wait statistics to help understand and resolve problems.
To open up Activity Monitor, just right-click on the registered server name in Object Explorer and then click Activity
Monitor, or utilize the standard toolbar icon in SQL Server Management Studio. Activity Monitor provides the DBA
with an overview section producing output similar to Windows Task Manager and drilldown components to look at
specific processes, resource waits, data file I/Os, and recent expensive queries, as noted in Figure 1.
Figure 1: Display of SQL Server 2008 Activity Monitor view from Management Studio
NOTE: There is a refresh interval setting accessed by right-clicking on the Activity Monitor. Setting this value to a low
threshold, under 10 seconds, in a high-volume production system can impact overall system performance.
DBAs can also use Activity Monitor to perform the following tasks:
Pause and resume Activity Monitor with a simple right-click. This can help the DBA to save a particular
point-in-time for further investigation without it being refreshed or overwritten. However, be careful,
because if you manually refresh, expand, or collapse a section, the data will be refreshed.
Right-click a line item to display the full query text or graphical execution plan via Recent Expensive Queries.
Execute a Profiler trace or kill a process from the Processes view. Profiler events include RPC:Completed,
SQL:BatchStarting, and SQL:BatchCompleted events, and Audit Login and Audit Logout.
Activity Monitor also provides the ability to monitor activity on any SQL Server 2005 instance, local or remote,
registered in SQL Server Management Studio.
2- [SQL Server] Audit
Having the ability to monitor and log events, such as who is accessing objects, what changes occurred, and what time
changes occurred, can help the DBA to meet compliance standards for regulatory or organizational security
requirements. Gaining insight into the events occurring within their environment can also help the DBA in creating a
risk mitigation plan to keep the environment secure.
Within SQL Server 2008 (Enterprise and Developer editions only), SQL Server Audit provides automation that allows
the DBA and others to enable, store, and view audits on various server and database components. The feature allows
for auditing at a granularity of the server and/or database level.
There are server-level audit action groups, such as:
FAILED_LOGIN_GROUP, which tracks failed logins.
BACKUP_RESTORE_GROUP, which shows when a database was backed up or restored.
DATABASE_CHANGE_GROUP, which audits when a database is created, altered, or dropped.
Database-level audit action groups include:
DATABASE_OBJECT_ACCESS_GROUP, which is raised whenever a CREATE, ALTER, or DROP statement is
executed on database objects.
DATABASE_OBJECT_PERMISSION_CHANGE_GROUP, which is raised when GRANT, REVOKE, or DENY is
utilized for database objects.
There are also audit actions, such as SELECT, DELETE, or EXECUTE. For more information, including a full list of the
audit groups and actions, see SQL Server Audit Action Groups and Actions.
Audit results can be sent to a file or event log (Windows Security or System) for viewing. Audit information is created
utilizing Extended Events, another new SQL Server 2008 feature.
By using SQL Server 2008 audits, the DBA can now answer questions that were previously very difficult to retroactively
determine, such as Who dropped this index?, When was the stored procedure modified?, What changed which
might not be allowing this user to access this table?, or even Who ran SELECT or UPDATE statements against the
[[Link]] table?
For more information about using SQL Server Audit and some examples of implementation, see the SQL Server 2008
Compliance Guide.
3 - Backup Compression
This feature has long been a popular request of DBAs for SQL Server. The wait is finally over, and just in time! Many
factors, including increased data retention periods and the need to physically store more data have contributed to the
recent explosion in database size. Backing up a large database can require a significant time window to be allotted to
backup operations and a large amount of disk space allocated for use by the backup file(s).
With SQL Server 2008 backup compression, the backup file is compressed as it is written out, thereby requiring less
storage, less disk I/O, and less time. In lab tests conducted with real customer data, we observed in many cases a
reduction in the backup file size between 70% and 85%. Testing also revealed around a 45% reduction in the backup
and restore time. It is important to note that the additional processing results in higher processor utilization. To help
segregate the CPU intensive backup and minimize its effect on other processes, one might consider utilizing another
feature mentioned in this paper, Resource Governor.
The compression is achieved by specifying the WITH COMPRESSION clause in the BACKUP command (for more
information, see SQL Server Books Online) or by selecting it in the Options page in the Back Up Database dialog
box. To prevent having to modify all existing backup scripts, there is also a global setting to enable compressing all
backups taken on a server instance by default. (This setting is accessed by using the Database Settings page of the
Server Properties dialog box or by running sp_configure with backup compression default set to 1.) While the
compression option on the backup command needs to be explicitly specified, the restore command automatically
detects that a backup is compressed and decompresses it during the restore operation.
Backup compression is a very useful feature that can help the DBA save space and time. For more information about
tuning backup compression, see the technical note on Tuning the Performance of Backup Compression in SQL Server
2008. NOTE: Creating compressed backups is only supported in SQL Server 2008 Enterprise and Developer editions;
however, every SQL Server 2008 edition allows for a compressed backup to be restored.
4 - Central Management Servers
DBAs are frequently responsible for managing not one but many SQL Server instances in their environment. Having
the ability to centralize the management and administration of a number of SQL Server instances from a single source
can allow the DBA to save significant time and effort. The Central Management Servers implementation, which is
accessed via the Registered Servers component in SQL Server Management Studio, allows the DBA to perform a
number of administrative tasks on SQL Servers within the environment, from a single management console.
Central Management Servers allow the DBA to register a group of servers and apply functionality to the servers, as a
group, such as:
Multiserver query execution: A script can now be executed from one source, across multiple SQL Servers, and
be returned to that source, without the need to distinctly log into every server. This can be extremely helpful
in cases where data from tables on two or more SQL Servers needs to be viewed or compared without the
execution of a distributed query. Also, as long as the syntax is supported in earlier server versions, a query
executed from the Query Editor in SQL Server 2008 can run against SQL Server 2005 and SQL Server 2000
instances as well. For more information, see the SQL Server Manageability Team Blog, specifically Multiple
Server Query Execution in SQL Server 2008 .
Import and evaluate policies across servers: As part of Policy-Based Management (another new SQL Server
2008 feature discussed in this article), SQL Server 2008 provides the ability to import policy files into
particular Central Management Server Groups and allows policies to be evaluated across all of the servers
registered in the group
Control Services and bring up SQL Server Configuration Manager: Central Management Servers help provide
a central place where DBAs can view service status and even change status for the services, assuming they
have the appropriate permissions
Import and export the registered servers: Servers within Central Management Servers can be exported and
imported for use between DBAs or different SQL Server Management Studio instance installations. This is an
alternative to DBAs importing or exporting into their own local groupings within SQL Server Management
Studio.
Be aware that permissions are enforced via Windows authentication, so a user might have different rights and
permissions depending on the server registered within the Central Management Server group. For more information,
see Administering Multiple Servers Using Central Management Servers and a Kimberly Tripp blog: SQL Server 2008
Central Management Servers-have you seen these?
5 - Data Collector and Management Data Warehouse
Performance tuning and troubleshooting are a time-consuming tasks that can require in-depth SQL Server skills and
an understanding of database internals. Windows System monitor (Perfmon), SQL Server Profiler, and dynamic
management views (DMVs) helped with some of this, but they were often intrusive, laborious to use, or the dispersed
data collection methods were cumbersome to easily summarize and interpret.
To provide actionable performance insight, SQL Server 2008 delivers a fully extensible performance data collection
and warehouse tool also known as the data collector. The tool includes several out-of-the-box data collection agents,
a centralized data repository for storing performance data called management data warehouse, and several
precanned reports to present the captured data. The data collector is a scalable tool that can collect and assimilate
data from multiple sources such as dynamic management views , Perfmon, Transact-SQL queries, by using a fully
customizable data collection frequency. The data collector can be extended to collect data for any measurable
attribute of an application.
Another helpful feature of the management data warehouse is that it can be installed on any SQL Server and then
collect data from one or more SQL Server instances within the environment. This can help minimize the performance
impact on production systems and improve the scalability in terms of monitoring and collecting data from a number
of servers. In lab testing we observed around a 4% reduction in throughput when running the agents and the
management data warehouse on a server running at capacity (via an OLTP workload). The impact can vary based on
the collection interval (as the test was over an extended workload with 15-minute-pulls into the warehouse), and it
can be exacerbated during intervals of data collection. Finally, some capacity should be considered, because the
[Link] process will take up some memory and processor resources, and writes to the management data
warehouse will increase the I/O workload and space allocation needed where the data and log files are located.
The diagram (Figure 2) below depicts a typical data collector report.
Figure 2: Display of SQL Server 2008 Data Collector Report
This report shows SQL Server processing over the period of time data was collected. Events such as waits, CPU, I/O,
memory usage, and expensive query statistics are collected and displayed. A DBA can also drill down into the reports
to focus on a particular query or operation to further investigate, detect, and resolve performance problems. This data
collection, storage, and reporting can allow the DBA to establish proactive monitoring of the SQL Server(s) in the
environment and go back over time to understand and assess changes to performance over the time period
monitored. The data collector and management data warehouse feature is supported in all editions (except SQL
Server Express) of SQL Server 2008.
6 - Data Compression
The ability to easily manage a database can greatly enhance the opportunity for DBAs to accomplish their regular task
lists. As table, index, and file sizes grow and very large databases (VLDBs) become commonplace, the management of
data and unwieldy file sizes has become a growing pain point. Also, with more data being queried, the need for large
amounts of memory or the necessity to do physical I/O can place a larger burden on DBAs and their organizations.
Many times this results in DBAs and organizations securing servers with more memory and/or I/O bandwidth or
having to pay a performance penalty.
Data compression, introduced in SQL Server 2008, provides a resolution to help address these problems. Using this
feature, a DBA can selectively compress any table, table partition, or index, resulting in a smaller on-disk footprint,
smaller memory working-set size, and reduced I/O. The act of compression and decompression will impact CPU;
however, this impact is in many cases offset by the gains in I/O savings. Configurations that are bottlenecked on I/O
can also see an increase in performance due to compression.
In some lab tests, enabling data compression resulted in a 50-80% saving in disk space. The space savings did vary
significantly with minimal savings on data that did not contain many repeating values or where the values required all
the bytes allocated by the specified data type. There were also workloads that did not show any gains in performance.
However, on data that contained a lot of numeric data and many repeating values, we saw significant space savings
and observed performance increases from a few percentage points up to 40-60% on some sample query workloads.
SQL Server 2008 supports two types of compressions: row compression, which compresses the individual columns of
a table, and page compression, which compresses data pages using row, prefix, and dictionary compression. The
amount of compression achieved is highly dependent on the data types and data contained in the database. In
general we have observed that using row compression results in lower overhead on the application throughput but
saves less space. Page compression, on the other hand, has a higher impact on application throughput and processor
utilization, but it results in much larger space savings. Page compression is a superset of row compression, implying
that an object or partition of an object that is compressed using page compression also has row compression applied
to it. Also, SQL Server 2008 does support the vardecimal storage format of SQL Server 2005 SP2. However, because
this storage format is a subset of row compression, it is a depreciated feature and will be removed from future
product versions.
Both row and page compression can be applied to a table or index in an online mode that is without any interruption
to the application availability. However, a single partition of a partitioned table cannot be compressed or
uncompressed online. In our testing we found that using a hybrid approach, where only the largest few tables were
compressed, resulted in the best performance in terms of saving significant disk space while having a minimal
negative impact on performance. Because there are disk space requirements, similar to what would be needed to
create or rebuild an index, care should be taken in implementing compression as well. We also found that
compressing the smallest objects first, from the list of objects you desire to compress, minimized the need for
additional disk space during the compression process.
Data compression can be implemented via Transact-SQL or the Data Compression Wizard. To determine how
compressing an object will affect its size, you can use the sp_estimate_data_compression_savings system stored
procedure or the Data Compression Wizard to calculate the estimated space savings. Database compression is only
supported in SQL Server 2008 Enterprise and Developer editions. It is implemented entirely within the database and
does not require any application modification.
For more information about using compression, see Creating Compressed Tables and Indexes.
7 - Policy-Based Management
In a number of business scenarios, there is a need to maintain certain configurations or enforce policies either within
a specific SQL Server, or many times across a group of SQL Servers. A DBA or organization may require a particular
naming convention to be implemented on all user tables or stored procedures that are created, or a required
configuration change to be defined across a number of servers in the same manner.
Policy-Based Management (PBM) provides DBAs with a wide variety of options in managing their environment.
Policies can be created and checked for compliance. If a target (such as a SQL Server database engine, a database, a
table, or an index) is out of compliance, the administrator can automatically reconfigure it to be in compliance. There
are also a number of evaluation modes (of which many are automated) that can help the DBA check for policy
compliance, log and notify when a policy violation occurs, and even roll back the change to keep in compliance with
the policy. For more information about evaluation modes and how they are mapped to facets (a PBM term also
discussed in the blog), see the SQL Server Policy-Based Management blog.
The policies can be exported and imported as .xml files for evaluation and implementation across multiple server
instances. Also, in SQL Server Management Studio and the Registered Servers view, policies can be evaluated across
multiple servers if they are registered under a local server group or a Central Management Server group.
Not all of the functionality of Policy-Based Management can be implemented on earlier versions of SQL Server.
However, the policy reporting feature can be utilized on SQL Server 2005 and SQL Server 2000. For more information
about administering servers by using Policy-Based Management, see Administering Servers by Using Policy-Based
Management in SQL Server Books Online. For more information about the technology itself, including examples, see
the SQL Server 2008 Compliance Guide.
8 - Predictable Performance and Concurrency
A significant problem many DBAs face is trying to support SQL Servers with ever-changing workloads, and achieving
some level of predictable performance (or minimizing variance in plans and performance). Unexpected query
performance, plan changes, and/or general performance issues can come about due to a number of factors, including
increased application load running against SQL Server or version upgrades of the database itself. Getting predictable
performance from queries or operations run against SQL Server can greatly enhance the DBAs ability to meet and
maintain availability, performance, and/or business continuity goals (OLAs or SLAs).
SQL Server 2008 provides a few feature changes that can help provide more predictable performance. In SQL Server
2008, there exist some enhancements to the SQL Server 2005 plan guides (or plan freezing) and a new option to
control lock escalation at a table level. Both of these enhancements can provide a more predictable and structured
interaction between the application and the database.
First, plan guides:
SQL Server 2005 enabled greater query performance stability and predictability by providing a new feature called plan
guides to enable specifying hints for queries that could not be modified directly in the application. For more
information, see the Forcing Query Plans white paper. While a very powerful feature, the USE PLAN query hint only
supported SELECT DML operations and were often cumbersome to use due to the sensitivity of the plan guides to the
formatting.
SQL Server 2008 builds on the plan guides mechanism in two ways: It expands the support for the USE PLAN query
hint to cover all DML statements (INSERT, UPDATE, DELETE, MERGE), and it introduces a new plan freezing feature that
can be used to directly create a plan guide (freeze) any query plan that exists in the SQL Server plan cache, as in the
following example.
sp_create_plan_guide_from_handle
@name = N'MyQueryPlan',
@plan_handle = @plan_handle,
@statement_start_offset = @offset;
A plan guide created by either means has a database scope and is stored in the sys.plan_guides table. Plan guides
are only used to influence the query plan selection process of the optimizer and do not eliminate the need for the
query to be compiled. A new function, sys.fn_validate_plan_guide, has also been introduced to validate existing SQL
Server 2005 plan guides and ensure their compatibility with SQL Server 2008. Plan freezing is available in the SQL
Server 2008 Standard, Enterprise, and Developer editions.
Next, lock escalation:
Lock escalation has often caused blocking and sometimes even deadlocking problems, which the DBA is forced to
troubleshoot and resolve. Previous versions of SQL Server permitted controlling lock escalation (trace flags 1211 and
1224), but this was only possible at an instance-level granularity. While this helped some applications work-around
the problem, it caused severe issues for others. Another problem with the SQL Server 2005 lock escalation algorithm
was that locks on partitioned tables were directly escalated to the table level, rather than the partition level.
SQL Server 2008 offers a solution for both of these problems. A new option has been introduced to control lock
escalation at a table level. By using an ALTER TABLE command, option locks can be specified to not escalate, or
escalate to the partition level for partitioned tables. Both these enhancements help improve the scalability and
performance without having negative side-effects on other objects in the instance. Lock escalation is specified at the
database-object level and does not require any application change. It is supported in all editions of SQL Server 2008.
9 - Resource Governor
Maintaining a consistent level of service by preventing runaway queries and guaranteeing resources for mission-
critical workloads has been a challenge. In the past there was no way of guaranteeing a certain amount of resources
to a set of queries and prioritizing the access. All queries had equal access to all the available resources.
SQL Server 2008 introduces a new feature called Resource Governor, which helps address this issue by enabling users
to differentiate workloads and allocate resources as they are requested. Resource Governor limits can easily be
reconfigured in real time with minimal impact on the workloads that are executing. The allocation of the workload to
a resource pool is configurable at the connection level, and the process is completely transparent to the application.
The diagram below depicts the resource allocation process. In this scenario three workload pools (Admin Workload,
OLTP Workload, and Report Workload) are configured, and the OLTP Workload pool is assigned a high priority. In
parallel, two resource pools (Admin Pool and Application Pool) are configured with specific memory and processor
(CPU) limits as shown. As a final step the Admin Workload is assigned to the Admin Pool and the OLTP and Report
workloads are assigned to the Application Pool.
Below are some other points you need to consider when using Resource Governor.
Resource Governor relies on login credentials, host name, or application name as a resource pool identifier,
so using a single login for an application, depending on the number of clients per server, might make
creating pools more difficult.
Database-level object grouping, in which the resource governing is done based on the database objects
being referenced, is not supported.
Resource Governor only allows resource management within a single SQL Server instance. For managing
multiple SQL Server instances or processes within a server from a single source, Windows System Resource
Manager should be considered.
Only processor and memory resources can be configured. I/O resources cannot be controlled.
Dynamically switching workloads between resource pools once a connection is made is not possible.
Resource Governor is only supported in SQL Server 2008 Enterprise and Developer editions and can only be
used for the SQL Server database engine; SQL Server Analysis Services (SSAS), SQL Server Integration
Services (SSIS), and SQL Server Reporting Services (SSRS) cannot be controlled.
10 - Transparent Data Encryption (TDE)
Security is one of the top concerns of many organizations. There are many different layers to securing one of the
most important assets of an organization: its data. In most cases, organizations do well at securing their active data
via the use of physical security, firewalls, and tightly controlled access policies. However, when physical medium such
as the backup tape or disk on which the data resides is compromised, the above security measures are of no use,
because a rouge user can simply restore the database and get full access to the data.
SQL Server 2008 offers a solution to this problem by way of transparent data encryption (TDE). TDE performs real-
time I/O encryption and decryption of the data and log files by using a database encryption key (DEK). The DEK is a
symmetric key secured by using a certificate stored in the master database of the server, or an asymmetric key
protected by an Extensible Key Management (EKM) module.
TDE is designed to protect data at rest, which means the data stored in the .mdf, .ndf, and .ldf files cannot be viewed
using a hex editor or other means. However, data that is not at rest, such as the results of a SELECT statement in SQL
Server Management Studio, will continue to be visible to users who have rights to view the table. Also, because TDE is
implemented at the database level, the database can leverage indexes and keys for query optimization. TDE should
not be confused with column-level encryption, which is a separate feature that allows encryption of data even when it
is not at rest.
Encrypting a database is a one-time process that can be initiated via a Transact-SQL command or SQL Server
Management Studio, and it is executed as a background thread. You can monitor the encryption or decryption status
using the sys.dm_database_encryption_keys dynamic management view. In a lab test we conducted, we were able to
encrypt a 100 GB database using the AES_128 encryption algorithm in about an hour. While the overhead of using
TDE is largely dictated by the application workload, in some of the testing conducted that overhead was measured to
be less than 5%. One potential performance impact to be aware of is this: If any database within the instance does
have TDE applied, the tempDB system database is also encrypted. Finally, of note when combining features:
When backup compression is used to compress an encrypted database, the size of the compressed backup
is larger than if the database were not encrypted, because encrypted data does not compress well.
Encrypting the database does not affect data compression (row or page).
TDE enables organizations to meet the demands of regulatory compliance and overall concern for data privacy. TDE is
only supported in the SQL Server 2008 Enterprise and Developer editions and can be enabled without changing
existing applications. For more information, see Database Encryption in SQL Server 2008 Enterprise Edition or the SQL
Server 2008 Compliance Guide discussion on Using Transparent Data Encryption.
In conclusion, SQL Server 2008 offers features, enhancements, and functionality to help improve the Database
Administrator experience. While a Top 10 list was provided above, there are many more features included within SQL
Server 2008 that help improve the experience for DBA and other users alike. For a Top 10 feature set for other SQL
Server focus areas, see the other SQL Server 2008 Top 10 articles on this site. For a full list of features and detailed
descriptions, see SQL Server Books Online and the SQL Server 2008 Overview Web site.
Top 10 SQL Server 2008 Features for ISV Applications
Microsoft SQL Server 2008 has hundreds of new and improved features, many of which are specifically designed
for large scale independent software vendor (ISV) applications, which need to leverage the power of the underlying
database while keeping their code database agnostic. This article presents details of the top 10 features that we
believe are most applicable to such applications based on our work with strategic ISV partners. Along with the
description of each feature, the main pain-points the feature helps resolve and some of the important limitations that
need to be considered are also presented. The features are grouped into two categories: ones that do not require any
application change (features 1-8) and those that require some application code change (features 9-10). The features
are not prioritized in any particular order.
1 - Data Compression
The disk I/O subsystem is the most common bottleneck for many database implementations. More disks
are needed to reduce the read/write latencies; but this is expensive, especially on high-performing
storage systems. At the same time, the need for storage space continues to increase due to rapid
growth of the data, and so does the cost of managing databases (backup, restore, transfer, etc.).
Data compression introduced in SQL Server 2008 provides a resolution to address all these problems.
Using this feature one can selectively compress any table, table partition, or index, resulting in a smaller
on-disk footprint, smaller memory working-set size, and reduced I/O. Configurations that are
bottlenecked on I/O may also see an increase in performance. In our lab test, enabling data compression
for some ISV applications resulted in a 50-80% saving in disk space.
SQL Server supports two types of compressions: ROW compression, which compresses the
individual columns of a table, and PAGE compression which compresses data pages using row,
prefix, and dictionary compression. The compression results are highly dependent on the data
types and data contained in the database; however, in general weve observed that using ROW
compression results in lower overhead on the application throughput but saves less space.
PAGE compression, on the other hand, has a higher impact on application throughput and
processor utilization, but it results in much larger space savings. PAGE compression is a
superset of ROW compression, implying that an object or partition of an object that is
compressed using PAGE compression also has ROW compression applied to it. Compressed
pages remain compressed in memory until rows/columns on the pages are accessed.
Both ROW and PAGE compression can be applied to a table or index in an online mode that is
without any interruption to the application availability. However, partitions of a partitioned
table cannot be compressed or uncompressed online. In our testing we found that using a
hybrid approach where only the largest few tables were compressed resulted in the best overall
performance, saving significant disk space while having a minimal negative impact on
performance. We also found that compressing the smallest objects first minimized the need for
additional disk space during the compression process.
To determine how compressing an object will affect its size you can use the
sp_estimate_data_compression_savings system stored procedure. Database compression is
only supported in SQL Server 2008 Enterprise and Developer editions. It is fully controlled at the
database level and does not require any application change.
2 - Backup Compression
The amount of data stored in databases has grown significantly in the last decade. resulting in larger
database sizes. At the same time the demands for applications to be available 24x7 have forced the
backup time-windows to shrink. In order to speed up the backup procedure, database backups are
usually first streamed to fast disk-based storage and moved out to slower media later. Keeping such
large disk-based backups online is expensive, and moving them around is time consuming.
With SQL Server 2008 backup compression, the backup file is compressed as it is written out, thereby
requiring less storage, less disk I/O, and less time, and utilizing less network bandwidth for backups that
are written out to a remote server. However, the additional processing results in higher processor
utilization. In a lab test conducted with an ISV workload we observed a 40% reduction in the backup file
size and a 43% reduction in the backup [Link] compression is achieved by specifying the WITH
COMPRESSION clause in the backup command (for more information, see SQL Server Books Online).
To prevent having to modify all the existing backup scripts, there is also a global setting (using the
Database Settings page of the Server Properties dialog box) to enable compression of all backups taken
on that server instance by default; this eliminates the need to modify existing backup scripts. While the
compression option on the backup command needs to be explicitly specified, the restore command
automatically detects that a backup is compressed and decompresses it during the restore operation.
Overall, backup compression is a very useful feature that does not require any change to the ISV
application. For more information about tuning backup compression, see the technical note on Tuning
the Performance of Backup Compression in SQL Server 2008.
Note: Creating compressed backups is only supported in SQL Server 2008 Enterprise and Developer
editions; however, every SQL Server 2008 edition can restore a compressed backup.
3 - Transparent Data Encryption
In most cases, organizations do well at securing their active data via the use of firewalls, physical
security, and tightly controlled access policies. However, when the physical media such as the backup
tape or disk on which the data resides is compromised, the above security measures are of no use, since
a rogue user can simply restore the database and get full access to the data.
SQL Server 2008 offers a solution to this problem by way of Transparent Data Encryption (TDE). TDE
performs real-time I/O encryption and decryption of the data and log files using a database encryption
key (DEK). The DEK is a symmetric key secured by using a certificate stored in the master database of the
server, or an asymmetric key protected by an Extensible Key Management (EKM) [Link] is
designed to protect data at rest; this means that the data stored in the .mdf, .ndf, and .ldf files cannot
be viewed using a hex editor or some other such means. However, data that is not at rest, such as the
results of a select statement in SQL Server Management Studio, continues to be visible to users who
have rights to view the table. TDE should not be confused with column-level encryption, which is a
separate feature that allows encryption of data even when it is not at [Link] a database is a
one-time process that can be initiated via a Transact-SQL command and is executed as a background
thread. You can monitor the encryption/decryption status using the
sys.dm_database_encryption_keys dynamic management view (DMV).
In a lab test we conducted we were able to encrypt a 100-gigabyte (GB) database using the AES_128
encryption algorithm in about one hour. While the overheads of using TDE are largely dictated by the
application workload, in some of the testing we conducted, the overhead was measured to be less than
5%.
One point worth mentioning is when backup compression is used to compress an encrypted database,
the size of the compressed backup is larger than if the database were not encrypted; this is because
encrypted data does not compress well.
TDE enables organizations to meet the demands of regulatory compliance and overall concern for data
privacy.
TDE is only supported in the SQL Server 2008 Enterprise and Developer editions, and it can be enabled
without changing an existing application.
4 - Data Collector and Management Data Warehouse
Performance tuning and troubleshooting is a time-consuming task that usually requires deep SQL Server
skills and an understanding of the database internals. Windows System monitor (Perfmon), SQL Server
Profiler, and dynamic management views helped with some of this, but they were often too intrusive or
laborious to use, or the data was too difficult to interpret.
To provide actionable performance insights, SQL Server 2008 delivers a fully extensible performance
data collection and warehouse tool also known as the Data Collector. The tool includes several out-of-
the-box data collection agents, a centralized data repository for storing performance data called
management data warehouse (MDW), and several precanned reports to present the captured data. The
Data Collector is a scalable tool that can collect and assimilate data from multiple sources such as
dynamic management views, Perfmon, Transact-SQL queries, etc., using a fully customizable data
collection and assimilation frequency. The Data Collector can be extended to collect data for any
measurable attribute of an application. For example, in our lab test we wrote a custom Data Collector
agent job (40 lines of code) to measure the processing throughput of the workload.
The diagram below depicts a typical Data Collector report.
The Performance data collection and warehouse feature is supported in all editions of SQL Server 2008.
5 - Lock Escalation
Lock escalation has often caused blocking and sometimes even deadlocking problems for many
ISV applications. Previous versions of SQL Server permitted controlling lock escalation (trace
flags 1211 and 1224), but this was only possible at an instance-level granularity. While this
helped some applications work around the problem, it caused severe issues for others. Another
problem with the SQL Server 2005 lock escalation algorithm was that locks on partitioned
tables were directly escalated to the table level, rather than the partition level.
SQL Server 2008 offers a solution for both these issues. A new option has been introduced to control
lock escalation at a table level. If an ALTER TABLE command option is used, locks can be specified to not
escalate, or to escalate to the partition level for partitioned tables. Both these enhancements help
improve the scalability and performance without having negative side-effects on other objects in the
instance. Lock escalation is specified at the database-object level and does not require any application
change. It is supported in all editions of SQL Server 2008.
6 - Plan Freezing
SQL Server 2005 enabled greater query performance stability and predictability by providing a new
feature called plan guides to enable specifying hints for queries that could not be modified directly in
the application (for more information, see the white paper Forcing Query Plans). While a very
powerful feature, plan guides were often cumbersome to use due to the sensitivity of the plan guides to
the formatting, and only supported SELECT DML operations when used in conjunction the USE PLAN
query hint.
SQL Server 2008 builds on the plan guides mechanism in two ways: it expands the support for plan
guides to cover all DML statements (INSERT, UPDATE, DELETE, MERGE), and introduces a new feature,
Plan Freezing, that can be used to directly create a plan guide (freeze) for any query plan that exists in
the SQL Server plan cache, for example:
sp_create_plan_guide_from_handle
@name = N'MyQueryPlan',
@plan_handle = @plan_handle,
@statement_start_offset = @offset;
A plan guide created by either means have a database scope and are stored in the sys.plan_guides
table. Plan guides are only used to influence the query plan selection process of the optimizer and do
not eliminate the need for the query to be compiled. A new function sys.fn_validate_plan_guide has
also been introduced to validate existing SQL Server 2005 plan guides and ensure their compatibility
with SQL Server 2008. Plan freezing is available in the SQL Server 2008 Standard, Enterprise, and
Developer editions.
7 - Optimize for Ad hoc Workloads Option
Applications that execute many single use ad hoc batches (e.g., nonparameterized workloads)
can cause the plan cache to grow excessively large and result in reduced efficiency. SQL Server
2005 offered the Parameterization Forced database option to address such scenarios, but that
sometimes resulted in adverse side-effects on workloads that had a large skew in the data and
had queries that were very sensitive to the underlying data.
SQL Server 2008 introduces a new option, optimize for ad hoc workloads, which is used to
improve the efficiency of the plan cache. When this option is set to 1, the SQL Server engine
stores a small stub for the compiled ad hoc plan in the plan cache instead of the entire
compiled plan, when a batch is compiled for the first time. The compiled plan stub is used to
identify that the ad hoc batch has been compiled before but has only stored a compiled plan
stub, so that when this batch is invoked again the database engine compiles the batch, removes
the compiled plan stub from the plan cache, and replaces it with the full compiled plan.
This mechanism helps to relieve memory pressure by not allowing the plan cache to become filled with
large compiled plans that are not reused. Unlike the Forced Parameterization option, optimizing for ad
hoc workloads does not parameterize the query plan and therefore does not result in saving any
processor cycles by way of eliminating compilations. This option does not require any application change
and is available in all editions of SQL Server 2008.
8 - Resource Governor
Maintaining a consistent level of service by preventing runaway queries and guaranteeing
resources for mission-critical workloads has been a challenge for SQL Server. In the past there
was no way of guaranteeing a certain amount of resources to a set of queries and prioritizing
the access; all queries had equal access to all the available resources.
SQL Server 2008 introduces a new feature, Resource Governor, which helps address this issue
by enabling users to differentiate workloads and allocate resources as they are requested. The
Resource Governor limits can easily be reconfigured in real time with minimal impact on the
workloads that are executing. The allocation of the workload to a resource pool is configurable
at the connection level, and the process is completely transparent to the application.
The diagram below depicts the resource allocation process. In this scenario three workload pools (Admin
workload, OLTP workload, and Report workload) are configured, and the OLTP workload pool is assigned
a high priority. In parallel two resource pools (Admin pool and Application pool) are configured with
specific memory and processor (CPU) limits as shown. As final steps, the Admin workload is assigned to
the Admin pool, and the OLTP and Report workloads are assigned to the Application pool.
Below are some other points you need to consider when using resource governor:
Since Resource Governor relies on login credentials, host name, or application name as a
resource pool identifier, most ISV applications that use a single login to connect multiple
application users to SQL Server will not be able to use Resource Governor without reworking the
application. This rework would require the application to utilize one of the resource identifiers
from within the application to help differentiate the workload.
Database-level object grouping, in which the resource governing is done based on the database
objects being referenced, is not supported.
Resource Governor only allows resource management within a single SQL Server instance. For
multiple instances. Windows System Resource Manager should be considered.
Only processor and memory resources can be configured. I/O resource cannot be controlled.
Dynamically switching workloads between resource pools once a connection is made is not
possible.
Resource Governor is only supported in SQL Server 2008 Enterprise and Developer editions and
can only be used for the SQL Server database engine; SQL Server Analysis Services (SSAS), SQL
Server Integration Services (SSIS), and SQL Server Reporting Services (SSRS) cannot be
controlled.
9 - Table-Valued Parameters
Often one of the biggest problems ISVs encountered while developing applications on earlier
versions of SQL Server was the lack of an easy way to execute a set of UPDATE, DELETE, INSERT
operations from a client as a single batch on the server. Executing the set of statements as
singleton operations resulted in a round trip from the client to the server for each operation
and could result in as much as a 3x slowdown in performance.
SQL Server 2008 introduces the table-valued parameter (TVP) feature, which helps resolve this
problem. Using the new TVP data type, a client application can pass a potentially unlimited
sized array of data directly from the client to the server in a single-batch operation. TVPs are
first-class data types and are fully supported by the SQL Server tools and SQL Server 2008 client
libraries (SNAC 10 or later). TVPs are read-only, implying that they can only be used to pass
array-type data into SQL Server; they cannot be used to return array-type data.
The graph below plots the performance of executing a batch of insert statements using a
parameter array (sequence of singleton operations) vs. executing the same batch using a TVP.
For batches of 10 statements or less, parameter arrays perform better than TVPs. This is due to
the one-time overhead associated with initiating the TVP, which outweighs the benefits of
transferring and executing the inserts as a single batch on the server.
However, for batches larger than 10 statements, TVPs outperform parameter arrays, because the entire
batch is transferred to the server and executed as a single operation. As can be seen in the graph for a
batch of 250 inserts the amount of time taken to execute the batch is 2.5 times more when the
operations are performed using a parameter array versus a TVP. The performance benefits scale almost
linearly and when the size of the batch increases to 2,000 insert statements, executing the batch using a
parameter array takes more than four times longer than using a TVP.
TVPs can also be used to perform other functions such as passing a large batch of parameters to a
stored procedure. TVPs are supported in all editions of SQL Server 2008 and require the application to
be modified.
10 - Filestream
In recent years there has been an increase in the amount of unstructured data (e-mail messages,
documents, images, videos, etc.) created. This unstructured data is often stored outside the database,
separate from its structured metadata. This separation can cause challenges and complexities in keeping
the data consistent, managing the data, and performing backup/restores.
The new Filestream data type in SQL Server 2008 allows large unstructured data to be stored as files on
the file system. Transact-SQL statements can be used to read, insert, update and manage the Filestream
data, while Win32 file system interfaces can be used to provide streaming access to the data. Using the
NTFS streaming APIs allows efficient performance of common file operations while providing all of the
rich database services, including security and backup. In our lab tests we observed the biggest
performance advantage of streaming access when the size of binary large objects (BLOBs) was greater
than 256 kilobytes (KB). The Filestream feature is initially targeted to objects that do not need to be
updated in place, as that is not yet supported.
Filestream is not automatically enabled when you install or upgrade SQL Server 2008. You need to
enable it by using SQL Server Configuration Manager and SQL Server Management Studio. Filestream
requires a special dedicated filegroup to be created to store the Filestream (varbinary(max)) data that
has been qualified with the Filestream attribute. This filegroup points to an NTFS directory on a file
system and is created similar to all the other filegroups. The Filestream feature is supported in all
editions of SQL Server 2008, and it requires the application to be modified to leverage the Win32 APIs (if
required) and to migrate the existing varbinary data.
SQL Server 2008 is a significant release that delivers many new features and key improvements, many of
which have been designed specifically for ISV workloads and require zero or minimal application change.
This article presented an overview of only the top-10 features that are most applicable to ISV
applications and help resolve key ISV problems that couldnt easily be addressed in the past. For more
information, including a full list of features and detailed descriptions, see SQL Server Books Online and
the SQL Server web site.
Top 10 Best Practices for Building a Large Scale Relational
Data Warehouse
Building a large scale relational data warehouse is a complex task. This article describes some design techniques that
can help in architecting an efficient large scale relational data warehouse with SQL Server. Most large scale data
warehouses use table and index partitioning, and therefore, many of the recommendations here involve partitioning.
Most of these tips are based on experiences building large data warehouses on SQL Server 2005.
1 - Consider partitioning large fact tables
Consider partitioning fact tables that are 50 to 100GB or larger.
Partitioning can provide manageability and often performance benefits.
o Faster, more granular index maintenance.
o More flexible backup / restore options.
o Faster data loading and deleting
Faster queries when restricted to a single partition..
Typically partition the fact table on the date key.
o Enables sliding window.
Enables partition elimination.
2- Build clustered index on the date key of the fact table
This supports efficient queries to populate cubes or retrieve a historical data slice.
If you load data in a batch window then use the options ALLOW_ROW_LOCKS = OFF and
ALLOW_PAGE_LOCKS = OFF for the clustered index on the fact table. This helps speed up table scan
operations during query time and helps avoid excessive locking activity during large updates.
Build nonclustered indexes for each foreign key. This helps pinpoint queries' to extract rows based on a
selective dimension [Link] filegroups for administration requirements such as backup / restore,
partial database availability, etc.
3 - Choose partition grain carefully
Most customers use month, quarter, or year.
For efficient deletes, you must delete one full partition at a time.
It is faster to load a complete partition at a time.
o Daily partitions for daily loads may be an attractive option.
o However, keep in mind that a table can have a maximum of 1000 partitions.
Partition grain affects query parallelism.
o For SQL Server 2005:
Queries touching a single partition can parallelize up to MAXDOP (maximum degree of
parallelism).
Queries touching multiple partitions use one thread per partition up to MAXDOP.
o For SQL Server 2008:
Parallel threads up to MAXDOP are distributed proportionally to scan partitions, and
multiple threads per partition may be used even when several partitions must be scanned.
Avoid a partition design where only 2 or 3 partitions are touched by frequent queries, if you need MAXDOP
parallelism (assuming MAXDOP =4 or larger).
4 - Design dimension tables appropriately
Use integer surrogate keys for all dimensions, other than the Date dimension. Use the smallest possible
integer for the dimension surrogate keys. This helps to keep fact table narrow.
Use a meaningful date key of integer type derivable from the DATETIME data type (for example: 20060215).
o Don't use a surrogate Key for the Date dimension
o Easy to write queries that put a WHERE clause on this column, which will allow partition elimination
of the fact table.
Build a clustered index on the surrogate key for each dimension table, and build a non-clustered index on
the Business Key (potentially combined with a row-effective-date) to support surrogate key lookups during
loads.
Build nonclustered indexes on other frequently searched dimension columns.
Avoid partitioning dimension tables.
Avoid enforcing foreign key relationships between the fact and the dimension tables, to allow faster data
loads. You can create foreign key constraints with NOCHECK to document the relationships; but dont
enforce them. Ensure data integrity though Transform Lookups, or perform the data integrity checks at the
source of the data.
5 - Write effective queries for partition elimination
Whenever possible, place a query predicate (WHERE condition) directly on the partitioning key (Date
dimension key) of the fact table.
6 - Use Sliding Window technique to maintain data
Maintain a rolling time window for online access to the fact tables. Load newest data, unload oldest data.
Always keep empty partitions at both ends of the partition range to guarantee that the partition split (before
loading new data) and partition merge (after unloading old data) do not incur any data movement.
Avoid split or merge of populated partitions. Splitting or merging populated partitions can be extremely
inefficient, as this may cause as much as 4 times more log generation, and also cause severe locking.
Create the load staging table in the same filegroup as the partition you are loading.
Create the unload staging table in the same filegroup as the partition you are deleteing.
It is fastest to load newest full partition at one time, but only possible when partition size is equal to the data
load frequency (for example, you have one partition per day, and you load data once per day).
If the partition size doesn't match the data load frequency, incrementally load the latest partition.
Various options for loading bulk data into a partitioned table are discussed in the whitepaper
[Link]
Always unload one partition at a time.
7- Efficiently load the initial data
Use SIMPLE or BULK LOGGED recovery model during the initial data load.
Create the partitioned fact table with the Clustered index.
Create non-indexed staging tables for each partition, and separate source data files for populating each
partition.
Populate the staging tables in parallel.
o Use multiple BULK INSERT, BCP or SSIS tasks.
Create as many load scripts to run in parallel as there are CPUs, if there is no IO
bottleneck. If IO bandwidth is limited, use fewer scripts in parallel.
Use 0 batch size in the load.
Use 0 commit size in the load.
Use TABLOCK.
Use BULK INSERT if the sources are flat files on the same server. Use BCP or SSIS if data is
being pushed from remote machines.
Build a clustered index on each staging table, then create appropriate CHECK constraints.
SWITCH all partitions into the partitioned table.
Build nonclustered indexes on the partitioned table.
Possible to load 1 TB in under an hour on a 64-CPU server with a SAN capable of 14 GB/Sec throughput
(non-indexed table). Refer to SQLCAT blog entry
[Link] for details.
8 - Efficiently delete old data
Use partition switching whenever possible.
To delete millions of rows from nonpartitioned, indexed tables
o Avoid DELETE FROM ...WHERE ...
Huge locking and logging issues
Long rollback if the delete is canceled
o Usually faster to
INSERT the records to keep into a non-indexed table
Create index(es) on the table
Rename the new table to replace the original
As an alternative, trickle' deletes using the following repeatedly in a loop
DELETE TOP (1000) ... ;
COMMIT
Another alternative is to update the row to mark as deleted, then delete later during non critical time.
9 - Manage statistics manually
Statistics on partitioned tables are maintained for the table as a whole.
Manually update statistics on large fact tables after loading new data.
Manually update statistics after rebuilding index on a partition.
If you regularly update statistics after periodic loads, you may turn off autostats on that table.
This is important for optimizing queries that may need to read only the newest data.
Updating statistics on small dimension tables after incremental loads may also help performance. Use
FULLSCAN option on update statistics on dimension tables for more accurate query plans.
10 - Consider efficient backup strategies
Backing up the entire database may take significant amount of time for a very large database.
o For example, backing up a 2 TB database to a 10-spindle RAID-5 disk on a SAN may take 2 hours
(at the rate 275 MB/sec).
Snapshot backup using SAN technology is a very good option.
Reduce the volume of data to backup regularly.
o The filegroups for the historical partitions can be marked as READ ONLY.
o Perform a filegroup backup once when a filegroup becomes read-only.
o Perform regular backups only on the read / write filegroups.
Note that RESTOREs of the read-only filegroups cannot be performed in parallel.
Top 10 Best Practices for SQL Server Maintenance for SAP
SQL Server provides an excellent database platform for SAP applications. The following recommendations provide an
outline of best practices for maintaining SQL Server database for an SAP implementation.
Perform a full database backup daily
Technically there are no problems to backing up SAP databases online. This means that end users or
nightly batch jobs can continue to use SAP applications without problems. SQL Server Backup consumes
few CPU resources. However, SQL Server Backup does require I/O bandwidth because SQL Server will try to
read every used extent to the backup device. Everything that is required for SAP (business data, metadata
and ABAP applications etc) is included in one database named <SID>. Sometimes the time needed to
take a full backup (generally a few hours) might become a problem, especially in SQL Server 2000 where
no transaction log backups can be made while an Online Database Backup was performed. SQL Server
2005 does not have this issue.
To create faster online backups using SAN Technology, SQL Server offers interfaces for SAN vendors to
perform a Snapshot Backup or to create clones of a SQL Server database. However, backing up terabytes
of data every night may overload the backup infrastructure. Another possibility would be to do differential
backups of the SAP database on a daily basis and do a full database backup on the weekend only.
Perform transaction log backup Every 10 to 30 minutes
In case of a disaster happening on the production server, it is vital that the most recent status can be
restored using online or differential database backups plus a series of transaction log backups which
ideally cover as close as possible to the time of the disaster. For this purpose it is vital to perform
transaction log backups on a regular basis. If you only create a transaction log backup every two hours, the
in the case of a disaster, up to two hours of committed business transactions would not be able to be
restored. Therefore it is vital to do transaction log backups often enough to reduce the risk of losing a
large number of committed business transactions in case of a disaster. In many productive customer
scenarios, a time frame of 10-30 minutes proved to be an acceptable frequency. However, in combination
with SQL Server log shipping, you can create SQL Server transaction log backups even every two or five
minutes. The finest granularity achievable is to perform SQL Server transaction log backups scheduled by
SQL Agent every minute. Besides reducing the risk of losing business transactions, transaction log backups
also truncate log data in the SQL Server transaction log, and reducing the possibility of the transaction log
becoming full.
Back up system partition in case of configuration changes
Back up the system partition after any configuration changes. Use Windows Server 2003 Automated
System Recovery (ASR), or other tools such as Symantec Ghost or SAN boot to restore the system
partitions.
Back up system databases in case of configuration changes
Back up the system databases (master, msdb, model) after any configuration changes. In SQL Server 2005,
the resource database does not need to be backed up because it does not experience any changes and is
installed with the SQL Server 2005 installation.
Run DBCC CHECKDB periodically (ideally before the full database backup)
Ideally, a consistency check using DBCC CHECKDB sould be run before performing an online database
backup. However, please note that DBCC CHECKDB is a very time and resource consuming activity that
puts heavy workload on SAP production systems, especially on databases over one terabyte. On
commodity hardware with a good I/O subsystem, I/O throughputs in the range of 100-150 GB/h can be
achieved. Given such I/O throughputs, and the fact that there are many SAP databases up to 10 terabytes
or more, it is clear that running a DBCC CHECKDB on a production system is not always practical.
Therefore, many people choose not to run DBCC CHECKDB. Although all components of hardware and
software have become more reliable over the last decade, physical corruptions can still happen. One
reason for physical corruptions is a catastrophic power outage without having battery backup for hardware
components. Another reason could be physical damage to connections or hardware components. In
massive cases there is no other way than to go back to a backup and restore the SAP database and then
apply all the transaction logs up to the most recent. However, to detect physical inconsistencies at an early
state, or to know that the backup method is reliable, or to minimize impact of physical corruptions, the
following three major measures should be considered:
o Consider running DBCC CHECKDB on a regular basis. This could be on a sandbox system that
runs a restored image of the production environment. On such a system, time and resource
consumption of DBCC CHECKDB would not be a concern and would not affect production users.
o Test actually restoring the SAP database from an online or differential and transaction log backup.
The fact that a backup is on tape does not necessarily mean that it is consistent on tape or that it
can be read from tape. Tape hardware or tape cassettes may fail over the years, and you do not
want to be in a position where you have tapes that cannot be read anymore. Having a backup in a
vault does not say anything about the ability to be able to restore in case of a disaster. The
backup must also be proven to be readable.
o For databases with terabytes of volume, maintain a second copy of the database at the most
recent status, using either log shipping or database mirroring. Both of these high-availability
methods will de-couple hardware components and hence may provide a physical consistent
image of the production database at a secondary site.
Evaluate security patches monthly (and install them if they are necessary)
For most of SAP customers, availability is the most important requirement. Especially if they need to serve
a single SAP instance globally, they dont want to stop and restart the SAP servers to apply security
patches. Plus, some testing in these environments is definitely necessary before installing the security
patches. Therefore one of realistic scenarios for SAP customers is carefully evaluating patches and reducing
the frequency of patch installations, hopefully almost to zero. Filtering unnecessary packets, disabling
unnecessary services, and so forth are good security measures.
If you have real time anti-virus monitoring, it is recommended that you exclude the SQL Server database
files (including data files, transaction log files, tempdb and other system database files) from real time
monitoring. If you perform backups to disks, exclude database backup files as well as transaction log
backup files.
Evaluate update modules of hardware drivers and firmwares and install if necessary
There have been critical issues due to bugs in hardware drivers and firmwares within the commodity
servers. It is sometimes difficult to find this kind of issue within Microsoft, and furthermore hardware
companies sometimes dont provide enough support services to commodity server customers. So it is a
customers responsibility to manage updates on drivers and firmwares regularly. Before updating the
drivers on production commodity servers, thorough tests must be conducted on test and sandbox
systems. Like nearly no other software component, a little flaw in a driver of an Host Bus Adapter (HBA) or
SCSI card can be responsible for physical inconsistency within a database.
Update statistics on the largest tables weekly or monthly
SQL Server provides two options to keep the statistics current: auto create statistics and auto update
statistics. These two options are ON by default. SAP recommends keeping them ON. There may be some
cases where auto update statistics may not be able to provide satisfactory performance. A specific case
came up in SAP BW. The issue was resolved by the functionality in SAP BW that is documented in SAP OSS
note #849062. Please keep in mind that auto update statistics is run only on tables with more than 500
rows. In some very specific cases of data developing into one direction, it is recommended to explicitly run
update statistics on specific columns of the table on a scheduled basis. However, you should not perform a
general manual update statistics. If performance problems are analyzed and the root cause is found in an
index, or some column statistics not being recent enough, then the solution often is simply to have a
certain column or index statistics updated on a more frequent basis.
Rebuild or defrag the most important indexes
The impact of reorganizing tables and indexes on performance is highly dependent on the type of query
that is executed and the I/O bandwidth that is available on the system. Simply going along measures like
(1) Average page density < 80 percent or (2) Logical scan fragmentation > 40 percent as thresholds to
start reorganizing are a waste of time and resources. Reasons are:
o Some SAP queuing tables will always show up as being highly fragmented
o A query reading a single row or a small number of rows which represents the majority of SAP
queries do not benefit from reorganizing a table.
o If there is enough I/O bandwidth and memory for SQL Server on the database server, the impact
of table fragmentation might be limited.
Many people never reorganize tables to speed up query performance. However, there are also people who
reorganize tables to compress them after they archived SAP data. Not all the tables are organized or
sorted according to the archiving criteria. Hence it can happen that despite deleting 25 percent of a table,
the table only decreased its volume by 10 percent. To maximize space reduction after archiving, you can
run DBCC INDEXDEFRAG on the affected tables. DBCC INDEXDEFRAG will compress the data content on
the pages of a table. DBCC INDEXDEFRAG treats every move of a bunch of rows to one page as a single
transaction. Hence DBCC INDEXDEFRAG will result in many small transactions as opposed to creating an
index which treats the entire index creation task as one large transaction. DBCC INDEXDEFRAG does not
consume much CPU resources, but it does create significant I/O traffic. Therefore do not run too many
DBCC INDEXDEFRAG commands in parallel. Completely reorganizing tables by re-creating their clustered
indexes should not be done on large tables because this will generate huge amount of transaction log.
Use a health check monitoring tool for performance, availability, and so forth
Unplanned downtime depends on how quickly system failures are notified to administrators and how soon
they can start the recovery process. For availability, SAP administrators should be aware that automatic
failover mechanism of Microsoft Clustering Services (MSCS) or database mirroring (DBM) is able to provide
continuous availability of the system. However, a failover itself will cause rollback of open transactions on
the database side which again will cause rollbacks on business transactions on the SAP side. The impact of
these batch processes breaking and data not being available might be serious (for example, with a payroll
calculation). Therefore monitoring the system and notification after failovers can be vital to having
interrupted SAP Business processes restarted as quickly as possible.
The document SAP with Microsoft SQL Server 2005: Best Practices for High Availability, Performance, and Scalability
(5.5 MB) describes best practices for tuning and configuring SAP on SQL Server 2005 in more detail.
-