基准测试期间有趣的 MySQL 行为
Posted
技术标签:
【中文标题】基准测试期间有趣的 MySQL 行为【英文标题】:Interesting MySQL behavior during benchmarking 【发布时间】:2019-01-06 12:34:13 【问题描述】:我试图了解在数据填充后立即选择数据和几分钟后选择数据之间 mysql 性能的巨大差异。
我写的代码是:
使用整数主键、一些 varchar(50) 列创建五个表,并且对于其中四个表 - 前一个表的整数外键。
用随机数据填充表,例如每个表 10k 行。
然后,它通过两种方法从所有表中选择数据:
方法 #1:使用 LEFT OUTER JOIN,例如:
SELECT SQL_NO_CACHE
Bench1.id AS a_id, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
Bench2.id AS b_id, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10,
Bench3.id AS c_id, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
Bench4.id AS d_id, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10,
Bench5.id AS e_id, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10
FROM Bench1
LEFT OUTER JOIN Bench2 ON Bench2.bench1Id = Bench1.id
LEFT OUTER JOIN Bench3 ON Bench3.bench2Id = Bench2.id
LEFT OUTER JOIN Bench4 ON Bench4.bench3Id = Bench3.id
LEFT OUTER JOIN Bench5 ON Bench5.bench4Id = Bench4.id
WHERE Bench1.id IN (342, 452, 81, 405, ...)
方法 #2:使用五个单独的 SELECT 查询,例如:
SELECT SQL_NO_CACHE id, a1, a2, a3, ... FROM Bench1
WHERE id IN (342, 452, 81, 405, ...)
SELECT SQL_NO_CACHE id, b1, b2, b3, ... FROM Bench2 WHERE bench1Id IN (...)
SELECT SQL_NO_CACHE id, c1, c2, c3, ... FROM Bench3 WHERE bench2Id IN (...)
SELECT SQL_NO_CACHE id, d1, d2, d3, ... FROM Bench4 WHERE bench3Id IN (...)
SELECT SQL_NO_CACHE id, e1, e2, e3, ... FROM Bench5 WHERE bench4Id IN (...)
...
两种方法产生相同的信息(但显然由于重复数据,JOIN 的响应更大)。
现在是有趣的部分。 性能上似乎存在巨大差异,具体取决于数据填充后执行 SELECT 的时间。
如果我填充数据,等待 10 分钟,然后运行基准测试,我会得到非常一致的结果,其中 JOIN 方法比多查询慢 40%。
但是,如果我填充数据然后立即运行基准测试 - JOIN 会比多个查询慢几百倍(慢 500 倍很常见,我也看到它慢了 1000 多倍)。
我还要提到,多查询性能似乎(显着)不受人口等待时间的影响。这似乎只影响 JOIN。
我还尝试颠倒 SELECT 的顺序(即在 JOIN 之前执行多查询 SELECT)——这没有任何区别。
我能够在本地 MySQL 5.7 安装以及 AWS RDS MySQL 上重现此行为(在 EC2 上运行代码时)。
谁能解释这种行为?插入大量数据后几分钟内会发生什么,从而极大地影响性能?
我考虑过某种后台重建或索引的优化,但如果是这样,为什么它不会影响多查询方法?这些查询依赖于相同的索引...
更新:
附加信息:CREATE TABLE、SHOW TABLE STATUS、innodb_buffer_pool_size 和 RAM 大小:
创建表
CREATE TABLE Bench1 (
id int(11) NOT NULL,
a1 varchar(50) DEFAULT NULL,
a2 varchar(50) DEFAULT NULL,
a3 varchar(50) DEFAULT NULL,
PRIMARY KEY (id)
)
CREATE TABLE Bench2 (
id int(11) NOT NULL,
bench1Id int(11) DEFAULT NULL,
b1 varchar(50) DEFAULT NULL,
b2 varchar(50) DEFAULT NULL,
b3 varchar(50) DEFAULT NULL,
PRIMARY KEY (id),
KEY bench1Id (bench1Id),
CONSTRAINT Bench2_ibfk_1 FOREIGN KEY (bench1Id) REFERENCES Bench1 (id)
)
CREATE TABLE Bench3 (
id int(11) NOT NULL,
bench2Id int(11) DEFAULT NULL,
c1 varchar(50) DEFAULT NULL,
c2 varchar(50) DEFAULT NULL,
c3 varchar(50) DEFAULT NULL,
PRIMARY KEY (id),
KEY bench2Id (bench2Id),
CONSTRAINT Bench3_ibfk_1 FOREIGN KEY (bench2Id) REFERENCES Bench2 (id)
)
CREATE TABLE Bench4 (
id int(11) NOT NULL,
bench3Id int(11) DEFAULT NULL,
d1 varchar(50) DEFAULT NULL,
d2 varchar(50) DEFAULT NULL,
d3 varchar(50) DEFAULT NULL,
PRIMARY KEY (id),
KEY bench3Id (bench3Id),
CONSTRAINT Bench4_ibfk_1 FOREIGN KEY (bench3Id) REFERENCES Bench3 (id)
)
CREATE TABLE Bench5 (
id int(11) NOT NULL,
bench4Id int(11) DEFAULT NULL,
e1 varchar(50) DEFAULT NULL,
e2 varchar(50) DEFAULT NULL,
e3 varchar(50) DEFAULT NULL,
PRIMARY KEY (id),
KEY bench4Id (bench4Id),
CONSTRAINT Bench5_ibfk_1 FOREIGN KEY (bench4Id) REFERENCES Bench4 (id)
)
`
显示表格状态
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
Bench1 InnoDB 10 Dynamic 500 163 81920 0 0 0 2019-01-06 21:36:39 2019-01-06 21:36:39 utf8_general_ci
Bench2 InnoDB 10 Dynamic 4964 320 1589248 0 147456 4194304 2019-01-06 21:36:39 2019-01-06 21:36:39 utf8_general_ci
Bench3 InnoDB 10 Dynamic 25045 147 3686400 0 540672 4194304 2019-01-06 21:36:39 2019-01-06 21:36:40 utf8_general_ci
Bench4 InnoDB 10 Dynamic 49914 136 6832128 0 1589248 4194304 2019-01-06 21:36:39 2019-01-06 21:36:41 utf8_general_ci
Bench5 InnoDB 10 Dynamic 49259 138 6832128 0 1589248 4194304 2019-01-06 21:36:39 2019-01-06 21:36:42 utf8_general_ci
显示像“innodb_buffer_pool_size”这样的变量
变量名值 innodb_buffer_pool_size 25769803776
内存大小
32GB(使用 AWS RDS db.m4.2xlarge)
更新 2:
附加信息:解释
方法一:
EXPLAIN
SELECT SQL_NO_CACHE Bench1.id AS a_id, a1, a2, a3, Bench2.id AS b_id, b1, b2, b3, Bench3.id AS c_id, c1, c2, c3, Bench4.id AS d_id, d1, d2, d3, Bench5.id AS e_id, e1, e2, e3 FROM Bench1
LEFT OUTER JOIN Bench2 ON Bench2.bench1Id = Bench1.id
LEFT OUTER JOIN Bench3 ON Bench3.bench2Id = Bench2.id
LEFT OUTER JOIN Bench4 ON Bench4.bench3Id = Bench3.id
LEFT OUTER JOIN Bench5 ON Bench5.bench4Id = Bench4.id
WHERE Bench1.id IN (27, 315, 429, 371, 126, 104, 3, 176, 376, 128)
产量:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE Bench1 range PRIMARY PRIMARY 4 10 100.00 Using where
1 SIMPLE Bench2 ref bench1Id bench1Id 5 pm.Bench1.id 9 100.00
1 SIMPLE Bench3 ref bench2Id bench2Id 5 pm.Bench2.id 4 100.00
1 SIMPLE Bench4 ref bench3Id bench3Id 5 pm.Bench3.id 2 100.00
1 SIMPLE Bench5 ALL bench4Id 49860 100.00 Using where; Using join buffer (Block Nested Loop)
方法二:
(我缩短了Bench2
(以及其他)查询的IN()
参数列表,但EXPLAIN
结果是完整查询的结果。
EXPLAIN SELECT SQL_NO_CACHE id, a1, a2, a3 FROM Bench1 WHERE id IN (271, 480, 422, 431, 256, 491, 440, 496, 225, 456);
EXPLAIN SELECT SQL_NO_CACHE id, b1, b2, b3 FROM Bench2 WHERE bench1Id IN (225, 256, 271, 422, 431, 440, 456, 480, 491, 496);
EXPLAIN SELECT SQL_NO_CACHE id, c1, c2, c3 FROM Bench3 WHERE bench2Id IN (323, 402, 1254, 1378, 1965, 2153, 2245, 2518, 2756);
EXPLAIN SELECT SQL_NO_CACHE id, d1, d2, d3 FROM Bench4 WHERE bench3Id IN (3429, 6746, 13014, 18942, 24579, 2269, 6805, 6850);
EXPLAIN SELECT SQL_NO_CACHE id, e1, e2, e3 FROM Bench5 WHERE bench4Id IN (36481, 40044, 11505, 4504, 20798, 4520, 48448, 24305);
产量:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE Bench1 range PRIMARY PRIMARY 4 10 100.00 Using where
1 SIMPLE Bench2 range bench1Id bench1Id 5 96 100.00 Using index condition
1 SIMPLE Bench3 range bench2Id bench2Id 5 484 100.00 Using index condition
1 SIMPLE Bench4 range bench3Id bench3Id 5 966 100.00 Using index condition
1 SIMPLE Bench5 ALL bench4Id 49860 100.00 Using where
【问题讨论】:
id
是 PRIMARY KEY
吗?请提供SHOW CREATE TABLE
、SHOW TABLE STATUS
、innodb_buffer_pool_size
和 RAM 大小。
@RickJames id
是 PRIMARY KEY
。我添加了您要求的所有信息。感谢您查看此内容!
嗯?根据docs.aws.amazon.com/AmazonRDS/latest/UserGuide/…,是32GiB,不是3.5?
@RickJames 你是对的......我从“可用内存”图中得到了数字,我想这是错误的。我不确定如何实际检查这个,但实例确实是db.m4.2xlarge
,所以我想它有 32GB...
AWS 擅长根据 RAM 设置 buffer_pool_size —— 24G 与 32G 是“一致的”。这些是“小”表。 (还在琢磨中……)
【参考方案1】:
在 5.7.4 中,eq_range_index_dive_limit
默认值从 10(在 5.6.5 中引入时)提高到 200。这会影响 IN()。
请根据 IN(list)
中用于 Bench5 的项目数来试验该数字。我想你会发现改变的“原因”。
【讨论】:
所以这可以解释为什么IN()
会比JOIN 表现更好,对吧?但在这种情况下,Bench5 查询的性能难道不会明显变差吗?另外,为什么在填充后立即运行 JOIN 和几分钟后运行它的性能差异如此之大?以上是关于基准测试期间有趣的 MySQL 行为的主要内容,如果未能解决你的问题,请参考以下文章