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 / 问题 / 284029
Accepted
Dirk Boer
Dirk Boer
Asked: 2021-01-27 09:13:37 +0800 CST2021-01-27 09:13:37 +0800 CST 2021-01-27 09:13:37 +0800 CST

通过对包含列的索引进行大量更新来节省性能

  • 772
Points
------------------
PK  QuestionId  int (+30.000.000 distinct values)
PK  EventId     int (large batches where 80.000 rows have the same EventId)
    Value       smallint

该表大约有 4000 万行,并且存在性能问题。

主要有两个查询:

在QuestionId上:

  • 大约 3000 万个不同QuestionId的值(变化很大)
  • 繁忙时间有很多查询(每分钟数千次)

在EventId上:

  • 将有 +150.000 行的批量更新where EventId=X来设置Value=NULL在非常忙碌的时刻。

所以我首先想到的要获得最佳性能是我制作EventId,QuestionId了ClusteredIndex,以便批量更新可以轻松找到所有靠近彼此的EventId并直接更新值。

我的第二个想法是添加一个包含QuestionId和包含列 Value的索引,以便它可以直接从索引中读取值(EventId在这种情况下无关紧要)。

但后来我想:聚集索引是否重要?由于索引中的包含列值也需要在批量更新期间进行更新。

  • 虽然不牺牲查询性能 - 是否可以快速(几秒钟)获得批量更新,或者我是否必须接受这个过程在不升级硬件的情况下总是很慢。
  • 任何其他想法设置 ClusteredIndex / 索引的最佳方法是什么?

我知道理论上我应该测试一切并衡量它,但该网站是活跃的并且被大量使用。

我是一名独立开发人员,我没有资源聘请某人。任何估计的猜测和想法都会非常有帮助,因为这已经给了我正确的方向!

sql-server performance
  • 1 1 个回答
  • 494 Views

1 个回答

  • Voted
  1. Best Answer
    bbaird
    2021-01-27T09:45:17+08:002021-01-27T09:45:17+08:00

    因此,如果您的主要访问路径有问题,那么最有意义的唯一聚集索引将是(QuestionId, EventId)。

    添加第二个索引EventId可能没有用,因为索引可能没有足够的选择性,并且查询引擎将决定读取整个表而不是做大量工作来读取它的大部分更快。

    或者,如果您始终完全或部分基于 查询EventId,则 的聚集索引(EventId,Questionid)更合适,并且具有使您的更新基于EventId需要更少的 I/O 来完成的额外好处。

    我不会包含Value附加索引,因为这实际上会复制整个表(只是聚集在不同的列上),并且您的更新将需要更长的时间,因为Value必须在聚集索引和附加索引之间保持同步。

    在某个时候没有免费的午餐,正确的解决方案可能是选择具有支持最多用例的前导列的聚集索引,然后添加 RAM/CPU/更快的存储来处理整个表(或大它的块)必须被读取或写入。有 4000 万行和如此狭窄的表格,我无法想象这是更多 RAM无法解决的问题。

    根据您的 SQL Server 版本,您还可以查看页面压缩是否会显着减小表大小,因为这会减少对磁盘的读取/写入次数(额外的 CPU 开销被更少的磁盘操作所抵消)。在你的情况下,我的猜测是它不会,但它正在寻找。

    因此,如果我理解正确(以目前的知识),您的直觉会是聚集索引QuestionId,EventId,然后是 批量更新的正常索引?EventId

    仅当主要用途是返回特定QuestionIds而不考虑EventId. 您可以尝试 中的附加索引EventId,但您可能会发现它并不经常(或根本不)用于更新(或更新仍然需要比您想要的更长的时间),具体取决于 EventIds 在您的数据中的分布方式到QuestionId.

    您还必须确定总体上对您更重要的是什么 - 选择性能或更新性能。如果更新是痛点,(EventId,QuestionId)无疑会是更好的选择。鉴于 的唯一值的数量QuestionId,在该列上添加索引可能对SELECT性能有用,但这将取决于QuestionId分布方式以及您一次搜索的数量。

    在任何一种情况下,保持最新统计数据都是至关重要的。

    一个非常简单的例子(为了完整起见):

    假设我们有一个 DBMS,它维护一个聚集索引并每页存储 4 行。我们有一个主键为 的表(QuestionId, EventId)和一个附加列,Value。

    如果我们将聚集索引创建为(QuestionId, EventId),我们想象中的 DBMS 中的数据(粗略地说)存储如下:

    Page | QuestionId | EventId | Value
    -----------------------------------
    A    | 1          | 2       | ...
    A    | 1          | 3       | ...
    A    | 1          | 6       | ...
    A    | 1          | 7       | ...
    B    | 1          | 8       | ...
    B    | 1          | 10      | ...
    B    | 1          | 11      | ...
    B    | 2          | 2       | ...
    C    | 3          | 2       | ...
    C    | 4          | 1       | ...
    C    | 5          | 6       | ...
    C    | 5          | 7       | ...
    D    | 6          | 1       | ...
    D    | 7          | 2       | ...
    D    | 7          | 6       | ...
    D    | 7          | 8       | ...
    

    因此,如果我需要执行基于 的操作QuestionId,引擎将不必读取不必要的页面。

    但是,如果我需要执行基于 的操作EventId,我将不得不读取整个表(聚集索引扫描),除非我添加一个额外的索引,它看起来像这样(并且需要四页):

    EventId | QuestionId
    --------------------
    1       | 4
    1       | 6
    2       | 1
    2       | 2
    2       | 3
    2       | 7
    3       | 1
    6       | 1
    6       | 5
    6       | 7
    7       | 1
    7       | 5
    8       | 1
    8       | 7
    10      | 1
    11      | 1
    

    这个索引对某些人来说是选择性的EventIds,但在极端情况下(EventId = 2)仍然需要读取整个表,并且对于某些情况(EventId = 6)我们的优化器可能会决定搜索索引和读取表比读取整个表更昂贵桌子。

    如果我们改为聚集在EventId, QuestionId我们的表上,如下所示:

    Page | EventId | QuestionId | Value
    -----------------------------------
    A    | 1        | 4         | ...
    A    | 1        | 6         | ...
    A    | 2        | 1         | ...
    A    | 2        | 2         | ...
    B    | 2        | 3         | ...
    B    | 2        | 7         | ...
    B    | 3        | 1         | ...
    B    | 6        | 1         | ...
    C    | 6        | 5         | ...
    C    | 6        | 7         | ...
    C    | 7        | 1         | ...
    C    | 7        | 5         | ...
    D    | 8        | 1         | ...
    D    | 8        | 7         | ...
    D    | 10       | 1         | ...
    D    | 11       | 1         | ...
    

    任何基于 的操作EventId都只会读取表的必要部分,并且像我们的第一个实例一样,任何基于 的操作QuestionId都需要扫描而不需要额外的索引。如果我们在 上创建索引QuestionId,则索引将是:

    QuestionId | EventId
    --------------------
    1          | 2
    1          | 3
    1          | 6
    1          | 7
    1          | 8
    1          | 10
    1          | 11
    2          | 2
    3          | 2
    4          | 1
    5          | 6
    5          | 7
    6          | 1
    7          | 2
    7          | 6
    7          | 8
    

    因此,与第一个示例一样,该索引对某些问题更有用,而对其他问题则不太有用。因为QuestionId = 1优化器可能会说读取一半索引然后查找一半表的成本不值得,并且只会读取整个表而不是使用索引。

    如果我们包含Value在索引中,我们现在必须在同一个事务中更改表和索引。最好的情况是,这会使任何操作的工作加倍。在最坏的情况下,这需要读取整个表或索引(它只是表的副本)并可能锁定。

    现在可以使用您的实际数据添加额外的索引QuestionId或EventId将提供很多好处。但这并不能解决所有问题,而且插入/更新/删除的开销可能不值得。

    • 8

相关问题

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

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

  • 我在哪里可以找到mysql慢日志?

  • 如何优化大型数据库的 mysqldump?

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