Mysql Innodb 性能 - 如何最小化多列索引?

Mysql Innodb Performance - How to minimise multicolumn index?

下面table包含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;

Select
为 select 行创建了四个多列索引,其中

具有以下条件

1) c2 和时间
例如:select c1, c5 from Sample1 where c2 = 'sometext' order by time limit 30;

2) c2 和 c3 和时间
例如:select c1, c5 from Sample1 where c2 = 'sometext' and c3 = int order by time limit 30;

3) c2 和 c4 和时间
例如:select c1, c5 from Sample1 where c2 = 'sometext' and c4 = int order by time limit 30;

4) c2 和 c3 和 c4 和时间
例如: select c1, c5 from Sample1 where c2 = 'sometext' and c3 = int and c4 = int order by时限30;

为了使上面select更快,创建了四个多列索引。

c2、c3 和 c4 的基数非常低。 (例如:在一百万个 c2 中,c3 和 c4 各有 100 个唯一列)。

分布也不均。 c2 中的每个组的行数不均匀。 (例如:c2 = 1 包含 100000,c2 = 2 包含 1500000 等等)

列时间(以毫秒为单位的时间戳)主要包含唯一字段。

Select正常发生(一小时10到30次,但应该是高速的)

插入
插入非常频繁。
但是它会依次处理(一个接一个)。

更新
所有更新都基于 C1(主键)。 (频率水平:20% 插入)
更新 Sample1 set c3 = INT, c4 = INT, time = CurrentTimeInMilliSecond where c1 = INT

表格有 5 个索引字段(4 个多列)。由于这个
1) 索引字段的插入和更新变得更昂贵
2) 随着 table 不断增长(可能达到 1 亿),索引大小也增长得更快

请在 mysql 中提出解决此用例的好方法。

其他必要的细节
innodb_buffer_pool_size:16106127360(15GB);
CPU Core:32;
RAM:32GB

警告:TMI 即将到来。我不得不做一些猜测;如果您提供更多详细信息,我可以更具体...

您拥有的 4 个辅助键最适合您列出的 4 个查询。

基数,与流行的妻子故事相反,与复合索引和 SELECT 性能无关。

在 100M 行时,table(包括索引)可能是 20GB。你有多少内存? innodb_buffer_pool_size 的值是多少?除非你有一个小 RAM,否则这些可能无关紧要。

返回'cardinality'。

让我们看看 INDEX(c2, Time),其中 c2 有 100 个不同的值,而 Time 本质上是 ever-increasing。每个新的 INSERT 都会将新行放在 100 个位置之一——每个 c2 块的末端。这意味着 100 "hot spots",并且意味着 100 个块(大部分)足以处理更新这个索引。 100 个区块 = buffer_pool 的 1.6MB——希望是一小部分。

同时,PRIMARY KEYAUTO_INCREMENT,所以有一个热点和一个块——更小的分数。

但是...其他 3 个辅助键将有更多的热点(块),因此它们可能更重要。让我们来看看最糟糕的(c2, c3, c4, Time)。暂定会有 100*100*100 个热点。但我认为这将超过整个索引中的块。 (所以,数学分崩离析了。)所以那会很忙。

暂时说个题外话……你在一个事务中INSERT有多少行?有多少 rows/second? innodb_flush_log_at_trx_commit (flatc) 的值是多少?好吧,让我们将其简化为一次完全刷新一行与一批刷新大量行。

回到计算...

在一个极端:小 buffer_pool and single-row 事务 and flatc=1 and HDD:您将需要一些 IOP。我希望你不需要插入超过 20 rows/second.

另一个极端:大缓冲池and batching and flatc=2 and SSD:平均小于 1 IOPs。您可能每秒可以处理超过 1000 行的插入。

规范化 c2 可能会将 20GB 估计值减半,从而在计算中进行多项调整。

回到 SELECTs -- 您真的为给定的 c2 获取 10 万行吗?如果你有更多的过滤,ORDERingLIMITing等,请展示它们;它可能会对该分析产生重大影响。

回到标题 -- 我还没有看到 change/minimize 这些索引的任何有用方法。它们似乎 非常SELECTs 有用,并且对 INSERTs.

的危害最小 ]

哦,UPDATEs。在考虑那里的后果之前,我们需要先查看 UPDATEs 上的 WHERE 子句。

更多(经过多次更新提问)

PRIMARY KEY(c1) 负责使 UPDATEs 尽可能快(除了需要最终更新索引)。

SELECTs很少见;我的索引使每个 运行 和 'possible'

一样快 15GB 的

Buffer_pool 表示整个 table 及其所有索引都将存在于池中(一旦预热)——对于当前的 1000 万行。在 100M 行时,它 可能 仍然可以。我这样说是因为可能导致流失的查询是SELECTs,但他们都说AND Time > ...。这意味着 "working set" 是 table 的 "end"。如果您达到十亿行,则需要重新访问此段落。

MySQL 应该能够每天处理一百万 INSERTs,即使是在最差的设置下。因此,如果您不希望在 3 个月内更快地获得 100M 行,我认为 INSERTs 不是问题。