MySQL 服务器性能受制于整个系统最薄弱的环节,承载它的操作系统和硬件往往是限制因素。磁盘大小、可用内存和 CPU 资源、网络,以及所有连接它们的组件,都会限制系统的最终容量。
因此,需要小心地选择硬件,并对硬件和操作系统进行合适的配置。
本文选自MySQL圣经级著作《高性能MySQL(第3版)》一书。硬件的更新换代非常迅速,我们的目标是帮助提升对这些概念的理解,这样对于即使没有直接覆盖到的知识也可以举一反三。
什么限制了 MySQL 的性能
许多不同的硬件都可以影响 MySQL 的性能,但我们认为最常见的两个瓶颈是 CPU 和 I/ O 资源。当数据可以放在内存中或者可以从磁盘中以足够快的速度读取时,CPU 可能出现瓶颈。把大量的数据集完全放到大容量的内存中,以现在的硬件条件完全是可行的。注 1
另一方面,I/O 瓶颈,一般发生在工作所需的数据远远超过有效内存容量的时候。如果应用程序是分布在网络上的,或者如果有大量的查询和低延迟的要求,瓶颈可能转移到网络上,而不再是磁盘 I/O 。注 2
虽然一些技巧可以帮助找到系统的限制因素,但即使你认为已经找到了瓶颈,也应该透过表象去看更深层次的问题。某一方面的缺陷常常会将压力施加在另一个子系统, 导致这个子系统出问题。
例如,若没有足够的内存,MySQL 可能必须刷出缓存来腾出 空间给需要的数据——然后,过了一小会,再读回刚刚刷新的数据(读取和写入操作都 可能发生这个问题)。本来是内存不足,却导致出现了 I/O 容量不足。当找到一个限制系 统性能的因素时,应该问问自己,“是这个部分本身的问题,还是系统中其他不合理的 压力转移到这里所导致的?”
还有另外一个例子 :内存总线的瓶颈也可能表现为 CPU 问题。事实上,我们说一个应用程序有“CPU 瓶颈”或者是“CPU 密集型”,真正的意思应该是计算的瓶颈。接下来将深入探讨这个问题。
注 1 :普通 PC Server 也能配到 192GB 内存。 注 2 :网络吞吐也是一种 I/O。
如何为 MySQL 选择 CPU
在升级当前硬件或购买新的硬件时,应该考虑下工作负载是不是 CPU 密集型。
可以通过检查 CPU 利用率来判断是否是 CPU 密集型的工作负载,但是仅看 CPU 整体的 负载是不合理的,还需要看看 CPU 使用率和大多数重要的查询的 I/O 之间的平衡,并注意 CPU 负载是否分配均匀。
▊ 哪个更好:CPU 选更快还是更多?
当遇到 CPU 密集型的工作时,MySQL 通常可以从更快的 CPU 中获益(相对更多的 CPU)。
但这不是绝对的,因为还依赖于负载情况和 CPU 数量。更古老的 MySQL 版本在多 CPU 上有扩展性问题,即使新版本也不能对单个查询并发利用多个 CPU。因此,CPU 速度限制了每个 CPU 密集型查询的响应时间。
当我们讨论 CPU 的时候,为保证本文易于阅读,对某些术语将不会做严格的定义。现在一般的服务器通常都有多个插槽(Socket),每个插槽上都可以插一个有多个核心的 CPU(有独立的执行单元),并且每个核心可能有多个“硬件线程”。这些复杂的架构需 要有点耐心去了解,并且我们不会总是明确地区分它们。不过,在一般情况下,当谈到 CPU 速度的时候,谈论的其实是执行单元的速度,当提到的 CPU 数量时,指的通常是在操作系统上看到的数量,尽管这可能是独立的执行单元数量的多倍。 注 3
注 3 :超线程技术。
这几年 CPU 在各个方面都有了很大的提升。例如,今天的 Intel CPU 速度远远超过前几代, 这得益于像直接内存连接(directly attached memory)技术以及 PCIe 卡之类的设备互联 上的改善等。这些改进对于存储设备尤其有效,例如 Fusion-io 和 Virident 的 PCIe 闪存 驱动器。
超线程的效果相比以前也要好得多,现在操作系统也更了解如何更好地使用超线程。而以前版本的操作系统无法识别两个虚拟处理器实际上是在同一芯片上,认为它们是独立 的,于是会把任务安排在两个实际上是相同物理执行单元上的虚拟处理器。实际上单个执行单元并不是真的可以在同一时间运行两个进程,所以这样做会发生冲突和争夺资源。而同时其他 CPU 却可能在闲置,从而浪费资源。操作系统需要能感知超线程,因为它必 须知道什么时候执行单元实际上是闲置的,然后切换相应的任务去执行。这个问题之前 常见的原因是在等待内存总线,可能花费需要高达一百个 CPU 周期,这已经类似于一个 轻量级的 I/O 等待。新的操作系统在这方面有了很大的改善。超线程现在已经工作得很好。过去,我们时常提醒人们禁用它,但现在已经不需要这样做了。
这就是说,现在可以得到大量的快速的 CPU。所以多和快哪个更重要?一般来说两个都想要。
从广义上来说,调优服务器可能有如下 两个目标 :
- 低延时(快速响应)
要做到这一点,需要高速 CPU,因为每个查询只能使用一个 CPU。
- 高吞吐
如果能同时运行很多查询语句,则可以从多个 CPU 处理查询中受益。然而,在实践中, 还要取决于具体情况。因为 MySQL 还不能在多个 CPU 中完美地扩展,能用多少 个 CPU 还是有极限的。在旧版本的 MySQL 中(MySQL 5.1 以后的版本已经有一些提升),这个限制非常严重。在新的版本中,则可以放心地扩展到 16 或 24 个 CPU, 或者更多,取决于使用的是哪个版本(Percona 往往在这方面略占优势)。
如果有多路 CPU,并且没有并发执行查询语句,MySQL 依然可以利用额外的 CPU 为后 台任务(例如清理 InnoDB 缓冲、网络操作,等等)服务。然而,这些任务通常比执行 查询语句更加轻量化。
MySQL 复制也能在高速 CPU 下工作得非常好,而多 CPU 对复制 的帮助却不大。如果工作负载是 CPU 密集型,主库上的并发任务传递到备库以后会被简 化为串行任务,这样即使备库硬件比主库好,也可能无法保持跟主库之间的同步。也就是说,备库的瓶颈通常是 I/O 子系统,而不是 CPU。
如果有一个 CPU 密集型的工作负载,考虑是需要更快的 CPU 还是更多 CPU 的另外一个因素是查询语句实际在做什么。在硬件层面,一个查询可以在执行或等待。处于等待状 态常见的原因是在运行队列中等待(进程已经是可运行状态,但所有的 CPU 都忙)、等待闩锁(Latch)或锁(Lock)、等待磁盘或网络。那么你期望查询是等待什么呢?如果 等待闩锁或锁,通常需要更快的 CPU ;如果在运行队列中等待,那么更多或者更快的 CPU 都可能有帮助。(也可能有例外,例如,查询等待 InnoDB 日志缓冲区的 Mutex,直 到 I/O 完成前都不会释放——这可能表明需要更多的 I/O 容量)。
这就是说,MySQL 在某些工作负载下可以有效地利用很多 CPU。例如,假设有很 多连接查询的是不同表(假设这些查询不会造成表锁的竞争,实际上对 MyISAM 和 MEMORY 表可能会有问题),并且服务器的总吞吐量比任何单个查询的响应时间都更重要。吞吐量在这种情况下可以非常高,因为线程可以同时运行而互不争用。
再次说明,在理论上这可能更好地工作 :不管查询是读取不同的表还是相同的表, InnoDB 都会有一些全局共享的数据结构,而 MyISAM 在每个缓冲区都有全局锁。而且不仅仅是存储引擎,服务器层也有全局锁。以前 InnoDB 承担了所有的骂名,但最近 做了一些改进后,暴露了服务器层中的其他瓶颈。例如臭名昭著的 LOCK_open 互斥量 (Mutex),在 MySQL 5.1 和更早版本中可能就是个大问题,另外还有其他一些服务器级 别的互斥量(例如查询缓存)。
通常可以通过堆栈跟踪来诊断这些类型的竞争问题,例如 Percona Toolkit 中的 pt-pmp 工 具。如果遇到这样的问题,可能需要改变服务器的配置,禁用或改变引起问题的组件, 进行数据分片(Sharding),或者通过某种方式改变做事的方法。这里无法列举所有的问 题和相应的解决方案,但是一旦有一个确定的诊断,答案通常是显而易见的。大部分不 幸遇到的问题都是边缘场景,最常见的问题随着时间的推移都在服务器上被修复了。
▊ CPU 架构
可能 99% 以上的 MySQL 实例(不含嵌入式使用)都运行在 Intel 或者 AMD 芯片的 x86 架构下。
64 位架构现在都是默认的了,32 位 CPU 已经很难买到了。MySQL 在 64 位架构上工作良好,尽管有些事暂时不能利用 64 位架构来做。因此,如果使用的是较老旧版本的 MySQL,在 64 位服务器上可能要小心。例如,在 MySQL 5.0 发布的早期时候,每个 MyISAM 键缓冲区被限制为 4 GB,由一个 32 位整数负责寻址。(可以创建多个键缓冲 区来解决这个问题。)
确保在 64 位硬件上使用 64 位操作系统!最近这种情况已经不太常见了,但以前经常可 以遇到,大多数主机托管提供商暂时还是在服务器上安装 32 位操作系统,即使是 64 位 CPU。32 位操作系统意味着不能使用大量的内存 :尽管某些 32 位系统可以支持大量的 内存,但不能像 64 位系统一样有效地利用,并且在 32 位系统上,任何一个单独的进程 都不能寻址 4 GB 以上的内存。
▊ 扩展到多个 CPU 和核心
多 CPU 在联机事务处理(OLTP)系统的场景中非常有用。这些系统通常执行许多小的 操作,并且是从多个连接发起请求,因此可以在多个 CPU 上运行。在这样的环境中,并 发可能成为瓶颈。大多数 Web 应用程序都属于这一类。
OLTP 服务器一般使用 InnoDB,尽管它在多 CPU 的环境中还存在一些未解决的并发问题。然而,不只是 InnoDB 可能成为瓶颈 :任何共享资源都是潜在的竞争点。InnoDB 之所以 获得大量关注是因为它是高并发环境下最常见的存储引擎,但 MyISAM 在大压力时的表 现也不好,即使不修改任何数据只是读取数据也是如此。许多并发瓶颈,如 InnoDB 的 行级锁和 MyISAM 的表锁,没有办法优化——除了尽可能快地处理任务之外,没有别的 办法解决,这样,锁就可以尽快分配给等待的任务。如果一个锁是造成它们(其他任务) 都在等待的原因,那么不管有多少 CPU 都一样。因此,即使是一些高并发工作负载,也 可以从更快的 CPU 中受益。
实际上有两种类型的数据库并发问题,需要不同的方法来解决,如下所示。
- 逻辑并发问题
应用程序可以看到资源的竞争,如表或行锁争用。这些问题通常需要好的策略来解决, 如改变应用程序、使用不同的存储引擎、改变服务器的配置,或使用不同的锁定提 示或事务隔离级别。
- 内部并发问题
比如信号量、访问 InnoDB 缓冲池页面的资源争用,等等。可以尝试通过改变服务 器的设置、改变操作系统,或使用不同的硬件解决这些问题,但通常只能缓解而无 法彻底消灭。在某些情况下,使用不同的存储引擎或给存储引擎打补丁,可以帮助 缓解这些问题。
MySQL 的“扩展模式”是指它可以有效利用的 CPU 数量,以及在压力不断增长的情 况下如何扩展,这同时取决于工作负载和系统架构。通过“系统架构”的手段是指通 过调整操作系统和硬件,而不是通过优化使用 MySQL 的应用程序。CPU 架构(RISC、 CISC、流水线深度等)、CPU 型号和操作系统都影响 MySQL 的扩展模式。这也是为什 么说基准测试是非常重要的 :一些系统可以在不断增加的并发下依然运行得很好,而另 一些的表现则糟糕得多。
有些系统在更多的处理器下甚至可能降低整体性能。这是相当普遍的情况,我们了解到 许多人试图升级到有多个 CPU 的系统,最后只能被迫恢复到旧系统(或绑定 MySQL 进 程到其中某些核心),因为这种升级反而降低了性能。在 MySQL 5.0 时代,Google 的补 丁和 Percona Server 出现之前,能有效利用的 CPU 核数是 4 核,但是现在甚至可以看到 操作系统报告多达 80 个“CPU”的服务器。如果规划一个大的升级,必须要同时考虑硬件、 服务器版本和工作负载。
某些 MySQL 扩展性瓶颈在服务器层,而其他一些在存储引擎层。存储引擎是怎么设计 的至关重要,有时更换到一个不同的引擎就可以从多处理器上获得更多效果。
我们看到在世纪之交围绕处理器速度的战争在一定程度上已经平息,CPU 厂商更多地专 注于多核 CPU 和多线程的变化。CPU 设计的未来很可能是数百个处理器核心,四核心 和六核心的 CPU 在今天是很常见的。不同厂商的内部架构差异很大,不可能概括出线程、 CPU 和内核之间的相互作用。内存和总线如何设计也是非常重要的。归根结底,多个内 核和多个物理 CPU 哪个更好,这是由硬件体系结构决定的。
现代 CPU 的另外两个复杂之处也值得提一下。首先是频率调整。这是一种电源管理技术, 可以根据 CPU 上的压力而动态地改变 CPU 的时钟速度。问题是,它有时不能很好地处 理间歇性突发的短查询的情况,因为操作系统可能需要一段时间来决定 CPU 的时钟是否 应该变化。结果,查询可能会有一段时间速度较慢,并且响应时间增加了。频率调整可 能使间歇性的工作负载性能低下,但可能更重要的是,它会导致性能波动。
第二个复杂之处是 boost 技术,这个技术改变了我们对 CPU 模式的看法。我们曾经以为 四核 2GHz CPU 有四个同样强大的核心,不管其中有些是闲置或非闲置。因此,一个完 美的可扩展系统,当它使用所有四个内核的时候,可以预计得到四倍的提升。但是现在 已经不是这样了,因为当系统只使用一个核心时,处理器会运行在更高的时钟速度上, 例如 3GHz。这给很多的规划容量和可扩展性建模的工具出了一个难题,因为系统性能 表现不再是线性的变化了。这也意味着,“空闲 CPU”并不代表相同规模的资源浪费, 如果有一台服务器上只运行了备库的复制,而复制执行是单线程的,所以有三个 CPU 是 空闲的,因此认为可以利用这些 CPU 资源执行其他任务而不影响复制,可能就想错了。