SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 

Glenn Berry's SQL Server Performance

Add to Technorati Favorites Add to Google
Author Bio
This blog is syndicated from Glenn Berry's SQL Server Performance.
More Posts Next page »
All Posts

SQL Server Utility in SQL Server 2008 R2

By Glenn Berry in Glenn Berry's SQL Server Performance 03-15-2010 1:50 AM | Categories: Filed under:
Rating: |  Discuss | 112 Reads | 112 Reads in Last 30 Days |no comments

One of the new management related features in SQL Server 2008 R2 is SQL Server Utility. SQL Server Utility gives you a way to manage and monitor multiple instances of SQL Server using a variation of Management Data Warehouse. You can use this to monitor things such as CPU utilization and disk space utilization. SQL Server Utility is an Enterprise Edition and above only feature, that requires SQL Server 2008 R2 for both the Utility Control Point (UCP) and all managed instances (at least in the November 2009 CTP of SQL Server 2008 R2). SQL Server 2008 R2 Enterprise Edition has a limit of 25 managed instances in each UCP.

If you want to use SQL Server Utility, the first thing you need to do is create a Utility Control Point. The UCP is where all of the monitoring data from your managed instances will be stored. You need to make sure that you have TCP/IP enabled on the instance where the UCP will live. You also need to make sure that SQL Agent is running and is set to start automatically.

Inside of SQL Server Management Studio (SSMS), you select Utility Explorer from the View menu. You will see the Utility Configuration Steps screen for a series of wizards that allow you to do most SQL Server Utility related tasks.

image

Choose “Create a Utility Control Point (UCP)”, and you will see the first screen in that wizard.

image

Click on Next, and you will see “Specify the Instance of SQL Server”. You need to specify the instance where the UCP will be located, and supply login credentials. You also need to give the UCP a relevant name.

image

Click Next, and you will see “Utility Collection Set Account”. You need to specify a Windows domain account that will be used as the SQL Server Agent proxy account for the utility collection set.

image

Click Next, and you will see  “SQL Server Instance Validation”, and the wizard will run a series of twelve validation tests to make sure the the instance will work as a UCP. The WMI validation test may take some time, so don’t be alarmed.

image

If every test passes (or you just have warnings and no errors), click Next, and you will see “Summary of UCP Creation”.

image

Click Next, and you will see “Utility Control Point Creation”, which will create the utility management data warehouse database, configure the UCP, and enroll the UCP instance as a managed instance.

image

Click Finish, and you will see a summary of the UCP instance’s health (with no data). Wait a few minutes, and you should start to see some more interesting data appear in the various reports.

image

If you look under databases in Object Explorer, you will see a new database called sysutility_mdw. If you go to SQL Agent, and look under Jobs, you will see seven new Agent jobs that are used to collect data from the managed instances into the sysutility_mdw.


SharePoint 2010 and Various Versions of SQL Server

By Glenn Berry in Glenn Berry's SQL Server Performance 03-11-2010 11:21 PM | Categories: Filed under:
Rating: |  Discuss | 168 Reads | 168 Reads in Last 30 Days |no comments

Microsoft has recently announced that SharePoint 2010 and Office 2010 will RTM in April 2010, and will be officially launched on May 12, 2010. From historical experience, this means that the RTM bits will be available on MSDN Subscribers probably one to two weeks after it is declared RTM, and that SharePoint 2010 and Office 2010 will be available for purchase on May 12.  That is not very far away…

SharePoint 2010 requires SQL Server. Here is some information regarding the relationship between SharePoint 2010 and SQL Server.

SharePoint Server 2010 Hardware and Software Requirements

SharePoint 2010 is 64-bit only. It requires Windows Server 2008 SP2 or Windows Server 2008 R2. It will not run on a Server Core installation.

It requires one of these versions of x64 SQL Server:

                SQL Server 2005 SP3 CU3 (Build 4220) or greater (Note: SQL Server 2005 goes out of mainstream support in April 2011)

                SQL Server 2008 SP1 CU2 (Build 2714) or greater

                SQL Server 2008 R2

Microsoft’s official position is still that SQL Server 2008 R2 will be available in the first half of 2010, even though the SharePoint team has announced a May 12 launch date.  If I were planning on a brand new SharePoint 2010 deployment, I would want to be running on Windows Server 2008 R2, using SQL Server 2008 R2. This will give you the best performance and the most functionality. For example, PowerPivot in SharePoint 2010 requires SQL Server 2008 R2.

I have put together some sample code that shows how to determine what edition and version of SQL Server is installed on an instance. This makes it easier to take advantage of version specific features (such as data compression) and edition specific features (such as online index creation) where appropriate.

-- Method 1: Retrieve complete version info, parse results in C#
SELECT @@VERSION AS [SQLVersionInfo];


-- Method 2: Detecting the edition and version of SQL Server from T-SQL
IF SERVERPROPERTY('EngineEdition') = 3
    BEGIN
        PRINT 'Enterprise Edition'
        IF CONVERT(INT, CONVERT(FLOAT, CONVERT(VARCHAR(3), SERVERPROPERTY('productversion')))) > 9
            BEGIN
                PRINT 'SQL Server 2008 or greater';
                -- Do something like use data compression on an index
                
            END
        ELSE
            BEGIN
                PRINT 'SQL Server 2005 or lower';
                -- Do something like create an index in online mode
                
            END
    END
ELSE
    BEGIN
        PRINT 'Standard Edition';
        -- Create an index in offline mode
    END

How to Easily Log SQL Server 2008 Instance Level Metrics

By Glenn Berry in Glenn Berry's SQL Server Performance 03-09-2010 10:20 PM | Categories:
Rating: |  Discuss | 195 Reads | 195 Reads in Last 30 Days |2 comment(s)

If you want to record the results of some useful, instance level DMV queries somewhere where you can easily query them later, you can use this technique.  This gives you a pretty decent overview of the health of your instance by capturing AvgTaskCount, AvgRunnableTaskCount, AvgPendingIOCount, SQlServerCPUUtilization, and PageLifeExpectancy on a periodic basis.

I like to create a utility database called ServerMonitor on each one of my SQL instances. Then, I can use that database to hold instance level DMV queries wrapped in stored procedures. This way, I can ensure that the database is always available regardless of the mirroring status of other user databases on the instance.

The script below creates a table called SQLServerInstanceMetricHistory in that database. Then, after checking for SQL Server 2008 or greater and for Enterprise Edition, it enables Page compression on the clustered index for that table. Next, it creates a stored procedure called DBAdminRecordSQLServerMetrics that inserts rows into the SQLServerInstanceMetricHistory table. The next step would be to create a SQL Server Agent job that runs once a minute. The SQL Agent job would simply call this command:

EXEC dbo.DBAdminRecordSQLServerMetrics;

You could then query the SQLServerInstanceMetricHistory table to get a better idea of the average workload over time for that instance of SQL Server.

-- Logging SQL Server instance metrics
-- Glenn Berry 
-- March 2010
-- http://glennberrysqlperformance.spaces.live.com/
-- Twitter: GlennAlanBerry

-- Drop table if it exists
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = 
            OBJECT_ID(N'[dbo].[SQLServerInstanceMetricHistory]') 
            AND type in (N'U'))
DROP TABLE [dbo].[SQLServerInstanceMetricHistory]
GO


SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- Create table to hold metrics
CREATE TABLE [dbo].[SQLServerInstanceMetricHistory]
(
    [SQLServerInstanceMetricHistoryID] [bigint] IDENTITY(1,1) NOT NULL,
    [MeasurementTime] [datetime] NOT NULL,
    [AvgTaskCount] [int] NOT NULL,
    [AvgRunnableTaskCount] [int] NOT NULL,
    [AvgPendingIOCount] [int] NOT NULL,
    [SQLServerCPUUtilization] [int] NOT NULL,
    [PageLifeExpectancy] [int] NOT NULL,
 CONSTRAINT [PK_SQLServerInstanceMetricHistory] PRIMARY KEY CLUSTERED 
(
    [SQLServerInstanceMetricHistoryID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
 ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 100) ON [PRIMARY]
) ON [PRIMARY]
GO


-- Check for SQL Server 2008 or 2008 R2 and Enterprise Edition
IF LEFT(CONVERT(CHAR(2),SERVERPROPERTY('ProductVersion')), 2) = '10' 
   AND SERVERPROPERTY('EngineEdition') = 3
    BEGIN
        -- Use Page Compression on the clustered index 
        -- if we have SQL Server 2008 Enterprise Edition
        ALTER TABLE [dbo].[SQLServerInstanceMetricHistory] REBUILD PARTITION = ALL
        WITH (DATA_COMPRESSION = PAGE);
    END
GO


-- Drop stored procedure if it exists
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = 
            OBJECT_ID(N'[dbo].[DBAdminRecordSQLServerMetrics]') 
            AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[DBAdminRecordSQLServerMetrics]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

/* DBAdminRecordSQLServerMetrics ===================================================
Description : Used to keep track of instance level SQL Server Metrics
                        
Author: Glenn Berry    
Date: 3/9/2010    
Input:                            
Output:    
Used By: Only used to maintain the database                

Last Modified          Developer        Description
-------------------------------------------------------------------------------------
3/9/2010            Glenn Berry        Added Modification Comment
===================================================================================*/
CREATE PROCEDURE [dbo].[DBAdminRecordSQLServerMetrics]
AS

    SET NOCOUNT ON;
    SET QUOTED_IDENTIFIER ON;
    SET ANSI_NULLS ON;

    
    DECLARE @PageLifeExpectancy int = 0;
    DECLARE @SQLProcessUtilization int = 0;
    

    -- Get PLE info
    SET @PageLifeExpectancy = (SELECT cntr_value AS [PageLifeExpectancy]
    FROM sys.dm_os_performance_counters
    WHERE object_name = 'SQLServer:Buffer Manager'
    AND counter_name = 'Page life expectancy'); 
    
    
    -- Get CPU Utilization History (SQL 2008 Only)
    SET @SQLProcessUtilization = (SELECT TOP(1) SQLProcessUtilization AS [SQLServerProcessCPUUtilization]              
    FROM ( 
          SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
                record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
                AS [SystemIdle], 
                record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 
                'int') 
                AS [SQLProcessUtilization], [timestamp] 
          FROM ( 
                SELECT [timestamp], CONVERT(xml, record) AS [record] 
                FROM sys.dm_os_ring_buffers 
                WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
                AND record LIKE '%<SystemHealth>%') AS x 
          ) AS y 
    ORDER BY record_id DESC);
    
    
    -- Add metrics info to SQLServerInstanceMetricHistory
    INSERT INTO dbo.SQLServerInstanceMetricHistory
    (MeasurementTime, AvgTaskCount, AvgRunnableTaskCount, AvgPendingIOCount, 
     SQLServerCPUUtilization, PageLifeExpectancy)
    (SELECT GETDATE() AS [MeasurementTime], AVG(current_tasks_count)AS [AvgTaskCount], 
            AVG(runnable_tasks_count) AS [AvgRunnableTaskCount], 
            AVG(pending_disk_io_count) AS [AvgPendingDiskIOCount], 
            @SQLProcessUtilization, @PageLifeExpectancy
     FROM sys.dm_os_schedulers
     WHERE scheduler_id < 255);
     
    RETURN;
    
GO







Easy Ways to Detect I/O Pressure in SQL Server 2008

By Glenn Berry in Glenn Berry's SQL Server Performance 03-09-2010 12:03 AM | Categories: Filed under:
Rating: |  Discuss | 1,057 Reads | 1057 Reads in Last 30 Days |8 comment(s)

It is pretty common for large, busy SQL Server instances to run into I/O bottlenecks. Even smaller, less busy systems often run into problems when they are not sized and configured correctly. As a database professional, you need to be able to detect when your SQL Server instances are experiencing poor performance due to I/O bottlenecks.

Quite often, you may need to convince someone from another part of the organization (such as a SAN engineer or server administrator) that your SQL Server instances are seeing decreased performance because of I/O issues.  It is also very possible that you have plenty of capacity, and that your I/O subsystem is configured correctly, but that you have some poorly written queries that are causing excessive disk I/O. Before you start talking to the rest of the organization, it is a good idea to know what the real issue is.

I have put together a small collection of queries (most of which are DMV queries) that should be very helpful in that effort. Even if you are not able to run PerfMon (because you don’t enough rights on the database server itself), you can still run these queries to get a pretty good idea what is going on from an I/O perspective.

This post is part of T-SQL Tuesday #004, which is hosted by Mike Walsh. You can see his invitation here.

-- Some I/O Specific DMV Queries
-- Glenn Berry
-- March 2010
-- http://glennberrysqlperformance.spaces.live.com/
-- Twitter: GlennAlanBerry

-- Always look at Avg Disk Sec/Read and Avg Disk Sec/Write 
-- in PerfMon for each Physical Disk 

-- Isolate top waits for server instance since last restart or statistics clear
-- Look for I/O specific waits at the top of the list
WITH Waits AS
(SELECT wait_type, wait_time_ms / 1000. AS wait_time_s,
100. * wait_time_ms / SUM(wait_time_ms) OVER() AS pct,
ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS rn
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN ('CLR_SEMAPHORE','LAZYWRITER_SLEEP','RESOURCE_QUEUE','SLEEP_TASK'
,'SLEEP_SYSTEMTASK','SQLTRACE_BUFFER_FLUSH','WAITFOR', 'LOGMGR_QUEUE','CHECKPOINT_QUEUE'
,'REQUEST_FOR_DEADLOCK_SEARCH','XE_TIMER_EVENT','BROKER_TO_FLUSH','BROKER_TASK_STOP'
,'CLR_MANUAL_EVENT','CLR_AUTO_EVENT','DISPATCHER_QUEUE_SEMAPHORE'
, 'FT_IFTS_SCHEDULER_IDLE_WAIT','XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN'))
SELECT W1.wait_type, 
CAST(W1.wait_time_s AS DECIMAL(12, 2)) AS wait_time_s,
CAST(W1.pct AS DECIMAL(12, 2)) AS pct,
CAST(SUM(W2.pct) AS DECIMAL(12, 2)) AS running_pct
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.rn <= W1.rn
GROUP BY W1.rn, W1.wait_type, W1.wait_time_s, W1.pct
HAVING SUM(W2.pct) - W1.pct < 95; -- percentage threshold

-- *** I/O Related Waits ***
-- ASYNC_IO_COMPLETION  Occurs when a task is waiting for I/Os to finish
-- IO_COMPLETION        Occurs while waiting for I/O operations to complete. 
--                      This wait type generally represents non-data page I/Os. 
--                      Data page I/O completion waits appear 
--                      as PAGEIOLATCH_* waits
-- PAGEIOLATCH_SH       Occurs when a task is waiting on a latch for a buffer that 
--                      is in an I/O request. The latch request is in Shared mode. 
--                      Long waits may indicate problems with the disk subsystem.
-- PAGEIOLATCH_EX       Occurs when a task is waiting on a latch for a buffer that 
--                      is in an I/O request. The latch request is in Exclusive mode. 
--                      Long waits may indicate problems with the disk subsystem.
-- WRITELOG             Occurs while waiting for a log flush to complete. 
--                      Common operations that cause log flushes 
--                      are checkpoints and transaction commits.
-- PAGELATCH_EX         Occurs when a task is waiting on a latch for a buffer that 
--                      is not in an I/O request. The latch request is in Exclusive mode.
-- BACKUPIO             Occurs when a backup task is waiting for data, or is waiting for a 
--                      buffer in which to store data


-- Check for IO Bottlenecks (run multiple times, look for values above zero)
SELECT cpu_id, pending_disk_io_count 
FROM sys.dm_os_schedulers
WHERE [status] = 'VISIBLE ONLINE'
ORDER BY cpu_id;

-- Look at average for all schedulers (run multiple times, look for values above zero)
SELECT AVG(pending_disk_io_count) AS [AvgPendingDiskIOCount]
FROM sys.dm_os_schedulers 
WHERE [status] = 'VISIBLE ONLINE';

-- High Latch waits (SH and EX) indicates the I/O subsystem is too busy 
SELECT wait_type, waiting_tasks_count, wait_time_ms, signal_wait_time_ms,
       wait_time_ms - signal_wait_time_ms AS [io_wait_time_ms]
FROM sys.dm_os_wait_stats
WHERE wait_type IN('PAGEIOLATCH_EX', 'PAGEIOLATCH_SH', 'PAGEIOLATCH_UP')
ORDER BY wait_type;


-- File Names and Paths for TempDB and all user databases in instance 
SELECT DB_NAME([database_id])AS [Database Name], [file_id], 
       name, physical_name, type_desc
FROM sys.master_files
WHERE [database_id] > 4 AND [database_id] <> 32767
OR [database_id] = 2;

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on C: drive?
-- Is TempDB on dedicated drives?
-- Are there multiple data files?


-- Which queries are causing the most IO operations (can take a few seconds)
SELECT TOP (20) total_logical_reads/execution_count AS [avg_logical_reads],
    total_logical_writes/execution_count AS [avg_logical_writes],
    total_worker_time/execution_count AS [avg_cpu_cost], execution_count,
    total_worker_time, total_logical_reads, total_logical_writes, 
    (SELECT DB_NAME(dbid) + ISNULL('..' + OBJECT_NAME(objectid), '') 
     FROM sys.dm_exec_sql_text([sql_handle])) AS query_database,
    (SELECT SUBSTRING(est.[text], statement_start_offset/2 + 1,
        (CASE WHEN statement_end_offset = -1
            THEN LEN(CONVERT(nvarchar(max), est.[text])) * 2
            ELSE statement_end_offset
            END - statement_start_offset
        ) / 2)
        FROM sys.dm_exec_sql_text(sql_handle) AS est) AS query_text,
    last_logical_reads, min_logical_reads, max_logical_reads,
    last_logical_writes, min_logical_writes, max_logical_writes,
    total_physical_reads, last_physical_reads, min_physical_reads, max_physical_reads,
    (total_logical_reads + (total_logical_writes * 5))/execution_count AS io_weighting,
    plan_generation_num, qp.query_plan
FROM sys.dm_exec_query_stats
OUTER APPLY sys.dm_exec_query_plan([plan_handle]) AS qp
WHERE [dbid] >= 5 AND (total_worker_time/execution_count) > 100
ORDER BY io_weighting DESC;


-- Calculates average stalls per read, per write, 
-- and per total input/output for each database file. 
SELECT DB_NAME(database_id) AS [Database Name], file_id ,io_stall_read_ms, num_of_reads,
CAST(io_stall_read_ms/(1.0 + num_of_reads) AS NUMERIC(10,1)) 
AS [avg_read_stall_ms],io_stall_write_ms, 
num_of_writes,CAST(io_stall_write_ms/(1.0+num_of_writes) AS NUMERIC(10,1)) 
AS [avg_write_stall_ms],io_stall_read_ms + io_stall_write_ms AS [io_stalls], 
num_of_reads + num_of_writes AS [total_io], CAST((io_stall_read_ms + 
io_stall_write_ms)/(1.0 + num_of_reads + num_of_writes) AS NUMERIC(10,1)) 
AS [avg_io_stall_ms]
FROM sys.dm_io_virtual_file_stats(null,null)
ORDER BY avg_io_stall_ms DESC;

-- Helps determine which database files on the entire instance have the most I/O bottlenecks


-- Analyze Database I/O, ranked by IO Stall%
WITH DBIO AS
(SELECT DB_NAME(IVFS.database_id) AS db,
 CASE WHEN MF.type = 1 THEN 'log' ELSE 'data' END AS file_type,
 SUM(IVFS.num_of_bytes_read + IVFS.num_of_bytes_written) AS io,
 SUM(IVFS.io_stall) AS io_stall
 FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS IVFS
 INNER JOIN sys.master_files AS MF
 ON IVFS.database_id = MF.database_id
 AND IVFS.file_id = MF.file_id
 GROUP BY DB_NAME(IVFS.database_id), MF.[type])
SELECT db, file_type, 
  CAST(1. * io / (1024 * 1024) AS DECIMAL(12, 2)) AS io_mb,
  CAST(io_stall / 1000. AS DECIMAL(12, 2)) AS io_stall_s,
  CAST(100. * io_stall / SUM(io_stall) OVER()
       AS DECIMAL(10, 2)) AS io_stall_pct,
  ROW_NUMBER() OVER(ORDER BY io_stall DESC) AS rn
FROM DBIO
ORDER BY io_stall DESC;


-- The queries below are database specific
USE yourdatabasename;
GO

-- Top Cached SPs By Total Physical Reads (SQL 2008). 
-- Physical reads relate to disk I/O pressure
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time 
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_physical_reads DESC;
       
-- Top Cached SPs By Total Logical Writes (SQL 2008). 
-- Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.cached_time
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_logical_writes DESC;


-- Lists the top statements by average input/output usage for the current database
SELECT TOP(50) OBJECT_NAME(qt.objectid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
        (CASE 
            WHEN qs.statement_end_offset = -1 
         THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
            ELSE qs.statement_end_offset 
         END - qs.statement_start_offset)/2) AS [Query Text]    
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC;

-- Helps you find the most expensive statements for I/O by SP



T-SQL Tuesday #004 Hosted by Mike Walsh

By Glenn Berry in Glenn Berry's SQL Server Performance 03-08-2010 9:04 PM | Categories:
Rating: (not yet rated) Rate this |  Discuss | 173 Reads | 173 Reads in Last 30 Days |no comments

I just wanted to remind people to participate in T-SQL Tuesday #004 on the subject of IO. You just need to post a blog post on something to do with IO between 00:00:00 UTC on Tuesday the 9th of March and 00:00:00 UTC on Wednesday the 10th. You also have to link back to Mike’s invitation post.

I’ll be trying my hand at a post for this one, which should be fun.


More SQL Azure Samples

By Glenn Berry in Glenn Berry's SQL Server Performance 03-05-2010 5:39 AM | Categories: Filed under:
Rating: |  Discuss | 246 Reads | 246 Reads in Last 30 Days |1 comment(s)

I have been playing around some more with SQL Azure in order to get ready for an upcoming presentation, so I thought I would share some of the T-SQL commands in the script below. I have a partial copy of the sample AdventureWorksLT2008R2 database installed on my SQL Azure logical “server” in the Southern US data center before this demo starts. You need at least the November CTP of SQL Server 2008 R2, in order to talk to SQL Azure with SSMS.

I have mainly just trying different commands to see what works and what does not work in SQL Azure, so I hope you find this interesting and useful.

-- Trying out SQL Azure
-- Glenn Berry
-- March 2010
-- http://glennberrysqlperformance.spaces.live.com/
-- Twitter: GlennAlanBerry


-- Get version information
SELECT @@VERSION AS [SQL Version Info];


-- Connect to a user database first
-- Remember, there is no USE databasename allowed
-- This does not work in SQL Azure
USE AdventureWorksLT2008R2;
GO


-- ***  Connect to AdventureWorksLT2008R2 database ***


-- Create a table and populate it

--Drop table if it exists
IF OBJECT_ID('dbo.UserAccount', 'U') IS NOT NULL
  DROP TABLE dbo.UserAccount;
GO

-- Create a table
-- Notice no file group or other options allowed in CREATE TABLE for SQL Azure
CREATE TABLE [dbo].[UserAccount](
    [UserID] [int] IDENTITY(1,1) NOT NULL,
    [UserName] [nvarchar](256) NOT NULL,
    [Password] [nvarchar](50) NOT NULL,
    [CreateDate] [datetime] NOT NULL,
    [ExpireDate] [datetime] NULL,
    [FName] [nvarchar](50) NOT NULL,
    [LName] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_UserAccount] PRIMARY KEY CLUSTERED 
([UserID] ASC));


-- Drop index if it exists
IF  EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[UserAccount]') 
            AND name = N'IX_UserAccount_UserName')
DROP INDEX [IX_UserAccount_UserName] ON [dbo].[UserAccount];
GO

-- Create NC Index
-- Notice no options allowed in CREATE INDEX for SQL Azure
CREATE NONCLUSTERED INDEX [IX_UserAccount_UserName] ON [dbo].[UserAccount] 
(
    [UserName] ASC
);

-- Insert a few rows
INSERT INTO dbo.UserAccount(UserName, [Password], CreateDate, [ExpireDate], FName, LName)
VALUES('GlennBerry', 'testpassword', GETDATE(), '12/31/2010', 'Glenn', 'Berry'),
      ('JessicaAlba', 'testpassword', GETDATE(), '12/31/2010', 'Jessica', 'Alba'),
      ('MirandaKerr', 'testpassword', GETDATE(), '12/31/2010', 'Miranda', 'Kerr');


-- See what we have in the table (notice GETDATE() actually returned UTC Date)
SELECT UserID, UserName, [Password], CreateDate, [ExpireDate], FName, LName
FROM dbo.UserAccount;

-- Get the UTC Date
SELECT GETUTCDATE() AS [UTC Date];


-- This works in SQL Azure
EXEC sp_HelpIndex [dbo.UserAccount];
EXEC sp_HelpIndex [SalesLT.Customer];
EXEC sp_HelpIndex [SalesLT.CustomerAddress];

-- This works in SQL Azure
EXEC sp_Help [SalesLT.Customer]


-- Stop the rowcount messsages
SET NOCOUNT ON;

-- Return IO statistics
SET STATISTICS IO ON;

-- Turn on graphical execution plan

-- Run a simple SELECT query
SELECT UserID, UserName, [Password], CreateDate, 
[ExpireDate], FName, LName
FROM dbo.UserAccount;

-- Run a simple SELECT query
SELECT CustomerID, NameStyle, Title, FirstName, MiddleName, 
LastName, Suffix, CompanyName, SalesPerson, EmailAddress, 
Phone, PasswordHash
FROM SALESLT.Customer;


-- Run a simple SELECT query with a WHERE clause
-- Notice SQL Server 2008 syntax works for DECLARE
DECLARE @CustomerID int = 1; 

SELECT CustomerID, NameStyle, Title, FirstName, MiddleName, 
LastName, Suffix, CompanyName, SalesPerson, EmailAddress, 
Phone, PasswordHash
FROM SALESLT.Customer
WHERE CustomerID = @CustomerID;


-- Create a stored procedure
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GetUserInfoByID]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[GetUserInfoByID]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[GetUserInfoByID]
(
    @UserID int
)
AS
    BEGIN
        
        SET NOCOUNT ON;

        SELECT UserID, UserName, [Password], CreateDate, [ExpireDate], FName, LName
        FROM dbo.UserAccount
        WHERE UserID = @UserID;
        
        RETURN;
        
    END
GO

-- Call the stored procedure
EXEC dbo.GetUserInfoByID 1;
EXEC dbo.GetUserInfoByID 2;





-- Must connect to master database first
-- Remember, there is no USE databasename allowed
-- This does not work in SQL Azure
USE [Master];
GO

-- *** Connect to master database ***


-- Get firewall rules
SELECT id, name, start_ip_address, end_ip_address, 
create_date, modify_date 
FROM sys.firewall_rules;


-- Switch to Business Edition ($99.99/month)
ALTER DATABASE AdventureWorksLT2008R2 
MODIFY (MAXSIZE = 10 GB);

-- Refresh SQL Azure Portal web page to see change

-- Switch to Web Edition ($9.99/month)
ALTER DATABASE AdventureWorksLT2008R2 
MODIFY (MAXSIZE = 1 GB);


-- Create an "Admin" login and user in the SQL Azure instance

-- List all logins on "instance" (must be connected to master)
SELECT *
FROM sys.sql_logins;

-- Cleanup if necessary
DROP LOGIN TestLogin;
GO
DROP USER TestLoginUser;
GO

-- Create a logon with a "strong" password that
-- can create databases and other logins
CREATE LOGIN TestLogin WITH password='1994Acura#';
GO
-- Create a database user that is linked to login
CREATE USER TestLoginUser FROM LOGIN TestLogin;
GO
EXEC sp_addrolemember 'loginmanager', 'TestLoginUser';
EXEC sp_addrolemember 'dbmanager', 'TestLoginUser';  -- like dbcreator


-- List all logins on "instance" 
SELECT *
FROM sys.sql_logins;

-- List all databases (must be connected to master)
SELECT name, database_id, create_date, [compatibility_level]
FROM sys.databases;

-- Drop database if necessary
DROP DATABASE TestDatabase;

-- Create new database on SQL Azure "instance"
CREATE DATABASE TestDatabase;


-- List all databases (must be connected to master)
SELECT name, database_id, create_date, [compatibility_level]
FROM sys.databases;

-- Connect to a user database first
-- Remember, there is no USE databasename allowed
-- This does not work in SQL Azure
USE TestDatabase;
GO

-- ***  Connect to TestDatabase ***

-- Create a table
CREATE TABLE [dbo].[TestTable](
    [TestID] [int] IDENTITY(1,1) NOT NULL,
    [TestName] [nvarchar](50) NOT NULL,    
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED 
([TestID] ASC));

-- Insert a few rows into table
INSERT INTO dbo.TestTable(TestName)
VALUES('Test1'),
      ('Test2'),
      ('Test3');
      
-- Get row counts
SELECT OBJECT_NAME(object_id) AS [ObjectName], object_id, index_id, row_count
FROM sys.dm_db_partition_stats;


-- This does not work in SQL Azure
DBCC FREEPROCCACHE;

-- This does not work in SQL Azure
DBCC FREEPROCINDB(5);

-- This does not work in SQL Azure
DBCC DROPCLEANBUFFERS;


-- ***  Connect to AdventureWorksLT2008R2 database ***

-- Run a stored procedure
EXEC dbo.GetCustomerInfoByFirstName N'James';
EXEC dbo.GetUserInfoByID 2;


-- Run some DMV queries against the database

-- Get row counts
SELECT OBJECT_NAME(object_id) AS [ObjectName], object_id, index_id, row_count
FROM sys.dm_db_partition_stats;


-- Monitor connections
SELECT s.session_id, s.login_name, e.connection_id,
      s.last_request_end_time, s.cpu_time
FROM sys.dm_exec_sessions AS s
INNER JOIN sys.dm_exec_connections AS e
ON s.session_id = e.session_id;

-- Find top Avg CPU time queries
SELECT TOP (5) MIN(query_stats.statement_text) AS [Statement Text], 
SUM(query_stats.total_worker_time) / SUM(query_stats.execution_count) AS [Avg CPU Time],
query_stats.query_hash AS [Query Hash]

FROM (SELECT QS.*, SUBSTRING(ST.[text], (QS.statement_start_offset/2) + 1,
    ((CASE statement_end_offset 
        WHEN -1 THEN DATALENGTH(st.[text])
        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 2 DESC;

-- Top Cached Plans By total worker time (CPU)
SELECT q.[text], hcpu.total_worker_time, 
       hcpu.execution_count, hcpu.plan_handle
FROM 
    (SELECT TOP (50) qs.* 
     FROM sys.dm_exec_query_stats AS qs 
     ORDER BY qs.total_worker_time DESC) AS hcpu 
     CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS q 
ORDER BY hcpu.total_worker_time DESC;


-- Top Cached Plans By total logical reads (Memory)
SELECT q.[text], hcpu.total_logical_reads, 
       hcpu.execution_count, hcpu.plan_handle
FROM 
    (SELECT TOP (50) qs.* 
     FROM sys.dm_exec_query_stats AS qs 
     ORDER BY qs.total_worker_time DESC) AS hcpu 
     CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS q 
ORDER BY hcpu.total_logical_reads DESC;


-- Top Cached Plans By total elapsed time
SELECT q.[text], hcpu.total_elapsed_time, 
       hcpu.execution_count, hcpu.plan_handle
FROM 
    (SELECT TOP (50) qs.* 
     FROM sys.dm_exec_query_stats AS qs 
     ORDER BY qs.total_worker_time DESC) AS hcpu 
     CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS q 
ORDER BY hcpu.total_elapsed_time DESC;



-- DMVs that are available in SQL Azure Service Update 1
SELECT * FROM sys.dm_exec_connections; 

SELECT * FROM sys.dm_exec_requests; 

SELECT * FROM sys.dm_exec_sessions; 

SELECT * FROM sys.dm_tran_database_transactions;  

SELECT * FROM sys.dm_tran_active_transactions; 


-- Drop the Stored procedure
DROP PROCEDURE dbo.GetUserInfoByID;

-- Drop the table
DROP TABLE dbo.UserAccount;

-- Drop the database
DROP DATABASE TestDatabase;

-- Drop the login
DROP LOGIN TestLogin;







Using the OUTPUT clause to retrieve new identity values from SQL Server 2008

By Glenn Berry in Glenn Berry's SQL Server Performance 03-03-2010 4:27 PM | Categories:
Rating: |  Discuss | 229 Reads | 229 Reads in Last 30 Days |no comments

Microsoft has a new KB article outlining issues with using SCOPE_IDENTITY and @@IDENTITY to retrieve values inserted into an identity column in a table. This particular issue shows up when you have parallel execution plans, but I have also seen DML triggers cause problems when you use @@IDENTITY.  The article lists several workarounds, including setting MAXDOP=1 at the query or instance level. In my opinion, the best option is to use the output clause of the INSERT table to retrieve the identity value.

Here is an example of how to use the OUTPUT clause:

-- Example of using OUTPUT clause in an INSERT statement
-- http://glennberrysqlperformance.spaces.live.com/
-- Twitter: GlennAlanBerry
USE AdventureWorks;
GO

-- Declare table variable to hold results
DECLARE @MyTableVar table( ScrapReasonID smallint,
                           Name varchar(50),
                           ModifiedDate datetime);

-- Insert into table with IDENTITY column
-- Use OUTPUT clause to retrieve new value                           
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO @MyTableVar
VALUES (N'Operator error', GETDATE());

--Display the result set of the table variable.
SELECT ScrapReasonID, Name, ModifiedDate 
FROM @MyTableVar;

Rocky Mountain Tech Trifecta Impressions

By Glenn Berry in Glenn Berry's SQL Server Performance 02-28-2010 10:09 PM | Categories:
Rating: |  Discuss | 249 Reads | 249 Reads in Last 30 Days |1 comment(s)

I attended and presented at the Rocky Mountain Tech Trifecta yesterday, where I had a very nice time. Julie Yack did a great job of organizing the free event, which had nearly 700 registrations. There were a lot of sponsors, so there was plenty of food (including breakfast, lunch and snacks) and lots of loot to give away during the sessions. After lunch, there were two stacks of empty pizza boxes that were each over six feet high. That was a lot of pizza!  It is very nice to see so many people working together to put on high quality, free events like this.

It was great to see Steve Jones, Eric Johnson, Paul Nielsen, and Louis Davidson (who had flown in from Tennessee for the event). Kevin Cox from SQLCAT was also there, where he stepped in at the last minute to do a presentation. Louis Davidson used an add-in for PowerPoint that let it send tweets to the Twitter API while he was giving his SQL Server keynote presentation.

My session was not until 4PM, but despite that, it was still well attended. I was concerned that a lot of people might have decided to leave earlier in the afternoon since it was a nice day in Denver. The North Classroom building on the Auraria campus in Denver has a very strong, free Wireless-N connection, which was very welcome. I actually used it to show SQL Azure for a couple of minutes before I started my presentation. After the event was over, I went to Cilantro Fusion for a nice meal and conversation with Carlos Bossy, Jason Horner , Marc Beacom, and Paul Paiva. Cilantro Fusion is owned by Carlos’ wife, Gabriela Bossy, and the food is excellent.


Weekly Electrical Usage from Google PowerMeter

By Glenn Berry in Glenn Berry's SQL Server Performance 02-28-2010 7:08 PM | Categories:
Rating: |  Discuss | 270 Reads | 270 Reads in Last 30 Days |2 comment(s)

Since I like to measure and monitor things (maybe that is why I am a DBA), and I care about the environment, I installed a TED 5000 and connected it to Google PowerMeter about six weeks ago. Every week, Google sends out an email that summarizes your power usage by day for the previous week, and by week for the previous six weeks. My latest report is shown below:

My goal for February was to come in under 400kWh for the month, but it looks like I am going to end up at around 430kWh for the month, which is not too bad for late winter. As we move into spring and average outside temperatures increase, I have a better chance of breaking the 400kWh barrier.

The sawtooth pattern in blue in the picture below shows the effect of the gas furnace blower fan coming on periodically. I think it also is an indication that I need to improve the insulation in my attic. It also may be a sign that the furnace is too large for the house. It is an Amana 95% efficient gas furnace that has worked well, but I suspect that the HVAC subcontractor that sized and installed it didn’t know what they were doing.

image

I am very curious what other people’s typical monthly electrical usage is in kWh, so feel free to post a comment!


Dr. DMV Script for Rocky Mountain Tech Trifecta

By Glenn Berry in Glenn Berry's SQL Server Performance 02-26-2010 10:25 PM | Categories: Filed under: ,
Rating: |  Discuss | 310 Reads | 310 Reads in Last 30 Days |1 comment(s)

Here is the script that has the diagnostic queries that I will be using during my presentation at the Rocky Mountain Tech Trifecta on February 27.

-- SQL Server 2008 and R2 Diagnostic Information Queries
-- Glenn Berry 
-- February 26, 2010
-- http://glennberrysqlperformance.spaces.live.com/
-- Twitter: GlennAlanBerry

-- Instance level queries *******************************

-- SQL and OS Version information for current instance
SELECT @@VERSION AS [SQL Version Info];

--   2008 RTM Builds                    2008 SP1 Builds
-- Build       Description        Build        Description
-- 1600        Gold RTM
-- 1763        RTM CU1
-- 1779        RTM CU2
-- 1787        RTM CU3    -->      2531        SP1 RTM
-- 1798        RTM CU4    -->      2710        SP1 CU1
-- 1806        RTM CU5    -->      2714        SP1 CU2 
-- 1812        RTM CU6    -->      2723        SP1 CU3
-- 1818        RTM CU7    -->      2734        SP1 CU4
-- 1823        RTM CU8    -->      2746        SP1 CU5
-- 1828        RTM CU9    -->      2757        SP1 CU6

-- SQL Azure Builds (most DMV queries don't work on SQL Azure)
-- Build            Description
-- 10.25.9200        RTM Service Update 1

-- 2008 R2 Builds
-- Build            Description
-- 10.50.1092        August CTP2
-- 10.50.1352        November CTP3



-- Hardware information from SQL Server 2008 
-- (Cannot distinguish between HT and multi-core)
SELECT cpu_count AS [Logical CPU Count], hyperthread_ratio AS [Hyperthread Ratio],
cpu_count/hyperthread_ratio AS [Physical CPU Count], 
physical_memory_in_bytes/1048576 AS [Physical Memory (MB)], sqlserver_start_time
FROM sys.dm_os_sys_info;


-- Get sp_configure values for instance
EXEC sp_configure 'Show Advanced Options', 1;
GO
RECONFIGURE;
GO
EXEC sp_configure;

-- Focus on
-- backup compression default
-- clr enabled (only enable if it is needed)
-- lightweight pooling (should be zero)
-- max degree of parallelism (consider setting to 1 for OLTP workloads)
-- max server memory (MB)
-- optimize for ad hoc workloads (should be 1)
-- priority boost (should be zero)


-- File Names and Paths for TempDB and all user databases in instance 
SELECT DB_NAME([database_id])AS [Database Name], [file_id], 
       name, physical_name, type_desc
FROM sys.master_files
WHERE [database_id] > 4 AND [database_id] <> 32767
OR [database_id] = 2;

-- Things to look at:
-- Are data files and log files on different drives?
-- Is everything on the C: drive?
-- Is TempDB on dedicated drives?
-- Are there multiple data files?


-- Calculates average stalls per read, per write, and per total input/output for each database file. 
SELECT DB_NAME(database_id) AS [Database Name], file_id ,io_stall_read_ms, num_of_reads,
CAST(io_stall_read_ms/(1.0 + num_of_reads) AS NUMERIC(10,1)) AS [avg_read_stall_ms],io_stall_write_ms, 
num_of_writes,CAST(io_stall_write_ms/(1.0+num_of_writes) AS NUMERIC(10,1)) AS [avg_write_stall_ms],
io_stall_read_ms + io_stall_write_ms AS [io_stalls], num_of_reads + num_of_writes AS [total_io],
CAST((io_stall_read_ms + io_stall_write_ms)/(1.0 + num_of_reads + num_of_writes) AS NUMERIC(10,1)) 
AS [avg_io_stall_ms]
FROM sys.dm_io_virtual_file_stats(null,null)
ORDER BY avg_io_stall_ms DESC;

-- Helps determine which database files on the entire instance have the most I/O bottlenecks


-- Recovery model, log reuse wait description, log file size, log usage size 
-- and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name], db.recovery_model_desc AS [Recovery Model], db.log_reuse_wait_desc, 
ls.cntr_value AS [Log Size (KB)], lu.cntr_value AS [Log Used (KB)],
CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT)AS DECIMAL(18,2)) * 100 AS [Log Used %], 
db.[compatibility_level] AS [DB Compatibility Level], db.page_verify_option_desc AS [Page Verify Option]
FROM sys.databases AS db
INNER JOIN sys.dm_os_performance_counters AS lu 
ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls 
ON db.name = ls.instance_name
WHERE lu.counter_name LIKE  'Log File(s) Used Size (KB)%' 
AND ls.counter_name LIKE 'Log File(s) Size (KB)%';

-- Things to look at
-- How many databases are on the instance?
-- What recovery models are they using?
-- What is the log reuse wait description?
-- How full are the transaction logs ?
-- What compatibility level are they on?


-- Clear Wait Stats 
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Isolate top waits for server instance since last restart or statistics clear
WITH Waits AS
(SELECT wait_type, wait_time_ms / 1000. AS wait_time_s,
100. * wait_time_ms / SUM(wait_time_ms) OVER() AS pct,
ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS rn
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN ('CLR_SEMAPHORE','LAZYWRITER_SLEEP','RESOURCE_QUEUE','SLEEP_TASK'
,'SLEEP_SYSTEMTASK','SQLTRACE_BUFFER_FLUSH','WAITFOR', 'LOGMGR_QUEUE','CHECKPOINT_QUEUE'
,'REQUEST_FOR_DEADLOCK_SEARCH','XE_TIMER_EVENT','BROKER_TO_FLUSH','BROKER_TASK_STOP','CLR_MANUAL_EVENT'
,'CLR_AUTO_EVENT','DISPATCHER_QUEUE_SEMAPHORE', 'FT_IFTS_SCHEDULER_IDLE_WAIT'
,'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN'))
SELECT W1.wait_type, 
CAST(W1.wait_time_s AS DECIMAL(12, 2)) AS wait_time_s,
CAST(W1.pct AS DECIMAL(12, 2)) AS pct,
CAST(SUM(W2.pct) AS DECIMAL(12, 2)) AS running_pct
FROM Waits AS W1
INNER JOIN Waits AS W2
ON W2.rn <= W1.rn
GROUP BY W1.rn, W1.wait_type, W1.wait_time_s, W1.pct
HAVING SUM(W2.pct) - W1.pct < 95; -- percentage threshold

-- Common Significant Wait types with BOL explanations

-- *** Network Related Waits ***
-- ASYNC_NETWORK_IO        Occurs on network writes when the task is blocked behind the network

-- *** Locking Waits ***
-- LCK_M_IX                Occurs when a task is waiting to acquire an Intent Exclusive (IX) lock
-- LCK_M_IU                Occurs when a task is waiting to acquire an Intent Update (IU) lock
-- LCK_M_S                Occurs when a task is waiting to acquire a Shared lock

-- *** I/O Related Waits ***
-- ASYNC_IO_COMPLETION  Occurs when a task is waiting for I/Os to finish
-- IO_COMPLETION        Occurs while waiting for I/O operations to complete. 
--                      This wait type generally represents non-data page I/Os. Data page I/O completion waits appear 
--                      as PAGEIOLATCH_* waits
-- PAGEIOLATCH_SH        Occurs when a task is waiting on a latch for a buffer that is in an I/O request. 
--                      The latch request is in Shared mode. Long waits may indicate problems with the disk subsystem.
-- PAGEIOLATCH_EX        Occurs when a task is waiting on a latch for a buffer that is in an I/O request. 
--                      The latch request is in Exclusive mode. Long waits may indicate problems with the disk subsystem.
-- WRITELOG             Occurs while waiting for a log flush to complete. 
--                      Common operations that cause log flushes are checkpoints and transaction commits.
-- PAGELATCH_EX            Occurs when a task is waiting on a latch for a buffer that is not in an I/O request. 
--                      The latch request is in Exclusive mode.
-- BACKUPIO                Occurs when a backup task is waiting for data, or is waiting for a buffer in which to store data

-- *** CPU Related Waits ***
-- SOS_SCHEDULER_YIELD  Occurs when a task voluntarily yields the scheduler for other tasks to execute. 
--                      During this wait the task is waiting for its quantum to be renewed.

-- THREADPOOL            Occurs when a task is waiting for a worker to run on. 
--                      This can indicate that the maximum worker setting is too low, or that batch executions are taking 
--                      unusually long, thus reducing the number of workers available to satisfy other batches.
-- CX_PACKET            Occurs when trying to synchronize the query processor exchange iterator 
--                        You may consider lowering the degree of parallelism if contention on this wait type becomes a problem


-- Signal Waits for instance
SELECT CAST(100.0 * SUM(signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) 
AS [%signal (cpu) waits],
CAST(100.0 * SUM(wait_time_ms - signal_wait_time_ms) / SUM (wait_time_ms) AS NUMERIC(20,2)) 
AS [%resource waits]
FROM sys.dm_os_wait_stats;

-- Signal Waits above 10-15% is usually a sign of CPU pressure


-- Get CPU Utilization History for last 30 minutes (in one minute intervals)
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks)FROM sys.dm_os_sys_info); 

SELECT TOP(30) SQLProcessUtilization AS [SQL Server Process CPU Utilization], 
               SystemIdle AS [System Idle Process], 
               100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization], 
               DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time] 
FROM ( 
      SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, 
            record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') 
            AS [SystemIdle], 
            record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 
            'int') 
            AS [SQLProcessUtilization], [timestamp] 
      FROM ( 
            SELECT [timestamp], CONVERT(xml, record) AS [record] 
            FROM sys.dm_os_ring_buffers 
            WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' 
            AND record LIKE '%<SystemHealth>%') AS x 
      ) AS y 
ORDER BY record_id DESC;


-- Good basic information about memory amounts and state
SELECT total_physical_memory_kb, available_physical_memory_kb, 
       total_page_file_kb, available_page_file_kb, 
       system_memory_state_desc
FROM sys.dm_os_sys_memory;


-- SQL Server Process Address space info 
--(shows whether locked pages is enabled, among other things)
SELECT physical_memory_in_use_kb,large_page_allocations_kb, locked_page_allocations_kb,
       total_virtual_address_space_kb, virtual_address_space_reserved_kb, 
       virtual_address_space_committed_kb, virtual_address_space_available_kb,
       page_fault_count, memory_utilization_percentage, available_commit_limit_kb, 
       process_physical_memory_low, process_virtual_memory_low
FROM sys.dm_os_process_memory;


-- Page Life Expectancy (PLE) value for default instance
SELECT cntr_value AS [Page Life Expectancy]
FROM sys.dm_os_performance_counters
WHERE OBJECT_NAME = 'SQLServer:Buffer Manager' -- Modify this if you have named instances
AND counter_name = 'Page life expectancy';

-- PLE is a good measurement of memory pressure.
-- Higher PLE is better. Below 300 is generally bad.
-- Watch the trend, not the absolute value.


-- Get Buffer cache hit ratio (higher is better)
SELECT ROUND(CAST(A.cntr_value1 AS NUMERIC) / 
CAST(B.cntr_value2 AS NUMERIC),3) AS [Buffer Cache Hit Ratio]
FROM ( SELECT cntr_value AS [cntr_value1]
FROM sys.dm_os_performance_counters
WHERE object_name = 'SQLServer:Buffer Manager' -- Modify this if you have named instances
AND counter_name = 'Buffer cache hit ratio'
) AS A,
(SELECT cntr_value AS [cntr_value2]
FROM sys.dm_os_performance_counters
WHERE object_name = 'SQLServer:Buffer Manager' -- Modify this if you have named instances
AND counter_name = 'Buffer cache hit ratio base'
) AS B;

-- Buffer cache hit ratio is another measure of memory pressure.
-- A higher value is better. Below 95% is generally bad.
-- Watch the trend, not the absolute value.


-- Buffer Pool Usage for instance
SELECT TOP(20) [type], SUM(single_pages_kb) AS [SPA Mem, Kb] 
FROM sys.dm_os_memory_clerks 
GROUP BY [type]  
ORDER BY SUM(single_pages_kb) DESC;

-- CACHESTORE_SQLCP  SQL Plans         - These are cached SQL statements or batches that aren't in 
--                                     stored procedures, functions and triggers
-- CACHESTORE_OBJCP  Object Plans      - These are compiled plans for stored procedures, 
--                                     functions and triggers
-- CACHESTORE_PHDR   Algebrizer Trees  - An algebrizer tree is the parsed SQL text that 
--                                     resolves the table and column names


-- Find single-use, ad-hoc queries that are bloating the plan cache
SELECT TOP(100) [text], cp.size_in_bytes
FROM sys.dm_Exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
WHERE cacheobjtype = 'Compiled Plan' 
AND cp.objtype = 'Adhoc' 
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC;

-- Gives you the text and size of single-use ad-hoc queries that waste space in plan cache
-- Enabling 'optimize for ad hoc workloads' for the instance can help (SQL Server 2008 only)
-- Enabling forced parameterization for the database can help


-- Database specific queries *****************************************************************

-- Switch to a user database
--USE YourDatabaseName;
--GO

-- Individual File Sizes and space available for current database
SELECT name AS [File Name] , physical_name AS [Physical Name], size/128 AS [Total Size in MB],
size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS [Available Space In MB]
FROM sys.database_files;

-- Look at how large and how full the files are and where they are located
-- Make sure the transaction log is not full!!


-- Top Cached SPs By Execution Count (SQL 2008)
SELECT TOP(25) p.name AS [SP Name], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.total_worker_time AS [TotalWorkerTime],  
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time],
qs.cached_time
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.execution_count DESC;

-- Top Cached SPs By Total Worker time (SQL 2008). Worker time relates to CPU cost
SELECT TOP(25) p.name AS [SP Name], qs.total_worker_time AS [TotalWorkerTime], 
qs.total_worker_time/qs.execution_count AS [AvgWorkerTime], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_worker_time DESC;

-- Top Cached SPs By Total Logical Reads (SQL 2008). Logical reads relate to memory pressure
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_reads AS [TotalLogicalReads], 
qs.total_logical_reads/qs.execution_count AS [AvgLogicalReads],qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second], 
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_logical_reads DESC;

-- Top Cached SPs By Total Physical Reads (SQL 2008). Physical reads relate to disk I/O pressure
SELECT TOP(25) p.name AS [SP Name],qs.total_physical_reads AS [TotalPhysicalReads], 
qs.total_physical_reads/qs.execution_count AS [AvgPhysicalReads], qs.execution_count, 
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count 
AS [avg_elapsed_time], qs.cached_time 
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_physical_reads DESC;
       
-- Top Cached SPs By Total Logical Writes (SQL 2008). Logical writes relate to both memory and disk I/O pressure 
SELECT TOP(25) p.name AS [SP Name], qs.total_logical_writes AS [TotalLogicalWrites], 
qs.total_logical_writes/qs.execution_count AS [AvgLogicalWrites], qs.execution_count,
ISNULL(qs.execution_count/DATEDIFF(Second, qs.cached_time, GETDATE()), 0) AS [Calls/Second],
qs.total_elapsed_time, qs.total_elapsed_time/qs.execution_count AS [avg_elapsed_time], 
qs.cached_time
FROM sys.procedures AS p
INNER JOIN sys.dm_exec_procedure_stats AS qs
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
ORDER BY qs.total_logical_writes DESC;


-- Lists the top statements by average input/output usage for the current database
SELECT TOP(50) OBJECT_NAME(qt.objectid) AS [SP Name],
(qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO],
SUBSTRING(qt.[text],qs.statement_start_offset/2, 
    (CASE 
        WHEN qs.statement_end_offset = -1 
     THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
        ELSE qs.statement_end_offset 
     END - qs.statement_start_offset)/2) AS [Query Text]    
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qt.[dbid] = DB_ID()
ORDER BY [Avg IO] DESC;

-- Helps you find the most expensive statements for I/O by SP


-- Possible Bad Indexes (writes > reads)
SELECT OBJECT_NAME(s.[object_id]) AS [Table Name], i.name AS [Index Name], i.index_id,
user_updates AS [Total Writes], user_seeks + user_scans + user_lookups AS [Total Reads],
user_updates - (user_seeks + user_scans + user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
INNER JOIN sys.indexes AS i WITH (NOLOCK)
ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
WHERE OBJECTPROPERTY(s.[object_id],'IsUserTable') = 1
AND s.database_id = DB_ID()
AND user_updates > (user_seeks + user_scans + user_lookups)
AND i.index_id > 1
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC;


-- Missing Indexes for entire instance by Index Advantage
SELECT user_seeks * avg_total_user_cost * (avg_user_impact * 0.01) AS [index_advantage], 
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table],
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact
FROM sys.dm_db_missing_index_group_stats AS migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH (NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH (NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC;

-- Look at last user seek time, number of user seeks to help determine source and importance
-- SQL Server is overly eager to add included columns, so beware


-- Breaks down buffers used by current database by object (table, index) in the buffer cache
SELECT OBJECT_NAME(p.[object_id]) AS [ObjectName], p.[object_id], 
p.index_id, COUNT(*)/128 AS [buffer size(MB)],  COUNT(*) AS [buffer_count] 
FROM sys.allocation_units AS a
INNER JOIN sys.dm_os_buffer_descriptors AS b
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p
ON a.container_id = p.hobt_id
WHERE b.database_id = DB_ID()
AND p.[object_id] > 100
GROUP BY p.[object_id], p.index_id
ORDER BY buffer_count DESC;

-- Tells you what tables and indexes are using the most memory in the buffer cache



-- Detect blocking (run multiple times)
SELECT t1.resource_type AS [lock type],DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req], --- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time], -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r                              -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],    -- statement blocked
t2.blocking_session_id AS [blocker sid],                         -- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p                        -- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_stmt]
FROM sys.dm_tran_locks AS t1 
INNER JOIN sys.dm_os_waiting_tasks AS t2
ON t1.lock_owner_address = t2.resource_address;



Upcoming Speaking Event Schedule

By Glenn Berry in Glenn Berry's SQL Server Performance 02-24-2010 5:14 PM | Categories: Filed under:
Rating: |  Discuss | 297 Reads | 297 Reads in Last 30 Days |no comments

I have a fairly busy schedule of upcoming speaking events (especially for someone with a full-time day job).

I will be giving my Dr. DMV: How to Use Dynamic Management Views to Monitor and Diagnose Performance Issues With High Volume OLTP Workloads presentation at the Rocky Mountain Tech Trifecta event on Saturday, February 27, 2010 at Metro State College in downtown Denver. This is a free all-day event, similar to a Code Camp or SQL Saturday. Other SQL Server speakers who will be there include Steve Jones, Paul Nielsen, Louis Davidson, Eric Johnson,  and Jeremy Lowell. There will also be a lot of good .NET and SharePoint content. You can register for the event here.

I am going to be filming three sessions for the SSWUG.ORG Spring 2010 Ultimate Virtual Conference on March 5 in Tucson. I have never done a filmed session in a studio with no live audience, so that should be fun. The Virtual Conference actually runs April 7-9, 2010, and all three of my sessions are on April 9, and I will be available during the live chat sessions during the event. They have rounded up a very good slate of speakers for this event. These include Denny Cherry, Donald Farmer, Kathi Kellenberger, Kevin Kline, Brian Knight, Devin Knight, Jason Strate, and Craig Utley.

Finally, I will presenting at the Colorado Springs SQL Server User’s Group (CSSQLUG) on Wednesday, May 19, 2010. I will be doing my Dr. DMV: How to Use Dynamic Management Views to Monitor and Diagnose Performance Issues With High Volume OLTP Workloads presentation, using SQL Server 2008 R2, which should be fun.


Power Plans and Windows Server 2008 R2

By Glenn Berry in Glenn Berry's SQL Server Performance 02-22-2010 11:37 PM | Categories: Filed under:
Rating: |  Discuss | 371 Reads | 371 Reads in Last 30 Days |no comments

After seeing an interesting blog post on AnandTech IT last week that showed slower query response time in x64 SQL Server 2008 running on x64 Windows Server 2008 when using the default “Balanced” power plan compared to the “High performance” power plan (in Windows), I decided to do some investigation of my own.

I found this KB article from Microsoft: SQL Server timing values may be incorrect when you use utilities or technologies that change CPU frequencies, which seemed somewhat relevant even though it refers to SQL Server 2005 SP2.

Then I decided to run the GeekBench 2.1.4 benchmark on an ASUS P7P55D motherboard, with a 2.8GHz Intel Core i7 860 CPU and 8GB of DDR3 RAM to see if I would see any performance difference on this processor and memory intensive benchmark. It turns out that I saw a very significant difference with different BIOS settings and Windows Power Plans.

With Intel Enhanced Speed Step and Intel TurboBoost enabled in the BIOS, I got the following GeekBench scores:

“Balanced” Power Plan                   6450

“High performance” Power Plan     7860

The High performance power plan showed a 22% increase over the default Balanced power plan (in an average of five runs).

Next, I rebooted the machine and went into the BIOS setup, and disabled Intel Enhanced Speed Step (which also disables Intel TurboBoost on this motherboard. Running GeekBench five times gave me these average scores:

“Balanced” Power Plan                   7323

“High performance” Power Plan     7330

This shows the effect of TurboBoost also being disabled (which happens on this motherboard when you disable Enhanced Speed Step) on the Intel Core i7 860 CPU (which is the equivalent of the Xeon X3460). The  benchmark scores for the two Windows power plans were the same (within the margin of error) because Enhanced Speed Step was disabled in the BIOS, so Windows 2008 R2 was not able to throttle back the CPU frequency when the processor was lightly loaded.

You can get to the Power Options screen by typing powercfg.cpl in a Run prompt.

image

Of course, this is just a synthetic benchmark, run on one machine so far, but it does give me some food for thought (and further testing with additional machines and with SQL Server specific benchmarks).  As always, YMMV, and you should do your own testing with your workload in your own environment before you change this setting.


Experiments with Data Compression in SQL Server 2008

By Glenn Berry in Glenn Berry's SQL Server Performance 02-22-2010 6:28 AM | Categories: Filed under:
Rating: |  Discuss | 1,371 Reads | 1371 Reads in Last 30 Days |4 comment(s)

Data compression is an Enterprise Edition only feature that was added in SQL Server 2008. It allows you to use either Page or Row compression on the clustered index or any non-clustered indexes on a table. Data compression lets SQL Server trade off disk space and I/O pressure for some extra CPU pressure. It is best suited to relatively large tables that that are relatively static. Small tables that are highly volatile are not usually good candidates for compression (especially page compression).

I thought I would do a simple experiment to measure the effects of page compression on a table with 2.3 million rows of data. I created four identical tables with the schema shown below. The EventLog table has a non-compressed clustered index, the EventLogCompressed table has a compressed clustered index, the EventLogNew table has a non-compressed clustered index and a non-compressed non-clustered index, and the EventLogNewCompressed table has a compressed clustered index and a compressed non-clustered index. Each table has the exact same data. I wrote four identical stored procedures that hit each of the four tables.

This stored procedure causes a clustered index scan on the tables that don’t have a non-clustered index (EventLog and EventLogCompressed), and an index seek on the tables that have the non-clustered index (EventLogNew and EventLogNewCompressed). The tables with Compressed in their name have page compression on each index in the table. For this data, page compression compressed the clustered index by about 4.5 to 1.

Page compression reduced the logical reads from 23941 to 5545 for the clustered index scan and the query cost by 66%. It also reduced the memory consumed by the clustered index scan from 186MB to 43MB. In my mind, this means that having a compressed clustered index gives you some extra protection from the effects of an expensive clustered index scan. More details about these results are shown below:

-- Table with 2.3 million rows
CREATE TABLE [dbo].[EventLog](
    [EventID] [bigint] IDENTITY(1,1) NOT NULL,
    [UserID] [int] NOT NULL,
    [GroupID] [int] NULL,
    [PostID] [bigint] NULL,
    [FeedID] [int] NULL,
    [EventChange] [int] NOT NULL,
    [EventObject] [int] NOT NULL,
    [EventType] [varchar](50) NOT NULL,
    [EventSource] [int] NOT NULL,
    [DataField] [nvarchar](max) NULL,
    [ExtensionData] [xml] NULL,
    [CreateDate] [datetime] NOT NULL,
    [ParentEventID] [bigint] NULL,
    [Deleted] [bit] NOT NULL,
 CONSTRAINT [PK_EventLog] PRIMARY KEY CLUSTERED 
(
    [EventID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
 ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]


-- Non-clustered "covering index"
CREATE NONCLUSTERED INDEX [IX_EventLogNew_Cover1] ON [dbo].[EventLogNew] 
(
    [UserID] ASC,
    [Deleted] ASC,
    [CreateDate] ASC
)
INCLUDE ( [EventID],
[GroupID],
[PostID],
[FeedID],
[EventChange],
[EventObject],
[EventType],
[EventSource]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO


-- Return IO Statistics
SET STATISTICS IO ON;

-- Clear out the procedure cache for instance    
DBCC FREEPROCCACHE;
    
-- Don't do this on a production server!
DBCC DROPCLEANBUFFERS;

-- Run four identical stored procedures
-- against identical tables    (except for index changes)
PRINT 'Query 1'; -- Clustered Index Scan
EXEC dbo.GetEventLogByUserID 137926;
PRINT 'Query 2'; -- Clustered Index Scan (compressed index)
EXEC dbo.GetEventLogCompressedByUserID 137926;
PRINT 'Query 3'; -- Index seek
EXEC dbo.GetEventLogNewByUserID 137926;
PRINT 'Query 4'; -- Index seek (compressed index)
EXEC dbo.GetEventLogNewCompressedByUserID 137926;

-- Statistics IO output
--Query 1 Clustered index scan against EventLog table
--Table 'EventLog'. Scan count 3, logical reads 23941, physical reads 176, 
--read-ahead reads 23858, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

--Query 2 Clustered index scan against EventLogCompressed table
--Table 'EventLogCompressed'. Scan count 3, logical reads 5545, physical reads 45, 
--read-ahead reads 5509, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

--Query 3 Index seek against EventLogNew
--Table 'EventLogNew'. Scan count 1, logical reads 3, physical reads 3, 
--read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

--Query 4 Index seek against EventLogNewCompressed
--Table 'EventLogNewCompressed'. Scan count 1, logical reads 3, physical reads 2, 
--read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.


-- Breaks down buffers used by current database 
-- by object (table, index) in the buffer cache
SELECT OBJECT_NAME(p.[object_id]) AS [ObjectName], 
p.index_id, COUNT(*)/128 AS [buffer size(MB)],  
COUNT(*) AS [buffer_count] 
FROM sys.allocation_units AS a
INNER JOIN sys.dm_os_buffer_descriptors AS b
ON a.allocation_unit_id = b.allocation_unit_id
INNER JOIN sys.partitions AS p
ON a.container_id = p.hobt_id
WHERE b.database_id = DB_ID()
AND p.[object_id] > 100
GROUP BY p.[object_id], p.index_id
ORDER BY buffer_count DESC;

-- Buffer space results
--ObjectName                index_id    buffer size(MB)    buffer_count
--EventLog                    1            186                23855
--EventLogCompressed          1             43                 5508
--EventLogNew                 5              0                   24
--EventLogNewCompressed       2              0                   16

New iSCSI Performance Benchmark from Microsoft and Intel

By Glenn Berry in Glenn Berry's SQL Server Performance 02-21-2010 5:33 PM | Categories:
Rating: (not yet rated) Rate this |  Discuss | 337 Reads | 337 Reads in Last 30 Days |no comments

The Windows Server Division blog has a post up trumpeting a very impressive iSCSI benchmarking result achieved with Intel hardware and Microsoft Windows Server 2008 R2 software in January of 2010. Running on server hardware equipped with an Intel Xeon 5580 CPU and an Intel 82599 10GbE network interface card (NIC), Windows Server 2008 R2 achieved 715,000 IOPs. The Windows Virtualization Team blog has some more details here. This is even more interesting because it was done on Hyper-V.

Both Intel and Microsoft recommend enabling jumbo frames for iSCSI workloads. It is important to make sure the entire network path has it enabled.


Testing New Features in SQL Azure Service Update 1

By Glenn Berry in Glenn Berry's SQL Server Performance 02-19-2010 6:58 AM | Categories: Filed under:
Rating: |  Discuss | 363 Reads | 363 Reads in Last 30 Days |no comments

It seems to be SQL Azure night for me tonight…  I thought I would try out some of the new features in SQL Azure Service Update 1. First I tried ALTER DATABASE to switch between the Web Edition and Business Edition size for a SQL Azure database. I discovered that you have to be connected to the master database in your SQL Azure “instance” (which makes sense). Remember, you cannot just run USE master, or switch the database context in the dropdown in the toolbar in SSMS. You have to be using the November CTP version of SQL Server 2008 R2 SSMS to work with SQL Azure in SSMS.

You have to change the connection to connect specifically to the database that you want (which is a little inconvenient). Then you can run your ALTER DATABASE command. I refreshed the Server Administration web page in the SQL Azure portal to confirm that the database edition had changed.

Then, I tried running the six DMV queries, and they all worked just fine. Finally, I waited more than five minutes (but less than 30 minutes) in SSMS, and verified that SQL Azure did not drop my connection. Everything worked just fine for these three new features.

-- Testing new features in SQL Azure Service Update 1
-- Glenn Berry
-- 2/18/2010

-- Must connect to master database first
-- Remember, there is no USE databasename allowed
-- Switch to Business Edition
ALTER DATABASE AdventureWorksLT2008R2 
MODIFY (MAXSIZE = 10 GB);

-- Switch to Web Edition
ALTER DATABASE AdventureWorksLT2008R2 
MODIFY (MAXSIZE = 1 GB);

-- Connect to a user database first
-- Remember, there is no USE databasename allowed
-- Test new DMV queries in SQL Azure

-- Get row counts
SELECT OBJECT_NAME(object_id) AS [ObjectName], *
FROM sys.dm_db_partition_stats; 

SELECT *
FROM sys.dm_exec_connections; 

SELECT *
FROM sys.dm_exec_requests; 

SELECT *
FROM sys.dm_exec_sessions; 

SELECT *
FROM sys.dm_tran_database_transactions;  

SELECT *
FROM sys.dm_tran_active_transactions; 
More Posts Next page »