【问题标题】:Increase MS SQL transaction performance提高 MS SQL 事务性能
【发布时间】:2017-02-15 04:28:47
【问题描述】:

如果您缺少信息,如果需要,我会附上。

工作区

我有一个在这种 MS SQL 2012 标准版上运行的数据库:

  • 表格:

    • 用户(id,softId(不唯一),生日)

      • 行数:1050 万
      • 索引:所有三列,生日(聚集)
    • docs(docId, userId, creationDate, deleteDate, lastname, forename, classificationId)

      • 行数:2300 万

      • 索引:姓氏、名字、docId、creationDate、userID(clustered)

      • 注意:在这种特定情况下,名称与文档相关,而不是与 userId 相关

    • 分类(id,描述)

      • 行数:200
    • 三个表“数据”

      • 行:10、13 和 30 万行
      • 索引:docIds
  • 关系:

    • 用户到文档:1 到 n

    • 文档分类:1 到 n

    • 文档到数据表:1 到 n

要选择完整的记录,我实际上是在以下陈述:

服务器执行时间 16 秒

SELECT * FROM (
    select * from docs 
    where userID in (
        select distinct userID from users where softId like '...'
    )
) as doc
LEFT JOIN users on users.userID = doc.userId
LEFT JOIN classifications on classifications.id = doc.classificationId
LEFT JOIN data1 on data1.docId = doc.docId
LEFT JOIN data2 on data2.docId = doc.docId
LEFT JOIN data3 on data3.docId = doc.docId;

更新 - 现在 15 秒

SELECT
docID, calssificationId, classificationDescription,
userId, softId, forename, lastname, birthdate,
data1.id, data1.date, data2.id, data2.date, data3.id, data3.date,
FROM docs
JOIN users on users.userID = doc.userId AND softId like '...'
LEFT JOIN classifications on classifications.id = doc.classificationId
LEFT JOIN data1 on data1.docId = doc.docId
LEFT JOIN data2 on data2.docId = doc.docId
LEFT JOIN data3 on data3.docId = doc.docId;

执行计划

服务器执行时间 17 秒

DECLARE @userIDs table( id bigint );
DECLARE @docIDs table( id bigint );

insert into @userIDs select userID from users where softId like '...';
insert into @docIDs select docId from docs where userId in ( select id from @userIDs);
SELECT * FROM users where userID in ( select id from @userIDs);
SELECT * FROM docs where docID in (select id from @docIDs);
SELECT * FROM data1 where data1.docId in (select id from @docIDs);
SELECT * FROM data2 where data2.docId in (select id from @docIDs);
SELECT * FROM data3 where data3.docId in (select id from @docIDs);
GO

更新 - 现在 14 秒

DECLARE @userIDs table( id bigint, softId varchar(12), birthdate varchar(8) );
DECLARE @docIDs table( id bigint, classification bigint, capture_date datetime, userId bigint, lastname varchar(50), forename varchar(50) );


INSERT INTO @userIDs select userID, softId, birthdate from users where softId like '...';
INSERT INTO @docIDs select docID, classification, capture_date, userID, lastname, forename from docs where userID in ( select id from @userIDs);

SELECT * FROM @userIDs;
SELECT * FROM @docIDs;

SELECT [only needed fields] FROM data1 where docID in (select id from @docIDs);
SELECT [only needed fields] FROM data2 where docID in (select id from @docIDs);
SELECT [only needed fields] FROM data3 where docID in (select id from @docIDs);

执行计划

一般更新 @AntonínLejsek 建议将文档的 docId 定义为聚集索引,将 pkId 定义为非聚集索引。这改变了执行时间如下:

  • 加入声明:-1 秒
  • 多选语句:-5 秒

我再次检查了索引并更改了包含的列,现在它们有了执行时间:

  • 加入声明:4 秒
  • 多选语句:6 秒

“简单”问题

有人有减少执行时间的建议吗?

【问题讨论】:

  • 亲爱的,第一个:为什么在您的子查询中使用 distinct 从 softId 像“...”的用户中选择不同的用户 ID。 userID 不是 users 表的主键?
  • @JoeTaras :你是对的,不需要区分。我删除了它,但它没有影响。
  • 这里是select * 合适还是您只需要检索特定字段?可以发一下执行计划吗?
  • @alroc 我通过更改其他内容减少了字段数量,但没有更改帖子,抱歉 - 执行计划现在已链接

标签: sql sql-server performance sql-server-2012


【解决方案1】:

我将逻辑表述为:

我会摆脱第一个子查询,只对users 表做必要的工作:

SELECT *
FROM docs JOIN
     users
     ON users.userID = doc.userId AND softId LIKE '...'  LEFT JOIN
     . . .

如果你正在做JOININ 中的逻辑是不必要的。

注意:这可能没有多大帮助,因为您的查询似乎返回了大量数据,包括列和行。

【讨论】:

  • 我按照建议更改了它,现在我们有 15 秒。执行时间并将更新主要帖子的状态 - 谢谢。查询通常返回约 1000 行。
  • userIDusers 上聚集索引中的第一个键吗?如果没有,您将需要一个单独的索引。而且,您应该对所有表的主键都有索引,包括classifications 等。您还应该仔细检查data 表上的索引,并通过一次构建一个left join 的结果集来为查询计时。查询似乎需要 10 多秒。
  • userId 是非聚集的 - 因为我的理解,聚集索引是树,所以在生日上聚集对我来说更有意义,还是这是错误的?所有表都有主键。
  • @coivip 。 . .您需要 users(userId) 成为 some 索引中的第一个键,无论是否聚集。
  • @GordonLinhoff 这是非聚集索引上的第一个
【解决方案2】:

我在计划中看到了两个不同的数据库,我会先尝试在一个数据库中进行测试。

数据库设计很奇怪。您在生日时有聚集索引。由于它不是唯一的,因此数据库必须再补一个4B号才能使其唯一。所以你在每个非聚集索引中都有 12B 键,这在空间和性能上都很低效。您甚至没有将 id 包含在非聚集索引中,因此必须查找它,这很浪费时间。在大多数情况下,您应该聚集在主键上,并且应该是 id。

--已删除-- 虽然 softIds 几乎是唯一的,但这一段变得无关紧要。

【讨论】:

  • 我知道,第二个数据库是一个测试数据库,我在其中合并了一些表以减少查询中的连接。我有 10120000 个 userIds 和 10100000 个 softIds。我将聚集理解为一棵树,所以生日对我来说更有意义,因为它是一个聚集索引,而 userIds 和 softIds 是非聚集的——或者我是否以错误的方式理解了这一点?通常我必须像“123456%”这样搜索 - 最大长度是 12
  • @coivip 谢谢,在这种情况下,将 softId 移动到另一个表不是一个好主意。集群索引请看这里:stackoverflow.com/questions/4332982/…
  • 稍后我会在家里查看链接。我现在不在办公室,直到明天早上才有机会改变这一点。当我进行更改时,我会发布它们。
  • 我在dos中切换了userId和docId之间的索引类型。在join版本它对执行时间没有影响,在多选语句是减少2秒的执行时间
【解决方案3】:

通过定义主键为表变量添加聚集索引。

DECLARE @userIDs table( id bigint primary key, softId varchar(12), birthdate varchar(8) );
DECLARE @docIDs table( id bigint primary key, classification bigint, capture_date datetime, userId bigint, lastname varchar(50), forename varchar(50) );

【讨论】:

  • 相同的执行时间
猜你喜欢
  • 1970-01-01
  • 2011-06-13
  • 2016-01-05
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
  • 2014-02-19
相关资源
最近更新 更多