第9章 操作系统和硬件优化
MySQL服务器性能受制于整个系统最薄弱的环节,承载它的操作系统和硬件往往是限制因素。磁盘大小、可用内存和CPU资源、网络,以及所有连接它们的组件,都会限制系统的最终容量。因此,需要小心地选择硬件,并对硬件和操作系统进行合适的配置。例如,若工作负载是I/O密集型的,一种方法是设计应用程序使得最大限度地减少MySQL的I/O操作。然而,更聪明的方式通常是升级I/O子系统,安装更多的内存,或重新配置现有的磁盘。
硬件的更新换代非常迅速,所以本章有关特定产品或组件的内容可能将很快变得过时。像往常一样,我们的目标是帮助提升对这些概念的理解,这样对于即使没有直接覆盖到的知识也可以举一反三。这里我们将通过现有的硬件来阐明我们的观点。
9.1 什么限制了MySQL的性能#
许多不同的硬件都可以影响MySQL的性能,但我们认为最常见的两个瓶颈是CPU和I/O资源。当数据可以放在内存中或者可以从磁盘中以足够快的速度读取时,CPU可能出现瓶颈。把大量的数据集完全放到大容量的内存中,以现在的硬件条件完全是可行的(1)。
另一方面,I/O瓶颈,一般发生在工作所需的数据远远超过有效内存容量的时候。如果应用程序是分布在网络上的,或者如果有大量的查询和低延迟的要求,瓶颈可能转移到网络上,而不再是磁盘I/O(2)。
第3章中提及的技巧可以帮助找到系统的限制因素,但即使你认为已经找到了瓶颈,也应该透过表象去看更深层次的问题。某一方面的缺陷常常会将压力施加在另一个子系统,导致这个子系统出问题。例如,若没有足够的内存,MySQL可能必须刷出缓存来腾出空间给需要的数据——然后,过了一小会,再读回刚刚刷新的数据(读取和写入操作都可能发生这个问题)。本来是内存不足,却导致出现了I/O容量不足。当找到一个限制系统性能的因素时,应该问问自己,“是这个部分本身的问题,还是系统中其他不合理的压力转移到这里所导致的?”在第3章的诊断案例中也有讨论到这个问题。
还有另外一个例子:内存总线的瓶颈也可能表现为CPU问题。事实上,我们说一个应用程序有“CPU瓶颈”或者是“CPU密集型”,真正的意思应该是计算的瓶颈。接下来将深入探讨这个问题。
9.2 如何为MySQL选择CPU#
在升级当前硬件或购买新的硬件时,应该考虑下工作负载是不是CPU密集型。
可以通过检查CPU利用率来判断是否是CPU密集型的工作负载,但是仅看CPU整体的负载是不合理的,还需要看看CPU使用率和大多数重要的查询的I/O之间的平衡,并注意CPU负载是否分配均匀。本章稍后讨论的工具可以用来弄清楚是什么限制了服务器的性能。
9.2.1 哪个更好:更快的CPU还是更多的CPU#
当遇到CPU密集型的工作时,MySQL通常可以从更快的CPU中获益(相对更多的CPU)。
但这不是绝对的,因为还依赖于负载情况和CPU数量。更古老的MySQL版本在多CPU上有扩展性问题,即使新版本也不能对单个查询并发利用多个CPU。因此,CPU速度限制了每个CPU密集型查询的响应时间。
当我们讨论CPU的时候,为保证本文易于阅读,对某些术语将不会做严格的定义。现在一般的服务器通常都有多个插槽(Socket),每个插槽上都可以插一个有多个核心的CPU(有独立的执行单元),并且每个核心可能有多个“硬件线程”。这些复杂的架构需要有点耐心去了解,并且我们不会总是明确地区分它们。不过,在一般情况下,当谈到CPU速度的时候,谈论的其实是执行单元的速度,当提到的CPU数量时,指的通常是在操作系统上看到的数量,尽管这可能是独立的执行单元数量的多倍(3)。
这几年CPU在各个方面都有了很大的提升。例如,今天的Intel CPU速度远远超过前几代,这得益于像直接内存连接(directly attached memory)技术以及PCIe卡之类的设备互联上的改善等。这些改进对于存储设备尤其有效,例如Fusion-io和Virident的PCIe闪存驱动器。
超线程的效果相比以前也要好得多,现在操作系统也更了解如何更好地使用超线程。而以前版本的操作系统无法识别两个虚拟处理器实际上是在同一芯片上,认为它们是独立的,于是会把任务安排在两个实际上是相同物理执行单元上的虚拟处理器。实际上单个执行单元并不是真的可以在同一时间运行两个进程,所以这样做会发生冲突和争夺资源。而同时其他CPU却可能在闲置,从而浪费资源。操作系统需要能感知超线程,因为它必须知道什么时候执行单元实际上是闲置的,然后切换相应的任务去执行。这个问题之前常见的原因是在等待内存总线,可能花费需要高达一百个CPU周期,这已经类似于一个轻量级的I/O等待。新的操作系统在这方面有了很大的改善。超线程现在已经工作得很好。过去,我们时常提醒人们禁用它,但现在已经不需要这样做了。
这就是说,现在可以得到大量的快速的CPU——比本书的第2版出版的时候要多得多。所以多和快哪个更重要?一般来说两个都想要。从广义上来说,调优服务器可能有如下两个目标:
低延时(快速响应)
要做到这一点,需要高速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),或者通过某种方式改变做事的方法。这里无法列举所有的问题和相应的解决方案,但是一旦有一个确定的诊断,答案通常是显而易见的。大部分不幸遇到的问题都是边缘场景,最常见的问题随着时间的推移都在服务器上被修复了。
9.2.2 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以上的内存。
9.2.3 扩展到多个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资源执行其他任务而不影响复制,可能就想错了。
9.3 平衡内存和磁盘资源#
配置大量内存最大的原因其实不是因为可以在内存中保存大量数据:最终目的是避免磁盘I/O,因为磁盘I/O比在内存中访问数据要慢得多。关键是要平衡内存和磁盘的大小、速度、成本和其他因素,以便为工作负载提供高性能的表现。在讨论如何做到这一点之前,暂时先回到基础知识上来。
计算机包含一个金字塔型的缓存体系,更小、更快、更昂贵的缓存在顶端,如图9-1所示。
图9-1:缓存层级