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 KEY
是 AUTO_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 万行吗?如果你有更多的过滤,ORDERing
,LIMITing
等,请展示它们;它可能会对该分析产生重大影响。
回到标题 -- 我还没有看到 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
不是问题。
下面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 KEY
是 AUTO_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 万行吗?如果你有更多的过滤,ORDERing
,LIMITing
等,请展示它们;它可能会对该分析产生重大影响。
回到标题 -- 我还没有看到 change/minimize 这些索引的任何有用方法。它们似乎 非常 对 SELECTs
有用,并且对 INSERTs
.
哦,UPDATEs
。在考虑那里的后果之前,我们需要先查看 UPDATEs
上的 WHERE
子句。
更多(经过多次更新提问)
PRIMARY KEY(c1)
负责使 UPDATEs
尽可能快(除了需要最终更新索引)。
SELECTs
很少见;我的索引使每个 运行 和 'possible'
Buffer_pool 表示整个 table 及其所有索引都将存在于池中(一旦预热)——对于当前的 1000 万行。在 100M 行时,它 可能 仍然可以。我这样说是因为可能导致流失的查询是SELECTs
,但他们都说AND Time > ...
。这意味着 "working set" 是 table 的 "end"。如果您达到十亿行,则需要重新访问此段落。
MySQL 应该能够每天处理一百万 INSERTs
,即使是在最差的设置下。因此,如果您不希望在 3 个月内更快地获得 100M 行,我认为 INSERTs
不是问题。