MySQL的操作系统和硬件优化

MySQL服务器性能受制于整个系统最薄弱的环节,承载它的操作系统和硬件往往是限制因素。磁盘大小、可用内存和CPU资源、网络,以及所有连接它们的组件,都会限制系统的最终容量。因此,需要小心地选择硬件,并对硬件和操作系统进行合适的配置。例如,若工作负载是I/O密集型的,一种方法是设计应用程序使得最大限度地减少MySQL的I/O操作。然而,更聪明的方式通常是升级I/O子系统,安装更多的内存,或重新配置现有的磁盘。

  硬件的更新换代非常迅速,所以本章有关特定产品或组件的内容可能将很快变得过时。像往常一样,我们的目标是帮助提升对这些概念的理解,这样对于即使没有直接覆盖到的知识也可以举一反三。这里我们将通过现有的硬件来阐明我们的观点。

1.什么限制了MySQL的性能

  许多不同的硬件都可以影响MySQL的性能,但我们认为最常见的两个瓶颈是CPU和I/0资源。当数据可以放在内存中或者可以从磁盘中以足够快的速度读取时,CPU可能出现瓶颈。把大量的数据集完全放到大容量的内存中,以现在的硬件条件完全是可行的。

  另一方面,I/O瓶颈,一般发生在工作所需的数据远远超过有效内存容量的时候。如果应用程序是分布在网络上的,或者如果有大量的査询和低延迟的要求,瓶颈可能转移到网络上,而不再是磁盘I/O。

   <服务器性能剖析>中提及的技巧可以帮助找到系统的限制因素,但即使你认为已经找到了瓶颈,也应该透过表象去看更深层次的问题。某一方面的缺陷常常会将压力施加在另一个子系统,导致这个子系统出问题。例如,若没有足够的内存,MySQL可能必须刷出缓存来腾出空间给需要的数据——然后,过了一小会,再读回刚刚刷新的数据(读取和写入操作都可能发生这个问题)。本来是内存不足,却导致出现了I/O容量不足。当找到一个限制系统性能的因素时,应该问问自己,“是这个部分本身的问题,还是系统中其他不合理的压力转移到这里所导致的? ”

  还有另外一个例子:内存总线的瓶颈也可能表现为CPU问题 。事实上,我们说一个应用程序有“CPU瓶颈”或者是“CPU密集型”,真正的意思应该是计算的瓶颈。接下来将深入探讨这个问题。

2.如何为MySQL选择CPU

  在升级当前硬件或购买新的硬件时,应该考虑下工作负载是不是CPU密集型。  

  可以通过检查CPU利用率来判断是否是CPU密集型的工作负载,但是仅看CPU整体的负载是不合理的,还需要看看CPU使用率和大多数重要的査询的I/O之间的平衡,并注意CPU负载是否分配均匀。本章稍后讨论的工具可以用来弄清楚是什么限制了服务器的性能。

2.1 哪个更好:更快的CPU还是更多的CPU

  当遇到CPU密集型的工作时,MySQL通常可以从更快的CPU中获益(相对更多的CPU)。

  但这不是绝对的,因为还依赖于负载情况和CPU数量。更古老的MySQL版本在多CPU上有扩展性问题,即使新版本也不能对单个査询并发利用多个CPU。因此,CPU速度限制了每个CPU密集型査询的响应时间。

  当我们讨论CPU的时候,为保证本文易于阅读,对某些术语将不会做严格的定义。现在一般的服务器通常都有多个插槽(Socket),每个插槽上都可以插一个有多个核心的CPU(有独立的执行单元),并且每个核心可能有多个“硬件线程”。这些复杂的架构需要有点耐心去了解,并且我们不会总是明确地区分它们。不过,在一般情况下,当谈到CPU速度的时候,谈论的其实是执行单元的速度,当提到的CPU数量时,指的通常是在操作系统上看到的数量,尽管这可能是独立的执行单元数量的多倍。

  这几年CPU在各个方面都有了很大的提升。例如,今天的Intel CPU速度远远超过前几代,这得益于像直接内存连接(directly attached memory)技术以及PCIe卡之类的设备互联上的改善等。这些改进对于存储设备尤其有效,例如Fusion-io和Virident的PCIe闪存驱动器。

  超线程的效果相比以前也要好得多,现在操作系统也更了解如何更好地使用超线程。而以前版本的操作系统无法识别两个虚拟处理器实际上是在同一芯片上,认为它们是独立的,于是会把任务安排在两个实际上是相同物理执行单元上的虚拟处理器。实际上单个执行单元并不是真的可以在同一时间运行两个进程,所以这样做会发生冲突和争夺资源。而同时其他CPU却可能在闲置,从而浪费资源。操作系统需要能感知超线程,因为它必须知道什么时候执行单元实际上是闲置的,然后切换相应的任务去执行。这个问题之前常见的原因是在等待内存总线,可能花费需要髙达一百个CPU周期,这已经类似于一个轻量级的I/O等待。新的操作系统在这方面有了很大的改善。超线程现在已经工作得很好。过去,我们时常提醒人们禁用它,但现在已经不需要这样做了。

  所以多和快哪个更重要? 一般来说两个都想要。从广义上来说,调优服务器可能有如下两个目标:

  低延时(快速响应)

要做到这一点,需要髙速CPU,因为每个査询只能使用一个CPU。

  高吞吐

如果能同时运行很多査询语句,则可以从多个CPU处理査询中受益。然而,在实践中,还要取决于具体情况。因为MySQL还不能在多个CPU中完美地扩展,能用多少个CPU还是有极限的。在旧版本的MySQL中(MySQL5.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承担了所有的骂名,但最近做了一些改进后,暴露了服务器层中的其他瓶颈。例如臭名昭著的L0CK_open互斥量(Mutex),在MySQL5.1和更早版本中可能就是个大问题,另外还有其他一些服务器级别的互斥量(例如査询缓存)。

  通常可以通过堆栈跟踪来诊断这些类型的竞争问题,例如Percona Toolkit中的pt-pmp工具。如果遇到这样的问题,可能需要改变服务器的配置,禁用或改变引起问题的组件,进行数据分片(Sharding),或者通过某种方式改变做事的方法。这里无法列举所有的问题和相应的解决方案,但是一旦有一个确定的诊断,答案通常是显而易见的。大部分不幸遇到的问题都是边缘场景,最常见的问题随着时间的推移都在服务器上被修复了。

2.2 CPU架构

  可能99%以上的MySQL实例(不含嵌入式使用)都运行在Intel或者AMD芯片的x86 架构下。这里基本都是针对这种情况。

  64位架构现在都是默认的了,32位CPU已经很难买到了。MySQL在64位架构上工作良好,尽管有些事暂时不能利用64位架构来做。因此,如果使用的是较老旧版本的MySQL,在64位服务器上可能要小心。例如,在MySQL5.0发布的早期时候,每个MyISAM键缓冲区被限制为4GB,由一个32位整数负责寻址。(可以创建多个键缓冲区来解决这个问题。)

  确保在64位硬件上使用64位操作系统!最近这种情况已经不太常见了,但以前经常可以遇到,大多数主机托管提供商暂时还是在服务器上安装32位操作系统,即使是64位CPU。32位操作系统意味着不能使用大量的内存:尽管某些32位系统可以支持大量的内存,但不能像64位系统一样有效地利用,并且在32位系统上,任何一个单独的进程都不能寻址4GB以上的内存。

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进程到其中某些核心),因为这种升级反而降低了性能。在MySQL5.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资源执行其他任务而不影响复制,可能就想错了。

3.平衡内存和磁盘资源

  配置大量内存最大的原因其实不是因为可以在内存中保存大量数据:最终目的是避免磁盘I/O,因为磁盘I/O比在内存中访问数据要慢得多。关键是要平衡内存和磁盘的大小、速度、成本和其他因素,以便为工作负载提供高性能的表现。在讨论如何做到这一点之前,暂时先回到基础知识上来。

  计算机包含一个金字塔型的缓存体系,更小、更快、更昂贵的缓存在顶端,如图9-1所示。

  在这个高速缓存层次中,最好是利用各级缓存来存放“热点”数据,以获得更快的访问速度,通常使用一些启发式的方法,例如“最近被使用的数据可能很快再次被使用”以及“相邻的数据可能很快需要使用”,这些算法非常有效,因为它们参考了空间和时间的局部性原理。

  从程序员的视角来看,CPU寄存器和高速缓存是透明的,并且与硬件架构相关。管理它们是编译器和CPU的工作。然而,程序员会有意识地注意到内存和硬盘的不同,并且在程序中通常区分使用它们。

  在数据库服务器上尤其明显,其行为往往非常符合我们刚才提到的预测算法所做的预测。设计良好的数据库缓存(如InnoDB缓冲池),其效率通常超过操作系统的缓存,因为操作系统缓存是为通用任务设计的。数据库缓存更了解数据库存取数据的需求,它包含特殊用途的逻辑(例如写入顺序)以帮助满足这些需求。此外,系统调用不需要访问数据库中的缓存数据。    

  这些专用的缓存需求就是为什么必须平衡缓存层次结构以适应数据库服务器特定的访问模式的原因。因为寄存器和芯片上的高速缓存不是用户可配置的,内存和存储是唯一可以改变的东西。

3.1 随机I/O和顺序I/O

  数据库服务器同时使用顺序和随机I/O,随机I/O从缓存中受益最多。想像有一个典型的混合工作负载,均衡地包含单行査找与多行范围扫描,可以说服自己相信这个说法。典型的情况是“热点”数据随机分布。因此,缓存这些数据将有助于避免昂贵的磁盘寻道。相反,顺序读取一般只需要扫描一次数据,所以缓存对它是没用的,除非能完全放在内存中缓存起来。

  顺序读取不能从缓存中受益的另一个原因是它们比随机读快。这有以下两个原因:

  顺序I/O比随机I/O快。

顺序操作的执行速度比随机操作快,无论是在内存还是磁盘上。假设磁盘每秒可以做100个随机I/O操作,并且可以完成每秒50MB的顺序读取(这大概是消费级磁盘现在能达到的水平)。如果每行100字节,随机读每秒可以读100行,相比之下顺序读可以每秒读560 000行——是随机读的5 000倍,或几个数量级的差异。因此,在这种情况下随机I/O可以从缓存中获得很多好处。

顺序访问内存行的速度也快于随机访问。现在的内存芯片通常每秒可以随机访问约250 000次100字节的行,或者每秒500万次的顺序访问。请注意,内存随机访问速度比磁盘随机访问快了2500倍,而内存中顺序访问只有磁盘10倍的速度。

  存储引擎执行顺序读比随机读快。

一个随机读一般意味着存储引擎必须执行索引操作。(这个规则也有例外,但对InnoDB和MyISAM都是对的)。通常需要通过B树的数据结构査找,并且和其他值比较。相反,连续读取一般需要遍历一个简单的数据结构,例如链表。这样就少了很多工作,反复这样操作,连续读取的速度就比随机读取要快了。

  最后,随机读取通常只要査找特定的行,但不仅仅只读取一行——而是要读取一整页的数据,其中大部分是不需要的。这浪费了很多工作。另一方面,顺序读取数据,通常发生在想要的页面上的所有行,所以更符合成本效益。

  综上所述,通过缓存顺序读取可以节省一些工作,但缓存随机读取可以节省更多的工作。 换句话说,如果能负担得起,增加内存是解决随机I/O读取问题最好的办法。

3.2 缓存,读和写

  如果有足够的内存,就完全可以避免磁盘读取请求。如果所有的数据文件都可以放在内存中,一旦服务器缓存“热”起来了,所有的读操作都会在缓存命中。虽然还是会有逻辑读取,不过物理读取就没有了。但写入是不同的问题。写入可以像读一样在内存中完成,但迟早要被写入到磁盘,所以它是需要持久化的。换句话说,缓存可延缓写入,但不能像消除读取一样消除写入。

  事实上,除了允许写入被延迟,缓存可以允许它们被集中操作,主要通以下两个重要途径:

  多次写入,一次刷新

一片数据可以在内存中改变很多次,而不需要把所有的新值写到磁盘。当数据最终被刷新到磁盘后,最后一次物理写之前发生的修改都被持久化了。例如,许多语句可以更新内存中的计数器。如果计数器递增100次,然后写入到磁盘,100次修改就被合并为一次写。

  I/O合并

许多不同部分的数据可以在内存中修改,并且这些修改可以合并在一起,通过一次磁盘操作完成物理写入。

  这就是为什么许多交易系统使用预写日志(WAL)策略。预写日志采用在内存中变更页面,而不马上刷新到磁盘上的策略,因为刷新磁盘通常需要随机I/0,这非常慢。相反,如果把变化的记录写到一个连续的日志文件,这就很快了。后台线程可以稍后把修改的页面刷新到磁盘;并在刷新过程中优化写操作。 

  写入从缓冲中大大受益,因为它把随机I/O更多地转换到连续I/O。异步(缓冲)写通常是由操作系统批量处理,使它们能以更优化的方式刷新到磁盘。同步(无缓冲)写必须在写入到磁盘之后才能完成。这就是为什么它们受益于RAID控制器中电池供电的回写(Write-Back)高速缓存。

3.3 工作集是什么

  每个应用程序都有一个数据的“工作集”——就是做这个工作确实需要用到的数据。很多数据库都有大量不在工作集内的数据。

  可以把数据库想象为有抽屉的办公桌。工作集就是放在桌面上的完成工作必须使用的文件。桌面是这个比喻中的主内存,而抽屉就是硬盘。

  就像完成工作不需要办公桌里每一张纸一样,也不需要把整个数据库装到内存中来获得最佳性能——只需要工作集就可以。

  工作集大小的不同取决于应用程序。对于某些应用程序,工作集可能是总数据大小的1%,而对于其他应用,也可能接近100%。当工作集不能全放在内存中时,数据库服务器必须在磁盘和内存之间交换数据,以完成工作。这就是为什么内存不足可能看起来却像I/O问题。有时没有办法把整个工作集的数据放在内存中,并且有时也并不真的想这么做(例如,若应用需要大量的顺序I/O)。工作集能否完全放在内存中,对应用程序体系结构的设计会产生很大的影响。

  工作集可以定义为基于时间的百分比。例如,一小时的工作集可能是一个小时内数据库 使用的95%的页面,除了5%的最不常用的页面。百分比是考虑这个问题最有用的方式,因为每小时可能需要访问的数据只有1%,但超过24小时,需要访问的数据可能会增加到整个数据库中20%的不同页面。根据需要被缓存起来的数据量多少,来思考工作集会更加直观,缓存的数据越多,工作负载就越可能成为CPU密集型。如果不能缓存足够的数据,工作集就不能完全放在内存中。

  应该依据最常用的页面集来考虑工作集,而不是最频繁读写的页面集。这意味着,确定工作集需要在应用程序内有测量的模块,而不能仅仅看外部资源的利用,例如I/O访问,因为页面的I/O操作跟逻辑访问页面不是同一回事。例如,MySQL可能把一个页面读入内存,然后访问它数百万次,但如果査看strace,只会看到一个I/O操作。缺乏确定工作集所需的检测模块,最大的原因是没有对这个主题有较多的研究。

  工作集包括数据和索引,所以应该采用缓存单位来计数。一个缓存单位是存储引擎工作的数据最小单位。

  不同存储引擎的缓存单位大小是不一样的,因此也使得工作集的大小不一样。例如,InnoDB在默认情况下是16KB的页。如果InnoDB做一个单行査找需要读取磁盘,就需要把包含该行的整个页面读入缓冲池进行缓存,这会引起一些缓存的浪费。假设要随机访问100字节的行。InnoDB将用掉缓冲池中很多额外的内存来缓存这些行,因为每一行都必须读取和缓存一个完整的16KB页面。因为工作集也包括索引,InnoDB也会读取并缓存査找行所需的索引树的一部分。InnoDB的索引页大小也是16KB,这意味着访问一个100字节的行可能一共要使用32KB的缓存空间(有可能更多,这取决于索引树有多深)。因此,缓存单位也是在InnoDB中精心挑选聚集索引非常重要的另一个原因。聚集索引不仅可以优化磁盘访问,还可以帮助在同一页面存储相关的数据,因此在缓存中可以尽量放下整个工作集。

3.4 找到有效的内存/磁盘比例

  找到一个良好的内存/磁盘比例最好的方式是通过试验和基准测试。如果可以把所有东西放入内存,你就大功告成了——后面没有必要再为此考虑什么。但大多数的时候不可能这么做,所以需要用数据的一个子集来做基准测试,看看将会发生什么。测试的目标是一个可接受的缓存命中率。缓存未命中是当有査询请求数据时,数据不能在内存中命中,服务器需要从磁盘获取数据。

  缓存命中率实际上也会决定使用了多少cpu,所以评估缓存命中率的最好方法是査看CPU使用率。例如,若CPU使用了 99%的时间工作,用了1%的时间等待I/0,那缓存命中率还是不错的。

  让我们考虑下工作集是如何影响高速缓存命中率的。首先重要的一点,要认识到工作集 不仅是一个单一的数字而是一个统计分布,并且缓存命中率是非线性分布的。例如,有10GB内存,并且缓存未命中率为10%,你可能会认为只需要增加11%以上的内存(正确的数字是11%而不是10%。10%的未命中率对应90%的命中率,所以你需要用10GB除以90%,就是11.111GB。), 就可以降低缓存的未命中率到0。但实际上,诸如缓存单位的大小之类的问题会导致缓存效率低下,可能意味着理论上需要50GB的内存,才能把未命中率降到1%。即使与一个完美的缓存单位相匹配,理论预测也可能是错误的:例如数据访问模式的因素也可能让事情更复杂。解决1%的缓存未命中率甚至可能需要500GB的内存,这取决于具体的工作负载! 

  有时候很容易去优化一些可能不会带来多少好处的地方。例如,10%的未命中率可能导致80%的CPU使用率,这已经是相当不错的了。假设增加内存,并能够让缓存未命中率下降到5%,简单来说,将提供另外约6%的数据给CPU。再简化一下,也可以说,把CPU使用率增加到了84.8%。然而,考虑到为了得到这个结果需要购买的内存,这可不一定是一个大胜利。在现实中,因为内存和磁盘访问速度之间的差异、CPU真正操作的数据,以及许多其他因素,降低缓存未命中率到5%可能都不会太多改变CPU使用率。

  这就是为什么我们说,你应该争取一个可接受的缓存命中率,而不是将缓存未命中率降低到零。没有一个应该作为目标的数字,因为“可以接受”怎么定义,取决于应用程序和工作负载。有些应用程序有1%的缓存未命中都可以工作得非常好,而另一些应用实际上需要这个比例低到0.01%才能良好运转。(“良好的缓存未命中率”是个模糊的概念,其实有很多方法来进一步计算未命中率。) 

  最好的内存/磁盘的比例还取决于系统上的其他组件。假设有16 GB的内存、20GB的数据,以及大量未使用的磁盘空间系统。该系统在80%的CPU利用率下运行得很好。如果想在这个系统上放置两倍多的数据,并保持相同的性能水平,你可能会认为只需要让CPU数量和内存量也增加到两倍。然而,即使系统中的每个组件都按照增加的负载扩展相同的量(一个不切实际的假设),这依然可能会使得系统无法正常工作。有20GB数据的系统可能使用了某些组件超过50%的容量——例如,它可能已经用掉了每秒I/O最大操作数的80%。并且在系统内排队也是非线性的。服务器将无法处理两倍的负载。因此,最好的内存/磁盘比例取决于系统中最薄弱的组件。

3.5 选择硬盘

  如果无法满足让足够的数据在内存中的目标——例如,估计将需要500GB的内存才能 完全让CPU负载起当前的I/O系统——那么应该考虑一个更强大的I/O子系统,有时甚至要以牺牲内存为代价,同时应用程序的设计应该能处理I/O等待。

  这听起来似乎有悖常理。毕竟,我们刚刚说过,更多的内存可以缓解I/O子系统的压力,并减少I/O等待。为什么要加强I/O子系统呢,如果只增加内存能解决问题吗?答案就在所涉及的因素之间的平衡,例如读写之间的平衡,每个I/O操作的大小,以及每秒有多少这样的操作发生。例如,若需要快速写日志,就不能通过增加大量有效内存来避免磁盘写入。在这种情况下,投资一个髙性能的I/O系统与带电池支持的写缓存或固态存储,可能是个更好的主意。

  作为一个简要回顾,从传统磁盘读取数据的过程分为三个步骤:

1.移动读取磁头到磁盘表面上的正确位置。

2.等待磁盘旋转,所有所需的数据在读取磁头下。

3.等待磁盘旋转过去,所有所需的数据都被读取磁头读出。

  磁盘执行这些操作有多快,可以浓缩为两个数字:访问时间(步骤1和2合并)和传输速度。这两个数字也决定延迟和吞吐量。不管是需要快速访问时间还是快速的传输速度——或混合两者——依赖于正在运行的査询语句的种类。从完成一次磁盘读取所需要的总时间来说,小的随机査找以步骤1和2为主,而大的顺序读主要是第3步。

  其他一些因素也可以影响磁盘的选择,哪个重要取决于应用。假设正在为一个在线应用选择磁盘,例如一个受欢迎的新闻网站,有大量小的磁盘随机读取。可能需要考虑下列因素:

  存储容量

对在线应用来说容量很少成为问题,因为现在的磁盘通常足够大了。如果不够,用RAID把小磁盘组合起来是标准做法。

  传输速度

现代磁盘通常数据传输速度非常快,正如我们前面看到的。究竟多快主要取决于主轴转速和数据存储在磁盘表面上的密度,再加上主机系统的接口的限制(许多现代磁盘读取数据的速度比接口可以传输的快)。无论如何,传输速度通常不是在线应用的限制因素,因为它们一般会做很多小的随机査找。

  访问时间

对随机査找的速度而言,这通常是个主要因素,所以应该寻找更快的访问时间的磁盘。

  主轴转速

现在常见的转速是7200RPM、10000RPM,以及15000RPM。转速不管对随机査找还是顺序扫描都有很大影响。

  物理尺寸

所有其他条件都相同的情况下,磁盘的物理尺寸也会带来差别:越小的磁盘,移动读取磁头需要的时间就越短。服务器级的2.5英寸磁盘性能往往比它们的更大的盘更快。它们还可以节省电力,并且通常可以融入机箱中。

  和CPU—样,MySQL如何扩展到多个磁盘上取决于存储引擎和工作负载。InnoDB能很好地扩展到多个硬盘驱动器。然而,MyISAM的表锁限制其写的可扩展性,因此写繁重的工作加在MyISAM上,可能无法从多个驱动器中收益。虽然操作系统的文件系统缓冲和后台并发写入会有点帮助,但MyISAM相对于InnoDB在写可扩展性上有更多的限制。

  和CPU—样,更多的磁盘也并不总是更好。有些应用要求低延迟需要的是更快的驱动器,而不是更多的驱动器。例如,复制通常在更快的驱动器上表现更好,因为备库的更新是单线程的。

4.固态存储

  固态(闪存)存储器实际上是有30年历史的技术,但是它作为新一代驱动器而成为热门则是最近几年的事。固态存储现在越来越便宜,并且也更成熟了,它正在被广泛使用,并且可能会在不久的将来在多种用途上代替传统磁盘。

  固态存储设备采用非易失性闪存芯片而不是磁性盘片组成。它们也被称为NVRAM,或非易失性随机存取存储器。固态存储设备没有移动部件,这使得它们表现得跟硬盘驱动器有很大的不同。

  目前MySQL用户感兴趣的技术可分为两大类:SSD (固态硬盘)和PCIe卡。SSD通过实现SATA (串行髙级技术附件)接口来模拟标准硬盘,所以可以替代硬盘驱动器,直接插入服务器机箱中的现有插槽。PCIe卡使用特殊的操作系统驱动程序,把存储设备作为一个块设备输出。PCIe和SSD设备有时可以简单地都认为是SSD。

  下面是闪存性能的快速小结。高质量闪存设备具备:

  • 相比硬盘有更好的随机读写性能。闪存设备通常读明显比写要快。
  • 相比硬盘有更好的顺序读写性能。但是相比而言不如随机I/O的改善那么大,因为硬盘随机I/O比顺序I/O要慢得多。入门级固态硬盘的顺序读取实际上还可能比传统硬盘慢。
  • 相比硬盘能更好地支持并发。闪存设备可以支持更多的并发操作,事实上,只有大量的并发请求才能真正实现最大吞吐量。

  最重要的事情是提升随机I/O和并发性。闪存记忆体可以在髙并发下提供很好的随机I/O性能,这正是范式化的数据库所需要的。设计非范式化的Schema最常见的原因之一是为了避免随机I/O,并且使得査询可能转化为顺序I/O。

4.1 闪存概述

  硬盘驱动器使用旋转盘片和可移动磁头,其物理结构决定了磁盘固有的局限性和特征。对固态存储也是一样,它是构建在闪存之上的。不要以为固态存储很简单,实际上比硬盘驱动器在某些方面更复杂。闪存的限制实际上是相当严重的,并且难以克服,所以典型的固态设备都有错综复杂的架构、缓存,以及独有的“法宝”。

  闪存的最重要的特征是可以迅速完成多次小单位读取,但是写入更有挑战性。闪存不能在没有做檫除操作前改写一个单元(Cell),并且一次必须擦除一个大块——例如,512KB。擦除周期是缓慢的,并且最终会磨损整个块。一个块可以容忍的檫除周期次数取决于所使用的底层技术。

  写入的限制是固态存储复杂的原因。这也是为什么一些设备供应商在设备的稳定、性能的一致性等方面和其他供应商有区别的原因。“魔法”全部都在其专有的固件、驱动程序,以及其他零零碎碎的东西里,这些东西使得固态设备良好运转。为了使写入表现良好,并避免闪存块过早损耗完寿命,设备必须能够搬迁页面并执行垃圾收集和所谓的磨损均衡。写放大用于描述数据从一个地方移动到另一个地方的额外写操作,多次写数据和元数据导致局部块经常写。如果你有兴趣,维基百科中的写放大的文章,是个学习的好地方,可以从其中了解更多关于闪存的知识。

  垃圾收集对理解闪存很重要。为了保持一些块是干净的并且可以被写入,设备需要回收脏块。这需要设备上有一些空闲空间。无论是设备内部有一些看不到的预留空间,或者通过不写那么多数据来预留需要的空间——不同的设备可能有所不同。无论哪种方式,设备填满了,垃圾收集就必须更加努力地工作,以保持一些块是干净的,所以写放大的倍数就增加了

  因此,许多设备在被填满后会开始变慢。到底会慢多少,不同的制造商和型号之间有所不同,依赖于设备的架构。有些设备为髙性能而设计,即使写得非常满,依然可以保持高性能。但是,通常一个100GB的文件在160GB和320GB的SSD上表现完全不同。 速度下降是由于没有空闲块时必须等待擦写完成所造成的。写到一个空闲块只需要花费数百微秒,但是擦写慢得多——通常需要几个毫秒。

4.2 闪存技术

  有两种主要的闪存设备类型,当考虑购买闪存存储时,理解两者之间的不同是很重要的。这两种类型分别是单层单元(SLC)和多层单元(MLC)。

  SLC的每个单元存储数据的一个比特:可以是0或1。SLC相对更昂贵,但非常快,并且擦写寿命高达100 000个写周期,具体值取决于供应商和型号。这听起来好像不多,但在现实中一个好的SLC设备应该持续使用大约20年左右,甚至比卡上安装的控制器更耐用和可靠。缺点则是存储密度相对较低,所以不能在每个设备上得到那么多空间。

  MLC每个单元存储2个比特、3个比特的设备也有。这使得通过MLC设备获得更高的存储密度(更大的容量)成为可能。成本更低了,但是速度和耐擦写性也下降了。一个不错的MLC设备可能被定为10 000个写循环周期。

  可以在大众市场上购买到这两种类型的闪存设备,它们之间的竞争有助于闪存的发展。目前,SLC仍持有“企业”级服务器的存储解决方案的声誉,通常被视为消费级的MLC设备,一般使用在笔记本电脑和数码相机等地方。然而,这种情况正在改变,出现了一种新兴的所谓企业级MLC(eMLC)存储。

  MLC技术的发展是很有意思的,如果正在考虑购买闪存存储,这个发展方向值得密切关注。MLC非常复杂,包含很多有助于设备质量和性能的重要因素。任何给定的芯片仅靠自身是不能持久化的,因为有着相对较短的信号保持周期,以及较高的错误率必须纠正。随着市场转移到更小、密度更髙的芯片,其中的芯片单元可以存储3比特,单个芯片变得更不可靠以及更容易出错。

  然而,这并不是一个不可逾越的工程问题。厂商正在制造一些有越来越多隐藏容量的设备,因此有足够的内部冗余。尽管闪存厂商非常注意保护自己的商业秘密,还是有传言称,某些设备可能有比它标称大小多出高达两倍的存储空间。使MLC芯片更耐用的另一种方法是通过固件逻辑。平衡磨损和重映射的算法是非常重要的。

  寿命的长短取决于真实的容量,固件逻辑等——所以最终是因供应商而异的。我们听说过在几个星期里密集使用导致设备报废的报告!

  一些最好的供应商提供的设备,是值得用eMLC这个标签的。这个领域随着时间的推移进步得很快,这里对MLC与SLC的意见可能很快会变得过时。硬件介绍这里仅仅只能做一个参考。

4.3 闪存的基准测试

  对闪存设备进行基准测试是复杂并且困难的。有很多情况会导致测试错误,需要了解特定设备的知识,并且需要有极大的耐心和关注,才能正确地操作。

  闪存设备有一个三阶段模式,我们称为A-B-C性能特性。它们开始阶段运行非常快(阶段A),然后垃圾回收器开始工作,这将导致在一段时间内,设备处于过渡到稳定状态(阶段B)的阶段,最后设备进入一个稳定状态(状态C)。所有我们测试过的设备都有这个特点。

  当然,我们感兴趣的是阶段C的性能,所以基准测试只需要测量这个部分的运行过程。这意味着基准测试要做的不仅仅是基准测试:还需要先进行一下预热,然后才能进行基准测试。但是,定义预热的终点和基准测试的起点会非常棘手。

  设备、文件系统,以及操作系统通过不同方式提供TRIM命令的支持,这个命令标记空间准备重用。有时当删除所有文件时设备会被TRIM。如果在基准测试运行的情况下发生,设备将重置到阶段A,然后必须重新执行A和B之间的运行阶段。另一个因素是设备被填充得很满或者不满时,不同的性能表现。一个可重复的基准测试必须覆盖到所有这些因素。

  通过上述分析,可知基准测试的复杂性,所以就算厂商如实地报告测试结果,但对于外行来说,厂商的基准测试和规格说明书依然可能有很多“坑”。

  通常可以从供应商那得到四个数字。这里有一个设备规格的例子:

1.设备读取性能最髙达520MB/S。

2.设备写入性能最高达480MB/s。

3.设备持续写入速度可以稳定在420MB/S。

4.设备每秒可以执行70 000个4KB的写操作。

  如果再次复核这些数字,你会发现峰值4KB写入达到70 000个IOPS(每秒输入/输出操作),这么算每秒写入大约只有274MB/s,这比第二点和第三点中说明的高峰写入带宽少了很多。这是因为达到峰值写入带宽时是用更大的块,例如64KB或128KB。用更小的块大小来达到峰值IOPS。 

  大部分应用不会写这么大的块。InnoDB写操作通常是16KB或512字节的块组合到一起写回。因此,设备应该只有274MB/S的写出带宽——这是阶段A的情况,在垃圾回收器开启和设备达到长期稳定的性能等级前!

  目前的MySQL基准测试,以及在固态硬盘上裸设备文件I/O:http://www.ssdperformanceblog.com and http://www.mysqlperformanceblog.com.

4.4 固态硬盘驱动器(SSD)

  SSD模拟SATA硬盘驱动器。这是一个兼容性功能:替换SATA硬盘不需要任何特殊的驱动程序或接口。

  英特尔的X-25E驱动器可能是我们几年前看到在服务器中最常见的固态硬盘,但也有很多其他选择。X-25E是为“企业”级消费市场开发的,但也有用MLC存储的X-25M,这是为笔记本电脑用户等大众市场准备的。此外,英特尔还销售320系列,也有很多人正在使用。再次,这仅仅是一个供应商——还有很多,这篇文章讲解的很多ssd知识已经过时。但是了解一下ssd的历史也不错。

  关于SSD的好处是,它们有大量的品牌和型号相对是比较便宜的,同时它们比硬盘快了很多。最大的缺点是,它们并不总是像硬盘一样可靠,这取决于品牌和型号。直到最近,大多数设备都没有板载电池,但大多数设备上有一个写缓存来缓冲写入。写入缓存在没有电池备份的情况下并不能持久化,但是在快速增长的写负载下,它不能关闭,否则闪存存储无法承受。所以,如果禁用了驱动器的高速缓存以获得真正持久化的存储,将会更快地耗完设备寿命,在某些情况下,这将导致保修失效。

  有些厂家完全不急于告诉购买他们固态硬盘的客户关于SSD的特点,并且他们对设备的内部架构等细节守口如瓶。是否有电池或电容保护写缓存的数据安全,在电源故障的情况下,通常是一个悬而未决的问题。在某些情况下,驱动器会接受禁用缓存的命令,但忽略了它。所以,除非做过崩溃试验,否则真的有可能不知道驱动器是否是持久化的,我们对一些驱动器进行了崩溃测试,发现了不同的结果。如今,一些驱动器有电容器保护缓存,使其可以持久化,但一般来说,如果驱动器不是自夸有一个电池或电容,那么它就没有。这意味着在断电的情况下不是持久化的,所以可能出现数据已经损坏却还不知情的情况。SSD是否配置电容或电池是我们必须关注的特性。

  通常,使用SSD都是值得的。但底层技术的挑战是不容易解决的。很多厂家做出的驱动 器在髙负载下很快就崩溃了,或不提供持续一致的性能。一些低端的制造商有一个习惯,每次发布新一代驱动器,就声称他们已经解决了老一代的所有问题。这往往是不真实的,当然,如果关心可靠性和持续的高性能,“企业级”的设备通常值得它的价钱。

  用SSD做RAID

  建议对SATA SSD盘使用RAID。单一驱动器的数据安全是无法让人信服的。许多旧的RAID控制器并不支持SSD。因为它们假设管理的是机械硬盘,包括写缓冲和写排序这些特性都是为机械硬盘而设计的。这不但纯属无效工作,也会增加响应时间,因为SSD暴露的逻辑位置会被映射到底层闪存记忆体中的任意位置。现在这种情况好一点。有些RAID控制器的型号末尾有一个字母,表明它们是为SSD做了准备的。例如,Adaptec控制器用Z标识。

  然而,即使支持闪存的控制器,也不一定真的就对闪存支持很好。例如,Vadim对Adaptec 5805Z控制器进行了基准测试,他用了多种驱动器做RAID 10, 16个并发操作500 GB的文件。结果是很糟糕的:95%的随机写延迟在两位数的毫秒,在最坏的情况下,超过一秒钟。(期望的应该是亚毫秒级写入。)

  这种特定的比较,是一家客户为了看到Micron SSD是否会比64GB的Intel SSD更好而做的,该比较是基于相同的配置的。当为英特尔驱动器进行基准测试时,我们发现了相同的性能特征。因此,我们尝试了一些其他驱动器的配置,不管有没有SAS扩展器,看看会发生什么。表9-1显示了这个结果。

9-1:Adaptec RAID控制器上用SSD进行的基准测试
驱动器数量厂家大小SAS扩展器随机读随机写
34Intel64GBYes310MB/s130MB/s
14Intel64GBYes305MB/s145MB/s
24Micron50GBNo350MB/s120MB/s
34Intel50GBNo350MB/s180MB/s

  这些结果都没有达到我扪对这么多驱动器的期望。在一般情况下,RAID控制器的性能表现,只能满足对6〜8个驱动器的期望,而不是几十个。原因很简单,RAID控制器达到了瓶颈。这个故事的重点是,在对硬件投入巨资前,应该先仔细进行基准测试——结果可能与期望的有相当大的区别。

4.5 PCIe存储设备

  相对于SATA SSD,PCIe设备没有尝试模拟硬盘驱动器。这种设计是好事:服务器和硬盘驱动器之间的接口不能完全发挥闪存的性能。SAS/SATA互联带宽比PCIe要低,所以PCIe对髙性能需求是更好的选择。PCIe设备延迟也低得多,因为它们在物理上更靠近CPU。

  没有什么比得上从PCIe设备上获得的性能。缺点就是它们太贵了。

  所有我们熟悉的型号都需要一个特殊的驱动程序来创建块设备,让操作系统把它认成一个硬盘驱动器。这些驱动程序使用着混合磨损均衡和其他逻辑的策略;有些使用主机系统的CPU和内存,有些使用板载的逻辑控制器和RAM(内存)。在许多场景下,主机系统有丰富的CPU和RAM资源,所以相对于购买一个自身有这些资源的卡,利用主机上的资源实际上是更划算的策略。

  不建议对PCIe设备建RAID。它们用RAID就太昂贵了,并且大部分设备无论以何种方式,都有它们自己板载的RAID。我们并不真的知道控制器坏了以后会怎么样,但是厂商说他们的控制器通常跟网卡或者RAID控制器一样好,看起来确实是这样。换句话说,这些设备的平均无故障时间间隔(MTBF)接近于主板,所以对这些设备使用RAID只是增加了大量成本而没有多少好处。

  有许多家供应商在生产PCIe闪存卡。对MySQL用户来说最著名的厂商是Fusion-io和Virident,但是像Texas Memory Systems、STEC和OCZ这样的厂商也有用户。SLC和MLC都有相应的PCIe卡产品。

4.6 其他类型的固态存储

  除了SSD和PCIe设备,也有其他公司的产品可以选择,例如ViolinMemory、SandForce和Texas Memory Systems。这些公司提供有几十TB存储容量,本质上是闪存SAN的大箱子。它们主要用于大型数据中心存储的整合。虽然价格非常昂贵,但是性能也非常高。我们知道一些使用的例子,并在某些场景下测量过性能。这些设备能够提供相当好的延迟,除了网络往返时间——例如,通过NFS有小于4毫秒的延迟。

  然而这些都不适合一般的MySQL市场。它们的目标更针对其他数据库,如Oracle,可以用来做共享存储集群。一般情况下,MySQL在如此大规模的场景下,不能有效利用如此强大的存储优势,因为在数十个TB的数据下MySQL很难良好地工作——MySQL对这样一个庞大的数据库的回答是,拆分、横向扩展和无共享(Shared-nothing)架构。

  虽然专业化的解决方案可能能够利用这些大型存储设备——例如Infobdght可能成为候选人。ScaleDB可以部署在共享存储(Shared-storage)架构,但我们还没有看到它在生产环境应用,所以我们不知道其工作得如何。

4.7 什么时候应该使用闪存

  固态存储最适合使用在任何有着大量随机I/O工作负载的场景下。随机I/O通常是由于数据大于服务器的内存导致的。用标准的硬盘驱动器,受限于转速和寻道延迟,无法提供很高的IOPS。闪存设备可以大大缓解这种问题。

  当然,有时可以简单地购买更多内存,这样随机工作负载就可以转移到内存,I/O就不存在了。但是当无法购买足够的内存时,闪存也可以提供帮助。另一个不能总是用内存解决的问题是,髙吞吐的写入负载。增加内存只能帮助减少写入负载到磁盘,因为更多的内存能创造更多的机会来缓冲、合并写。这允许把随机写转换为更加顺序的I/O。

  然而,这并不能无限地工作下去,一些事务或插入繁忙的工作负载不能从这种方法中获益。闪存存储在这种情况下却也有帮助。

  单线程工作负载是另一个闪存的潜在应用场景。当工作负载是单线程的时候,它是对延迟非常敏感的,固态存储更低的延迟可以带来很大的区别。相反,多线程工作负载通常可以简单地加大并行化程度以获得更高的吞吐量。MySQL复制是单线程工作的典型例子,它可以从低延迟中获得很多收益。在备库跟不上主库时,使用闪存存储往往可以显著提高其性能。

  闪存也可以为服务器整合提供巨大的帮助,尤其是PCIe方式的。我们已经看到了机会, 把很多实例整合到一台物理服务器——有时髙达10或15倍的整合都是可能的。

  然而闪存也可能不一定是你要的答案。一个很好的例子是,像InnoDB日志文件这样的 顺序写的工作负载,闪存不能提供多少成本与性能优势,因为在这种情况下,闪存连续写方面不比标准硬盘快多少。这样的工作负载也是髙吞吐的,会更快耗尽闪存的寿命。在标准硬盘上存放日志文件通常是一个更好的主意,用具有电池保护写缓存的RAID控制器。

  有时答案在于内存/磁盘的比例,而不只是磁盘。如果可以买足够的内存来缓存工作负载,就会发现这更便宜,并且比购买闪存存储设备更有效。

4.8 使用 Flashcache

  虽然有很多因素需要在闪存、硬盘和RAM之间权衡,在存储层次结构中,这些设备没有被当作一个整体处理。有时可以使用磁盘和内存技术的结合,这就是Flashcache。

  Flashcache是这种技术的一个实现,可以在许多系统上发现类似的使用,例如Oracle数据库、ZFS文件系统,甚至许多现代的硬盘驱动器和RAID控制器。下面讨论的许多东西应用广泛,但我们将只专注于Flashcache,因为它和厂商、文件系统无关。

  Flashcache是一个Linux内核模块,使用Linux的设备映射器(Device Mapper)。它在内存和磁盘之间创建了一个中间层。这是Facebook开源和使用的技术之一,可以帮助其优化数据库负载。

  Flashcache创建了一个块设备,并且可以被分区,也可以像其他块设备一样创建文件系统,特点是这个块设备是由闪存和磁盘共同支撑的。闪存设备用作读取和写入的智能高速缓存。

  虚拟块设备远比闪存设备要大,但是没关系,因为数据最终都存储在磁盘上。闪存设备只是去缓冲写入和缓存读取,有效弥补了服务器内存容量的不足(意思就是内存放不下要缓存的数据时,换出到Flashcache上,Flashache的闪存设备可以帮助继续缓存,而不会立刻落到磁盘。)。

  这种性能有多好呢? Flashcache似乎有相对较高的内核开销。(设备映射并不总是像看起来那么有效,但我们还没深入调査找出原因。)但是,尽管Flashcache理论上可能更髙效,但最终的性能表现并不如底层的闪存存储那么好,不过它仍然比磁盘快很多,所以还是值得考虑的方案。

  我们用包含数百个基准测试的一系列测试来评估Flashcache的性能,但是我们发现在人工模拟的工作负载下,测出有意义的数据是非常困难的。于是我们得出结论,虽然并不清楚Flashcache通常对写负载有多大好处,但是对读肯定是有帮助的。于是它适合这样的情况使用:有大量的读I/0,并且工作集比内存大得多。

  除了实验室测试,我们有一些生产环境中应用Flashcache的经验。想到的一个例子是,有个4TB的数据库,这个数据库遇到了很大的复制延迟。我们给系统加了半个TB的Virident PCIe卡作为存储。然后安装了Flashcache,并且把PCIe卡作为绑定设备的闪存部分,复制速度就翻了一倍。

  当闪存卡用得很满时使用Flashcache是最经济的,因此选择一张写得很满时其性能不会降低多少的卡非常重要。这就是为什么我们选择Virident卡。

  Flashcache就是一个缓存系统,所以就像任何其他缓存一样,它也有预热问题。虽然预热时间可能会非常长。例如,在我们刚才提到的情况下,Flashcache需要一个星期的预热,才能真正对性能产生帮助。

  应该使用Flashcache吗?根据具体情况可能会有所不同,所以我们认为在这一点上,如果你觉得不确定,最好得到专家的意见。理解Flashcache的机制和它们如何影响你的数据库工作集大小是很复杂的,在数据库下层(至少)有三层存储:

  • 首先,是InnoDB缓冲池,它的大小跟工作集大小一起可以决定缓存的命中率。缓存命中是非常快的,响应时间非常均匀。
  • 在缓冲池中没有命中,就会到Flashcache设备上去取,这就会产生分布比较复杂的响应时间。Flashcache的缓存命中率由工作集大小和闪存设备大小决定。从闪存上命中比在磁盘上査找要快得多。
  • Flashcache设备缓存也没有命中,那就得到磁盘上找,这也会看到分布相当均匀的比较慢的响应时间。

  有可能还有更多层次:例如,SAN或RAID控制器的缓存。

  这有一个思维实验,说明这些层是如何交互的。很显然,从Flashcache设备访问的响应时间不会像直接访问闪存设备那么稳定和高速。但是想象一下,假设有1TB的数据,其中100GB在很长一段时间会承受99%的I/O操作。也就是说,大部分时候99%的工作集只有100GB。

  现在,假设有以下的存储设备:一个很大的RAID卷,可以执行1000IOPS,以及一个可以达到100 000 I0PS的更小的闪存设备。闪存设备不足以存放所有的数据——假设只有128GB-因此单独使用闪存不是一种可能的选择。如果用闪存设备做Flashcache,就可以期望缓存命中远远快于磁盘检索,但Flashcache整体比单独使用闪存设备要慢。我们坚持用数字说话,如果90%的请求落到Flashcache设备,相当于达到50000I0PS。

  这个思维实验的结果是什么呢?有两个要点:

1.系统使用Flashcache比不使用的性能要好很多,因为大多数在缓冲池未命中的页面 访问都被缓存在闪存卡上,相对于磁盘可以提供快得多的访问速度。(99%的工作集可以完全放在闪存卡上。)

2.Flashcache设备上有90%的命中率意味着有10%没有命中。因为底层的磁盘只能提供1000I0PS,因此整个Flashcache设备可以支持10000的I0PS。为了明白为什么是这样的,想象一下如果我们要求不止于此会发生什么:10%的I/O操作在缓存中没有命中而落到了RAID卷上,则肯定要求RAID卷提供超过1000I0PS,很显然是没法处理的。因此,即使Flashcache比闪存卡慢,系统作为一个整体仍然受限于RAID卷,不止是闪存卡或Flashcache。

  归根到底,Flashcache是否合适是一个复杂的决定,涉及的因素很多。一般情况下,它似乎最适合以读为主的I/O密集型负载,并且工作集太大,用内存优化并不经济的情况。

4.9 优化固态存储上的MySQL

  如果在闪存上运行MySQL,有一些配置参数可以提供更好的性能。InnoDB的默认配置从实践来看是为硬盘驱动器定制的,而不是为固态硬盘定制的。不是所有版本的InnoDB都提供同样等级的可配置性。尤其是很多为提升闪存性能设计的参数首先出现在Percona Server中,尽管这些参数很多已经在Oracle版本的InnoDB中实现,或者计划在未来的版本中实现。

  改进包括:

  增加InnoDB的I/O容量

闪存比机械硬盘支持更高的并发量,所以可以增加读写I/O线程数到10或15来获得更好的结果。也可以在2 000〜20 000范围内调整innodb_io_capacity选项,这要看设备实际上能支撑多大的IOPS。尤其是对Oracle官方的InnoDB这个很有必要,内部有更多算法依赖于这个设置。

  让InnoDB日志文件更大

即使最近版本的InnoDB中改进了崩溃恢复算法,也不应该把磁盘上的日志文件调得太大,因为崩溃恢复时需要随机I/O访问,会导致恢复需要很长一段时间。闪存存储让这个过程快很多,所以可以设置更大的InnoDB日志文件,以帮助提升和稳定性能。对于Oracle官方的InnoDB,这个设置尤其重要,它维持一个持续的脏页刷新比例有点麻烦,除非有相当大的日志文件——4GB或者更大的日志文件,在写的时候对服务器来说是个不错的选择。Percona Server和MySQL5.6支持大于4GB的日志文件。

  把一些文件从闪存转移到RAID

除了把InnoDB日志文件设置得更大,把日志文件从数据文件中拿出来,单独放在一个带有电池保护写缓存的RAID组上而不是固态设备上,也是个好主意。这么做有几个原因。一个原因是日志文件的I/O类型,在闪存设备上不比在这样一个RAID组上要快。InnoDB写日志是以512字节为单位的顺序I/O写下去,并且除了崩溃恢复会顺序读取,其他时候绝不会去读。这样的I/O操作类型用闪存设备是很浪费的。并且把小的写入操作从闪存转移到RAID卷也是个好主意,因为很小的写入会增加闪存设备的写放大因子,会影响一些设备的使用寿命。大小写操作混合到一起也会引起某些设备延时的增加。

基于相同的原因,有时把二进制日志文件转移到RAID卷也会有好处。并且你可能会认为ibdata1文件也适合放在RAID卷上,因为ibdata1文件包含双写缓冲(Doublewrite Buffer)和插入缓冲(Insert Buffer),尤其是双写缓冲会进行很多重复写入。在Percona Server中,可以把双写缓冲从ibdata1文件中拿出来,单独存放到一个文件,然后把这个文件放在RAID卷上。

还有另一个选择:可以利用Percona Server的特性,使用4KB的块写事务日志,而不是512字节。因为这会匹配大部分闪存本身的块大小,所以可以获得更好的效果。所有的上述建议是对特定硬件而言的,实际操作的时候可能会有所不同,所以在大规模改动存储布局之前要确保已经理解相关的因素——并辅以适当的测试。

  禁用预读

预读通过通知和预测读取模式来优化设备的访问,一旦认为某些数据在未来需要被访问到,就会从设备上读取这些数据。实际上在InnoDB中有两种类型的预读,我们发现在多种情况下的性能问题,其实都是预读以及它的内部工作方式造成的。在许多情况下开销比收益大,尤其是在闪存存储,但我们没有确凿的证据或指导,禁用预读究竟可以提高多少性能。

在MySQL5.1的InnoDB Plugin中,MySQL禁用了所谓的“随机预读”,然后在MySQL5.5又重新启用了它,可以在配置文件用一个参数配置。Percona Server能让你在旧版本里也一样可以配置为random或linear read-ahead (线性预读)。

  配置InnoDB刷新算法

这决定InnoDB什么时候、刷新多少、刷新哪些页面,这是个非常复杂的主题,这里我们没有足够的篇幅来讨论这些具体的细节。这也是个研究比较活跃的主题,并且实际上在不同版本的InnoDB和MySQL中有多种有效的算法。

标准InnoDB算法没有为闪存存储提供多少可配置性,但是如果用的是Percona XtraDB(包含在Percona Server和MariaDB中),我们建议设置innodb_adaptive_checkpoint选项为keep_average,不要用默认值estimate。这可以确保更持续的性能,并且避免服务器抖动,因为estimate算法会在闪存存储上引起抖动。我们专门为闪存存储开发了keep_average,因为我们意识到对于闪存设备,把希望操作的大量I/O推到设备上,并不会引起瓶颈或发生抖动。

另外,建议为闪存设备设置:innodb_flush_neighbor_pages=0。这样可以避免InnoDB尝试查找相邻的脏页一起刷写。这个算法可能会导致更大块的写、更髙的延迟,以及内部竞争。在闪存存储上这完全没必要,也不会有什么收益,因为相邻的页面单独刷新不会冲击性能。

  禁用双写缓冲的可能

相对于把双写缓存转移到闪存设备,可以考虑直接关闭它。有些厂商声称他们的设备支持16KB的原子写入,使得双写缓冲成为多余的。如果需要确保整个存储系统被配置得可以支持16KB的原子写入,通常需要0_DIRECT和XFS文件系统。

没有确凿的证据表明原子操作的说法是真实的,但由于闪存存储的工作方式,我们相信写数据文件发生页面写一部分的情况是大大减少的,并且这个收益在闪存设备上比在传统磁盘上要高得多,禁用双写缓冲在闪存存储上可以提髙MySQL整体性能差不多50%,尽管我们不知道这是不是100%安全的,但是你可以考虑下这么做。 

  限制插入缓冲大小

插入缓冲(在新版InnoDB中称为变更缓冲(Change Buffer))设计来用于减少当更新行时不在内存中的非唯一索引引起的随机I/O。在硬盘驱动器上,减少随机I/O可以带来巨大的性能提升。对某些类型的工作负载,当工作集比内存大很多时,差异可能达到近两个数量级。插入缓冲在这类场景下就很有用。

然而,对闪存就没有必要了。闪存上随机I/O非常快,所以即使完全禁用插入缓冲,也不会带来太大影响,尽管如此,可能你也不想完全禁用插入缓存。所以最好还是启用,因为I/O只是修改不在内存中的索引页面的开销的一部分。对闪存设备而言,最重要的配置是控制最大允许的插入缓冲大小,可以限制为一个相对比较小的值,而不是让它无限制地增长,这可以避免消耗设备上的大量空间,并避免ibdata1文件变得非常大的情况。在mysql5.5之前时候,标准InnoDB还不能配置插入缓存的容量上限,但是在Percona XtraDB(Percona Server和MariaDB都包含XtraDB)里可以。MySQL5.6里也会增加一个类似的变量。

  除了上述的配置建议,我们还提出或讨论了其他一些闪存优化策略。然而,不是所有的策略都非常容易明白,所以我们只是提到了一部分,最好自己研究在具体情况下的好处。首先是InnoDB的页大小。我们发现了不同的结果,所以我们现在还没有一个明确的建议。好消息是,在Percona Server中不需要重编译也能配置页面大小,在MySQL5.6中这个功能也可能实现。以前版本的MySQL需要重新编译服务器才能使用不同大小的页面,所以大部分情况都是运行在默认的16KB页面。当页面大小更容易让更多人进行实验时,我们期待更多非标准页面大小的测试,可能能从中得到很多重要的结论。

  另一个提到的优化是InnoDB页面校验(Checksum)的替代算法。当存储系统响应很快时,校验值计算可能开始成为I/O相关操作中显著影响时间的因素,并且对某些人来说这个计算可能替代I/O成为新的瓶颈。我们的基准测试还没有得出可适用于普遍场景的结论,所以每个人的情况可能有所不同。Percona XtraDB允许修改校验算法,MySQL5.6也有了这个功能。

  可能已经提醒过了,我们提到的很多功能和优化在标准版本的InnoDB中是无效的。我们希望并且相信我们引入Percona Server和XtraDB中的改进点,最终将会被广大用户接受。与此同时,如果正使用Oracle官方MySQL分发版本,依然可以对服务器采取措施为闪存进行优化。建议使用innodb_file_per_table,并且把数据文件目录放到闪存设备。然后移动ibdata1和日志文件,以及其他所有日志文件(二进制日志、复制日志,等等),到RAID卷,正如我们之前讨论的。这会把随机I/O集中到闪存设备上,然后把大部分顺序写入的压力尽可能转移出闪存,因而可以节省闪存空间并且减少磨损。

  另外,所有版本的MySQL服务器,都应该确认超线程开启了。当使用闪存存储时,这有很大的帮助,因为磁盘通常不再是瓶颈,任务会更多地从I/O密集变为CPU密集。

5.为备库选择硬件

  为备库选择硬件与为主库选择硬件很相似,但是也有些不同。如果正计划着建一个备库做容灾,通常需要跟主库差不多的配置。不管备库是不是仅仅作为一个主库的备用库,都应该强大到足以承担主库上发生的所有写入,额外的不利因素是备库只能序列化串行执行。

  备库硬件主要考虑的是成本:需要在备库硬件上花费跟主库一样多的成本吗?可以把备库配置得不一样以便从备库上获得更多性能吗?如果备库跟主库工作负载不一样,可以从不一样的硬件配置上获得隐含的收益吗? 

  这一切都取决于备库是否只是备用的,你可能希望主库和备库有相同的硬件和配置,但是,如果只是用复制作为扩展更多读容量的方法,那备库可以有多种不同的捷径。例如,可能在备库使用不一样的存储引擎,并且有些人使用更便宜的硬件或者用RAID 0代替RAID 5或RAID 10。也可以取消一些一致性和持久性的保证让备库做更少的工作。

  这些措施在大规模部署的情况下具有很好的成本效益,但是在小规模的情况下,可能只会使事情变得更加复杂。在实践中,似乎大多数人都会选择以下两种策略为备库选择硬件:主备使用相同的硬件,或为主库购买新的硬件,然后让备库使用主库淘汰的老硬件。

  在备库很难跟上主库时,使用固态硬盘有很大的意义。很好的随机I/O性能有助于缓解单个复制线程的影响。

6.RAID性能优化

  存储引擎通常把数据和索引都保存在一个大文件中,这意味着用RAID(Redundant Array of Inexpensive Disks,磁盘冗余阵列)存储大量数据通常是最可行的方法。RAID可以帮助做冗余、扩展存储容量、缓存,以及加速。但是从我们看到的一些优化案例来说,RAID上有多种多样的配置,为需求选择一个合适的配置非常重要。

  这里raid的配置以及类型就不做介绍了。

6.1 RAID的故障转移、恢复和镜像

  RAID配置(除了RAID 0)都提供了冗余。这很重要,但很容易让人低估磁盘同时发生故障的可能性。千万不要认为RAID能提供一个强有力的数据安全性保证。

  RAID不能消除甚至减少备份的需求。当出现问题的时候,恢复时间要看控制器、RAID等级、阵列大小、硬盘速度,以及重建阵列时是否需要保持服务器在线。

  硬盘在完全相同的时间损坏是有可能的。例如,峰值功率或过热,可以很容易地废掉两个或更多的磁盘。然而,更常见的是,两个密切相关的磁盘出现故障。许多这样的隐患可能被忽视了。一个常见的情况是,很少被访问的数据,在物理媒介上损坏了。这可能几个月都检测不到,直到尝试读取这份数据,或另一个硬盘也失效了,然后RAID控制器尝试使用损坏的数据来重建阵列。越大的硬盘驱动器,越容易发生这种情况。

  这就是为什么做RAID阵列的监控如此重要。大部分控制器提供了一些软件来报告阵列的状态,并且需要持续跟踪这些状态,因为不这么做可能就会忽略了驱动器失效。你可能丧失恢复数据和发现问题的时机,当第二块硬盘损坏时,已经晚了。因此应该配置一个监控系统来提醒硬盘或者RAID卷发生降级或失效了。

  对阵列积极地进行定期一致性检査,可以减少潜在的损坏风险。某些控制器有后台巡检(Background Patrol Read)功能,当所有驱动器都在线服务时,可以检査媒介是否有损坏并且修复,也可以帮助避免此类问题的发生。在恢复时,非常大型的阵列可能会降低检査速度,所以创建大型阵列时一定要确保制定了相应的计划。

  也可以添加一个热备盘,这个盘一般是未使用状态,并且配置为备用状态,有硬盘坏了之后控制器会自动把这块盘恢复为使用状态。如果依赖于每个服务器的可用性,这是一个好主意。对只有少数硬盘驱动器的服务器,这么做是很昂贵的,因为一个空闲磁盘的成本比例比较高,但如果有多个磁盘,而不设一个热备盘,就是愚蠢的做法。请记住,更多的磁盘驱动器会让发生故障的概率迅速增加。

  除了监控硬盘失效,还应该监控RAID控制器的电池备份单元以及写缓存策略。如果电池失效,大部分控制器默认设置会禁用写缓存,把缓存策略从WriteBack改为WriteThrough。这可能导致服务器性能下降。很多控制器会通过一个学习过程周期性地对电池充放电,在这个过程中缓存是被禁用的。RAID控制器管理工具应该可以浏览和配置电池充放电计划,不会让人措手不及。

  也许希望把缓存策略设为WriteThrouhg来测试系统,这样就可以知道系统性能的期望值。也许需要计划电池充放电的周期,安排在晚上或者周末,重新配置服务器修改innodb_flush_log_at_trx_commit和sync_binlog变量,或者在电池充放电时简单地切换到另一台服务器。

6.2 平衡硬件RAID和软件RAID

  操作系统、文件系统和操作系统看到的驱动器数量之间的相互作用可以是复杂的。Bug、限制或只是错误配置,都可能会把性能降低到远远低于理论值。

  如果有10块硬盘,理想中应该能够承受10个并行的请求,但有时文件系统、操作系统 或RAID控制器会把请求序列化。面对这个问题一个可行的办法是尝试不同的RAID配置。例如,如果有10个磁盘,并且必须使用镜像冗余,性能也要好,可以考虑下面几种配置:

  • 配置一个包含五个镜像对(RAID1)的RAID10卷。操作系统只会看到一个很大的单独的硬盘卷,RAID控制器会隐藏底层的10块硬盘。
  • 在RAID控制器中配置五个RAID1镜像对,然后让操作系统使用五个卷而不是一个卷。
  • 在RAID控制器中配置五个RAID1镜像对,然后使用软件RAID0把五个卷做成一个逻辑卷,通过部分硬件、部分软件的实现方式,有效地实现了RAID10。

  哪个选项是最好的?这依赖于系统中所有的组件如何相互协作。不同的配置可能获得相同的结果,也可能不同。

  我们已经提醒了多种配置可能导致串行化。例如,ext3文件系统每个inode有一个单一的Mutex,所以当InnoDB是配置为innodb_flush_method=0_DIRECT(常见的配置)时,在文件系统会有inode级别的锁定。这使得它不可能对文件进行I/O并发操作,因而系统表现会远低于其理论上的能力。

  我们见过的另一个案例,请求串行地发送到一个10块盘的RAID10卷中的每个设备,使 用ReiserFS文件系统,InnoDB打开了innodb_file_per_table选项。尝试在硬件RAID1的基础上用软件RAID0做成RAID10的方式,获得了五倍多的吞吐,因为存储系统开始表现出五个硬盘同时工作的特性,而不再是一个了。造成这种情况的是一个已经被修复的Bug,但是这是一个很好的例证,说明这类事情可能发生。

  串行化可能发生在任何的软件或硬件堆栈层。如果看到这个问题发生了,可能需要更改文件系统、升级内核、暴露更多的设备给操作系统,或使用不同的软件或硬件RAID组合方式。应该检査你的设备的并发性以确保它确实是在做并发I/O。

  最后,当准备上线一种新服务器时,不要忘了做基准测试!这会帮助你确认能获得所期望的性能。例如,若一个硬盘驱动器每秒可以做200个随机读,一个有8个硬盘驱动器的RAID 10卷应该接近每秒1 600个随机读。如果观察到的结果比这个少得多,比如说每秒500个随机读,就应该研究下哪里可能有问题了。确保基准测试对I/O子系统施加了跟MySQL—样的方式的压力——例如,使用0_DIRECT标记,并且如果使用没有打开innodb_file_per_table选项的InnoDB,要用一个单一的文件测试I/O性能。通常可以使用来验证新的硬件设置都是正确的。

6.3 RAID配置和缓存

  配置RAID控制器通常有几种方法,一是可以在机器启动时进入自带的设置工具,或从命令行中运行。虽然大多数控制器提供了很多选项,但其中有两个是我们特别关注的,一是条带化阵列的块大小,还有就是控制器板载缓存(也称为RAID缓存,我们使用术语)。

  RAID条带块大小

  最佳条带块大小和具体工作负载以及硬件息息相关。从理论上讲,对随机i/o来说更大 的块更好,因为这意味着更多的读取可以从一个单一的驱动器上满足。为什么会是这样?在工作负载中找出一个典型的随机I/O操作,如果条带的块大小足够大,至少大到数据不用跨越块的边界,就只有单个硬盘需要参与读取。但是,如果块大小比要读取的数据量小,就没有办法避免多个硬盘参与读取。

  这只是理论上的观点。在实践中,许多RAID控制器在大条带下工作得不好。例如,控制器可能用缓存中的缓存单元大小作为块大小,这可能有浪费。控制器也可能把块大小、缓存大小、读取单元的大小(在一个操作中读取的数据量)匹配起来。如果读的单位太大,RAID缓存可能不太有效,最终可能会读取比真正需要的更多的数据,即使是微小的请求。

  当然,在实践中很难知道是否有数据会跨越多个驱动器。即使块大小为16 KB,与InnoDB的页大小相匹配,也不能让所有的读取对齐16KB的边界。文件系统可能会把文件分成片段,每个片段的大小通常与文件系统的块大小对齐,大部分文件系统的块大小为4KB。一些文件系统可能更聪明,但不应该指望它。

  可以配置系统以便从应用到底层存储所有的块都对齐:InnoDB的块、文件系统的块、LVM,分区偏移、RAID条带、磁盘扇区。我们的基准测试表明,当一切都对齐时,随机读和随机写的性能可能分别提髙15%和23%。对齐一切的精密技术太特殊了,无法在这细说,但其他很多地方有很多不错的信息,包括这里的博客,http://www.mysqlperformanceblog.com

  RAID缓存

  RAID缓存就是物理安装在RAID控制器上的(相对来说)少量内存。它可以用来缓冲硬盘和主机系统之间的数据。下面是RAID卡使用缓存的几个原因:

  缓存读取

控制器从磁盘读取数据并发送到主机系统后,通过缓存可以存储读取的数据,如果将来的请求需要相同的数据,就可以直接使用而无须再次去读盘。

这实际上是RAID缓存一个很糟糕的用法。为什么呢?由于操作系统和数据库服务器有自己更大得多的缓存。如果数据在这些上层缓存中命中了,RAID缓存中的数据就不会被使用。相反,如果上层的缓存没有命中,就有可能在RAID缓存中命中,但这个概率是微乎其微的。因为RAID缓存要小得多,几乎肯定会被刷新掉,被其他数据填上去了。无论哪种方式,缓冲读都是浪费RAID缓存的事。

  缓存预读数据

如果RAID控制器发现连续请求的数据,可能会决定做预读操作——就是预先取出估计很快会用到的数据。在数据被请求之前,必须有地方放这些数据。这也会使用RAID缓存来放。预读对性能的影响可能有很大的不同,应该检查确保预读确实有帮助。如果数据库服务器做了自己的智能预读(例如InnoDB的预读),RAID控制器的预读可能就没有帮助,甚至可能会干扰所有重要的缓冲和同步写入。

  缓冲写入

RAID控制器可以在高速缓存里缓冲写操作,并且一段时间后再写到硬盘。这样做有双重的好处:首先,可以快得多地返回给主机系统写“成功”的信号,远远比写入到物理磁盘上要快;其次,可以通过积累写操作从而更有效地批量操作。

  内部操作

某些RAID的操作是非常复杂的——尤其是RAID5的写入操作,其中要计算校验位,用来在发生故障时重建数据。控制器做这类内部操作需要使用一些内存。

这也是RAID5在一些RAID控制器上性能差的原因:为了好的性能需要读取大量数据到内存。有些控制器不能较好地平衡缓存写和RAID5校验位操作所需要的内存。

  一般情况下,RAID控制器的内存是一种稀缺资源,应该尽量用在刀刃上。缓存读取通常是一种浪费,但是缓冲写入是加速I/O性能的一个重要途径。许多控制器可以选择如何分配内存。例如,可以选择有多少缓存用于写入和多少用于读取。对于RAID0、RAID1和RAID10,应该把控制器缓存100%分配给写入用。对于RAID5,应该保留一些内存给内部操作。通常这是正确的建议,但并不总是适用——不同的RAID卡需要不同的配置。

  当正在用RAID缓存缓冲写入时,许多控制器可以配置延迟写入多久时间(例如一秒钟、五秒钟,等等)是可以接受的。较长的延迟意味着更多的写入可以组合在一起更有效地刷新到磁盘。缺点是写入会变得更加“突发的”。但这不是一件坏事,除非应用一连串的写请求把控制器的缓存填满了才被刷新到磁盘。如果没有足够的空间存放应用程序的写入请求,写操作就会等待。保持短的延迟意味着可以有更多的写操作,并且会更低效,但能抚平性能波动,并且有助于保持更多的空闲缓存,来接收应用程序的爆发请求。(我们在这里简化了——事实上控制器往往很复杂,不同的供应高有自己的均衡算法,所以我们只是试图覆盖基本原则。)

  写入缓冲对同步写入非常有用,例如事务日志和二进制日志(sync_binlog设置为1) 调用的fsync(),但是除非控制器有电池备份单元(BBU)或其他非易失性存储,否则不应该启用RAID缓存。不带BBU的情况下缓冲写,在断电时,有可能损坏数据库,甚至是事务性文件系统。然而,如果有BBU,启用写入缓存可以提升很多日志刷新的工作的性能,例如事务提交时刷新事务日志。

  最后要考虑的是,许多硬盘驱动器有自己的缓存,可能有“假”的fsync()操作,欺骗RAID控制器说数据已被写入物理介质。有时可以让硬盘直接挂载(而不是挂到RAID控制器上),让操作系统管理它们的缓存,但这并不总是有效。这些缓存通常在做fsync()操作时被刷新,另外同步I/O也会绕过它们直接访问磁盘,但是再次提醒,硬盘驱动器可能会骗你。应该确保这些缓存在fsync()时真的刷新了,否则就禁用它们,因为磁盘缓存没有电池供电(所以断电会丢失)。操作系统或RAID固件没有正确地管理硬盘管理已经造成了许多数据丢失的案例。 

  由于这个以及其他原因,当安装新硬件时,做一次真实的宕机测试(比如拔掉电源)是很有必要的。通常这是找出隐藏的错误配置或者诡异的硬盘问题的唯一办法。在http://brad.livejournal.com/2116715.html有个方便的脚本可以使用。 

  为了测试是否真的可以依赖RAID控制器的BBU,必须像真实情况一样切断电源一段时间,因为某些单元断电超过一段时间后就可能会丢失数据。这里再次重申,任何一个环节出现问题都会使整个存储组件失效。

7.SAN和NAS

  SAN(Storage Area Network)和NAS(Network-Attached Storage)是两个外部文件存储设备加载到服务器的方法。不同的是访问存储的方式。访问SAN设备时通过块接口,服务器直接看到一块硬盘并且可以像硬盘一样使用,但是NAS设备通过基于文件的协议来访问,例如NFS或SMB。SAN设备通常通过光纤通道协议(FCP)或iSCSI连接到服务器,而NAS设备使用标准的网络连接。还有一些设备可以同时通过这两种方式访问,比如NetApp Filer存储系统。.

  在接下来的讨论中,我们将把这两种类型的存储统一称为SAN。在后面的阅读应该记住这一点。主要区别在于作为文件还是块设备访问存储。

  SAN允许服务器访问非常大量的硬盘驱动器——通常在50块以上——并且通常配置大容量智能高速缓存来缓冲写入。块接口在服务器上以逻辑单元号(LUN)或者虚拟卷(除非使用NFS)出现。许多SAN也允许多节点组成集群来获得更好的性能或者增加存储容量。 

  目前新一代SAN跟几年前的不同。许多新的SAN混合了闪存和机械硬盘,而不仅仅是机械硬盘了。它们往往有大到TB级或以上的闪存作为缓存,不像旧的SAN,只有相对较小的缓存。此外,旧的SAN无法通过配置更大的缓存层来“扩展缓冲池”,而新的SAN有时可以。因此,相比之下新的SAN可以提供更好的性能。

7.1 SAN基准测试

  我们已经测试了多个SAN厂商的多种产品。表9-3展示了一些低并发场景下的典型测试结果。

表9-3:以16KB为单位同步单线程操作单个4GB文件的IOPS
设备顺序写顺序读随机写随机读
SAN1做了RAID524285794629258
SAN1做了RAID10176534271725213
SAN2 通过 NFS176831542056166
10k RPM硬盘,RAID1707247732302310
Intel SSD3045626624274397

  具体的SAN厂商名字和配置做了保密处理,但是可以透露的是这些都不是便宜的SAN。测试都是用同步的16KB操作,模拟InnoDB配置在0_DIRECT模式时的操作方式。

  从表9-3中可以得出什么结论?我们测试的系统不是都可以直接比较的,所以盯着这些好看的数据点来看不能客观地做出评价。然而,这些结果很好地说明了这类设备的总体性能表现,SAN可以承受大量的连续写入,因为可以缓冲并合并I/O。SAN提供顺序读取没有问题,因为可以做预读并从缓存中提出数据。在随机写上会慢一些,因为写入操作不能较好地合并。因为读取通常在缓存中无法命中,必须等待硬盘驱动器响应,所以SAN很不适合做随机读取。最重要的是,服务器和SAN之间有传输延迟。这是为什么通过NFS连接SAN时,提供的每秒随机读还不如一块本地磁盘的原因。

  我们已经用较大尺寸的文件做了基准测试,但没有用其他尺寸的文件在上述的系统中测试。然而,无论结果如何,可以预见的是:不管多么强大的SAN,对于小的随机操作,都无法获得良好的响应时间和吞吐量。延时的大部分都是由于服务器和SAN之间的链路导致的。 

  我们的基准测试显示的每秒操作吞吐量,并没有说出完整的故事。至少有三个重要指标:每秒吞吐量字节数、并发性和响应时间。在一般情况下,相对于直接连接存储(DAS),SAN无论读写都可以提供更好的顺序I/O吞吐量。大多数SAN可以支持大量的并发性,但基准测试只有一个线程,这可以说明最坏的情况。但是,当工作集不能放到SAN的缓存时,随机读在吞吐量和延迟方面将变得很差,甚至延迟将高于直接访问本地存储。

7.2 使用基于NFS或SMB的SAN

  某些SAN,例如NetApp Filet存储,通常通过NFS访问,而不是通过光纤或者iSCSI。这曾经是我们希望避免的情况,但是NFS今天比以前好了很多。通过NFS可以获得相当好的性能,尽管需要专门配置网络。SAN厂商提供的最佳实践指导可以帮助了解怎样配置。

  主要考虑的事情是NFS协议自身怎样影响性能。许多文件元信息操作,通常在本地文件系统或者SAN设备(非NAS)的内存中执行,但是在NAS上可能需要一次网络来回发送。例如,我们提醒过把二进制日志存在NFS上会损害服务器性能,即使关闭Sync_binlog也无济于事。 

  也可以通过SMB协议访问SAN或者NAS,需要考虑的问题类似:可能有更多的网络通信,会对延迟产生影响。对传统桌面用户这没什么影响,他们通常只是在挂载的驱动器上存储电子表格或者其他文档,或者只是为了备份复制一些东西到另一台服务器。但是用作MySQL读写它的文件,就会有严重的性能问题。

7.3 MySQL在SAN上的性能

  I/O基准测试只是一种观察的方式,MySQL在SAN上具体性能表现如何?在许多情况下,MySQL运行得还可以,可以避免很多SAN可能导致性能下降的情况。仔细地做好逻辑和物理设计,包括索引和适当的服务器硬件(尽量配置更多的内存!)可避免很多的随机I/O操作,或者可以转化为顺序的I/O。然而,应该知道的是,通过一段时间的运行,这种系统可以达到一个微妙的平衡——引入一个新的査询,Schema的变化或频繁的操作,都很容易扰乱这种平衡。

  例如,一个SAN用户,我们知道他对每天的性能表现非常满意,直到有一天他想清理一张变得非常大的旧表中的大量数据行。这会导致一个长时间运行的DELETE语句,每秒只能删几百行,因为删除每行所需的随机I/0,SAN无法有效快速地执行。有没有办法来加快操作,它只是要花费很长的时间才能完成。另一个让他大吃一惊的事是,当对一个大表执行ALTER类似的操作时明显速度减慢。

  这些都是些典型的例子,哪些工作放在SAN上不合适:执行大量的随机I/O的单线程任务。在当前版本的MySQL中,复制是另一个单线程任务。因此,备库的数据存储在SAN上,可能更容易落后于主库。批处理作业也可能运行得更慢。在非高峰时段或周末执行一个一次性的延迟敏感的操作是可以的,但是服务器的很多部分依然需要很好的性能,例如拷贝、二进制日志,以及InnoDB的事务日志上总是需要很好的小随机I/O性能。

7.4 应该用SAN吗

  嗯,这是个长期存在的问题——在某些情况下,数百万美元的问题。有很多因素要考虑, 以下我们列出其中的几个。

  备份

集中存储使备份更易于管理。当所有数据都存储在一个地方时,可以只备份SAN,只要确保已经确认过了所有的数据都在。这简化了问题,例如“你确定我们要备份所有的数据吗?”此外,某些设备有如连续数据保护(CDP)以及强大的快照功能等功能,使得备份更容易、更灵活。

  简化容量规划

不确定需要多大容量吗?SAN可以提供这种能力——购买大容量存储、分享给很多应用,并且可以调整大小并按需求重新发布。

  存储整合还是服务器整合

某些CIO盘点数据中心运行了哪些东西时,可能会得出结论说大量的I/O容量被浪费了,这是把存储空间和I/O容量混为一谈了。毫无疑问的是,如果集中存储可以确保更好地利用存储资源,但这样做将会如何影响使用存储的系统?典型的数据库操作在性能上可以达到数量级的差异,因此可能会发现,如果集中存储可能需要增加10倍的服务器(或更多)才能处理原来的工作。尽管数据中心的I/O容量在SAN上可以更好地被利用,但是会导致其他系统无法充分被利用(例如数据库服务器花费大量时间等待I/O、应用程序服务器花费大量时间等待数据库,依此类推)。在现实中我们已经看到过很多通过分散存储来整合服务器并削减成本的例子。

  高可用

有时人们认为SAN是高可用解决方案。之所以会这样认为,可能是因为对高可用的真实含义的理解出现了分歧。

根据我们的经验,SAN经常与故障和停机联系在一起,这不是因为它们不可靠——它们没什么问题,也确实很少出故障——只是因为人们都不愿意相信这样的工程奇迹其实也会坏的,因而缺乏这方面的准备。此外,SAN有时是一个复杂的、神秘的黑盒子,当出问题的时候没有人知道该如何解决,并且价格昂贵,难以快速构建管理SAN所需的专业知识。大多数的SAN都对外缺乏可见性(就是个黑盒子),这也是为什么不应该只是简单地信任SAN管理员、支持人员或管理控制台的原因。我们看到过所有这三种人都错了的情况:当SAN出了问题,如出现硬盘驱动器故障导致性能下降的案例。这是另一个推荐使用sysbench的理由:sysbench可以快速地完成一个I/O基准测试以证明是否是SAN的问题。

  服务器之间的交互

共享存储可能会导致看似独立的系统实际上是相互影响的,有时甚至会很严重。例如,我们知道一个SAN用户有个很粗放的认识,当开发服务器上有I/O密集型操作时,会引起数据库服务器几乎陷于停顿。批处理作业、ALTER TABLE、备份——任何一个系统上产生大量的I/O操作都可能会导致其他系统的I/O资源不足。有时的影响远远比直觉想象的糟糕,一个看似不起眼的操作可能会导致严重的性能下降。

  成本

成本是什么?管理和行政费用?每秒I/O操作数(IOPS)中每个I/O操作的成本? 标价?

有充分的理由使用SAN,但无论销售人员说什么,至少从MySQL需要的性能类型来看,SAN不是最佳的选择。(选择一个SAN供应商并跟它们的销售谈,你可能听到他们一般也是同意的,然后告诉你他们的产品是一个例外。)如果考虑性价比,结论会更加清楚,因为闪存存储或配置有电池支持写缓存的RAID控制器加上老式硬盘驱动器,可以在低得多的价格下提供更好的性能。

关于这个话题,不要忘了让销售给你两台SAN的价格。至少需要两台,否则这台昂贵的SAN可能会成为故障中的单点。

  有许多“血泪史”可以引以为戒,这不是试图吓唬你远离SAN。我们知道的SAN用户都非常地爱这些存储!如果正在考虑是否使用SAN,最重要的事情是想清楚要解决什么问题。SAN可以做很多事情,但解决性能问题只是其中很小的一部分。相比之下,当不要求很多髙性能的随机I/0,但是对某些功能感兴趣的话,如快照、存储整合、重复数据删除和虚拟化,SAN可能非常适合。

  因此,大多数Web应用不应该让数据库使用SAN,但SAN在所谓的企业级应用很受欢迎。企业通常不太受预算限制,所以能够负担得起作为“奢侈品”的SAN。(有时SAN甚至作为一种身份的象征!)

8.使用多磁盘卷

  我们迟早都会碰到文件应该放哪的问题,因为MySQL创建了多种类型的文件:

  • 数据和索引文件
  • 事务日志文件
  • 二进制日志文件
  • 常规日志(例如,错误日志、査询日志和慢査询日志)
  • 临时文件和临时表

  MySQL没有提供复杂的空间管理功能。默认情况下,只是简单地把每个Schema的文件放入一个单独的目录。有少量选项来控制数据文件放哪。例如,可以指定MyISAM表的索引位置,也可以使用MySQL5.1的分区表。

  如果正在用InnoDB默认配置,所有的数据和文件都放在一组数据文件(共享表空间)中,只有表定义文件放在数据目录。因此,大部分用户把所有的数据和文件放在了单独的卷。 

  然而,有时使用多个卷可以帮助解决I/O负载高的问题。例如,一个批处理作业需要写入很多数据到一张巨大的表,将这张表放在单独的卷上,可以避免其他查询的I/O受到影响。理想的情况下,应该分析不同数据的I/O访问类型,才能把数据放在适当的位置,但这很难做到,除非已经把数据放在不同的卷上。

  你可能已经听过标准建议,就是把事务日志和数据文件放在不同的卷上面,这样日志的顺序I/O和数据的随机I/O不会互相影响。但是除非有很多硬盘(20或更多)或者闪存存储,否则在这样做之前应该考虑清楚代价。

  二进制日志和数据文件分离的真正的优势,是减少事故中同时丢失数据和日志文件的可能性。如果RAID控制器上没有电池支持的写缓存,把它们分开是很好的做法。

  但是,如果有备用电池单元,分离卷就可能不是想象中那么必要了。性能差异很小是主要原因。这是因为即使有大量的事务日志写入,其中大部分写入都很小。因此,RAID缓存通常会合并I/O请求,通常会得到每秒的物理顺序写请求。这通常不会干预数据文件的随机I/O,除非RAID控制器真的整体上饱和了。一般的日志,其中有连续的异步写入、负荷也低,可以较好地与数据分享一个卷。    ^

  将日志放在独立的卷是否可以提升性能?通常情况下是的,但是从成本的角度来看这个问题,是否真的值得这么做,答案往往是否定的,尽管很多人不这么认为。

  原因是:为事务日志提供专门的硬盘是很昂贵的。假设有六个硬盘驱动器,最常规的做法是把所有六块盘放到一个RAID卷,或者分成两部分,四个放数据,两个放事务日志。不过如果这样做,就减少了三分之一的硬盘放数据文件,这会导致性能显著地下降。此外,专门提供两个驱动器,对负载的影响也微不足道(假设RAID控制器有电池支持的写缓存)。 

  另一方面,如果有很多硬盘,投入一些给事务日志可能会从中受益。例如,一共有30块硬盘,可以分两块硬盘(配置为一个RAID1的卷)给日志,能让日志写尽可能快。对于额外的性能,也可以在RAID控制器中分配一些写缓存空间给这个RAID卷。

  成本效益不是唯一考虑的因素。可能想保持InnoDB的数据和事务日志在同一个卷的另一个原因是,这种策略可以使用LVM快照做无锁的备份。某些文件系统允许一致的多卷快照,并且对这些文件系统,这是一个很轻量的操作,但对于ext3有很多东西需要注意。

  如果已经启用sync_binlog,二进制日志在性能方面与事务日志相似了。然而,二进制 日志存储跟数据放在不同的卷,实际上是一个好主意——把它们分开存放更安全,因此即使数据丢失,二进制日志也可以保存下来。这样,可以使用二进制日志做基于时间点的恢复。这方面的考虑并不适用于InnoDB的事务日志,因为没有数据文件,它们就没用了,你不能将事务日志应用到昨晚的备份。(事务日志和二进制日志之间的区别在其他数据库的DBA看来,很难搞明白,在其他数据库这就是同一个东西。)

  另外一个常见的场景是分离出临时目录的文件,MySQL做filesorts(文件排序)和使用 磁盘临时表时会写到临时目录。如果这些文件不会太大的话,最好把它们放在临时内存文件系统,如tmpfs。这是速度最快的选择。如果在你的系统上这不可行,就把它们放在操作系统盘上。 

  典型的磁盘布局是有操作系统盘、交换分区和二进制日志的盘,它们放在RAID1卷上。还要有一个单独的RAID5或RAID10卷,放其他的一切东西。

9.网络配置

  就像延迟和吞吐量是硬盘驱动器的限制因素一样,延迟和带宽(实际上和吞吐量是同一回事)也是网络连接的限制因素。对于大多数应用程序来说,最大的问题是延时。典型的应用程序都需要传输很多很小的网络包,并且每次传输的轻微延迟最终会被累加起来。

  运行不正常的网络通常也是主要的性能瓶颈之一。丢包是一个普遍存在的问题。即使1%的丢包率也足以造成显著的性能下降,因为在协议栈的各个层次都会利用各种策略尝试修复问题,例如等待一段时间再重新发送数据包,这就增加了额外的时间。另一个常见的问题是域名解析系统(DNS)损坏或者变慢了。

  DNS足以称为“阿基里斯之踵”,因此在生产服务器上启用skip_name_resolve是个好主意。损坏或缓慢的DNS解析对许多应用程序都是个问题,对MySQL尤为严重。当MySQL收到一个连接请求时,它同时需要做正向和反向DNS査找。有很多原因可能导致这个过程出问题。当问题出现时,会导致连接被拒绝,或者使得连接到服务器的过程变慢,这通常都会造成严重的影响,甚至相当于遭遇了拒绝服务攻击(DDOS)。如果启用skip_name_resolve选项,MySQL将不会做任何DNS査找的工作。然而,这也意味着,用户账户必须在host列使用具有唯一性的IP地址,  “localhost”或者IP地址通配符。那些在host列使用主机名的用户账户都将不能登录。

  典型的Web应用中另一个常见的问题来源是TCP积压,可以通过MySQL的back_log选项来配置。这个选项控制MySQL的传入TCP连接队列的大小。在每秒有很多连接创建和销毁的环境中,默认值50是不够的。设置不够的症状是,客户端会看到零星的“连接被拒绝”的错误,配以三秒超时规则。在繁忙的系统中这个选项通常应加大。把这个选项增加到数百甚至数千,似乎没有任何副作用,事实上如果你看得远一些,可能还需要配置操作系统的TCP网络设置。在GNU/Linux系统,需要增加somaxconn限制,默认只有128,并且需要检査sysctl的tcp_max_syn_back_log设置。

  应该设计性能良好的网络,而不是仅仅接受默认配置的性能。首先,分析节点之间有多 少跳跃点,以及物理网络布局之间的映射关系。例如,假设有10个网页服务器,通过千兆以太网(1GigE)连接到“网页”交换机,这个交换机也通过千兆网络连接到“数据库”交换机。如果不花时间去追踪连接,可能不会意识到从所有数据库服务器到所有网页服务器的总带宽是有限的!并且每次跨越交换机都会增加延时。

  监控网络性能和所有网络端口的错误是正确的做法,要监控服务器、路由器和交换机的每个端口。多路由流量绘图器(Multi Router Traffic Grapher),或者说MRTG(http://oss.oetiker.ch/mrtg/)对设备监控而言是个靠得住的开源解决方案。其他常见的网络性能监控工具(与设备监控不同)还有Smokeping和Cacti。

  网络物理隔离也是很重要的因素。城际网络相比数据中心的局域网的延迟要大得多,即使从技术上来说带宽是一样的。如果节点真的相距甚远,光速也会造成影响。例如,在美国的西部和东部海岸都有数据中心,相隔约3 000公里。光的速度是186 000米每秒,因此一次通信不可能低于16毫秒,往返至少需要32毫秒。物理距离不仅是性能上的考虑,也包括设备之间通信的考虑。中继器、路由器和交换机,所有的性能都会有所降级。再次,越广泛地分隔开的网络节点,连接的不可预知和不可靠因素越大。

  尽可能避免实时的跨数据中心的操作是明智的。如果不可能做到这一点,也应该确保应用程序能正常处理网络故障。例如,我们不希望看到由于Web服务器通过丢包严重的网络连接远程的数据中心时,由于Apache进程挂起而新建了很多进程的情况发生。 

  在本地,请至少用千兆网络。骨干交换机之间可能需要使用万兆以太网。如果需要更大的带宽,可以使用网络链路聚合:连接多个网卡(NIC),以获得更多的带宽。链路聚合本质上是并行网络,作为高可用策略的一部分也很有帮助。

  如果需要非常高的吞吐量,也许可以通过改变操作系统的网络配置来提髙性能。如果连接不多,但是有很多的査询和很大的结果集,则可以增加TCP缓冲区的大小。具体的实现依赖于操作系统,对于大多数的linux系统,可以改变/etc/sysctl.conf中的值并执行sysctl -p或者使用文件系统写入一个新的值到/proc/sys/net里面的文件。搜索“TCP tuning guide”,可以找到很多好的在线教程。

  然而,调整设置以有效地处理大量连接和小査询的情况通常更重要。比较常见的调整之一,就是要改变本地端口的范围。系统的默认值如下:

[root@server ~]# cat /proc/sys/net/ipv4/ip_local_port_range
32768   61000

  有时你也许需要改变这些值,调整得更大一些。例如:

[root@server ~]# echo 1024 65535 > /proc/sys/net/ipv4/ip_local_port_range

  如果要允许更多的连接进入队列,可以做如下操作:

[root@server ~]# echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog

  对于只在本地使用的数据库服务器,对于连接端还未断开,但是通信已经中断的事件中使用的套接字,可以缩短TCP保持状态的超时时间。在大多数系统上默认是一分钟,这个时间太长了:

[root@server ~]# echo <value> > /proc/sys/net/ipv4/tcp_fin_timeout

  这些设置大部分时间可以保留默认值。通常只有当发生了特殊情况,例如网络性能极差或非常大量的连接,才需要进行修改。在互联网上搜索“TCP variables”,可以发现很多不错的阅读资料,除了上面提到的,还能看到很多其他的变量。

10.选择操作系统

  GNU/Linux如今是髙性能MySQL最常用的操作系统,但是MySQL本身可以运行在很多操作系统上。

  Solaris是SPARC硬件上的领跑者,在x86硬件上也可以运行。Solaris常用在要求高可靠的应用上面。Solaris在某些方面的易用性可能没有GNU/Linux的名气大,但确实是一个坚固的操作系统,包含许多先进的功能。尤其是Solaris10增加了ZFS文件系统,包含了很多先进的故障排除工具(如DTrace)、良好的多线程性能,以及称为Solaris Zones的虚拟化技术,有助于资源管理。

  FreeBSD是另一种选择。它历来与MySQL配合有一些问题,大多时候都涉及到线程支持,但新的版本要好得多。如今,看到MySQL在FreeBSD上大规模部署的场景并不是什么稀罕事。ZFS也可以在FreeBSD上使用。

  通常用于开发和桌面应用程序的MySQL选择的是Windows。也有企业级的MySQL部署在Windows上,但一般的企业级MySQL更多的还是部署在类UNIX操作系统上。我们不希望引起任何有关操作系统的争论,需要指出的是在异构操作系统环境中使用MySQL是不存在问题的。在类UNIX的操作系统上运行的MySQL服务器,同时在Windows上运行Web服务器,然后通过高品质的.NET连接器(这是MySQL免费提供的)进行连接,这是一个非常合理的架构。从UNIX连接到Windows上的MySQL服务器和连接到另一台UNIX上的MySQL服务器一样简单。

  谈到GNU/Linux发行版时,个人的喜好往往是决定性的因素。我们认为最好的策略是使用专为服务器应用程序设计的发行版,而不是桌面发行版。要考虑发行版的生命周期、发布和更新政策,并检査供应商的支持是否有效。红帽子企业版Linux是一个高品质、稳定的发行版;CentOS是一个受欢迎的二进制兼容替代品(免费),但已经因为延后时间较长获得了一些批评;还有Oracle发行的Oracle Enterprise Linux;另外,Ubuntu和Debian也是流行的发行版。

11.选择文件系统

  文件系统的选择非常依赖于操作系统。在许多系统中,如Windows就只有一到两个选择,而且只有一个(NTFS)真的是能用的。比较而言,GNU/Linux则支持多种文件系统。

  许多人想知道哪个文件系统在GNU/Linux上能提供最好的MySQL性能,或者更具体一些,哪个对InnoDB和MyISAM而言是最好的选择。实际的基准测试表明,大多数文件系统在很多方面都非常接近,但测试文件系统的性能确实是一件烦心事。文件系统的性能是与工作负载相关的,没有哪个文件系统是“银弹”。大部分情况下,给定的文件系统不会明显地表现得与其他文件系统不一样。除非遇到了文件系统的限制,例如,它怎么支持并发、怎么在多文件下工作、怎么对文件切片,等等。

  要考虑的更重要的问题是崩溃恢复时间,以及是否会遇到特定的限制,如目录下有许多 文件会导致运行缓慢(这是ext2和旧版本ext3下一个臭名昭著的问题,但当前版本的ext3和ext4中用dir_index选项解决了问题)。文件系统的选择对确保数据安全是非常重要的,所以我们强烈建议不要在生产系统做实验。

  如果可能,最好使用日志文件系统,例如ext3、ext4、XFS、ZFS或者JFS。如果不这么做,崩溃后文件系统的检査可能耗费相当长的时间。如果系统不是很重要,非日志文件系统性能可能比支持事务的好。例如,ext2可能比ext3工作得好,或者可以使用加关闭ext3的日志记录功能。挂载时间对某些文件系统也是一个因素。例如,ReiserFS,在一个大的分区上可能用很长时间来挂载和执行日志恢复。

  如果使用ext3或者其继承者ext4,有三个选项来控制数据怎么记日志,这可以放在/etc/fstab中作为挂载选项:

  data=writeback

这个选项意味着只有元数据写入日志。元数据写入和数据写入并不同步。这是最快的配置,对InnoDB来说通常是安全的,因为InnoDB有自己的事务日志。唯一的例外是,崩溃时恰好导致.frm文件损坏了。

这里给出一个使用这个配置可能导致问题的例子。当程序决定扩展一个文件使其更大,元数据(文件大小)会在数据实际写到(更大的)文件之前记录并写下操作情况。结果就是文件的尾部——最新扩展的区域——会包含垃圾数据。 

  data=ordered

这个选项也只会记录元数据,但提供了一些一致性保证,在写元数据之前会先写数据,使它们保持一致。这个选项只是略微比writeback选项慢,但是崩溃时更安全。在此配置中,如果我们再次假设程序想要扩展一个文件,该文件的元数据将不能反映文件的新大小,直到驻留在新扩展区域中的数据被写到文件中了。 

  data=journal

此选项提供了原子日志的行为,在数据写入到最终位置之前将记录到日志中。这个选项通常是不必要的,它的开销远远高于其他两个选项。然而,在某些情况下反而可以提高性能,因为日志可以让文件系统延迟把数据写入最终位置的操作。

  不管哪种文件系统,都有一些特定的选项最好禁用,因为它们没有提供任何好处,反而增加了很多开销。最有名的是记录访问时间的选项,甚至读文件或目录时也要进行一次写操作。在/etc/fstab中添加noatime、nodiratime挂载选项可以禁用此选项,这样做有时可以提高5%〜10%的性能,具体取决于工作负载和文件系统(虽然在其他场景下差别可能不是太大)。下面是中的一个例子,对ext3选项做设置的行:

/dev/sda2 /usr/lib/mysql ext3 noatime,nodiratime,data=writeback 0 1

  还可以调整文件系统的预读的行为,因为这可能也是多余的。例如,InnoDB有自己的预读策略,所以文件系统的预读就是重复多余的。禁用或限制预读对Solaris的UFS尤其有利。使用0_DIRECT选项会自动禁用预读。

  一些文件系统可能不支持某些需要的功能。例如,若让InnoDB使用0_DIRECT刷新方式,文件系统能支持Direct I/O是非常重要的。此外,一些文件系统处理大量底层驱动器比其他的文件系统更好,举例来说XFS在这方面通常比ext3好。最后,如果打算使用LVM快照来初始化备库或进行备份,应该确认选择的文件系统和LVM版本能很好地协同工作。

  表9-4某些常见文件系统的特性总结。

9-4:常见文件系统特性
文件系统操作系统支持日志大目录
ext2GNU/Linux
ext3GNU/Linux可选可选/部分
ext4GNU/Linux
HFS PlusMac OS可选
JFSGNU/Linux
NTFSWindows
ReiserFSGNU/Linux
UFS(Solaris)Solaris可调的
UFS(FreeBSD)FreeBSD可选/部分
UFS2FreeBSD可选/部分
XFSGNU/Linux
ZFSSolaris,FreeBSD

  我们通常建议客户使用XFS文件系统。ext3文件系统有太多严重的限制,例如inode只有一个互斥变量,并且fsync()时会刷新所有脏块,而不只是单个文件。很多人感觉ext4文件系统用在生产环境有点太新了,不过现在似乎正日益普及。

12.选择磁盘队列调度策略

  在GNU/Linux上,队列调度决定了到块设备的请求实际上发送到底层设备的顺序。默认情况下使用cfq (Completely Fair Queueing,完全公平排队)策略。随意使用的笔记本和台式机使用这个调度策略没有问题,并且有助于防止I/O饥饿,但是用于服务器则是有问题的。在MySQL的工作负载类型下,cfq会导致很差的响应时间,因为会在队列中延迟一些不必要的请求。

  可以用下面的命令来査看系统所有支持的以及当前在用的调度策略:

$ cat /sys/block/sda/queue/scheduler
noop deadline [cfq]

  这里sda需要替换成想査看的磁盘的盘符。在我们的例子中,方括号表示正在使用的调度策略。cfq之外的两个选项都适合服务器级的硬件,并且在大多数情况下,它们工作同样出色。noop调度适合没有自己的调度算法的设备,如硬件RAID控制器和SAN。deadline则对RAID控制器和直接使用的磁盘都工作良好。我们的基准测试显示,这两者之间的差异非常小。重要的是别用cfq,这可能会导致严重的性能问题。

  不过这个建议也需要有所保留的,因为磁盘调度策略实际上在不同的内核有很多不一样的地方,千万不能望文生义。

13.线程

  MySQL每个连接使用一个线程,另外还有内部处理线程、特殊用途的线程,以及所有存储引擎创建的线程。在MySQL5.5中,Oracle提供了一个线程池插件,但目前尚不清楚在实际应用中能获得什么好处。

  无论哪种方式,MySQL都需要大量的线程才能有效地工作。MySQL确实需要内核级线程的支持,而不只是用户级线程,这样才能更有效地使用多个CPU。另外也需要有效的同步原子,例如互斥变量。操作系统的线程库必须提供所有的这些功能。

  GNU/Linux提供两个线程库:LinuxThreads和新的原生POSIX线程库(NPTL)。LinuxThreads在某些情况下仍然使用,但现在的发行版已经切换到NPTL,并且大部分应用已经不再加载LinuxThreads。NPTL更轻量,更高效,也不会有那些LinuxThreads遇到的问题。

  FreeBSD会加载许多线程库。从历史上看,它对线程的支持很弱,但现在已经变得好多了,在一些测试中,甚至优于SMP系统上的GNU/Linux。在FreeBSD 6和更新版本,推荐的线程库是libthr,早期版本使用的linuxthreads,是FreeBSD从GNU/Linux上移植的LinuxThreads库。

  通常,线程问题都是过去的事了,现在GNU/Linux和FreeBSD都提供了很好的线程库。

  Solaris和Windows—直对线程有很好的支持,尽管直到5.5发布之前,MyISAM都不能 在Windows下很好地使用线程,但5.5里有显著的提升。

14.内存交换区

  当操作系统因为没有足够的内存而将一些虚拟内存写到磁盘就会发生内存交换。内存交换对操作系统中运行的进程是透明的。只有操作系统知道特定的虚拟内存地址是在物理内存还是硬盘。

  内存交换对MySQL性能影响是很糟糕的。它破坏了缓存在内存的目的,并且相对于使用很小的内存做缓存,使用交换区的性能更差。MySQL和存储引擎有很多算法来区别对待内存中的数据和硬盘上的数据,因为一般都假设内存数据访问代价更低。

  因为内存交换对用户进程不可见,MySQL(或存储引擎)并不知道数据实际上已经移动到磁盘,还会以为在内存中。

  结果会导致很差的性能。例如,若存储引擎认为数据依然在内存,可能觉得为“短暂” 的内存操作锁定一个全局互斥变量(例如InnoDB缓冲池Mutex)是0K的。如果这个操作实际上引起了硬盘I/O,直到I/O操作完成前任何操作都会被挂起。这意味着内存交换比直接做硬盘I/O操作还要糟糕。

  在GNU/Linux上,可以用vmstat(在下一部分展示了一些例子)来监控内存交换。最好查看si和so列报告的内存交换I/O活动,这比看swpd列报告的交换区利用率更重要。swpd列可以展示那些被载入了但是没有被使用的进程,它们并不是真的会成为问题。我们喜欢si和so列的值为0,并且一定要保证它们低于每秒10个块。

  极端的场景下,太多的内存交换可能导致操作系统交换空间溢出。如果发生了这种情况,缺乏虚拟内存可能让MySQL崩溃。但是即使交换空间没有溢出,非常活跃的内存交换也会导致整个操作系统变得无法响应,到这种时候甚至不能登录系统去杀掉MySQL进程。有时当交换空间溢出时,甚至Linux内核都会完全hang住。

  绝不要让系统的虚拟内存溢出!对交换空间利用率做好监控和报警。如果不知道需要多少交换空间,就在硬盘上尽可能多地分配空间,这不会对性能造成冲击,只是消耗了硬盘空间。有些大的组织清楚地知道内存消耗将有多大,并且内存交换被非常严格地控制,但是对于只有少量多用途的MySQL实例,并且工作负载也多种多样的环境,通常不切实际。如果后者的描述更符合实际情况,确认给服务器一些“呼吸”的空间,分配足够的交换空间。

  在特别大的内存压力下经常发生的另一件事是内存不足(OOM),这会导致踢掉和杀掉一些进程。在MySQL进程这很常见。在另外的进程上也挺常见,比如SSH,甚至会让系统不能从网络访问。可以通过设置SSH进程的oom_adj或oom_score_adj值来避免这种情况。

  可以通过正确地配置MySQL缓冲来解决大部分内存交换问题,但是有时操作系统的虚 拟内存系统还是会决定交换MySQL的内存。这通常发生在操作系统看到MySQL发出了大量I/0,因此尝试增加文件缓存来保存更多数据时。如果没有足够的内存,有些东西就必须被交换出去,有些可能就是MySQL本身。有些老的Linux内核版本也有一些适得其反的优先级,导致本不应该被交换的被交换出去,但是在最近的内核都被缓解了。

  有些人主张完全禁用交换文件。尽管这样做有时在某些内核拒绝工作的极端场景下是可行的,但这降低了操作系统的性能(在理论上不会,但是实际上会的)。同时这样做也是很危险的,因为禁用内存交换就相当于给虚拟内存设置了一个不可动摇的限制。如果MySQL需要临时使用很大一块内存,或者有很耗内存的进程运行在同一台机器(如夜间批量任务),MySQL可能会内存溢出、崩溃,或者被操作系统杀死。

  操作系统通常允许对虚拟内存和I/O进行一些控制。我们提供过一些GNU/Linux上控制它们的办法。最基本的方法是修改/proc/sys/vm/swappiness为一个很小的值,例如0或1。这告诉内核除非虚拟内存完全满了,否则不要使用交换区。下面是如何检査这个值的例子:

$ cat /proc/sys/vm/swappiness
60

  这个值显示为60,这是默认的设置(范围是0〜100)。对服务器而言这是个很糟糕的默认值。这个值只对笔记本适用。服务器应该设置为0:

$ echo 0 > /proc/sys/vm/swappiness

  另一个选项是修改存储引擎怎么读取和写入数据。例如,使用innodb_flush_method=0_DIRECT,减轻I/O压力。Direct I/O并不缓存,因此操作系统并不能把MySQL视为增加文件缓存的原因。这个参数只对InnoDB有效。你也可以使用大页,不参与换入换出。这对MyISAM和InnoDB都有效。

  另一个选择是使用MySQL的memlock配置项,可以把MySQL锁定在内存。这可以避免交换,但是也可能带来危险:如果没有足够的可锁定内存,MySQL在尝试分配更多内存时会崩溃。这也可能导致锁定的内存太多而没有足够的内存留给操作系统。

  很多技巧都是对于特定内核版本的,因此要小心,尤其是当升级的时候。在某些工作负载下,很难让操作系统的行为合情合理,并且仅有的资源可能让缓冲大小达不到最满意的值。

15.操作系统状态

  操作系统会提供一些帮助发现操作系统和硬件正在做什么的工具。在这一节,我们会展示一些例子,包括关于怎样使用两个最常用的工具——iostat和vmstat。如果系统不提供它们中的任何一个,有可能提供了相似的替代品。因此,我们的目的不是让大家熟练使用如iostat和vmstat,而是告诉你用类似的工具诊断问题时应该看什么指标。

  除了这些,操作系统也许还提供了其他的工具,如mpstat或者sar。如果对系统的其他部分感兴趣,例如网络,你可能希望使用ifconfig(除了其他信息,它能显示发生了多少次网络错误)或者netstat。

  默认情况下,vmstat和iostat只是生成一个报告,展示自系统启动以来很多计数器的平均值,这其实没什么用。然而,两个工具都可以给出一个间隔参数,让它们生成增量值的报告,展示服务器正在做什么,这更有用。(第一行显示的是系统启动以来的统计,通常可以忽略这一行。)

15.1 如何阅读vmstat的输出

  先看一个vmstat的例子。用下面的命令让它每5秒钟打印出一个报告:

$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 0  0   2632  25728  23176 740244    0    0   527   521   11    3 10  1 86  3
 0  0   2632  27808  23180 738248    0    0     2   430  222   66  2  0 97  0

  可以用Ctrl+C停止vmstat。可以看到输出依赖于所用的操作系统,因此可能需要阅读一下手册来解读报告。

  刚启动不久,即使采用增量报告,第一行的值还是显示自系统启动以来的平均值,第二行开始展示现在正在发生的情况,接下来的行会展示每5秒的间隔内发生了什么。每一列的含义在头部,如下所示:

  procs

r这一列显示了多少进程正在等待CPU,b列显示多少进程正在不可中断地休眠(通常意味着它们在等待I/0,例如磁盘、网络、用户输入,等等)。 

  memory

swpd列显示多少块被换出到了磁盘(页面交换)。剩下的三个列显示了多少块是空闲的(未被使用)、多少块正在被用作缓冲,以及多少正在被用作操作系统的缓存。 

  swap

这些列显示页面交换活动:每秒有多少块正在被换入(从磁盘)和换出(到磁盘)。它们比监控swpd列重要多了。

大部分时间我们喜欢看到si和so列是0,并且我们很明确不希望看到每秒超过10个块。突发性的高峰一样很糟糕。

  io

这些列显示有多少块从块设备读取(bi)和写出(bo)。这通常反映了硬盘I/O。 

  system

这些列显示了每秒中断(in)和上下文切换(cs)的数量。 

  cpu

这些列显示所有的CPU时间花费在各类操作的百分比,包括执行用户代码(非内核)、执行系统代码(内核)、空闲,以及等待I/O。如果正在使用虚拟化,则第五个列可能是st,显示了从虚拟机中“偷走”的百分比。这关系到那些虚拟机想运行但是系统管理程序转而运行其他的对象的时间。如果虚拟机不希望运行任何对象,但是系统管理程序运行了其他对象,这不算被偷走的CPU时间。

  vmstat的输出跟系统有关,所以如果看到跟我们展示的例子不同的输出,应该阅读系统的vmstat(8)手册。一个重要的提示是:内存、交换区,以及I/O统计是块数而不是字节。在GNU/Linux,块大小通常是1 024字节。

15.2 如何阅读iostat的输出

  现在让我们转移到iostat。默认情况下,它显示了与相同的CPU使用信息。我们通常只是对I/O统计感兴趣,所以使用下面的命令只展示扩展的设备统计:

$ iostat -dx 5
Device:  rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda         1.6    2.8 2.5 1.8  138.8   36.9     40.7      0.1  23.2   6.0   2.6

  与vmstat—样,第一行报告显示的是自系统启动以来的平均值(通常删掉它节省空间),然后接下来的报告显示了增量的平均值,每个设备一行。

  有多种选项显示和隐藏列。官方文档有点难以理解,因此我们必须从源码中挖掘真正显示的内容是什么。说明的列信息如下:

  rrqm/s和wrqm/s

每秒合并的读和写请求。“合并的”意味着操作系统从队列中拿出多个逻辑请求合并为一个请求到实际磁盘。 

  r/s和w/s

每秒发送到设备的读和写请求。 

  rsec/s和wsec/s

每秒读和写的扇区数。有些系统也输出为rkB/s和wkB/s,意为每秒读写的千字节数。

为了简洁,我们省略了那些指标说明。 

  avgrq-sz

请求的扇区数。 

  avgqu-sz

在设备队列中等待的请求数。 

  await

磁盘排队上花费的毫秒数。很不幸,iostat没有独立统计读和写的请求,它们实际上不应该被一起平均。当你诊断性能案例时这通常很重要。 

  svctm

服务请求花费的毫秒数,不包括排队时间。

  %Util  

至少有一个活跃请求所占时间的百分比。如果熟悉队列理论中利用率的标准定义,那么这个命名很莫名其妙。它其实不是设备的利用率。超过一块硬盘的设备(例如RAID控制器)比一块硬盘的设备可以支持更高的并发,但是%util从来不会超过100%,除非在计算时有四舍五入的错误。因此,这个指标无法真实反映设备的利用率,实际上跟文档说的相反,除非只有一块物理磁盘的特殊例子。

  可以用iostat的输出推断某些关于机器I/O子系统的实际情况。一个重要的度量标准是请求服务的并发数。因为读写的单位是每秒而服务时间的单位是千分之一秒,所以可以利用利特尔法则(Little’s Law)得到下面的公式,计算出设备服务的并发请求数。

concurrency = (r/s + w/s) * (svctm/1000)

  这是一个iostat的输出示例:

Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda        105    311 298 820   3236   9052       10      127   113     9    96

  把数字带入并发公式,可以得到差不多9.6的并发性。这意味着在一个采样周期内,这个设备平均要服务9.6次的请求。例子来自于一个10块盘的RAID 10卷,所以操作系统对这个设备的并行请求运行得相当好。另一方面,这是一个出现串行请求的设备:

Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sdc         81      0 280   0   3164      0       11        2     7     3    99

  并发公式展示了这个设备每秒只处理一个请求。两个设备都接近于满负荷利用,但是它们的性能表现完全不一样。如果设备一直都像这些例子展示的一样忙,那么应该检査一下并发性,不管是不是接近于设备中的物理盘数,都需要注意。更低的值则说明有如文件系统串行的问题,就像我们前面讨论的。

15.3 其他有用的工具

  我们展示vmstat和iostat(是因为它们部署最广泛,并且vmstat通常默认安装在许多类UNIX操作系统上。然而,每种工具都有自身的限制,例如莫名奇妙的度量单位、当操作系统更新统计时取样间隔不一致,以及不能一次性看到所有重要的点。如果这些工具不能符合需求,你可能会对dstat和collectl感兴趣。

  我们也喜欢用mpstat来观察CPU统计;它提供了更好的办法来观察CPU每个工作是如何进行的,而不是把它们搅在一块。有时在诊断问题时这非常重要。当分析硬盘I/O利用的时候,blktrace可能也非常有用。

  还有一个pt-diskstats,这是PerconaToolkit的一部分。它解决了一些对iostat的抱怨,例如显示读写统计的方式,以及缺乏对并发量的可见性。它也是交互式的,并且是按键驱动的,所以可以放大缩小、改变聚集、过滤设备,以及显示和隐藏列。即使没有安装这个工具,也可以通过简单的Shell脚本来收集一些硬盘统计状态,这个工具也支持分析这样采集出来的文本。可以抓取一些硬盘活动样本,然后发邮件或者保存起来,稍后分析。实际上,之前介绍的pt-stalk、pt-collect、pt-sift三件套,都被设计得可以跟pt-diskstats很好地配合。

15.4 CPU密集型的机器

  CPU密集型服务器的vmstat如输出通常在us列会有一个很髙的值,报告了花费在非内核代码上的CPU时钟;也可能在sy列有很高的值,表示系统CPU利用率,超过20%就足以令人不安了。在大部分情况下,也会有进程队列排队时间(在r列报告的)。下面是一个例子:

$ vmstat 5
procs -----------memory----------   ---swap-- -----io---- --system-- ----cpu----
 r  b   swpd   free   buff  cache     si   so    bi    bo   in    cs us sy id wa
10  2 740880  19256  46068 13719952    0    0  2788 11047 1423 14508 89  4  4  3
11  0 740880  19692  46144 13702944    0    0  2907 14073 1504 23045 90  5  2  3
 7  1 740880  20460  46264 13683852    0    0  3554 15567 1513 24182 88  5  3  3
10  2 740880  22292  46324 13670396    0    0  2640 16351 1520 17436 88  4  4  3

  注意,这里也有合理数量的上下文切换(在CS列),除非每秒超过100 000次或更多,一般都不用担心上下文切换。当操作系统停止一个进程转而运行另一个进程时,就会产生上下文切换。

  例如,一査询语句在MyISAM上执行了一个非覆盖索引扫描,就会先从索引中读取元素,然后根据索引再从磁盘上读取页面。如果页面不在操作系统缓存中,就需要从磁盘进行物理读取,这就会导致上下文切换中断进程处理,直到I/O完成。这样一个査询可以导致大量的上下文切换。

  如果我们在同一台机器观察iostat的输出(再次剔除显示启动以来平均值的第一行),可以发现磁盘利用率低于50% :

$ iostat -dx 5
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda          0   3859  54 458   2063  34546       71        3     6     1    47
dm-0         0      0  54 4316  2063  34532        8       18     4     0    47
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda          0   2898  52 363   1767  26090       67        3     7     1    45
dm-0         0      0  52 3261  1767  26090        8       15     5     0    45

  这台机器不是I/O密集型的,但是依然有相当数量的I/O发生,在数据库服务器中这种情况很少见。另一方面,传统的Web服务器会消耗大量CPU资源,但是很少发生I/0,所以Web服务器的输出不会像这个例子。

15.5 I/O密集型的机器

  在I/O密集型工作负载下,CPU花费大量时间在等待I/O请求完成。这意味着vmstat会显示很多处理器在非中断休眠(b列)状态,并且在wa这一列的值很高,下面是个例子:

$ vmstat 5
procs -----------memory----------   ---swap-- -----io---- --system-- ----cpu----
 r  b   swpd   free   buff  cache     si   so    bi    bo   in    cs us sy id wa
 5  7 740632  22684  43212 13466436    0    0  6738 17222 1738 16648 19  3 15 63
 5  7 740632  22748  43396 13465436    0    0  6150 17025 1731 16713 18  4 21 58
 1  8 740632  22380  43416 13464192    0    0  4582 21820 1693 15211 16  4 24 56
 5  6 740632  22116  43512 13463484    0    0  5955 21158 1732 16187 17  4 23 56

  这台机器的iostat输出显示硬盘一直很忙:

$ iostat -dx 5
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda          0   5396 202  626  7319  48187       66       12    14     1   101
dm-0         0      0 202 6016  7319  48130        8       57     9     0   101
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda          0   5810 184  665  6441  51825       68       11    13     1   102
dm-0         0      0 183 6477  6441  51817        8       54     7     0   102

  %util的值可能因为四舍五入的错误超过100%。什么迹象意味着机器是I/O密集的呢?只要有足够的缓冲来服务写请求,即使机器正在做大量的写操作,也可能可以满足,但是却通常意味着硬盘可能会无法满足读请求。这听起来好象违反直觉,但是如果思考读和写的本质,就不会这么认为了:

  • 写请求能够缓冲或同步操作。它们可以被我们之前讨论过的任意一层缓冲:操作系统层、RAID控制器层,等等。
  • 读请求就其本质而言都是同步的。当然程序可以猜测到可能需要某些数据,并异步地提前读取(预读)。无论如何,通常程序在继续工作前必须得到它们需要的数据。这就强制读请求为同步操作:程序必须被阻塞直到请求完成。

  想想这种方式:你可以发出一个写请求到缓冲区的某个地方,然后过一会完成。甚至可 以每秒发出很多这样的请求。如果缓冲区正确工作,并且有足够的空间,每个请求都可以很快完成,并且实际上写到物理硬盘是被重新排序后更有效地批量操作的。然而,没有办法对读操作这么做一不管多小或多少的请求,都不可能让硬盘响应说“这是你的数据,我等一会读它”。这就是为什么读需要I/O等待是可以理解的原因。

15.6 发生内存交换的机器

  一台正在发生内存交换的机器可能在swpd列有一个很高的值,也可能不高。但是可以看到si和so列有很髙的值,这是我们不希望看到的。下面是一台内存交换严重的机器的vmstat输出:

$ vmstat 5
 r  b    swpd   free   buff    cache    si    so    bi    bo   in    cs us sy id wa
 4 11 3797936  21268  27068 14519324 15913 30870 40513 30924 3600  7191  6 11 36 47
procs -----------memory------------- ---swap---- -----io---- --system-- ----cpu----
 0 10 3794292  24436  27076 14412764 19853  9781 57874  9833 4084  8339  6 14 58 22
 0 37 3847364  20764  27112 14547112   171 38815 22358 39146 2417  4640  6  8  9 7

15.7 空闲的机器

  为完整起见,下面也给出一台空闲机器上的vmstat输出。注意,没有在运行或被阻塞的进程,idle列显示CPU是100%的空闲。这个例子来源于一台运行红帽企业版Linux5的机器,并且st列展示了从“虚拟机”偷来的时间。

$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy  id wa st
 0  0    108 492556   6768 360092    0    0   345   209    2   65  2  0  97  1  0
 0  0    108 492556   6772 360088    0    0     0    14  357   19  0  0 100  0  0
 0  0    108 492556   6776 360084    0    0     0     6  355   16  0  0 100  0  0

16.总结

  为MySQL选择和配置硬件,以及根据硬件配置MySQL,并不是什么神秘的艺术。通常,对于大部分目标需要的都是相同的技巧和知识。当然,也需要知道一些MySQL特有的特点。

  我们通常建议大部分人在性能和成本之间找到一个好的平衡点。首先,出于多种原因,我们喜欢使用廉价服务器。举个例子,如果在使用服务器的过程中遇到了麻烦,并且在诊断时需要停止服务,或者希望只是简单地把出问题的服务器用另一台替换,如果使用的是一台$5000的廉价服务器,肯定比使用一台超过$50 000或者更贵的服务器要简单得多。MySQL通常也更适应廉价服务器,不管是从软件自身而言还是从典型的工作负载而言。

  MySQL需要的四种基本资源是:CPU、内存、硬盘以及网络资源。网络一般不会作为很严重的瓶颈出现,而CPU、内存和磁盘通常是主要的瓶颈所在。对MySQL而言,通常希望有很多快速CPU可以用,但如果必须在快和多之间做选择,则一般会选择更快而不是更多(其他条件相同的情况下)。

  CPU、内存以及磁盘之间的关系错综复杂,一个地方的问题可能会在其他地方显现出来。在对一个资源抛出问题时,问问自己是不是可能是由另外的问题导致的。如果遇到硬盘密集的操作,需要更多的I/O容量吗?或者是更多的内存?答案取决于工作集大小,也就是给定的时间内最常用的数据集。

  Mysql5.5时代,我们觉得以下做法是合理的。首先,通常不要超过两个插槽。现在即使双路系统也可以提供很多CPU核心和硬件线程了,而且四路服务器的CPU要贵得多。另外,四路CPU的使用不够广泛(也就意味着缺少测试和可靠性),并且使用的是更低的时钟频率。最终,四路插槽的系统跨插槽的同步开销也显著增加。在内存方面,我们喜欢用价格经济的服务器内存。许多廉价服务器目前有18个DIMM槽,单条8GB的DIMM是最好的选择——每GB的价格与更低容量的DIMM相比差不多,但是比16GB的DIMM便宜多了。这是为什么我们今天看到很多服务器是144GB的内存的原因。这个等式会随着时间的变化而变化——可能有一天具有最佳性价比的是16GB的DIMM,并且服务器出厂的内存槽数量也可能不一样——但是一般的原则还是一样的。

  持久化存储的选择本质上归结为三个选项,以提高性能的次序排序:SAN、传统硬盘,以及固态存储设备。

  • 当需要功能和纯粹的容量时,SAN是不错的。它们对许多工作负载都运行得不错,但缺点是很昂贵,并且对小的随机I/O操作有很大的延时,尤其是使用更慢的互联方式(如NFS)或工作集太大不足以匹配SAN内存的缓存时,延时会更大。要注意SAN的性能突变的情况,并且要非常小心避免灾难的场景。
  • 传统硬盘很大,便宜,但是对随机读很慢。对大部分场景,最好的选择是服务器硬盘组成RAID10卷。通常应该使用带有电池保护单元的RAID控制器,并且设置写缓存为WriteBack策略。这样一个配置对大部分工作负载都可以运行良好。
  • 固态盘相对比较小并且昂贵,但是随机I/O非常快。一般分为两类:SSD和PCIe设备。广泛地来说,SSD更便宜,更慢,但缺少可靠性验证。需要对SSD做RAID以提升可靠性,但是大多数硬件RAID控制器不擅长这个任务。PCIe设备很昂贵并且有容量限制,但是非常快并且可靠,而且不需要RAID。

  固态存储设备可以很大地提升服务器整体性能。有时候一个不算昂贵的SSD,可以帮助解决经常在传统硬盘上遇到的特定工作负载的问题,如复制。如果真的需要很强的性能,应该使用PCIe设备。增加高速I/O设备会把服务器的性能瓶颈转移到CPU,有时也会转移到网络。

  MySQL和InnoDB并不能完全发挥高端固态存储设备的性能,并且在某些场景下操作系统也不能发挥。但是提升依然很明显。Percona Server对固态存储做了很多改进,并且很多改进在5.6发布时已经进入了MySQL主干代码。 

  对操作系统而言,只有很少的一些重要配置需要关注,大部分是关于存储、网络和虚拟内存管理的。如果像大部分MySQL用户一样使用GNU/Linux,建议采用XFS文件系统,并且为服务器的页面交换倾向率(swapiness)和硬盘队列调度器设置恰当的值。有一些网络参数需要改变,可能还有一些其他的地方(例如禁用SELinux)需要调优,但是前面说的那些改动的优先级应该更高一些。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值