【问题标题】:SQL Server 2012 join perfomance features?SQL Server 2012 加入性能特点?
【发布时间】:2013-06-17 10:11:57
【问题描述】:

考虑一个简单的 3 表数据库 i SQL Server 2012。

表 A

AId
Name
Other1
Other2

表 B

BId
Name

表 A_B​​h2>
BId
AId

简单示例查询:

SELECT TOP(20) A.Aid, A.Name, B.Bid, B.Name 
FROM A 
INNER JOIN A_B ON A.AId = A_B.Aid
INNER JOIN A as AA ON AA.Aid = A_B.Aid
INNER JOIN B ON B.BId = A_B.Bid
WHERE AA.Aid = @aid
AND A.Other1 = @other1

表 A 中有数百万行。
表 B 中有数千行。
表 A_B 中的行数是表 A 的十倍。
Other1 和 Other2 字段可用于过滤查询。 使用 Top(20) 的连接查询可以以每秒 100 个或更多请求的速度完成(规格尚不清楚)。 查询几乎总是使用不同的参数,因此结果缓存不会有太大帮助。

SQL Server 2012 中的哪些功能可以帮助提高上述示例的联接查询性能?

我最初的想法是,由于都是 PK int 连接,因此我无能为力。但是我不知道分区视图是否有帮助。
我在想可能只是增加内存。

【问题讨论】:

  • 一个非常模糊的问题。索引/索引视图。
  • 如何改进问题?
  • 提供实际查询将是一个开始。
  • 您的意思是AND B.Other1 = @other1?此外,您是否只需要任何TOP 20,或者您是否需要基于特定订单的前 20 个?数据多久更改一次? (一个可能的答案涉及覆盖索引,当它发布时我可能会赞成。)
  • 另外,数据的选择性如何(即 A 中的行与 A 中 Other1 的不同值的比率)?例如,“Male”或“Female”的值不是选择性的,数百万行中的 50 个州不是很选择性,LastName 可能非常选择性。

标签: sql sql-server tsql join sql-server-2012


【解决方案1】:

首先要了解(可能不是第一个)是所有当前版本都内置了一个性能模型,该模型取决于磁头寻道时间与连续读取,这可能会随着固态驱动器而改变。您选择的聚集索引对于将可能经常查询的数据保持在一起很重要。此外,查询的每个部分都有一个覆盖索引,这意味着可以在不读取表本身的情况下访问数据。分区可能会有所帮助(但它可能在列表中很长的路要走)。保持最新的统计数据是必不可少的。性能不佳通常来自维护不足的索引和统计信息。实际上,所有这些事情在 SQL7 中都是正确的(除了我认为 SQL7 没有分区视图)。拥有正确的 RAID 结构可以将性能改变 4 倍。 tempdb 的数量应等于处理器的数量(最多约 16 个),并且 tempdb 负载平衡选项应设置为 true。将 Tempdbs、日志和数据分布在不同的 i/os 上。没有自动收缩 - 它的邪恶。这些是比较明显的。如果您真的想掌握大型数据库,那么 Kalen Delany 的“Inside SQL”几乎是必读的,尽管可能需要更多 GB 的 RAM。正如你所说 - 更多的内存。

【讨论】:

  • 谢谢!有很多东西要消化。 :) 我会确保我能拿到那本书。
  • 这是那些在 SQL Stack 上的 johnies 早餐吃的东西,但恕我直言,任何设计 dbSoftware 的人都应该有更多的知识,所以恕我直言,这么大的优点指向你。随意标记为答案,我的答案只是对其他人所写书籍的概括,并为真正理解而收取大笔费用。
  • 事后一点,但sswug.org/editorials/readed.aspx?id=2824 可能证明是一个有用的线程
【解决方案2】:

首先是有PK的聚集索引

如果表 B 小于 Int16,则使用 Int16
不是为了磁盘空间,而是为了相同内存量中的更多行

有趣的部分是表 A_B
该 PK 的顺序可能会影响性能
仅针对第二个的单个 PK 索引将是一个较慢的连接

尝试每种方式的顺序
检查查询计划
检查调优顾问

我的想法是
PK AId,BId
基于该索引的 BId 上的非聚集索引更小

然后将它们翻转并比较
如果相同,则使用 AId、BId 以获得更小的索引大小和插入速度

然后你可以进入关于连接的提示

定期进行碎片整理

按PK顺序插入

如果数据以自然顺序出现并且插入速度存在问题,则使用该顺序进行 PK

如果插入速度有问题,那么禁用非聚集索引,插入,然后重建非聚集索引可能会有所帮助

百万和数千仍然不是巨大的。

我不会这样写查询
保持人数减少

SELECT TOP(20) A.Aid, A.Name, B.Bid, B.Name 
  FROM A_B 
  JOIN A  
    ON A.Aid = A_B.Aid
  JOIN B 
    ON B.BId = A_B.Bid
 WHERE AA.Aid = @aid
   AND A.Other1 = @other1

那个查询很浪费
为什么加入所有 A.Aid = A_B.Aid 以过滤到 where
中的单个 AA.Aid 让过滤器提前执行

这可能会更好

SELECT TOP(20) A.Aid, A.Name, B.Bid, B.Name 
  FROM A_B 
  JOIN A  
    ON A.Aid = A_B.Aid
   AND A.Aid = @aid 
   AND A.Other1 = @other1
  JOIN B 
    ON B.BId = A_B.Bid

如果您可以在它加入之前对其进行过滤,那么工作量就会减少
检查查询计划

具有条件的 A 上的 CTE 可能会强制它首先执行过滤器。

如果您无法通过单个语句首先进行过滤,则创建一个#tempA,其 ID 作为声明的 PK
(不是 CTE,目的是实现)

Insert into #tempA 
select Id, Name 
  from Table A 
 where A.Aid = @aid 
   AND A.Other1 = @other1

如果 Id 是表 A 上的 PK,则该查询返回 0 或 1 条记录
#tempA 的连接很简单

【讨论】:

  • 谢谢!你让我想到了连接,实际上我可能可以减少到一个连接,但将其改为针对 A_B 的 IN() 查询。我将使用查询分析器,看看如何获​​得最佳执行路径。谢谢!
  • 您需要来自 A 和 B 的数据,并且您认为可以将其缩减为针对表 A_B 的一个联接。祝你好运。
  • 要么我写错了查询,要么你误解了它。无论哪种方式都没关系,你让我想到了不同的方法来解决它,我认为我可以在内存中已经有很多数据,并且可能能够使用一组不同的参数。你让我考虑了大量的新选择,这对我来说是最有价值的部分,我真的很感激!
猜你喜欢
  • 1970-01-01
  • 2015-12-22
  • 2012-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多