Tempdb作为一个公共数据库,存储着一些临时的数据。有些是用户自己创建的,有些是SQL Server自己创建的。Tempdb空间被使用的一些常见场景有
用户自定义:临时表和表变量、游标。
SQL Server自己创建:快照事务级别(使用了行版本技术),触发器需要用到的inserted和deleted表,Hash Join需要用到一部分临时数据空间,执行计划中各种Spool操作符,索引重建指定SORT_In_tempdb选项。
差不多这些。那么Tempdb数据库必然是一个经常被使用的地方。怎么确定它是否健康?它的文件增长是否处于一个合理正常的状态?怎样维护好这个数据库是最佳实践?
那么首先从Tempdb的文件规划上,数据文件的数量和大小就是一个点。
文件数量:
作为最佳时间,文件的数量应该是和机器的CPU核心数量是一致的。原因是Tempdb默认情况下只有一个数据文件,这样的情况下容易引起锁的争夺。因为一个文件只有一个叫GAM(Global Allocation Map)的页面,记录着这个文件中的页面分配情况。我们已经知道Tempdb会作为许多上面提到的数据库活动的目标,同一时间却只有一个活动可以锁定这个页面,就必然造成其他线程的等待。那么通过添加新的文件来避开这种锁争夺。同一时间最多也就是有与CPU核心数量一致的线程数可以是处于活动状态。
文件大小:
这个需要根据应用程序的实际情况而定,有一点是不要设置成百分比增长。也不要是shrink这个数据库或者数据库文件。保持大概初始化500MB,然后按100-200MB这样就差不多了。
文件的位置:不要把数据文件和用户数据库的文件放在同一个磁盘上,这样对于性能本身是没有好处了。虽说Tempdb最好放在RAID0上,但是你要专门付出两块硬盘来实现RAID0。至少是不要和用户数据库的文件放在同一个磁盘上。
其次就是监控Tempdb的空间增长情况
通过Data Collector收集Disk Usage数据,通过观察过去Tempdb的增长情况来看是否有性能问题。一旦发现有过快的增长,通过下面的语句查找tempdb空间使用最多的会话。看出代码的特点。再搜索数据库,找出对应的存储过程。寻找语句优化空间。还有一个点是Tempdb所在磁盘空间使用是否可以支持它继续增长?需要制定一个报警线。你可以通过网络监控软件或者自己通过使用像sys.dm_os_volume_stats这样的系统函数来获取文件系统的磁盘使用信息。自己通过写一套自己的程序去做这种邮件通知。
SELECT st.dbid AS QueryExecutionContextDBID, DB_NAME(st.dbid) AS QueryExecContextDBNAME, st.objectid AS ModuleObjectId, SUBSTRING(st.TEXT, dmv_er.statement_start_offset/2 + 1, (CASE WHEN dmv_er.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX),st.TEXT)) * 2 ELSE dmv_er.statement_end_offset END - dmv_er.statement_start_offset)/2) AS Query_Text, dmv_tsu.session_id , dmv_tsu.request_id, dmv_tsu.exec_context_id, (dmv_tsu.user_objects_alloc_page_count - dmv_tsu.user_objects_dealloc_page_count) AS OutStanding_user_objects_page_counts, (dmv_tsu.internal_objects_alloc_page_count - dmv_tsu.internal_objects_dealloc_page_count) AS OutStanding_internal_objects_page_counts, dmv_er.start_time, dmv_er.command, dmv_er.open_transaction_count, dmv_er.percent_complete, dmv_er.estimated_completion_time, dmv_er.cpu_time, dmv_er.total_elapsed_time, dmv_er.reads,dmv_er.writes, dmv_er.logical_reads, dmv_er.granted_query_memory, dmv_es.HOST_NAME, dmv_es.login_name, dmv_es.program_name FROM sys.dm_db_task_space_usage dmv_tsu INNER JOIN sys.dm_exec_requests dmv_er ON (dmv_tsu.session_id = dmv_er.session_id AND dmv_tsu.request_id = dmv_er.request_id) INNER JOIN sys.dm_exec_sessions dmv_es ON (dmv_tsu.session_id = dmv_es.session_id) CROSS APPLY sys.dm_exec_sql_text(dmv_er.sql_handle) st WHERE (dmv_tsu.internal_objects_alloc_page_count + dmv_tsu.user_objects_alloc_page_count) > 0 ORDER BY (dmv_tsu.user_objects_alloc_page_count - dmv_tsu.user_objects_dealloc_page_count) + (dmv_tsu.internal_objects_alloc_page_count - dmv_tsu.internal_objects_dealloc_page_count) DESC