高性能MySql学习笔记-第九章:操作系统和硬件优化
1. 什么限制了 MySQL 的性能
- MySQL 最常见的两个瓶颈是 CPU 和 IO 资源。
- 当数据可以放在内存中或者可以从磁盘中以足够快的速度读取时,CPU 可能出现瓶颈。
- 当工作所需的数据远远超过有效内存容量时,IO 资源则可能出现瓶颈。
2. 如何为 MySQL 选择 CPU
更快的 CPU 还是更多的 CPU
- 一般而言,MySQL 服务器希望达成如下两个目标:
- 低延时(快速响应)。高速的 CPU 对达成这一点更有益,因为每个查询只能使用一个 CPU。
- 高吞吐。如果能同时运行很多查询语句,则可以从多个 CPU 处理查询中收益。新版本是 MySQL 可以拓展到16或24个 CPU,或者更多。
- 即便没有并发查询任务,MySQL 依然可以利用额外的 CPU 为后台任务服务。
- MySQL 复制在高速 CPU 下工作得非常好,但是多CPU 对复制的帮助不大。这是因为主库上的并发任务传递到备库以后,会被简化为串行任务。
扩展到多个 CPU和核心
- 多 CPU 在联机事务处理(OLTP)系统的场景中非常有用。这些系统通常执行许多小的操作,并且从多个连接发起请求,因此可以在多个 CPU上运行。
- 现代 CPU 的一些特点,比如频率调整和 boost 技术(超频),为MySQL 服务器在规划多 CPU 时带来了新的挑战。
3. 平衡内存和磁盘资源
- 配置大量内存最大的原因不是为了在内存中保持大量数据,最终目的是避免磁盘IO,因为磁盘 IO 比内存访问要慢的多。
随机 IO 和顺序 IO
- 服务器同时使用顺序 IO 和随机 IO,随机 IO 从缓存中收益最多。而顺序 IO 由于比随机 IO 快,且往往只需要扫描一次数据,所以从缓存中受益不大。
- MySQL 中顺序 IO 比随机 IO 快有这么几个原因:
- 无论是在内存还是磁盘或者固态存储中,顺序 IO 总是比随机 IO 要快很多。
- 存储引擎执行顺序读时,一般只需要遍历一个简单的数据结构,比如链表。而随机 IO 则需要通过 B 树进行查找。
- 随机读取通常只需要查找特定的行,但是仍需要把一整页数据读出来。而顺序读取往往需要读取页面上所有的数据,更符合成本效益。
缓存、读和写
- 如果内存足够大,就会完全避免磁盘读取请求。但是写入最终仍需要访问磁盘,因为写入需要持久化。
- 缓存虽然无法避免写入,但是可以延迟写入。缓存还可以将写入集中操作,比如:多次写入,一次刷新,一份数据可以在内存中变更多次,但是只将最终的值写入磁盘。IO 合并,将不同部分的数据,合并在一起通过一次磁盘操作写入磁盘。
- 预写日志(WAL)策略,在内存中变更页面数据,但是不立即刷新到磁盘,而是将变化日志写到一个连续的日志文件。这使得写入操作由随机 IO 转换为顺序 IO,提高了写入的效率。
选择硬盘
- 如果无法满足让足够的数据在内存中的目标,则应该考虑一个更强大的 IO 系统。
- 在选择磁盘时,需要考虑如下因素:存储容量、传输速度、访问时间、主轴转速、物理尺寸等。
- InnoDB 能够很好地拓展到多个磁盘处理器。MyISAM 的表锁限制导致其可能无法从多个驱动器中受益。
4. 固态存储
闪存概述
- 固态存储设备采用非易失性闪存芯片而不是磁性盘片组成。又称 NVRAM,或非易失性随机存取存储器。闪存设备具有以下一些优点:
- 相比磁盘有更好的随机读写性能
- 相比磁盘有更好的顺序读写性能,但是不如随机 IO 改善那么大
- 相比磁盘能更好地支持并发
- 闪存最重要的特征是可以迅速完成多次小单位的读取,但是写入会有一些挑战。闪存不能在没有做擦除操作前改写一个单元(Cell),并且一次必须擦除一个大块。擦除周期是缓慢的,并且会磨损整个块,一个块容忍的擦除周期次数是有一定上限的。
- 为了保存一些块是干净的并且可以被写入,设备需要回收脏块。
- 有些闪存设备在被将要填满时,性能会出现下降。这是因为没有空闲块时必须等待擦除完成造成的。写一个空闲块只需要几百微秒,但是擦除往往需要几毫秒。
闪存技术
- 有两种主要的闪存设备类型,分别是:单层单元(SLC)和多层单元(MLC)。
- SLC 的每个单元存储一个比特。SLC 相对更昂贵,但是非常快,且擦写寿命高达十万个写周期,缺点则是存储密度相对较低。
- MLC 的每个单元存储2个或3个比特。这使得 MLC设备可以获得更高的存储密度,成本更低,但是速度和耐写性也下降了。MLC 设备一般支持1万个擦写周期
- 使MLC 芯片更耐用的一种方法是通过固件逻辑,平衡磨损。
- 更大的容量会使设备寿命显著增长。
闪存的基准测试
- 闪存设备有一个三阶段模式,称之为 A-B-C 性能特性。它们在开始阶段运行非常快(阶段 A),然后垃圾回收起开始进行工作,这将导致一段时间内,设备处于过渡到稳定状态(阶段 B)的阶段,最后设备进入一个稳定状态(状态 C)。基准测试时,应当更多关注阶段 C的性能。
固态硬盘驱动器(SSD)
- SSD 模拟 SATA 硬盘驱动器。这是一个兼容性功能:替换 SATA 硬盘不需要任何特殊的驱动程序或接口。
- 大多数 SSD 有一个写缓存来缓冲写入。写入缓存在没有电池备份的情况下并不能持久化,但是在快速增长的写负载下,它不能关闭,否则闪存存储无法承受。所以,如果禁用了驱动器的高速缓存以获得真正持久化的存储,将会加速设备寿命损耗。
- 建议对 SATA SSD 盘使用 RAID。单一驱动器的数据总是不够安全的。RAID 控制器的性能表现,一般只能满足6-8个驱动器的期望。
PCIe 存储设备
- PCIe 设备没有像 SATA 那样尝试模拟硬盘驱动器。PCIe设备的带宽一般比 SATA 要大,设备延迟也更低。不建议对 PCIe 设备建立 RAID,因为过于昂贵。
其他类型的固态存储
- 有些产品可以提供几十 TB 的闪存设备。但是 MySQL 在如此大规模的场景下,不能有效利用如此强大的存储优势,因为 MySQL 很难在如此大的数据下良好地工作。MySQL 的解决方案是:拆分、横向扩展和无共享架构。
什么时候使用闪存
- 适合任何有着大量随机 IO 工作负载的场景下。
- 适合高吞吐的写入负载场景。内存缓冲虽然能够将随机 IO转换为顺序 IO 写入提高性能,但无法减小写入的负载。
- 单线程的工作负载场景。单线程往往对延时更敏感,适合使用闪存,比如 MySQL 的复制。
使用 Flashcache
- Flashcache是一个 Linux 内核模块,使用 Linux 的设备映射器。Flashcache创建了一个块设备,并且可以被分区和创建文件系统。但是这个块设备是由闪存和磁盘共同组成,闪存设备作为读取和写入的智能高速缓存。Flashcache相当于在内存和磁盘之间创建了一个中间层。
- Flashcache最终性能不如底层的闪存存储,但是仍比磁盘快很多。
优化固态存储上的 MySQL
- 如果 MySQL 运行在闪存上,有一些配置参数可以提供更好的性能。比如:增加InnoDB的 IO 容量、让 InnoDB 的日志文件更大、禁用预读等。
- 当时有闪存存储时,任务可能会从 IO 密集型转向 CPU密集型,所以确保开启了超线程。
5. RAID 性能优化
-
RAID (Redundant Array of Independent Disks,磁盘冗余阵列)可以帮助做冗余,拓展存储容量,缓存以及加速。
-
常见的 RAID 级别有 RAID0、RAID1、RAID5、RAID10、RAID50等。
-
RAID0
- 将多块磁盘组合在一起形成一个大容量的存储。当我们要写数据的时候,会将数据分为N份,以独立的方式实现N块磁盘的读写,那么这N份数据会同时并发的写到磁盘中,因此执行性能非常的高。
- RAID0的问题是,它并不提供数据校验或冗余备份,因此一旦某块磁盘损坏了,数据就会损坏。
-
RAID1
- RAID1将同一份数据在不同的磁盘中冗余两份。读性能相对更好,但是磁盘利用率较低。
-
RAID5
- RAID5通过分布奇偶校验块把数据分散到多个磁盘。如果任何一个盘的数据失效,都可以从奇偶校验块中重建。但如果有两个磁盘失效了,则整个卷的数据无法恢复。
- 在 RAID5 上随机写是昂贵的,因为每次写需要在底层磁盘发送两次读和两次写,以计算和存储校验。而顺序写和随机读,顺序读在 RAID5 的表现则尚可。
- RAID5的奇偶校验块会带来额外的性能开销,这会限制它的可拓展性,超过10块硬盘后,RAID5就不能很好地拓展。
-
RAID10
- RAID10 是RAID1与RAID0的混合实现。首先基于RAID1模式将磁盘分为2份,当要写入数据的时候,将所有的数据在两份磁盘上同时写入,相当于写了双份数据,起到了数据保障的作用。且在每一份磁盘上又会基于RAID0技术讲数据分为N份并发的读写,这样也保障了数据的效率。
-
RAID50
- RAID50是 RAID5和 RAID0的混合实现。由并列的 RAID5阵列组成 RAID0。是 RAID5的经济性和 RAID10的高性能之间的一个折中。
RAID 的配置和缓存
- RAID 的缓存就是物理安装在 RAID 控制器的少量内存。它可以用于以下几个场景:
- 缓存读取。控制器从磁盘读取数据并发送到主机系统后,通过缓存可以存储读取的数据,以供下次使用。
- 缓存预读数据。如果 RAID 控制器发现连续请求的数据,可能会做预读操作,预先取出估计很快就会用到的数据,并放入缓存。
- 缓冲写入。可以将写入的数据先放入缓存,一段时间后再放入磁盘。这样可以更快的返回,也可以积累写操作从而更有效地批量操作。
- 内部操作。比如 RAID5的写入操作,需要计算校验位。控制器做这类内部操作时需要使用一些内存。
- RAID 控制器的缓存是一种稀缺资源。除了像 RAID5的内部使用外,应全部分配给缓冲写入用。这是因为对于缓存读取和缓存预读而言,驱动器上层(操作系统、应用程序)往往有更多的内存做此类操作。
- 写入缓冲对同步写入非常有用。但是如果没有电池备份单元(BBU)或其他非易失性存储,则在断电时很容易丢失数据。
6. SAN 和 NAS
- SAN(Storage Area Network)和 NAS(Network-Attached Storage)是两个外部文件存储设备加载到服务器的方法。 不同的是访问存储的方式。访问 SAN 设备时通过块接口,服务器可以直接看到一块硬盘并可以像硬盘一样使用。NAS 设备则通过基于文件的协议来访问,例如 NFS 或 SMB。SAN 设备通常通过光纤通道协议(FCP)或 iSCSI 链接到服务器,而 NAS 设备使用标准的网络链接。下文讨论中统称 SAN。
- SAN 允许服务器访问大量的硬盘驱动器,并且通常配置大容量智能高速缓存缓冲写入。
- SAN 可以承受大量的连续写入,因为可以缓冲并合并 IO。SAN 对于顺序读取的支持也尚可,因为可以做预读并从缓存中提取数据。在随机写上会慢一点,因为不能很好地合并。SAN 非常不适合做随机读取。更重要的是,服务器和 SAN 之间还有网络延迟。所以不论多强大的SAN,可以预见的是,对于小的随机操作都无法获得良好的响应时间和吞吐量。
- 对于 MySQL 的性能影响上,在 SAN 上删除数据行,或者执行 ALTER 类的操作将耗费大量时间,因为这涉及到很多随机 IO。由于复制是一个单线程任务,所以存储在 SAN 上的备库可能更容易落后于主库。
- 从 MySQL 的需要的性能类型看,SAN 不是最佳的选择。大多数 Web 应用不应该让数据库使用 SAN。
7. 使用多磁盘卷
- MySQL没有提供复杂的空间管理功能。默认只是简单地把每个Schema的文件放入到一个单独的目录。
- 二进制文件和数据文件分离的真正优势,是减小事故中同时丢失数据和日志文件的可能性。如果RAID控制器上没有电池支持的缓存,这样做是有必要的。
- 将日志放在独立的卷确实可以提高性能,但是需要考虑下成本。
8. 网络配置
- 如果启用skip_name_resolve选项,MySQL将不会做任何DNS查找的工作。这也意味着用户账户必须在host列使用具有唯一性的IP地址。启用这个选项通常是个比较好的做法。
- 应当尽量避免实时的跨数据中心的操作。
9. 选择文件系统
- 最好使用日志文件系统,例如:ext3、ext4、XFS、ZFS或者JFS。否则,崩溃后文件系统的检查可能耗费相当长时间。
- 还可以调整文件系统的预读行为,因为InnoDB有自己的预读的策略,所以文件系统的预读就是重复多余的。
10. 选择磁盘队列调度策略
- 队列调度决定了到块设备的请求实际上发送到底层设备的顺序。默认情况下使用cfq(Completely Fair Queueing,完全公平排队)策略。在MySQL的工作负载类型下,cfq会导致很差的响应时间,因为会在队列中延迟一些不必要的请求。
- 除此之外还有两个选项。noop调度适合那些自己在背后实现了调度算法的设备,如RAID控制器和SAN。deadline则对RAID控制器和直接使用的磁盘都工作良好。这两者之间的差异非常小。
11. 内存交换区
- 当操作系统因为没有足够的内存而将一些虚拟内存写到磁盘就会发生内存交换。内存交换对MySQL的性能影响很大。他破坏了缓存在内存的目的,并且相对于使用很小的内存做缓存,使用交换区的性能更差。
- 操作系统通常允许对虚拟内存和IO进行一些控制。最基本的方法是修改/proc/sys/vm/swappiness为一个很小的值,如0或2。这告诉内核除非虚拟内存完全满了,否则不要使用交换区。
12. 操作系统状态
如何阅读 vmstat 的输出
- 使用
vmstat 5
命令可以让它每5s钟打印一个报告。 - 其中第一行值显示自系统启动以来的平均值,接下来的行会展示每5秒的间隔发生了什么。
- 其中每一列的含义如下:
- procs:r这一列显示多少进程在等待CPU,b列显示多少进程正在不可中断地休眠。
- memory:swpd列显示多少块被换出了磁盘(页面交换)。剩下三列显示了多少块是空闲的,多少块正在被用作缓冲、以及操作系统正在使用的缓存。
- swap:显示每秒有多少块正在从磁盘被换入以及每秒有多少块正在被换出到磁盘。
- io:反映磁盘IO。
- system:显示了每秒中断(in)和上下文切换(cs)的数量。
- cpu: 显示所有的CPU时间花费在各类操作的百分比。
如何阅读 iostat的输出
- 使用
iostat 5
命令可以让它每5s钟打印一个报告。 - 其中第一行值显示自系统启动以来的平均值,接下来的行会展示每5秒的间隔发生了什么。