Mysql Innodb 性能 - 如何最小化多列索引?
Posted
技术标签:
【中文标题】Mysql Innodb 性能 - 如何最小化多列索引?【英文标题】:Mysql Innodb Performance - How to minimise multicolumn index? 【发布时间】:2017-05-13 11:50:53 【问题描述】:下表包含1000万行,
CREATE TABLE Sample1 (
c1 bigint(20) NOT NULL AUTO_INCREMENT,
c2 varchar(45) NOT NULL,
c3 tinyint(4) NOT NULL DEFAULT 0,
c4 tinyint(4) NOT NULL DEFAULT 0,
c5 varchar(45) DEFAULT NULL,
time bigint(20) DEFAULT NULL,
PRIMARY KEY (c1),
KEY varchar_time_idx (c2,Time),
KEY varchar_c3_time_idx (c2,c3,Time),
KEY varchar_c4_time_idx (c2,c4,Time),
KEY varchar_c3_c4_time_idx (c2,c3, c4,Time)
) ENGINE=InnoDB AUTO_INCREMENT=10093495 DEFAULT CHARSET=utf8;
选择 创建了四个多列索引以在 where 中选择具有以下条件的行
1) c2 和时间 例如:从 Sample1 中选择 c1、c5,其中 c2 = 'sometext' order by time limit 30;
2) c2 和 c3 以及时间 例如:从 Sample1 中选择 c1、c5,其中 c2 = 'sometext' and c3 = int order by time limit 30;
3) c2 和 c4 以及时间 例如:从 Sample1 中选择 c1、c5,其中 c2 = 'sometext' 和 c4 = int 按时间限制 30 排序;
4) c2 和 c3 和 c4 和时间 例如:从 Sample1 中选择 c1、c5,其中 c2 = 'sometext' and c3 = int and c4 = int order by time limit 30;
为了使上面的选择更快,创建了四个多列索引。
基数明智的 c2、c3 和 c4 非常低。 (例如:在一百万个 c2 中,c3 和 c4 各有 100 个唯一列)。
同样分布不均。 c2 中的每个组的行数都是奇数。 (例如:c2 = 1 包含 100000,c2 = 2 包含 1500000 等等)
列时间(以毫秒为单位的时间戳)主要包含唯一字段。
选择正常发生(一小时10到30次,但应该是高速)
插入 插入非常频繁。 但它是按顺序处理的(一个接一个)。
更新 所有更新基于 C1(主键)。 (频率水平:插入时为 20%) 更新 Sample1 设置 c3 = INT,c4 = INT,时间 = CurrentTimeInMilliSecond 其中 c1 = INT
表格有 5 个索引字段(4 个多列)。由于这个 1) 索引字段的插入和更新变得更昂贵 2)随着表的不断增长(可能达到1亿),索引大小也增长得更快
请在 mysql 中提出解决此用例的好方法。
其他必要的细节 innodb_buffer_pool_size:16106127360(15 GB); CPU 核心:32; 内存:32GB
【问题讨论】:
实际上有 10,093,495 行 @RiggsFolly 你遇到过同样的情况吗? 请向我们展示所有需要这些索引的查询。我们应该首先讨论您是否可以摆脱或合并某些索引。 10M 只是十亿的 1/100。也许表“将增长到 1B”?你有多少内存?innodb_buffer_pool_size
的设置是什么?当INSERTing
时,c2
的值有多随机? time
本质上是当前时间吗? (这些问题会导致您判断INSERTs
在您到达 1B 时的表现。)
如果c2
具有低基数,那么它可能应该被规范化并替换为INT
(和UNSIGNED
)的某种风格。
【参考方案1】:
注意:TMI 即将到来。我不得不做一些猜测;如果您提供更多详细信息,我可以更具体...
您拥有的 4 个辅助键最适合您列出的 4 个查询。
与流行的妻子故事相反,基数与复合索引和SELECT
性能无关。
在 100M 行时,表(包括索引)可能为 20GB。你有多少内存? innodb_buffer_pool_size
的值是多少?除非您的 RAM 很小,否则这些可能无关紧要。
回到“基数”。
让我们看一下INDEX(c2, Time)
,其中c2
有100 个不同的值,Time
基本上在不断增加。每个新的INSERT
都会将新行放在 100 个位置之一——每个 c2 簇的末端。这意味着 100 个“热点”,这意味着 100 个块(大部分)足以处理更新这一索引。 100 个块 = 1.6MB 的 buffer_pool——希望是一小部分。
同时,PRIMARY KEY
是AUTO_INCREMENT
,所以有一个热点和一个区块——甚至更小。
但是...其他 3 个辅助键将有更多的热点(块),因此它们可能更重要。让我们去最糟糕的(c2, c3, c4, Time)
。试探性地,那将有 100*100*100 个热点。但我认为这将超过整个索引中的块。 (所以,数学崩溃了。)所以那会很忙。
题外话...你INSERT
在一个事务中有多少行?多少行/秒? innodb_flush_log_at_trx_commit
(flatc) 的值是多少?好吧,让我们将其简化为一次完全刷新的一行,而不是批量刷新的许多行。
回到计算...
在一个极端:小型缓冲池和单行事务和 flatc=1 和 HDD:您将需要一些IOP。我希望你不需要插入超过 20 行/秒。
在另一个极端:大型缓冲池和批处理和 flatc=2 和 SSD:平均不到 1 IOPS。您可能每秒可以处理超过 1000 行插入。
标准化 c2
可能会将 20GB 估计值减少一半,从而对计算进行多次调整。
回到SELECTs
——你真的为给定的c2
获取了100K行吗?如果你有更多的过滤,ORDERing
,LIMITing
等,请给他们看;这可能会对分析产生重大影响。
回到标题——我还没有看到任何有用的方法来更改/最小化这些索引。它们似乎对SELECTs
非常有用,而对INSERTs
的危害最小。
哦,UPDATEs
。在考虑那里的后果之前,我们需要查看 UPDATEs
上的 WHERE
子句。
更多(经过多次更新问题)
PRIMARY KEY(c1)
负责使UPDATEs
尽可能快(除了需要最终更新索引)。
SELECTs
非常罕见;我的索引使每次运行都尽可能快
15GB 的 Buffer_pool 表示整个表及其所有索引都将存在于池中(一旦它被预热)——对于当前的 10M 行。在 100M 行时,它可能仍然可以。我这样说是因为可能导致流失的查询是SELECTs
,但他们都说AND Time > ...
。这意味着一个“工作集”,它是表的“末端”。如果你达到十亿行,这一段需要重新审视。
MySQL 应该能够每天处理一百万个INSERTs
,即使是最差的设置。因此,如果您不希望在 3 个月内获得 100M 行,我认为INSERTs
不是问题。
【讨论】:
感谢里克·詹姆斯。更新更多细节。以上是关于Mysql Innodb 性能 - 如何最小化多列索引?的主要内容,如果未能解决你的问题,请参考以下文章