提高SQL Server中sys表的性能

时间:2017-09-25 11:35:11

标签: sql-server performance

我们在SQL Server应用程序中遇到性能问题(用PHP编写,但在管理工作室运行查询时遇到相同的时间,所以我不认为这是相关的)。

违规查询如下:

SELECT
    c.name, t.name AS type, c.is_nullable, c.is_identity, 
    object_definition(c.default_object_id) AS default_value, 
    c.precision, c.scale, c.max_length, c.collation_name,
    CASE WHEN p.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_primary, 
    CASE WHEN u.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_unique 
FROM
    sys.columns AS c
    LEFT JOIN sys.types AS t
        ON c.user_type_id = t.user_type_id
    LEFT JOIN (
        SELECT DISTINCT
            ic.object_id, ic.column_id
            FROM sys.indexes ix
            JOIN sys.index_columns ic
                ON ix.object_id = ic.object_id
                AND ix.index_id = ic.index_id
            WHERE is_primary_key = 1
        ) AS p
        ON p.object_id = c.object_id AND p.column_id = c.column_id
    LEFT JOIN (
        SELECT DISTINCT
            ic.object_id, ic.column_id
            FROM sys.indexes ix
            JOIN sys.index_columns ic
                ON ix.object_id = ic.object_id
                AND ix.index_id = ic.index_id
            WHERE is_unique = 1
        ) AS u
        ON u.object_id = c.object_id AND u.column_id = c.column_id
WHERE
    c.object_id = object_id('tblTestTable');

在SQL Server 2014 Express上进行本地测试,我们获得约0.3秒的首次运行时间,后续运行时间介于0.1秒和0.2秒之间。在我们的生产服务器上,运行完整版的SQL Server 2014,性能更差!

我希望像这样的查询(使用系统表)运行得更快,例如在0.01 - > 0.05范围,这是我们针对自己的用户表进行类似查询所获得的性能。

  1. 我应该期待这样的查询速度慢吗?
  2. 如果是,是否有另一种更快的方法来获取此信息?如果不是,我们该怎么做才能优化呢?
  3. 我注意到这些系统视图似乎没有索引。这是一个因素吗?

    同样相关的是这个查询最初使用了INFORMATION_SCHEMA视图,但这些视图至少是我们使用sys获得的当前性能的两倍(尽管可能是因为子选择在字段列表中而不是在加入)。

    请注意,时间来自管理工作室中的属性窗口,并且与我在microtime()中从PHP中执行查询时获得的结果一致。

    更新#1

    我使用我们的用户数据表构建了一个查询,它与上面的结构基本相同(或尽可能接近)。

    此查询在首次运行时运行大约0.14秒,然后在后续运行时运行在0.015和0.07之间。这是我期望sys查询的性能。因此,这似乎是sys表特有的问题,而不是一般的服务器配置问题。

    我可以在这里发布查询,如果它有帮助,但是现在暂时停止,以防它只是垃圾邮件。

    更新#2

    根据要求,以下是来自冷缓存的原始查询的SET STATISTICS TIME|IO ON的统计信息输出。

    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 0 ms.
    
     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 0 ms.
    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 0 ms.
    
     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 0 ms.
    
     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 0 ms.
    
    (56 row(s) affected)
    Table 'sysiscols'. Scan count 112, logical reads 224, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'sysidxstats'. Scan count 112, logical reads 224, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'sysschobjs'. Scan count 0, logical reads 448, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'syssingleobjrefs'. Scan count 0, logical reads 112, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'sysscalartypes'. Scan count 0, logical reads 112, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'syscolpars'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    
    (1 row(s) affected)
    
     SQL Server Execution Times:
       CPU time = 16 ms,  elapsed time = 210 ms.
    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 0 ms.
    
     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 0 ms.
    

2 个答案:

答案 0 :(得分:3)

系统视图确实有索引(实际上底层系统目录表有索引)。首先,首先要确定缓慢原因。阅读How to analyse SQL Server performance。我会以某种方式怀疑这是索引问题的根源(即由元数据视图中的数据大小驱动)。由于各种原因,你很可能会遇到阻止。扫描系统视图仍受锁定,DDL操作(创建/更改/删除)将阻止扫描,直到DDL提交。

此外,只需应用一些常识查询优化。您正在按c.object_id = object_id('tblTestTable');过滤结果,但内部查询(SELECT DISTINCT ...FROM sys.indexes ix JOIN sys.index_columns ic)可能无法推送此谓词。试着强迫它,即。将WHERE object_id = object_id('tblTestTable')子句添加到内部查询中。

答案 1 :(得分:0)

根据本主题和我自己的研究中的各种评论,我得出以下结论:

<强> Q1。我应该期望这样的查询速度慢吗?

答案:显然,是的。

根据我的标准,我将查询减少到了以下,这仍然非常慢(即在热缓存上为0.11到0.17秒,而不是0.0x秒,这是我期望的):

SELECT
    c.name, c.is_nullable, c.is_identity,
    c.precision, c.scale, c.max_length, c.collation_name
FROM
    sys.columns AS c;

如果没有连接或计算字段的直接查询这么慢,那么我只能断定它是SQL Server的限制而不是我正在做的事情。

<强> Q2。如果是,是否有另一种更快的方法来获取此信息?如果不是,我们应该做些什么来优化它?

答案:似乎没有更快的方法,也没有任何有意义的优化(假设所有列都是必需的)。

以下是Remus Rusanu在对其他答案之一的评论中的引用:

  
    

你是说这些是复杂的观点,因此我应该期望它们很慢?

  
     

我说你会得到比与视图类似结构的表相比更慢的响应。基础表针对元数据维护操作进行了优化(通过Id和名称查找)并防止DDL过度锁定甚至死锁。在系统表之上查询视图的性能必须“足够好”,而不是主要目标。

基于此,以及我自己的一些实验,似乎不太可能通过替代查询制定或通过不同的查找机制以更快的方式进行任何进一步的优化。< / p>

解决方案,在我的情况下

因此,鉴于上述情况,我们用例的唯一解决方案是通过框架提供的缓存机制在本地缓存表模式。这要求我们每次更新数据库模式时都运行重新缓存脚本,并且如果在没有运行此脚本的情况下进行模式更改,则会大量运行应用程序。但是,它已经从我们的应用程序中删除了这个性能瓶颈,只需删除在正常使用期间运行查询的需要。