MyISAM 和 KPTI – Meltdown 修复带来的性能影响
最近我们收到一位用户的报告,他在将服务器升级到带有 KPTI(内核页表隔离 - Meltdown 漏洞的补救措施)的 Linux 内核后,看到了惊人的 90% 性能退步。
这 90% 的很大一部分是由于在旧版本的 VMware 中运行引起的,旧版本 VMware 不会将 CPU 的 PCID 和 INVPCID 功能传递给访客机。但我即使在裸金属上也能重现大约 40% 的性能退步。
这是测试用例
CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT, c2 INT) ENGINE=InnoDB; INSERT INTO t1 (c2) VALUES (FLOOR(1000*RAND())); INSERT INTO t1 (c2) SELECT FLOOR(1000*RAND()) FROM t1; -- repeat last insert until there are at least 1024 rows in t1 SELECT COUNT(*) FROM t1 AS a JOIN t1 AS b WHERE b.c1>a.c1 AND b.c2<a.c1;
让我们看看在非 KPTI 内核和 KPTI 内核下,对 t1 表中不同行数执行最终 SELECT 查询的执行时间。
行数 | 非 KPTI | KPTI | 性能退步 |
---|---|---|---|
1024 | 0.40 s | 0.64 s | 37.5% |
2048 | 1.24 s | 1.94 s | 36.1% |
4096 | 4.22 s | 7.05 s | 40.1% |
8192 | 16.10 s | 26.92 s | 40.2% |
为什么?
如果我们查看 handler 状态变量,可以看到对于 8K 行,查询调用 Handler_read_rnd_next 超过 5000 万次。对于 MyISAM,这样的 handler 调用会导致 fget() 调用,进而导致一次 __fget 系统调用。
这是因为 MyISAM 引擎没有行缓存。虽然它在 Key Buffer 中缓存索引页,但没有用于行数据的此类缓存。为此,它依赖于操作系统中的通用页缓存。这运行得很好,然而由于该缓存位于内核中,因此在 MariaDB 服务器和缓存之间存在系统调用壁垒。
KPTI 引入的页表隔离增加了系统调用的开销。因此,像上述那样在一个紧密循环中读取许多 MyISAM 行的工作负载会显著变慢。当行已经在页缓存中时,相对减慢实际上更大!
如何解决
在 MyISAM 引擎中无法修复此问题,因为它需要彻底重新设计。但好消息是,大多数其他引擎确实有行缓存。对于 InnoDB 而言是 InnoDB Buffer Pool,对于 ARIA 而言是 ARIA Page Cache。
为我们的表使用不同的引擎可以消除性能退步。以下是使用默认 128M 页缓存的 ARIA 的数字
行数 | 非 KPTI | KPTI |
---|---|---|
1024 | 0.18 s | 0.18 s |
2048 | 0.57 s | 0.57 s |
4096 | 1.85 s | 1.84 s |
8192 | 6.34 s | 6.30 s |
结论
MyISAM 和 KPTI 配合得不好。如果您正在对 MyISAM 表运行表扫描工作负载,那么您可能会看到 KPTI 修复 Meltdown 带来的相当严重的性能影响。切换到 ARIA 或 InnoDB,并确保行缓存足够大。
还有一个相关的帖子,关于性能退步以及 PCID 在 4.14 内核中带来的差异,链接如下。对于在整个技术栈中经历性能退步的人来说可能有用
http://www.brendangregg.com/blog/2018-02-09/kpti-kaiser-meltdown-performance.html