AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 163189
Accepted
pacreely
pacreely
Asked: 2017-02-04 13:15:18 +0800 CST2017-02-04 13:15:18 +0800 CST 2017-02-04 13:15:18 +0800 CST

Simple Query 优化后变为 Serial,需要 Parallel

  • 772

我继承了一个具有支持 Web 应用程序的存储过程的遗留系统。它每天运行数千次。该应用程序允许用户输入部分客户详细信息,并且底层存储过程将执行 LIKE 命令以将可能客户的候选名单返回给 Web 应用程序。

它甚至允许部分输入客户 ID(整数值),因此如果将值“123”输入到应用程序中,则存储过程应返回 ID 包含“123”的所有客户,即“612345”或“222123” '.....是的,我知道疯了,输入'1'会返回一个巨大的数据集,但我们无法更改应用程序。

搜索客户 ID 是存储过程中最慢的部分。我已经“优化”了存储过程(在 DEV 中),因此它使用更少的 IO 和更少的 CPU ......但它已经串行化并且运行时间太长......帮助!

这是重新创建问题的方法。

--CREATE TEST TABLE
CREATE TABLE dbo.Test(ID INT IDENTITY(1,1) PRIMARY KEY
                        , Val Float
                        , CodeVal CHAR(100)
                        )
GO

--GENERATE A FEW MILLION TEST RECORD
INSERT INTO dbo.Test
SELECT
    RAND()
    ,CONVERT(varchar(255), NEWID())
FROM 
    sys.objects --Contains 639 Rows
GO 10000

--CREATE INDEX ON ID FIELD
CREATE UNIQUE NONCLUSTERED INDEX idx ON dbo.Test(ID)

现有查询执行并行索引扫描。

DECLARE @ID VARCHAR(20) = '123456'

--DROP TABLE #TMP1
CREATE TABLE #TMP1(ID INT)

INSERT INTO #TMP1
SELECT  ID 
FROM    dbo.Test
WHERE   CONVERT(VARCHAR(20),ID) LIKE '%'+@ID+'%'

然后我意识到,如果用户输入的部分客户 ID 为“123456”,那么结果集永远不会包含小于“123456”的值。所以我在代码中添加了一个额外的行(见下文),现在我有一个 INDEX SEEK,它减少了 IO 并减少了 CPU 时间。但令我恐惧的是,它已经消失了串行所以现在它需要永远。如果上线,那么用户的等待时间将从 4 秒跳到 20 秒。

INSERT INTO #TMP1
SELECT  ID 
FROM    dbo.Test
WHERE   CONVERT(VARCHAR(20),ID) LIKE '%'+@ID+'%'
AND     ID >= @ID   --New line of code

我现在已经到了盲目编码的地步,希望它能并行。

谁能解释为什么会发生这种情况(除了“优化器认为它更好”的股票答案)?

谁能弄清楚如何使查询并行?

跟踪标志 8649 对我来说不是一个选项。

我已经阅读了Paul White的这篇非常有帮助的文章。

更新:

看起来提供的示例会根据系统规范和测试表上的行数的组合产生不同的结果。

抱歉,如果您无法重现问题。但这是我的问题答案的一部分。

更新:(执行计划)

Aaron:很抱歉,如果它在您的系统上不起作用,会令人沮丧。(由于信息安全的原因,我无法分享我公司的实际执行计划)

我已经在我的家庭系统上重新创建了这个问题。

这是执行计划,(我将完整上传到计划后) 在此处输入图像描述

我当前的 dbo.Test 行数是 2124160,非聚集索引有 2627 页和 0.04% 碎片,下面是我的统计信息。

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.
Table 'Test'. Scan count 3, logical reads 2652, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 688 ms,  elapsed time = 368 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 2 ms.
Table 'Test'. Scan count 1, logical reads 1106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 281 ms,  elapsed time = 302 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

与客户打交道的呼叫中心工作人员每天使用存储过程大约 5000 次。增加 16 秒的等待时间将花费员工每天 22 多小时等待结果。以 8 小时的轮班时间计算,这相当于 3 名全职员工每年的成本约为 36,000 英镑。有钱升级到 SQL Sentry 许可证(大粉丝),我们目前有 Spotlight(仍然是一个很好的产品),但我想要更深入的东西。

更新:(幻数)

有 12,000 行,两个查询都是串行的。

对于 2,124,160 行,一个查询是并行的。

66,000,000 行都是并行的。

优化器肯定有一个规则/指标使用,如果你有一个索引扫描或索引搜索,它是不同的。这是否意味着将查询从 Scan 转换为 Seek 会对特定大小的表/索引产生不利影响?

更新:(解决方案)

谢谢乔,您对最大并行度非常了解。

我用了:

OPTION (OPTIMIZE FOR (@ID  = 1))

UNKNOWN 对我的环境没有作用。

对于任何对此感兴趣的人,这里是Kendra Little的一篇有用的文章。

sql-server t-sql
  • 1 1 个回答
  • 1550 Views

1 个回答

  • Voted
  1. Best Answer
    Joe Obbish
    2017-02-04T15:19:20+08:002017-02-04T15:19:20+08:00

    我怀疑您遇到了并行参数的成本阈值问题。

    使用并行性成本阈值选项来指定 Microsoft SQL Server 为查询创建和运行并行计划的阈值。仅当为同一查询运行串行计划的估计成本高于并行性成本阈值中设置的值时,SQL Server 才会为查询创建并运行并行计划。

    较长的查询通常受益于并行计划;性能优势抵消了初始化、同步和终止并行计划所需的额外时间。当同时运行短查询和长查询时,会积极使用并行选项的成本阈值。短查询运行串行计划,而较长查询使用并行计划。并行性的成本阈值决定了哪些查询被认为是短的,因此它们应该使用串行计划运行。

    在我的测试机器cost threshold for parallelism上,默认值为 5。下面是MAXDOP 1旧查询和新查询的成本表:

    ╔═════════╦═══════════════════╦═══════════════════╗
    ║  Rows   ║ Cost of Old Query ║ Cost of New Query ║
    ╠═════════╬═══════════════════╬═══════════════════╣
    ║   12000 ║ 0.037717          ║ 0.0199238         ║
    ║ 1610200 ║ 6.28954           ║ 1.86659           ║
    ║ 4610200 ║ 17.9959           ║ 6.46108           ║
    ╚═════════╩═══════════════════╩═══════════════════╝
    

    正如预期的那样,有 1610200 行的旧查询是并行的,但新查询是串行的。这是因为 DOP 1 成本高于 5,因此符合并行计划的条件。SQL Server 估计并行计划的成本较低,因此它采用并行方式。有 4610200 行,两个查询并行。您可能会在机器上看到略有不同的结果。

    有两件事不利于您在这里获得并行计划。首先是 SQL Server 无法知道此查询对您的业务至关重要,并且您希望向其投入尽可能多的资源。它不知道您的估计,您的组织可能会损失数万美元。它只是将其视为另一个廉价/琐碎的查询。第二个是查询优化器将始终为查询分配相同的估计成本,而不管@ID. 它使用默认估计值,因为您正在使用变量。你可以通过提示来解决这个RECOMPILE问题,但如果这每天运行数千次,那可能不是最好的主意。

    我的建议是使用OPTIMIZE FOR查询提示来增加聚集索引搜索的估计行数。这将增加计划的成本,并使其更有可能使用并行计划。来自BOL:

    优化 (@variable_name { UNKNOWN | = literal_constant } [ , ...n ] )

    指示查询优化器在编译和优化查询时使用局部变量的特定值。该值仅在查询优化期间使用,而不在查询执行期间使用。

    @变量的名称

    是查询中使用的局部变量的名称,可以为其分配一个值以与 OPTIMIZE FOR 查询提示一起使用。

    文字常量

    是要分配给 @variable_name 的文字常量值,用于 OPTIMIZE FOR 查询提示。literal_constant 仅在查询优化期间使用,而不是在查询执行期间作为 @variable_name 的值。literal_constant 可以是可以表示为文字常量的任何 SQL Server 系统数据类型。literal_constant 的数据类型必须隐式转换为 @variable_name 在查询中引用的数据类型。

    对于您的查询,您可以优化'1'. 这应该会大大增加计划的成本。事实上,在我的测试中,这使得查询与没有过滤器的旧查询具有相同的估计成本。这是有道理的,因为 SQL Server 将无法过滤掉任何带有ID > '1'. 如果您使用此提示,您应该会遇到与代码更改之前相同的并行/串行行为。

    DECLARE @ID VARCHAR(20) = '123456';
    
    CREATE TABLE #TMP1 (ID INT);
    
    INSERT INTO #TMP1 -- cost is 96.8547
    SELECT  ID 
    FROM    dbo.Test
    WHERE   CONVERT(VARCHAR(20),ID) LIKE '%'+@ID+'%'
    AND     ID >= @ID  
    OPTION (OPTIMIZE FOR (@ID = '1'));
    

    查询计划

    请注意,如果查询变得更复杂,此提示可能会产生负面影响。例如,人为地夸大估计的行数可能会导致大于必要的查询内存授予。如果您的生产数据足够大以至于不需要提示,我建议您不要使用它。如果您在生产中的查询与问题中列出的查询不同,请在使用前仔细测试。

    • 1

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve