Catalog views
Information schema views
Dynamic management views and functions
System functions
System stored procedures
In this chapter, we will discuss how metadata can be used to perform actions at the instance level, such as expose registry values, examine how metadata can assist in capacity planning, and discuss how metadata can be used for troubleshooting and performance tuning. Finally, we will see how metadata can be used to drive automated maintenance.
Metadata is a huge topic, worthy of a book in its own right. I therefore encourage you to play with other metadata objects, which may not be covered in this chapter.
Introducing Metadata Objects
Using Catalog Views
Information schema views reside in the INFORMATION_SCHEMA schema. They return less detail than catalog views but are based on the ISO standards. This means that you can port your queries between RDBMS (relational database management systems). Listing 18-2 shows an example of using information schema views to produce a list of principals that have been granted SELECT access to the Chapter10.dbo.SensitiveData table.
A script for creating the Chapter10 database can be found in Chapter 10.
Using Information Schema Views
AlwaysOn Availability Groups
Big Data Clusters
Change data capture
Change tracking
Common language runtime (CLR)
Database mirroring
Databases
Execution
Extended events
FILESTREAM and FileTable
Full-text search and semantic search
Indexes
I/O
Memory-optimized tables
Objects
Query notifications
Replication
Resource Governor
Security
Server
Service broker
Spatial
SQL Server operating system
Stretch Databases
Transactions
We demonstrate and discuss how to use DMVs many times throughout this chapter. DMVs always begin with a dm_ prefix, followed by two to four characters that describe the category of the object—for example, os_ for operating system, db_ for database, and exec_ for execution. This is followed by the name of the object. In Listing 18-3, you can see three things: an example of how to retrieve a full list of dynamic management views and functions, an example of how to use a dynamic management view to find a list of logins that are currently connected to the Chapter16 database, and a dynamic management function you can use to produce details of the pages that store the data relating to the Chapter17.dbo.Customers table.
A full list of DMVs and DMFs can be exposed by querying sys.all_objects demonstrated in Listing 18-3. I strongly encourage you to explore the full range of dynamic management objects available.
Using Dynamic Management Views and Functions
Using System Functions
Server-Level and Instance-Level Metadata
Many forms of metadata are available for the server and instance. Server-level metadata can be very useful for DBAs who need to find configuration information or troubleshoot an issue when they do not have access to the underlying operating system. For example, the dm_server category of DMVs offers views that allow you to check the status of server audits, view SQL Server’s Registry keys, find the location of memory dump files, and find details of the instance’s services. In the following sections, we discuss how to view the Registry keys associated with the instance, expose details of SQL Server’s services, and view the contents of the buffer cache.
Exposing Registry Values
sys.dm_server_registry Columns
Column | Description |
---|---|
Registry_key | The name of the Registry key |
Value_name | The name of the key’s value |
Value_data | The data contained within the value |
Finding the Port Number
Finding Startup Parameters
Exposing Service Details
sys.dm_server_services Columns
Column | Description |
---|---|
Servicename | The name of the service. |
Startup_type | An integer representing the startup type of the service. |
Startup_desc | A textual description of the startup type of the service. |
Status | An integer representing the current status of the service. |
Status_desc | A textual description of the current service state. |
Process_id | The process ID of the service. |
Last_startup_time | The date and time that the service last started. |
Service_account | The account used to run the service. |
Filename | The file name of the service, including the full file path. |
Is_clustered | 1 indicates that the service is clustered; 0 indicates that it is stand-alone. |
Clusternodename | If the service is clustered, this column indicates the name of the node on which the service is running. |
Exposing Service Details
Analyzing Buffer Cache Usage
sys.dm_os_buffer_descriptors Columns
Column | Description |
---|---|
Database_id | The ID of the database that the page is from |
File_id | The ID of the file that the page is from |
Page_id | The ID of the page |
Page_level | The index level of the page |
Allocation_unit_id | The ID of the allocation unit that the page is from |
Page_type | The type of page, for example, DATA_PAGE, INDEX_PAGE, IAM_PAGE, or PFS_PAGE |
Row_count | The number of rows stored on the page |
Free_space_in_bytes | The amount of free space on the page |
Is_modified | A flag that indicates if the page is dirty |
Numa_node | The NUMA node for the buffer |
Read_microset | The amount of time taken to read the page into cache, specified in microseconds |
Determining Buffer Cache Usage per Database
More DMVs within the dm_os category are discussed in the “Metadata for Troubleshooting and Performance Tuning” section of this chapter.
Metadata for Capacity Planning
One of the most useful ways you can use metadata is during your pursuit of proactive capacity management. SQL Server exposes metadata that provides you with information about the current size and usage of your database files, and you can use this information to plan ahead and arrange additional capacity, before your enterprise monitoring software starts generating critical alerts.
Exposing File Stats
sys.dm_db_file_space_usage Columns
Column | Description |
---|---|
database_id | The ID of the database to which the file belongs. |
file_id | The ID of the file within the database. These IDs are repeated between databases. For example, the primary file always has an ID of 1, and the first log file always has an ID of 2. |
filegroup_id | The ID of the filegroup in which the file resides. |
total_page_count | The total number of pages within the file. |
allocated_extent_page_count | The number of pages within the file that are in extents that have been allocated. |
unallocated_extent_page_count | The number of pages within the file that are in extents that have not been allocated. |
version_store_reserved_page_count | The number of pages reserved to support transactions using snapshot isolation. Only applicable to TempDB. |
user_object_reserved_page_count | The number of pages reserved for user objects. Only applicable to TempDB. |
internal_object_reserved_page_count | The number of pages reserved for internal objects. Only applicable to TempDB. |
mixed_extent_page_count | The number of extents that have pages allocated to different objects. |
modified_extend_page_count | The total number of modified pages since the last time a full database backup was taken |
sys.dm_io_virtual_file_stats Columns
Column | Description |
---|---|
database_id | The ID of the database to which the file belongs. |
file_id | The ID of the file within the database. These IDs are repeated between databases. For example, the primary file always has an ID of 1 and the first log file always has an ID of 2. |
sample_ms | The number of milliseconds since the computer started. |
num_of_reads | The total number of reads against the file. |
num_of_bytes_read | The total number of bytes read from the file. |
io_stall_read_ms | The total time waiting for reads to be issued against the file, specified in milliseconds. |
num_of_writes | The total number of write operations performed against the file. |
num_of_bytes_written | The total number of bytes written to the file. |
io_stall_write_ms | The total time waiting for writes to complete against the file, specified in milliseconds. |
io_stall | The total time waiting for all I/O requests against the file to be completed, specified in milliseconds. |
size_on_disk_bytes | The total space used by the file on disk, specified in bytes. |
file_handle | The Windows file handle. |
io_stall_queued_read_ms | Total I/O latency for read operations against the file, caused by Resource Governor. |
io_stall_queued_write_ms | Total I/O latency for write operations against the file, caused by Resource Governor. |
I/O stalls are the amount of time it takes the I/O subsystem to respond to SQL Server.
sys.master_files Columns
Column | Description |
---|---|
database_id | The ID of the database to which the file belongs. |
file_id | The ID of the file within the database. These IDs are repeated between databases. For example, the primary file always has an ID of 1 and the first log file always has an ID of 2. |
file_guid | The GUID of the file. |
type | An integer representing the file type. |
type_desc | A textual description of the file type. |
data_space_id | The ID of the filegroup in which the file resides. |
name | The logical name of the file. |
physical_name | The physical path and name of the file. |
state | An integer indicating the current state of the file. |
state_desc | A textual description of the current state of the file. |
size | The current size of the file, specified as a count of pages. |
max_size | The maximum size of the file, specified as a count of pages. |
growth | The growth setting of the file. 0 indicates autogrowth is disabled. If is_percent_growth is 0, then the value indicates the growth increment as a count of pages. If is_percent_growth is 1, then the value indicates a whole number percentage increment. |
is_media_read_only | Specifies if the media on which the file resides is read only. |
is_read_only | Specifies if the file is in a read-only filegroup. |
is_sparse | Specifies that the file belongs to a database snapshot. |
is_percent_growth | Indicates if the growth output is a percentage or a fixed rate. |
is_name_reserved | Specifies if the filename is reusable. |
create_lsn | The LSN (log sequence number) at which the file was created. |
drop_lsn | The LSN at which the file was dropped (if applicable). |
read_only_lsn | The most recent LSN at which the filegroup was marked read only. |
read_write_lsn | The most recent LSN at which the filegroup was marked read/write. |
differential_base_lsn | The LSN at which changes in the file started being marked in the DIFF pages. |
differential_base_guid | The GUID of the full backup on which differential backups for the file are made. |
differential_base_time | The time of the full backup on which differential backups for the file are made. |
redo_start_lsn | The LSN at which the next roll forward will start. |
redo_start_fork_guid | The GUID of the recovery fork. |
redo_target_lsn | The LSN at which an online roll forward for the file can stop. |
redo_target_fork_guid | The GUID of the recovery fork. |
backup_lsn | The most recent LSN at which a full or differential backup was taken. |
Using File Stats for Capacity Analysis
File Capacity Details
Analyzing Drive Space with xp_fixeddrives
The issue with xp_fixeddrives is that it cannot see mapped drives. Therefore, as an alternative, you can employ the script in Listing 18-11, which uses PowerShell to return the information.
The drawback of this approach is that it requires xp_cmdshell to be enabled, which is against security best practice.
Analyzing Drive Space with PowerShell
A third way of working with drive information is the sys.dm_os_enumerate_fixed_drives DMV. This DMV returns the path of the drive, for example, C: as well as the free space on the drive, expressed in bytes. Additionally, columns are returned for a drive type ID and description.
Metadata for Troubleshooting and Performance Tuning
You can use many metadata objects to tune performance and troubleshoot issues within SQL Server. In the following sections, we explore how to capture performance counters from within SQL Server, how to analyze waits, and how to use DMVs to troubleshoot issues with expensive queries.
Retrieving Perfmon Counters
sys.dm_os_performance_counters Columns
Column | Description |
---|---|
object_name | The category of the counter. |
counter_name | The name of the counter. |
instance_name | The instance of the counter. For example, database-related counters have an instance for each database. |
cntr_value | The value of the counter. |
cntr_type | The type of counter. Counter types are described in Table 18-8. |
Counter Types
Counter Type | Description |
---|---|
1073939712 | You will use PERF_LARGE_RAW_BASE as a base value in conjunction with the PERF_LARGE_RAW_FRACTION type to calculate a counter percentage, or with PERF_AVERAGE_BULK to calculate an average. |
537003264 | Use PERF_LARGE_RAW_FRACTION as a fractional value in conjunction with PERF_LARGE_RAW_BASE to calculate a counter percentage. |
1073874176 | PERF_AVERAGE_BULK is a cumulative average that you use in conjunction with PERF_LARGE_RAW_BASE to calculate a counter average. The counter, along with the base, is sampled twice to calculate the metric over a period of time. |
272696320 | PERF_COUNTER_COUNTER is a 32-bit cumulative rate counter. The value should be sampled twice to calculate the metric over a period of time. |
272696576 | PERF_COUNTER_BULK_COUNT is a 64-bit cumulative rate counter. The value should be sampled twice to calculate the metric over a period of time. |
65792 | PERF_COUNTER_LARGE_RAWCOUNT returns the last sampled result for the counter. |
Using Counter Type 65792
Using Counter Types 272696576 and 272696320
The script in Listing 18-14 demonstrates capturing the plan cache hit ratio for the instance. The Plan Cache Hit Ratio counter is counter type 537003264. Therefore, we need to multiply the value by 100 and then divide by the base counter to calculate the percentage.
Before running the script, you should change the instance name to match your own.
Using Counter Type 537003264
Using Counter Type 1073874176
Analyzing Waits
Waits are a natural aspect of any RDBMS, but they can also indicate a performance bottleneck. Analyzing these waits, with a view to reducing them, often improve performance. A full explanation of all wait types can be found at msdn.microsoft.com, but all wait types break down into three categories: resource waits, queue waits, and external waits.
A query in SQL Server is either running, waiting for its turn on the processor (runnable), or waiting for another resource (suspended). If it is waiting for another resource, SQL Server records the reason why it is waiting and the duration of this wait.
Resource waits occur when a thread requires access to an object, but that object is already in use, and therefore, the thread has to wait. This can include the thread waiting to take a lock out on an object or waiting for a disk resource to respond. Queue waits occur when a thread is idle and is waiting for a task to be assigned. This does not necessarily indicate a performance bottleneck, since it is often a background task, such as the Deadlock Monitor or Lazy Writer waiting until it is needed. External waits occur when a thread is waiting for an external resource, such as a linked server. The hidden gotcha here is that an external wait does not always mean that the thread is actually waiting. It could be performing an operation external to SQL Server, such as an extended stored procedure running external code.
Any task that has been issued is in one of three states: running, runnable, or suspended. If a task is in the running state, then it is actually being executed on a processor. When a task is in the runnable state, it sits on the processor queue, awaiting its turn to run. This is known as a signal wait. When a task is suspended, it means that the task is waiting for any reason other than a signal wait. In other words, it is experiencing a resource wait, a queue wait, or an external wait. Each query is likely to alternate between the three states as it progresses.
Resetting Wait Stats
sys.dm_os_wait_stats Columns
Column | Description |
---|---|
wait_type | The name of the wait type that has occurred. |
waiting_tasks_count | The number of tasks that have occurred on this wait type. |
wait_time_ms | The cumulative time of all waits against this wait type, displayed in milliseconds. This includes signal wait times. |
max_wait_time_ms | The maximum duration of a single wait against this wait type. |
signal_wait_time_ms | The cumulative time for all signal waits against this wait type. |
Finding the Highest Waits
Calculating Signal Waits
Calculating the Highest Waits over a Defined Period
Database Metadata
Parameters Accepted by sys.dm_db_page_info
Parameter | Description |
---|---|
Database_id | The database_id of the database that you wish to return details for. |
File_id | The file_id of the file that you wish to return details for. |
Page_id | The page_id of the page that you are interested in. |
Mode | Mode can be set to either LIMITED or DETAILED. The only difference between the modes is that when LIMITED is used, the description columns are not populated. This can improve performance against large tables. |
In order to populate these parameters, an additional system function has been added, called sys.fn_PageResCracker. This function can be cross applied to a table, passing %%physloc%% as a parameter. Alternatively, if cross applied to the sys.dm_exec_requests DMV, or sys.sysprocesses, a deprecated system view, an additional column has been added, called page_resource, which can be passed as a parameter to the function. This is helpful if you are diagnosing an issue with page waits. When passed a page resource/physical location object, the function will return the database_id, file_id, and page_id of each row in a result set.
When used with %%physloc%% as opposed to a page_resource object, the sys.fn_PageResCracker function returns a slot_id, as opposed to a database_id. Therefore, when used with %%physloc%%, the DB_ID() function should be used to obtain the database_id and the database_id column returned by the function should be discarded.
Columns Returned by sys.db_db_page_info
Column | Description |
---|---|
Database_id | The ID of the database |
File_id | The ID of the file |
Page_id | The ID of the page |
page_type | The internal ID associated with the page type description |
Page_type_desc page_flag_bits page_flag_bits_desc | The type of page. For example, data page, index page, IAM page, PFS page, etc. |
page_type_flag_bits | Hexadecimal value representing the page flags |
page_type_flag_bits_desc | A description of the page flags |
object_id | The ID of the object that the page is a part of |
index_id | The ID of the index that the page is part of |
partition_id | The partition ID of the partition that the page is part of |
alloc_unit_id | The ID of the allocation unit where the page is stored |
page_level | The level of the page within a B-Tree structure |
slot_count | The number of slots within the page |
ghost_rec_count | The number of records of the page that have been marked for deletion, but have not yet been physically removed |
torn_bits | Used to detect data corruption, by storing 1 bit for every torn write detected |
is_iam_pg | Indicates if the page is an IAM page |
is_mixed_ext | Indicates if the page is part of a mixed extent (an extent allocated to multiple objects) |
pfs_file_id | The file ID of the file where the page’s associated PFS (Page Free Space) page is stored |
pfs_page_id | The page ID of the PFS page that is associated with the page |
pfs_alloc_percent | The amount of free space on the page |
pfs_status | The value of the page’s PFS byte |
pfs_status_desc | A description of the page’s PFS byte |
gam_file_id | The file ID of the file where the page’s associated GAM (global allocation map) page is stored |
gam_page_id | The page ID of the GAM page, which is associated with the page |
gam_status | Indicates if the page is allocated in GAM |
gam_status_desc | Describes the GAM status marker |
sgam_file_id | The file ID of the file where the page’s associated SGAM (shared global allocation map) page is stored |
sgam_page_id | The page ID of the SGAM page, which is associated with the page |
sgam_status | Indicates if the page is allocated in SGAM |
sgam_status_desc | Describes the SGAM status marker |
diff_map_file_id | The file ID of the file containing the page’s associated differential bitmap page |
diff_map_page_id | The page ID of the differential bitmap page associated with the page |
diff_status | Indicates if the page has changed since the last differential backup |
diff_status_desc | Describes the differential status marker |
ml_file_id | The file ID of the file that stores the page’s associated minimally logged bitmap page |
ml_page_id | The page ID of the minimally logged bitmap page, associated with the page |
ml_status | Indicates if the page is minimally logged |
ml_status_desc | Describes the minimally logged status marker |
free_bytes | The amount of free space on the page (in bytes) |
free_data_offset | The page offset, to the start of the free space on the page |
reserved_bytes | If the page is a leaf-level index page, indicates the amount of rows awaiting ghost cleanup. If the page is on a heap, then indicates the number of free bytes reserved by all transactions |
reserved_xdes_id | Used for MSFT support for debugging |
xdes_id | Used for MSFT support for debugging |
prev_page_file_id | The file ID of the previous page in the IAM chain |
prev_page_page_id | The page ID of the previous page in the IAM chain |
next_page_file_id | The file ID of the next page in the IAM chain |
next_page_page_id | The page ID of the next page in the IAM chain |
min_len | The length of fixed width rows |
Page_lsn | The last LSN (log sequence number) to modify the page |
header_version | The version of the page header |
Find the Most Recent LSN to Modify a Table
Metadata-Driven Automation
You can use metadata to drive intelligent scripts that you can use to automate routine DBA maintenance tasks while at the same time incorporating business logic. In the following sections, you see how you can use metadata to generate rolling database snapshots and also to rebuild only those indexes that are fragmented.
Dynamically Cycling Database Snapshots
As discussed in Chapter 16, we can use database snapshots to create a read-only copy of the database that can reduce contention for read-only reporting. The issue is that the data becomes stale, as data in the source database is modified. For this reason, a useful tool for managing snapshots is a stored procedure, which dynamically creates a new snapshot and drops the oldest existing snapshot. You can then schedule this procedure to run periodically, using SQL Server Agent. (SQL Server Agent is discussed in Chapter 21.) The script in Listing 18-21 creates a stored procedure that, when passed the name of the source database, drops the oldest snapshot and creates a new one.
The procedure accepts two parameters. The first specifies the name of the database that should be used to generate the snapshot. The second parameter specifies how many snapshots you should have at any one time. For example, if you pass in a value of Chapter18 to the @DBName parameter and a value of 2 to the @RequiredSnapshots parameter, the procedure creates a snapshot against the Chapter18 database but only removes the oldest snapshot if at least two snapshots already exist against the Chapter18 database.
Dynamically Cycling Database Snapshots
Running the DynamicSnapshot Procedure
Rebuilding Only Fragmented Indexes
When you rebuild all indexes with a maintenance plan, which we discuss in Chapter 21, SQL Server supplies no intelligent logic out of the box. Therefore, all indexes are rebuilt, regardless of their fragmentation level, which requires unnecessary time and resource utilization. A workaround for this issue is to write a custom script that rebuilds indexes only if they are fragmented.
Rebuilding Only Required Indexes
Running RebuildIndexes.sql
Summary
SQL Server exposes a vast array of metadata, which describes the data structures within SQL Server as well as the data itself. Metadata is exposed through a series of catalog views, dynamic management views and functions, system functions, and the INFORMATION_SCHEMA. Normally you only use the INFORMATION_SCHEMA if you need your scripts to be transferable to other RDBMS products. This is because it provides less detail than SQL Server–specific metadata but conforms to ISO standards, and therefore, it works on all major RDBMS.
This chapter also covered much useful information about the underlying operating system, as well as SQLOS. For example, you can use the dm_server category of DMV to find details of the instance’s Registry keys and expose details of the instance’s services. You can use the dm_os category of DMV to expose many internal details regarding the SQLOS, including the current contents of the buffer cache.
SQL Server also exposes metadata that you can use for capacity planning, such as the usage statistics for all files within a database (e.g., I/O stalls) and the amount of free space remaining. You can use this information to proactively plan additional capacity requirements before alerts start being triggered and applications are put at risk.
Metadata can also help in the pursuit of troubleshooting and performance tuning. The sys.dm_os_performance_counters DMV allows DBAs to retrieve Perfmon counters, even if they do not have access to the operating system. This can remove inter-team dependencies. You can use sys.dm_os_wait_stats to identify the most common cause of waits within the instance, which can in turn help diagnose hardware bottlenecks, such as memory or CPU pressure. The dm_exec category of DMV can help identify expensive queries, which may be tuned, to improve performance.
DBAs can also use metadata to create intelligent scripts, which can reduce their workload by adding business rules to common maintenance tasks. For example, a DBA can use metadata for tasks such as dynamically rebuilding only indexes that have become fragmented, or dynamically managing the cycling of database snapshots. I encourage you to explore the possibilities of metadata-driven automation further; the possibilities are endless.