【问题标题】:SQL Server performance with many concurrent, long-running queriesSQL Server 性能与许多并发、长时间运行的查询
【发布时间】:2019-01-20 01:31:59
【问题描述】:

我想知道同时执行多个长时间运行的查询将如何影响 SQL Server 及时为每个查询提供服务的能力。

[编辑]

我并不想含糊其辞,这更像是一种假设。让我们假设查询是在具有数百万行的表上带有某种谓词的 select 语句。

【问题讨论】:

  • 我想知道我什么时候能中奖。你不觉得这有点模糊。
  • 这个问题不够具体,无法在不猜测提问者的意思的情况下回答。上述问题的答案显然是“这将对它及时为每个查询提供服务的能力产生负面影响”,但也许提问者也有别的意思......
  • 我们在这里讨论的是什么类型的工作负载?典型的 OLTP 组合或繁重的读取活动,也许是数据分析?无论哪种方式,如果您可以向我们提供更多细节,我们都可以为您提供具体的指导。
  • 我认为这是一个完全有效且明确的问题。它是关于性能如何随着同时查询的数量而扩展。
  • @the-locster:你不能说没有更多信息。我们不是在谈论在桌面上运行客户端代码线程,而是在数据库引擎中使用声明性语言语句

标签: sql-server concurrency


【解决方案1】:

CPU

到达服务器的每个请求(即每个“批次”)都将与一个“任务”相关联,请参阅sys.dm_os_tasks。该任务在“调度程序”上排队,粗略地说是一个 CPU 内核,请参阅sys.dm_os_schedulers。每个调度程序都有几个“工作人员”(即线程或光纤,请参阅sys.dm_os_workers),一个空闲工作人员将从调度程序的队列中获取下一个任务并“逃跑”,执行它直到任务完成(即。请求已完成)。这种调度机制适用于 SQL 内部的一切,包括系统任务、CLR 运行代码等等。

可以创建的任务数量受可用内存的限制。请求(“批处理”)并不等同于一对一的任务,因为一些请求一旦开始就会安排更多任务执行,并行查询是典型的例子。系统中的工作人员数量是动态的,但受“max worker threads”配置设置的限制。如果达到工作人员上限,则新的计划任务将在调度程序中排队,但直到工作人员释放(完成任务)并变得可用时才会被拾取。当达到此条件时,称为“工人饥饿”并导致服务器无响应,因为新客户端登录握手需要执行登录任务(服务器似乎拒绝连接)并且现有客户端的新请求将在等待任务之后排队(服务器需要很长时间来响应琐碎的请求)。

因此,如果您有大量并行、长时间运行的查询,您将消耗大量工作人员来执行许多长时间运行的任务。这减少了空闲工作人员池的大小,导致可用于服务于服务器的其他短任务(如 OLTP 请求、登录握手等)的工作人员减少。服务器似乎没有响应,因为任务在调度程序队列中堆积(这可以在sys.dm_os_schedulers DMV work_queue_count 列中看到)。在极端情况下,您可以有效地使工作人员系统处于饥饿状态,使服务器完全没有响应,直到一些工作人员空闲为止。

内存

包含并行操作的查询计划通常与大索引(大表)的全扫描相关联。扫描索引是通过遍历其叶页来完成的,读取大表中的所有叶页意味着所有这些页必须在查询执行期间一次或一次地存在于内存中。这反过来又会从缓冲池中产生对空闲页面的需求,以容纳扫描的页面。对空闲页面的需求会产生内存压力,导致通知缓存开始驱逐旧条目,并删除缓冲池中旧访问的数据页面。缓存通知可以在sys.dm_os_memory_cache_clock_hands 中看到。可以通过检查良好的 ole'Page Life Expectancy 性能计数器来控制数据页驱逐。

驱逐缓存条目的效果是,下次需要被驱逐的条目(作为编译计划、权限令牌或其他)时,它必须从头开始创建,导致消耗更多的 CPU、内存和 IO,即使在长时间运行的查询完成后,效果也会显现出来。

现在的情况可能是,您的系统安装了如此庞大的 RAM,以至于扫描几个大表并没有什么区别,您的 RAM 可以容纳整个数据库并有剩余空间。在这种情况下没有问题。但大多数时候情况并非如此。

IO

这与上面的一点(记忆)有关。所有为满足索引扫描而读取的页面都必须传输到内存中,这意味着(可能很大)一部分 IO 带宽被长时间运行的查询所消耗。此外,所有从缓冲池中清除的脏数据页都必须写入磁盘,从而导致更多的 IO。而且被驱逐的干净页面可能会在未来某个时间被需要,因此需要更多的 IO。

如果扫描生成的 IO 超出系统带宽,则 IO 操作开始在磁盘控制器上排队。这可以在Physical Disk/Avg Queue Length 性能计数器中轻松检查。

争用

最后,最大的问题:锁争用。如前所述,并行查询几乎总是意味着表扫描。表扫描在它们访问的每一行上都使用一个共享锁。确实,一旦在正常操作模式下读取记录,他们就会释放锁,但您仍然保证您将在表中的每一行上请求 S 锁时间>。这几乎可以保证这些扫描将命中被更新锁定 X 的行。当发生这种情况时,扫描必须停止并等待 X 锁被释放,这发生在更新事务最终提交时。结果是即使表上的适度 OLTP 活动也会阻止长时间运行的查询。理想情况下,这就是所有发生的事情,结果只是性能不佳。但是,如果长时间运行的查询做了一些花哨的事情,比如获取页锁而不是行锁,事情就会变得很糟糕。由于这些扫描是端到端遍历索引,并且保证会与更新发生冲突,因此这些查询获取的更高粒度的锁不再只是与更新锁发生冲突,而是实际上会导致死锁。解释这是如何发生的超出了本回复的重点。

为了消除争用,当查询合法进行全面扫描时,最好的替代方法是使用神奇快照:为报告创建database snapshots,或使用snapshot isolation 级别。请注意,有些人可能会建议使用脏读,我还没有找到实际可以接受的情况。

【讨论】:

    【解决方案2】:

    我会使用execution plan 来了解如何优化查询。您还想知道,如果它们需要很长时间,它们可能会lock 行或表。要回答这个问题,您需要考虑您的 sql server 可以处理多少内存和 CPU 功率。在开发环境中对其进行测试可以让您很好地了解将要发生的事情,查询时间越长,占用的资源就越多,它们可能会成为整个系统的瓶颈。

    【讨论】:

      【解决方案3】:

      很难说。

      • 大量并行查询?
      • 成千上万的小?
      • OLTP 还是仓库?
      • CPU 或 IO 或内存受限?
      • 服务器硬件和设置? MAXDOP、RAID 等
      • 同一组数据? (在缓冲池中或大量的内存数据搅动)

      我们有 1 亿行表,在工作时间多次运行低于 1 秒的聚合查询,以及 10,000 行表查询需要 20 秒但只在凌晨 4 点运行一次。

      【讨论】:

        【解决方案4】:

        显然,运行的查询越多,性能就会越慢。

        范围取决于数据和查询类型(更新/删除/插入?)。

        锁定表的查询可能特别有问题;在适当的情况下使用 nolock 可以提高性能。

        【讨论】:

          【解决方案5】:

          除非您开始遇到严重的 i/o 问题,否则性能通常会线性下降。

          但是,证明是单独和并行测试您的查询,并监控 SQL Server 统计信息。

          【讨论】:

          • 1 和两个查询之间的性能比较通常会给出非线性关系,也就是说,如果两个查询都需要访问慢速外部存储,则所需时间将远远超过原始查询的 2 倍。那时,磁盘“抖动”会大大减慢这两个查询。从那里开始,性能下降可能是线性的。
          • 我是根据数百次计时的经验发言的。我们有一个 2 分钟的查询,这是我们数据库中 i/o 最密集的查询。如果我们并行运行大约需要 4 分钟。请注意,我对我的声明进行了限定,“除非您开始遇到重大的 i/o 问题。”我对你的观点提出异议,如果存在严重的分页问题,​​它只需要超过 2 倍的时间。如果数据库有足够的内存资源,则可能不会发生这种情况。使用 64 位 8GB RAM 服务器,您可能必须非常努力地尝试引发分页问题。这取决于您的应用程序。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-23
          • 1970-01-01
          • 2019-07-09
          • 1970-01-01
          相关资源
          最近更新 更多