{ "metadata": { "kernelspec": { "name": "SQL", "display_name": "SQL", "language": "sql" }, "language_info": { "name": "sql", "version": "" } }, "nbformat_minor": 2, "nbformat": 4, "cells": [ { "cell_type": "markdown", "source": [ "# Exploring Memory-Optimized TempDB Metadata\r\n", "\r\n", "TempDB metadata contention has historically been a bottleneck to scalability for many workloads running on SQL Server. SQL Server 2019 introduces a new feature that is part of the [In-Memory Database](https://docs.microsoft.com/sql/relational-databases/in-memory-database) feature family, memory-optimized tempdb metadata, which effectively removes this bottleneck and unlocks a new level of scalability for tempdb-heavy workloads. In SQL Server 2019, the system tables involved in managing temp table metadata can be moved into latch-free non-durable memory-optimized tables.\r\n", "\r\n", "To learn more about tempdb metadata contention, along with other types of tempdb contention, check out the blog article [TEMPDB - Files and Trace Flags and Updates, Oh My!](https://techcommunity.microsoft.com/t5/SQL-Server/TEMPDB-Files-and-Trace-Flags-and-Updates-Oh-My/ba-p/385937). Keep reading to explore the new memory-optimized tempdb metadata feature." ], "metadata": { "azdata_cell_guid": "0e65e08d-0c31-4214-9380-f13be28dd5e8" } }, { "cell_type": "markdown", "source": [ "## Configure the AdventureWorks sample database\r\n", "Follow these steps to configure your environment in preparation for the demo. Alternatively, you can use the pre-configured container using the instructions in the Python companion notebook. Keep in mind that whether you use the container or your own server, you will need at least 4 cores to generate TempDB metadata contention.\r\n", "\r\n", "1. Ensure you have the latest version of SQL Server 2019 installed. You will need Evaluation, Enterprise, or Developer Edition in order to execute this demo.\r\n", "2. Download the [AdventureWorks2017.bak](https://github.com/Microsoft/sql-server-samples/releases/download/adventureworks/AdventureWorks2017.bak) sample database backup from GitHub. For your convenience, a copy of this backup has been included in the demo folder.\r\n", "3. Restore the database as `AdventureWorks`.\r\n", "> NOTE\r\n", ">
You will need to change the paths in the following example to match your server file paths." ], "metadata": { "azdata_cell_guid": "f3dc9c3f-a060-457a-afac-5befa4146462" } }, { "cell_type": "code", "source": [ "USE [master]\r\n", "RESTORE DATABASE [AdventureWorks] FROM DISK = N'C:\\Program Files\\Microsoft SQL Server\\MSSQL15.SQL2019\\MSSQL\\Backup\\AdventureWorks2017.bak' \r\n", "WITH FILE = 1, \r\n", "\t\tMOVE N'AdventureWorks2017' TO N'C:\\Program Files\\Microsoft SQL Server\\MSSQL15.SQL2019\\MSSQL\\DATA\\AdventureWorks.mdf', \r\n", "\t\tMOVE N'AdventureWorks2017_log' TO N'C:\\Program Files\\Microsoft SQL Server\\MSSQL15.SQL2019\\MSSQL\\DATA\\AdventureWorks_log.ldf', NOUNLOAD\r\n", "GO" ], "metadata": { "azdata_cell_guid": "14617686-43fb-42b1-afe3-9e1ca9a2d4e3" }, "outputs": [], "execution_count": 0 }, { "cell_type": "markdown", "source": [ "4. Run the 01b-Create_EmployeeBirthdayList.sql script to create the workload procedure." ], "metadata": { "azdata_cell_guid": "6913e3cb-c3e6-48ed-ba3d-4178a1bd69dc" } }, { "cell_type": "code", "source": [ "USE AdventureWorks\r\n", "GO\r\n", "\r\n", "CREATE OR ALTER PROCEDURE usp_EmployeeBirthdayList @month int AS\r\n", "BEGIN\r\n", "\r\n", "\tIF OBJECT_ID('tempdb..#Birthdays') IS NOT NULL DROP TABLE #Birthdays;\r\n", "\r\n", "\tCREATE TABLE #Birthdays (BusinessEntityID int NOT NULL PRIMARY KEY);\r\n", "\r\n", "\tINSERT #Birthdays (BusinessEntityID)\r\n", "\tSELECT BusinessEntityID\r\n", "\tFROM HumanResources.Employee \r\n", "\tWHERE MONTH(BirthDate) = @month\r\n", "\r\n", "\tSELECT p.FirstName, p.LastName, a.AddressLine1, a.AddressLine2, a.City, sp.StateProvinceCode, a.PostalCode\r\n", "\tFROM #Birthdays b\r\n", "\tINNER JOIN Person.Person p ON b.BusinessEntityID = p.BusinessEntityID\r\n", "\tINNER JOIN Person.BusinessEntityAddress bea ON p.BusinessEntityID = bea.BusinessEntityID\r\n", "\tINNER JOIN Person.Address a ON bea.AddressID = a.AddressID\r\n", "\tINNER JOIN Person.StateProvince sp ON a.StateProvinceID = sp.StateProvinceID\r\n", "\tINNER JOIN Person.AddressType at ON at.AddressTypeID = bea.AddressTypeID\r\n", "\tWHERE at.Name = N'Home'\r\n", "\r\n", "END;" ], "metadata": { "azdata_cell_guid": "e1a478af-f345-43f8-a74a-c795715dfe40" }, "outputs": [], "execution_count": 0 }, { "cell_type": "markdown", "source": [ "## Run the demo scenario to generate the workload\r\n", "\r\n", "Use the included ostress.exe tool to generate a multi-threaded load against your server. You will need a minimum of 4 cores to generate the contention. When configuring the ostress command, generally a 1:4 ratio of cores to concurrent threads works best to simulate the scenario and demonstrate the improvement. For example, this demo was tested with 4 cores and 16 concurrent threads. If you have 8 cores, start with 32 concurrent threads. You will also need to configure the number of iterations to allow the scenario to run long enough to observe the server. For 4 cores and 16 threads, 120 iterations should take around 30 seconds without Memory-Optimized TempDB Metadata enabled, 20 seconds with it enabled. Use the `-r` parameter to increase this number if you would like the script to run longer. Switch to the Python notebook to run the command, or you can open a Command Prompt window and execute the following command:\r\n", "\r\n", "`ostress.exe -Slocalhost,1455 -Usa -PP@ssw0rd! -dAdventureWorks -Q\"EXEC dbo.usp_EmployeeBirthdayList 4\" -mstress -quiet -n20 -r120 | FINDSTR \"QEXEC Starting Creating elapsed\"`" ], "metadata": { "azdata_cell_guid": "a500d78a-4e93-4795-8bd5-1b2d01b50968" } }, { "cell_type": "markdown", "source": [ "## Monitor your workload for page contention\r\n", "\r\n", "You can use the following script to view all the sessions that are waiting for page-related wait types and get information about the objects that the pages belong to." ], "metadata": { "azdata_cell_guid": "ab3146e1-652f-41a2-83f3-21847f81ca2c" } }, { "cell_type": "code", "source": [ "USE master\r\n", "GO\r\n", "\r\n", "SELECT \r\n", "er.session_id, er.wait_type, er.wait_resource, \r\n", "OBJECT_NAME(page_info.[object_id],page_info.database_id) as [object_name],\r\n", "er.blocking_session_id,er.command, \r\n", " SUBSTRING(st.text, (er.statement_start_offset/2)+1, \r\n", " ((CASE er.statement_end_offset \r\n", " WHEN -1 THEN DATALENGTH(st.text) \r\n", " ELSE er.statement_end_offset \r\n", " END - er.statement_start_offset)/2) + 1) AS statement_text,\r\n", "page_info.database_id,page_info.[file_id], page_info.page_id, page_info.[object_id], \r\n", "page_info.index_id, page_info.page_type_desc\r\n", "FROM sys.dm_exec_requests AS er\r\n", "CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st \r\n", "CROSS APPLY sys.fn_PageResCracker (er.page_resource) AS r \r\n", "CROSS APPLY sys.dm_db_page_info(r.[db_id], r.[file_id], r.page_id, 'DETAILED') AS page_info\r\n", "WHERE er.wait_type like '%page%'\r\n", "GO" ], "metadata": { "azdata_cell_guid": "526098c3-5404-4735-b112-b5e6e2d4906d" }, "outputs": [ { "output_type": "display_data", "data": { "text/html": "Commands completed successfully." }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": "(3 rows affected)" }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": "Total execution time: 00:00:00.267" }, "metadata": {} }, { "output_type": "execute_result", "execution_count": 11, "data": { "application/vnd.dataresource+json": { "schema": { "fields": [ { "name": "session_id" }, { "name": "wait_type" }, { "name": "wait_resource" }, { "name": "object_name" }, { "name": "blocking_session_id" }, { "name": "command" }, { "name": "statement_text" }, { "name": "database_id" }, { "name": "file_id" }, { "name": "page_id" }, { "name": "object_id" }, { "name": "index_id" }, { "name": "page_type_desc" } ] }, "data": [ { "0": "70", "1": "PAGELATCH_EX", "2": "2:1:118", "3": "sysschobjs", "4": "71", "5": "EXECUTE", "6": "EXEC dbo.usp_EmployeeBirthdayList 4", "7": "2", "8": "1", "9": "118", "10": "34", "11": "2", "12": "INDEX_PAGE" }, { "0": "78", "1": "PAGELATCH_EX", "2": "2:1:118", "3": "sysschobjs", "4": "71", "5": "EXECUTE", "6": "EXEC dbo.usp_EmployeeBirthdayList 4", "7": "2", "8": "1", "9": "118", "10": "34", "11": "2", "12": "INDEX_PAGE" }, { "0": "79", "1": "PAGELATCH_EX", "2": "2:1:118", "3": "sysschobjs", "4": "72", "5": "CREATE TABLE", "6": "CREATE TABLE #Birthdays (BusinessEntityID int NOT NULL PRIMARY KEY)", "7": "2", "8": "1", "9": "118", "10": "34", "11": "2", "12": "INDEX_PAGE" } ] }, "text/html": "
session_idwait_typewait_resourceobject_nameblocking_session_idcommandstatement_textdatabase_idfile_idpage_idobject_idindex_idpage_type_desc
70PAGELATCH_EX2:1:118sysschobjs71EXECUTEEXEC dbo.usp_EmployeeBirthdayList 421118342INDEX_PAGE
78PAGELATCH_EX2:1:118sysschobjs71EXECUTEEXEC dbo.usp_EmployeeBirthdayList 421118342INDEX_PAGE
79PAGELATCH_EX2:1:118sysschobjs72CREATE TABLECREATE TABLE #Birthdays (BusinessEntityID int NOT NULL PRIMARY KEY)21118342INDEX_PAGE
" }, "metadata": {} } ], "execution_count": 11 }, { "cell_type": "markdown", "source": [ "## Enable and Disable Memory-Optimized TempDB Metadata\r\n", "\r\n", "The following script will enable Memory-Optimized TempDB Metadata. **Note that this change requires a server restart to take effect**." ], "metadata": { "azdata_cell_guid": "ceda1d60-f135-4438-9587-8f8e27dea6ea" } }, { "cell_type": "code", "source": [ "ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA=ON;\n", "GO" ], "metadata": { "azdata_cell_guid": "2d95de38-e28a-4642-be3c-c4668bcfa504" }, "outputs": [ { "output_type": "display_data", "data": { "text/html": "Commands completed successfully." }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": "Total execution time: 00:00:00.141" }, "metadata": {} } ], "execution_count": 2 }, { "cell_type": "markdown", "source": [ "Once the command is complete, restart the SQL Server service, then run the following command to verify that Memory-Optimized TempDB Metadata has been enabled." ], "metadata": { "azdata_cell_guid": "12ff967e-f2c3-4e60-880e-49bd4d06163e" } }, { "cell_type": "code", "source": [ "SELECT SERVERPROPERTY('IsTempDBMetadataMemoryOptimized') AS IsTempDBMetadataMemoryOptimized; \r\n", "GO" ], "metadata": { "azdata_cell_guid": "e62081d9-fe1d-4a0a-a367-1f5892b5451f" }, "outputs": [ { "output_type": "display_data", "data": { "text/html": "(1 row affected)" }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": "Total execution time: 00:00:00.132" }, "metadata": {} }, { "output_type": "execute_result", "metadata": {}, "execution_count": 4, "data": { "application/vnd.dataresource+json": { "schema": { "fields": [ { "name": "IsTempDBMetadataMemoryOptimized" } ] }, "data": [ { "0": "1" } ] }, "text/html": "
IsTempDBMetadataMemoryOptimized
1
" } } ], "execution_count": 4 }, { "cell_type": "markdown", "source": [ "The system stored procedure `sp_configure` can also be used. If the `config_value` does not equal the `run_value`, it means that the configuration has been changed, but the server must be restarted in order for it to take effect." ], "metadata": { "azdata_cell_guid": "4d0f3cc3-665c-4e0f-b0b3-a5bdd043c509" } }, { "cell_type": "code", "source": [ "EXEC sp_configure 'show advanced options', 1\r\n", "RECONFIGURE\r\n", "\r\n", "EXEC sp_configure 'tempdb metadata memory-optimized'" ], "metadata": { "azdata_cell_guid": "43bf54dc-86da-48a3-a581-955e228c7f6a" }, "outputs": [ { "output_type": "display_data", "data": { "text/html": "Configuration option 'show advanced options' changed from 1 to 1. Run the RECONFIGURE statement to install." }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": "Total execution time: 00:00:00.180" }, "metadata": {} }, { "output_type": "execute_result", "metadata": {}, "execution_count": 5, "data": { "application/vnd.dataresource+json": { "schema": { "fields": [ { "name": "name" }, { "name": "minimum" }, { "name": "maximum" }, { "name": "config_value" }, { "name": "run_value" } ] }, "data": [ { "0": "tempdb metadata memory-optimized", "1": "0", "2": "1", "3": "1", "4": "1" } ] }, "text/html": "
nameminimummaximumconfig_valuerun_value
tempdb metadata memory-optimized0111
" } } ], "execution_count": 5 }, { "cell_type": "markdown", "source": [ "Simlarly, you can use the following script to disable Memory-Optimized TempDB Metadata. This also requires a server restart." ], "metadata": { "azdata_cell_guid": "aeb7e67e-a436-42d6-9e4f-b6994dc1cfbe" } }, { "cell_type": "code", "source": [ "ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA=OFF;\r\n", "GO" ], "metadata": { "azdata_cell_guid": "5af4903e-61f9-4bb2-9342-3624894619dd" }, "outputs": [ { "output_type": "display_data", "data": { "text/html": "Commands completed successfully." }, "metadata": {} }, { "output_type": "display_data", "data": { "text/html": "Total execution time: 00:00:00.037" }, "metadata": {} } ], "execution_count": 16 } ] }