<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>高性能MySQL on 随记</title><link>https://blog.liuyi2026.asia/zh/tags/%E9%AB%98%E6%80%A7%E8%83%BDMySQL/</link><description>Recent content in 高性能MySQL on 随记</description><generator>Hugo</generator><language>zh</language><lastBuildDate>Sun, 05 Jan 2025 08:42:21 +0800</lastBuildDate><atom:link href="https://blog.liuyi2026.asia/zh/tags/%E9%AB%98%E6%80%A7%E8%83%BDMySQL/index.xml" rel="self" type="application/rss+xml"/><item><title>封面-版权</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E5%B0%81%E9%9D%A2-%E7%89%88%E6%9D%83/</link><pubDate>Sun, 05 Jan 2025 08:42:21 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E5%B0%81%E9%9D%A2-%E7%89%88%E6%9D%83/</guid><description>&lt;p>&lt;img src="img/000001.jpeg" alt="" />&lt;/p>
&lt;p>&lt;strong>内容简介&lt;/strong>&lt;/p>
&lt;p>本书是MySQL领域的经典之作，拥有广泛的影响力。第3版更新了大量的内容，不但涵盖了最新MySQL 5.5版本的新特性，也讲述了关于固态盘、高可扩展性设计和云计算环境下的数据库相关的新内容，原有的基准测试和性能优化部分也做了大量的扩展和补充。全书共分为16章和6个附录，内容涵盖MySQL架构和历史，基准测试和性能剖析，数据库软硬件性能优化，复制、备份和恢复，高可用与高可扩展性，以及云端的MySQL和MySQL相关工具等方面的内容。每一章都是相对独立的主题，读者可以有选择性地单独阅读。&lt;/p>
&lt;p>本书不但适合数据库管理员（DBA）阅读，也适合开发人员参考学习。不管是数据库新手还是专家，相信都能从本书有所收获。&lt;/p>
&lt;p>©2012 by Baron Schwartz，Peter Zaitsev，Vadim Tkachenko.&lt;/p>
&lt;p>Simplified Chinese Edition，jointly published by O&amp;rsquo;Reilly Media，Inc. and Publishing House of Electronics Industry，2013. Authorized translation of the English edition，2012 O&amp;rsquo;Reilly Media，Inc.，the owner of all rights to publish and sell the same.&lt;/p>
&lt;p>All rights reserved including the rights of reproduction in whole or in part in any form.&lt;/p>
&lt;p>本书简体中文版专有出版权由O&amp;rsquo;Reilly Media，Inc.授予电子工业出版社。未经许可，不得以任何方式复制或抄袭本书的任何部分。专有出版权受法律保护。&lt;/p>
&lt;p>版权贸易合同登记号图字：01-2013-1661&lt;/p>
&lt;p>&lt;strong>图书在版编目（CIP）数据&lt;/strong>&lt;/p>
&lt;p>高性能MySQL：第3版／（美）施瓦茨（Schwartz，B.），（美）扎伊采夫（Zaitsev，P.），（美）特卡琴科（Tkachenko，V.）著；宁海元等译．—北京：电子工业出版社，2013.5&lt;/p>
&lt;p>书名原文：High Performance MySQL，Third Edition&lt;/p>
&lt;p>ISBN 978-7-121-19885-4&lt;/p></description></item><item><title>第9章操作系统和硬件优化</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC9%E7%AB%A0%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%92%8C%E7%A1%AC%E4%BB%B6%E4%BC%98%E5%8C%96/</link><pubDate>Sun, 05 Jan 2025 08:42:19 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC9%E7%AB%A0%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%92%8C%E7%A1%AC%E4%BB%B6%E4%BC%98%E5%8C%96/</guid><description>&lt;p>第9章　操作系统和硬件优化&lt;/p>
&lt;p>MySQL服务器性能受制于整个系统最薄弱的环节，承载它的操作系统和硬件往往是限制因素。磁盘大小、可用内存和CPU资源、网络，以及所有连接它们的组件，都会限制系统的最终容量。因此，需要小心地选择硬件，并对硬件和操作系统进行合适的配置。例如，若工作负载是I/O密集型的，一种方法是设计应用程序使得最大限度地减少MySQL的I/O操作。然而，更聪明的方式通常是升级I/O子系统，安装更多的内存，或重新配置现有的磁盘。&lt;/p>
&lt;p>硬件的更新换代非常迅速，所以本章有关特定产品或组件的内容可能将很快变得过时。像往常一样，我们的目标是帮助提升对这些概念的理解，这样对于即使没有直接覆盖到的知识也可以举一反三。这里我们将通过现有的硬件来阐明我们的观点。&lt;/p>
&lt;h1 id="91什么限制了mysql的性能">9.1　什么限制了MySQL的性能&lt;a class="anchor" href="#91%e4%bb%80%e4%b9%88%e9%99%90%e5%88%b6%e4%ba%86mysql%e7%9a%84%e6%80%a7%e8%83%bd">#&lt;/a>&lt;/h1>
&lt;p>许多不同的硬件都可以影响MySQL的性能，但我们认为最常见的两个瓶颈是CPU和I/O资源。当数据可以放在内存中或者可以从磁盘中以足够快的速度读取时，CPU可能出现瓶颈。把大量的数据集完全放到大容量的内存中，以现在的硬件条件完全是可行的(1)。&lt;/p>
&lt;p>另一方面，I/O瓶颈，一般发生在工作所需的数据远远超过有效内存容量的时候。如果应用程序是分布在网络上的，或者如果有大量的查询和低延迟的要求，瓶颈可能转移到网络上，而不再是磁盘I/O(2)。&lt;/p>
&lt;p>第3章中提及的技巧可以帮助找到系统的限制因素，但即使你认为已经找到了瓶颈，也应该透过表象去看更深层次的问题。某一方面的缺陷常常会将压力施加在另一个子系统，导致这个子系统出问题。例如，若没有足够的内存，MySQL可能必须刷出缓存来腾出空间给需要的数据——然后，过了一小会，再读回刚刚刷新的数据（读取和写入操作都可能发生这个问题）。本来是内存不足，却导致出现了I/O容量不足。当找到一个限制系统性能的因素时，应该问问自己，“是这个部分本身的问题，还是系统中其他不合理的压力转移到这里所导致的？”在第3章的诊断案例中也有讨论到这个问题。&lt;/p>
&lt;p>还有另外一个例子：内存总线的瓶颈也可能表现为CPU问题。事实上，我们说一个应用程序有“CPU瓶颈”或者是“CPU密集型”，真正的意思应该是计算的瓶颈。接下来将深入探讨这个问题。&lt;/p>
&lt;h1 id="92如何为mysql选择cpu">9.2　如何为MySQL选择CPU&lt;a class="anchor" href="#92%e5%a6%82%e4%bd%95%e4%b8%bamysql%e9%80%89%e6%8b%a9cpu">#&lt;/a>&lt;/h1>
&lt;p>在升级当前硬件或购买新的硬件时，应该考虑下工作负载是不是CPU密集型。&lt;/p>
&lt;p>可以通过检查CPU利用率来判断是否是CPU密集型的工作负载，但是仅看CPU整体的负载是不合理的，还需要看看CPU使用率和大多数重要的查询的I/O之间的平衡，并注意CPU负载是否分配均匀。本章稍后讨论的工具可以用来弄清楚是什么限制了服务器的性能。&lt;/p>
&lt;h2 id="921哪个更好更快的cpu还是更多的cpu">9.2.1　哪个更好：更快的CPU还是更多的CPU&lt;a class="anchor" href="#921%e5%93%aa%e4%b8%aa%e6%9b%b4%e5%a5%bd%e6%9b%b4%e5%bf%ab%e7%9a%84cpu%e8%bf%98%e6%98%af%e6%9b%b4%e5%a4%9a%e7%9a%84cpu">#&lt;/a>&lt;/h2>
&lt;p>当遇到CPU密集型的工作时，MySQL通常可以从更快的CPU中获益（相对更多的CPU）。&lt;/p>
&lt;p>但这不是绝对的，因为还依赖于负载情况和CPU数量。更古老的MySQL版本在多CPU上有扩展性问题，即使新版本也不能对单个查询并发利用多个CPU。因此，CPU速度限制了每个CPU密集型查询的响应时间。&lt;/p>
&lt;p>当我们讨论CPU的时候，为保证本文易于阅读，对某些术语将不会做严格的定义。现在一般的服务器通常都有多个插槽（Socket），每个插槽上都可以插一个有多个核心的CPU（有独立的执行单元），并且每个核心可能有多个“硬件线程”。这些复杂的架构需要有点耐心去了解，并且我们不会总是明确地区分它们。不过，在一般情况下，当谈到CPU速度的时候，谈论的其实是执行单元的速度，当提到的CPU数量时，指的通常是在操作系统上看到的数量，尽管这可能是独立的执行单元数量的多倍(3)。&lt;/p>
&lt;p>这几年CPU在各个方面都有了很大的提升。例如，今天的Intel CPU速度远远超过前几代，这得益于像直接内存连接（directly attached memory）技术以及PCIe卡之类的设备互联上的改善等。这些改进对于存储设备尤其有效，例如Fusion-io和Virident的PCIe闪存驱动器。&lt;/p>
&lt;p>超线程的效果相比以前也要好得多，现在操作系统也更了解如何更好地使用超线程。而以前版本的操作系统无法识别两个虚拟处理器实际上是在同一芯片上，认为它们是独立的，于是会把任务安排在两个实际上是相同物理执行单元上的虚拟处理器。实际上单个执行单元并不是真的可以在同一时间运行两个进程，所以这样做会发生冲突和争夺资源。而同时其他CPU却可能在闲置，从而浪费资源。操作系统需要能感知超线程，因为它必须知道什么时候执行单元实际上是闲置的，然后切换相应的任务去执行。这个问题之前常见的原因是在等待内存总线，可能花费需要高达一百个CPU周期，这已经类似于一个轻量级的I/O等待。新的操作系统在这方面有了很大的改善。超线程现在已经工作得很好。过去，我们时常提醒人们禁用它，但现在已经不需要这样做了。&lt;/p>
&lt;p>这就是说，现在可以得到大量的快速的CPU——比本书的第2版出版的时候要多得多。所以多和快哪个更重要？一般来说两个都想要。从广义上来说，调优服务器可能有如下两个目标：&lt;/p>
&lt;p>低延时（快速响应）&lt;/p>
&lt;p>要做到这一点，需要高速CPU，因为每个查询只能使用一个CPU。&lt;/p>
&lt;p>高吞吐&lt;/p>
&lt;p>如果能同时运行很多查询语句，则可以从多个CPU处理查询中受益。然而，在实践中，还要取决于具体情况。因为MySQL还不能在多个CPU中完美地扩展，能用多少个CPU还是有极限的。在旧版本的MySQL中（MySQL 5.1以后的版本已经有一些提升），这个限制非常严重。在新的版本中，则可以放心地扩展到16或24个CPU，或者更多，取决于使用的是哪个版本（Percona往往在这方面略占优势）。&lt;/p>
&lt;p>如果有多路CPU，并且没有并发执行查询语句，MySQL依然可以利用额外的CPU为后台任务（例如清理InnoDB缓冲、网络操作，等等）服务。然而，这些任务通常比执行查询语句更加轻量化。&lt;/p>
&lt;p>MySQL复制（将在下一章中讨论）也能在高速CPU下工作得非常好，而多CPU对复制的帮助却不大。如果工作负载是CPU密集型，主库上的并发任务传递到备库以后会被简化为串行任务，这样即使备库硬件比主库好，也可能无法保持跟主库之间的同步。也就是说，备库的瓶颈通常是I/O子系统，而不是CPU。&lt;/p>
&lt;p>如果有一个CPU密集型的工作负载，考虑是需要更快的CPU还是更多CPU的另外一个因素是查询语句实际在做什么。在硬件层面，一个查询可以在执行或等待。处于等待状态常见的原因是在运行队列中等待（进程已经是可运行状态，但所有的CPU都忙）、等待闩锁（Latch）或锁（Lock）、等待磁盘或网络。那么你期望查询是等待什么呢？如果等待闩锁或锁，通常需要更快的CPU；如果在运行队列中等待，那么更多或者更快的CPU都可能有帮助。（也可能有例外，例如，查询等待InnoDB日志缓冲区的Mutex，直到I/O完成前都不会释放——这可能表明需要更多的I/O容量）。&lt;/p>
&lt;p>这就是说，MySQL在某些工作负载下可以有效地利用很多CPU。例如，假设有很多连接查询的是不同表（假设这些查询不会造成表锁的竞争，实际上对MyISAM和MEMORY表可能会有问题），并且服务器的总吞吐量比任何单个查询的响应时间都更重要。吞吐量在这种情况下可以非常高，因为线程可以同时运行而互不争用。&lt;/p>
&lt;p>再次说明，在理论上这可能更好地工作：不管查询是读取不同的表还是相同的表， InnoDB都会有一些全局共享的数据结构，而MyISAM在每个缓冲区都有全局锁。而且不仅仅是存储引擎，服务器层也有全局锁。以前InnoDB承担了所有的骂名，但最近做了一些改进后，暴露了服务器层中的其他瓶颈。例如臭名昭著的LOCK_open互斥量（Mutex），在MySQL 5.1和更早版本中可能就是个大问题，另外还有其他一些服务器级别的互斥量（例如查询缓存）。&lt;/p>
&lt;p>通常可以通过堆栈跟踪来诊断这些类型的竞争问题，例如Percona Toolkit中的&lt;em>pt-pmp&lt;/em>工具。如果遇到这样的问题，可能需要改变服务器的配置，禁用或改变引起问题的组件，进行数据分片（Sharding），或者通过某种方式改变做事的方法。这里无法列举所有的问题和相应的解决方案，但是一旦有一个确定的诊断，答案通常是显而易见的。大部分不幸遇到的问题都是边缘场景，最常见的问题随着时间的推移都在服务器上被修复了。&lt;/p>
&lt;h2 id="922cpu架构">9.2.2　CPU架构&lt;a class="anchor" href="#922cpu%e6%9e%b6%e6%9e%84">#&lt;/a>&lt;/h2>
&lt;p>可能99%以上的MySQL实例（不含嵌入式使用）都运行在Intel或者AMD芯片的x86架构下。本书中我们基本都是针对这种情况。&lt;/p>
&lt;p>64位架构现在都是默认的了，32位CPU已经很难买到了。MySQL在64位架构上工作良好，尽管有些事暂时不能利用64位架构来做。因此，如果使用的是较老旧版本的MySQL，在64位服务器上可能要小心。例如，在MySQL 5.0发布的早期时候，每个MyISAM键缓冲区被限制为4 GB，由一个32位整数负责寻址。（可以创建多个键缓冲区来解决这个问题。）&lt;/p>
&lt;p>确保在64位硬件上使用64位操作系统！最近这种情况已经不太常见了，但以前经常可以遇到，大多数主机托管提供商暂时还是在服务器上安装32位操作系统，即使是64位CPU。32位操作系统意味着不能使用大量的内存：尽管某些32位系统可以支持大量的内存，但不能像64位系统一样有效地利用，并且在32位系统上，任何一个单独的进程都不能寻址4 GB以上的内存。&lt;/p>
&lt;h2 id="923扩展到多个cpu和核心">9.2.3　扩展到多个CPU和核心&lt;a class="anchor" href="#923%e6%89%a9%e5%b1%95%e5%88%b0%e5%a4%9a%e4%b8%aacpu%e5%92%8c%e6%a0%b8%e5%bf%83">#&lt;/a>&lt;/h2>
&lt;p>多CPU在联机事务处理（OLTP）系统的场景中非常有用。这些系统通常执行许多小的操作，并且是从多个连接发起请求，因此可以在多个CPU上运行。在这样的环境中，并发可能成为瓶颈。大多数Web应用程序都属于这一类。&lt;/p>
&lt;p>OLTP服务器一般使用InnoDB，尽管它在多CPU的环境中还存在一些未解决的并发问题。然而，不只是InnoDB可能成为瓶颈：任何共享资源都是潜在的竞争点。InnoDB之所以获得大量关注是因为它是高并发环境下最常见的存储引擎，但MyISAM在大压力时的表现也不好，即使不修改任何数据只是读取数据也是如此。许多并发瓶颈，如InnoDB的行级锁和MyISAM的表锁，没有办法优化——除了尽可能快地处理任务之外，没有别的办法解决，这样，锁就可以尽快分配给等待的任务。如果一个锁是造成它们（其他任务）都在等待的原因，那么不管有多少CPU都一样。因此，即使是一些高并发工作负载，也可以从更快的CPU中受益。&lt;/p>
&lt;p>实际上有两种类型的数据库并发问题，需要不同的方法来解决，如下所示。&lt;/p>
&lt;p>逻辑并发问题&lt;/p>
&lt;p>应用程序可以看到资源的竞争，如表或行锁争用。这些问题通常需要好的策略来解决，如改变应用程序、使用不同的存储引擎、改变服务器的配置，或使用不同的锁定提示或事务隔离级别。&lt;/p>
&lt;p>内部并发问题&lt;/p>
&lt;p>比如信号量、访问InnoDB缓冲池页面的资源争用，等等。可以尝试通过改变服务器的设置、改变操作系统，或使用不同的硬件解决这些问题，但通常只能缓解而无法彻底消灭。在某些情况下，使用不同的存储引擎或给存储引擎打补丁，可以帮助缓解这些问题。&lt;/p>
&lt;p>MySQL的“扩展模式”是指它可以有效利用的CPU数量，以及在压力不断增长的情况下如何扩展，这同时取决于工作负载和系统架构。通过“系统架构”的手段是指通过调整操作系统和硬件，而不是通过优化使用MySQL的应用程序。CPU架构（RISC、CISC、流水线深度等）、CPU型号和操作系统都影响MySQL的扩展模式。这也是为什么说基准测试是非常重要的：一些系统可以在不断增加的并发下依然运行得很好，而另一些的表现则糟糕得多。&lt;/p>
&lt;p>有些系统在更多的处理器下甚至可能降低整体性能。这是相当普遍的情况，我们了解到许多人试图升级到有多个CPU的系统，最后只能被迫恢复到旧系统（或绑定MySQL进程到其中某些核心），因为这种升级反而降低了性能。在MySQL 5.0时代，Google的补丁和Percona Server出现之前，能有效利用的CPU核数是4核，但是现在甚至可以看到操作系统报告多达80个“CPU”的服务器。如果规划一个大的升级，必须要同时考虑硬件、服务器版本和工作负载。&lt;/p>
&lt;p>某些MySQL扩展性瓶颈在服务器层，而其他一些在存储引擎层。存储引擎是怎么设计的至关重要，有时更换到一个不同的引擎就可以从多处理器上获得更多效果。&lt;/p>
&lt;p>我们看到在世纪之交围绕处理器速度的战争在一定程度上已经平息，CPU厂商更多地专注于多核CPU和多线程的变化。CPU设计的未来很可能是数百个处理器核心，四核心和六核心的CPU在今天是很常见的。不同厂商的内部架构差异很大，不可能概括出线程、CPU和内核之间的相互作用。内存和总线如何设计也是非常重要的。归根结底，多个内核和多个物理CPU哪个更好，这是由硬件体系结构决定的。&lt;/p>
&lt;p>现代CPU的另外两个复杂之处也值得提一下。首先是频率调整。这是一种电源管理技术，可以根据CPU上的压力而动态地改变CPU的时钟速度。问题是，它有时不能很好地处理间歇性突发的短查询的情况，因为操作系统可能需要一段时间来决定CPU的时钟是否应该变化。结果，查询可能会有一段时间速度较慢，并且响应时间增加了。频率调整可能使间歇性的工作负载性能低下，但可能更重要的是，它会导致性能波动。&lt;/p>
&lt;p>第二个复杂之处是boost技术，这个技术改变了我们对CPU模式的看法。我们曾经以为四核2GHz CPU有四个同样强大的核心，不管其中有些是闲置或非闲置。因此，一个完美的可扩展系统，当它使用所有四个内核的时候，可以预计得到四倍的提升。但是现在已经不是这样了，因为当系统只使用一个核心时，处理器会运行在更高的时钟速度上，例如3GHz。这给很多的规划容量和可扩展性建模的工具出了一个难题，因为系统性能表现不再是线性的变化了。这也意味着，“空闲CPU”并不代表相同规模的资源浪费，如果有一台服务器上只运行了备库的复制，而复制执行是单线程的，所以有三个CPU是空闲的，因此认为可以利用这些CPU资源执行其他任务而不影响复制，可能就想错了。&lt;/p>
&lt;h1 id="93平衡内存和磁盘资源">9.3　平衡内存和磁盘资源&lt;a class="anchor" href="#93%e5%b9%b3%e8%a1%a1%e5%86%85%e5%ad%98%e5%92%8c%e7%a3%81%e7%9b%98%e8%b5%84%e6%ba%90">#&lt;/a>&lt;/h1>
&lt;p>配置大量内存最大的原因其实不是因为可以在内存中保存大量数据：最终目的是避免磁盘I/O，因为磁盘I/O比在内存中访问数据要慢得多。关键是要平衡内存和磁盘的大小、速度、成本和其他因素，以便为工作负载提供高性能的表现。在讨论如何做到这一点之前，暂时先回到基础知识上来。&lt;/p>
&lt;p>计算机包含一个金字塔型的缓存体系，更小、更快、更昂贵的缓存在顶端，如图9-1所示。&lt;/p>
&lt;p>&lt;img src="img/000000.jpeg" alt="" />
&lt;strong>图9-1：缓存层级&lt;/strong>&lt;/p></description></item><item><title>第8章优化服务器设置</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC8%E7%AB%A0%E4%BC%98%E5%8C%96%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AE%BE%E7%BD%AE/</link><pubDate>Sun, 05 Jan 2025 08:42:18 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC8%E7%AB%A0%E4%BC%98%E5%8C%96%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AE%BE%E7%BD%AE/</guid><description>&lt;p>第8章　优化服务器设置&lt;/p>
&lt;p>在这一章，我们将解释为&lt;em>&lt;strong>这是我的撒旦JFK数据库嘎斯公开就开始打山豆根士大夫 圣诞节复活节是是国家开始大幅机啊可是对方看见噶开暗杀是的JFK开始讲课的感觉爱看书的JFK史蒂夫卡卡萨丁咖啡碱撒快递费始东方会i二位人家儿童科技数据库的房价开始JFK注释&lt;/strong>&lt;/em>MySQL服务器创建一个靠谱的配置文件的过程。这是一个很绕的过程，有很多有意思的关注点和值得关&lt;strong>注的思路。关注这&lt;/strong>些点很有必要，因为创建一个好配置的最快方法不是从学习配置项开始，也不是从问哪个配置项应该怎么设置或者怎么修改开始，更不是从检查服务器行为和询问哪个配置项可以提升性能开始。最好是从理解MySQL内核和行为开始。然后可以利用这些知识来指导配置MySQL。最后，可以将想要的配置和当前配置进行比较，然后纠正重要并且有价值的不同之处。&lt;/p>
&lt;p>人们经常问，“我的服务器有32GB内存，12核CPU，怎样配置最好？”很遗憾，问题没这么简单。服务器的配置应该符合它的工作负载、数据，以及应用需求，并不仅仅看硬件的情况。&lt;/p>
&lt;p>MySQL有大量可以修改的参数——但不应该随便去修改。通常只需要把基本的项配置正确（大部分情况下只有很少一些参数是真正重要的），应该将更多的时间花在schema的优化、索引，以及查询设计上。在正确地配置了MySQL的基本配置项之后，再花力气去修改其他配置项的收益通常就比较小了。&lt;/p>
&lt;p>从另一方面来说，没用的配置导致潜在风险的可能更大。我们碰到过不止一个“高度调优”过的服务器不停地崩溃，停止服务或者运行缓慢，结果都是因为错误的配置导致的。我们将花一点时间来解释为什么会发生这种情况，并且告诉大家什么是不该做的。&lt;/p>
&lt;p>那么什么是该做的呢？确保基本的配置是正确的，例如InnoDB的Buffer Pool和日志文件缓存大小，如果想防止出问题（提醒一下，这样做通常不能提升性能——它们只能避免问题），就设置一个比较安全和稳健的值，剩下的配置就不用管了。如果碰到了问题，可以使用第3章提到的技巧小心地进行诊断。如果问题是由于服务器的某部分导致的，而这恰好可以通过某个配置项解决，那么需要做的就是更改配置。&lt;/p>
&lt;p>有时候，在某些特定的场景下，也有可能设置某些特殊的配置项会有显著的性能提升。但无论如何，这些特殊的配置项不应该成为服务器基本配置文件的一部分。只有当发现特定的性能问题才应该设置它们。这就是为什么我们不建议通过寻找有问题的地方修改配置项的原因。如果有些地方确实需要提升，也需要在查询响应时间上有所体现。最好是从查询语句和响应时间入手来开始分析问题，而不是通过配置项。这可以节省大量的时间，避免很多的问题。&lt;/p>
&lt;p>另一个节省时间和避免麻烦的好办法是使用默认配置，除非是明确地知道默认值会有问题。很多人都是在默认配置下运行的，这种情况非常普遍。这使得默认配置是经过最多实际测试的。对配置项做一些不必要的修改可能会遇到一些意料之外的bug。&lt;/p>
&lt;h1 id="81mysql配置的工作原理">8.1　MySQL配置的工作原理&lt;a class="anchor" href="#81mysql%e9%85%8d%e7%bd%ae%e7%9a%84%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/h1>
&lt;p>在讨论如何配置MySQL之前，我们先来解释一下MySQL的配置机制。MySQL对配置要求非常宽松，但是下面这些建议可能会为你节省大量的工作和时间。&lt;/p>
&lt;p>首先应该知道的是MySQL从哪里获得配置信息：命令行参数和配置文件。在类UNIX系统中，配置文件的位置一般在*/etc/my.cnf&lt;em>或者&lt;/em>/etc/mysql/my.cnf*。如果使用操作系统的启动脚本，这通常是唯一指定配置设置的地方。如果手动启动MySQL，例如在测试安装时，也可以在命令行指定设置。实际上，服务器会读取配置文件的内容，删除所有注释和换行，然后和命令行选项一起处理。&lt;/p>
&lt;p>&lt;img src="img/000000.jpeg" alt="i-" />关于术语的说明：因为很多MySQL命令行选项跟服务器变量相同，我们有时把选项和变量替换使用。大部分变量和它们对应的命令行选项名称一样，但是有一些例外。例如，&lt;em>&amp;ndash;memlock&lt;/em>选项设置了locked_in_memory变量。&lt;/p>
&lt;p>任何打算长期使用的设置都应该写到全局配置文件，而不是在命令行特别指定。否则，如果偶然在启动时忘了设置就会有风险。把所有的配置文件放在同一个地方以方便检查也是个好办法。&lt;/p>
&lt;p>一定要清楚地知道服务器配置文件的位置！我们见过有些人尝试修改配置文件但是不生效，因为他们修改的并不是服务器读取的文件，例如Debian下，&lt;em>/etc/mysql/my.cnf&lt;/em>才是MySQL读取的配置文件，而不是*/etc/my.cnf*。有时候好几个地方都有配置文件，也许是因为之前的系统管理员也没搞清楚情况（因此在各个可能的位置都放了一份）。如果不知道当前使用的配置文件路径，可以尝试下面的操作：&lt;/p>
&lt;pre>&lt;code> ** $ which mysqld** 
 /usr/sbin/mysqld
 ** $ /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'** 
 Default options are read from the following files in the given order:
 /etc/mysql/my.cnf ~/.my.cnf /usr/etc/my.cnf
&lt;/code>&lt;/pre>
&lt;p>对于服务器上只有一个MySQL实例的典型安装，这个命令很有用。也可以设计更复杂的配置，但是没有标准的方法告诉你怎么来做。MySQL发行版包含了一个现在废弃了的程序，叫mysqlmanager，可以在一个有多个独立部分的配置文件上运行多个实例。（现在已经被一样古老的mysqld_multi脚本替代。）然而许多操作系统发行版本在启动脚本中并不包含或使用这个程序。实际上，很多系统甚至没有使用MySQL提供的启动脚本。&lt;/p>
&lt;p>配置文件通常分成多个部分，每个部分的开头是一个用方括号括起来的分段名称。MySQL程序通常读取跟它同名的分段部分，许多客户端程序还会读取client部分，这是一个存放公用设置的地方。服务器通常读取mysqld这一段。一定要确认配置项放在了文件正确的分段中，否则配置是不会生效的。&lt;/p>
&lt;h2 id="811语法作用域和动态性">8.1.1　语法、作用域和动态性&lt;a class="anchor" href="#811%e8%af%ad%e6%b3%95%e4%bd%9c%e7%94%a8%e5%9f%9f%e5%92%8c%e5%8a%a8%e6%80%81%e6%80%a7">#&lt;/a>&lt;/h2>
&lt;p>配置项设置都使用小写，单词之间用下画线或横线隔开。下面的例子是等价的，并且可能在命令行和配置文件中都看到这两种格式：&lt;/p>
&lt;pre>&lt;code> /usr/sbin/mysqld --auto-increment-offset=5
 /usr/sbin/mysqld --auto-increment-offset=5
&lt;/code>&lt;/pre>
&lt;p>我们建议使用一种固定的风格。这样在配置文件中搜索配置项时会容易得多。&lt;/p>
&lt;p>配置项可以有多个作用域。有些设置是服务器级的（全局作用域），有些对每个连接是不同的（会话作用域），剩下的一些是对象级的。许多会话级变量跟全局变量相等，可以认为是默认值。如果改变会话级变量，它只影响改动的当前连接，当连接关闭时所有参数变更都会失效。下面有一些例子，你应该清楚这些不同类型的行为：&lt;/p>
&lt;ul>
&lt;li>query_cache_sizey变量是全局的。&lt;/li>
&lt;li>sort_buffer_sizey变量默认是全局相同的，但是每个线程里也可以设置。&lt;/li>
&lt;li>join_buffer_sizey变量也有全局默认值且每个线程是可以设置的，但是若一个查询中关联多张表，可以为每个关联分配一个关联缓冲（join buffer），所以每个查询可能有多个关联缓冲。&lt;/li>
&lt;/ul>
&lt;p>另外，除了在配置文件中设置变量，有很多变量（但不是所有）也可以在服务器运行时修改。MySQL把这些归为动态配置变量。下面的语句展示了动态改变sort_buffer_size的会话值和全局值的不同方式：&lt;/p></description></item><item><title>第7章MySQL高级特性</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC7%E7%AB%A0MySQL%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7/</link><pubDate>Sun, 05 Jan 2025 08:42:17 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC7%E7%AB%A0MySQL%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7/</guid><description>&lt;p>第7章　MySQL高级特性&lt;/p>
&lt;p>MySQL从5.0和5.1版本开始引入了很多高级特性，例如分区、触发器等，这对有其他关系型数据库使用背景的用户来说可能并不陌生。这些新特性吸引了很多用户开始使用MySQL。不过，这些特性的性能到底如何，还需要用户真正使用过才能知道。本章我们将为大家介绍，在真实的世界中，这些特性表现如何，而不是只简单地介绍参考手册或者宣传材料上的数据。&lt;/p>
&lt;h1 id="71分区表">7.1　分区表&lt;a class="anchor" href="#71%e5%88%86%e5%8c%ba%e8%a1%a8">#&lt;/a>&lt;/h1>
&lt;p>对用户来说，分区表是一个独立的逻辑表，但是底层由多个物理子表组成。实现分区的代码实际上是对一组底层表的句柄对象（Handler Object）的封装。对分区表的请求，都会通过句柄对象转化成对存储引擎的接口调用。所以分区对于SQL层来说是一个完全封装底层实现的黑盒子，对应用是透明的，但是从底层的文件系统来看就很容易发现，每一个分区表都有一个使用#分隔命名的表文件。&lt;/p>
&lt;p>MySQL实现分区表的方式——对底层表的封装——意味着索引也是按照分区的子表定义的，而没有全局索引。这和Oracle不同，在Oracle中可以更加灵活地定义索引和表是否进行分区。&lt;/p>
&lt;p>MySQL在创建表时使用PARTITION BY子句定义每个分区存放的数据。在执行查询的时候，优化器会根据分区定义过滤那些没有我们需要数据的分区，这样查询就无须扫描所有分区——只需要查找包含需要数据的分区就可以了。&lt;/p>
&lt;p>分区的一个主要目的是将数据按照一个较粗的粒度分在不同的表中。这样做可以将相关的数据存放在一起，另外，如果想一次批量删除整个分区的数据也会变得很方便。&lt;/p>
&lt;p>在下面的场景中，分区可以起到非常大的作用：&lt;/p>
&lt;ul>
&lt;li>表非常大以至于无法全部都放在内存中，或者只在表的最后部分有热点数据，其他均是历史数据。&lt;/li>
&lt;li>分区表的数据更容易维护。例如，想批量删除大量数据可以使用清除整个分区的方式。另外，还可以对一个独立分区进行优化、检查、修复等操作。&lt;/li>
&lt;li>分区表的数据可以分布在不同的物理设备上，从而高效地利用多个硬件设备。&lt;/li>
&lt;li>可以使用分区表来避免某些特殊的瓶颈，例如InnoDB的单个索引的互斥访问、ext3文件系统的inode锁竞争等。&lt;/li>
&lt;li>如果需要，还可以备份和恢复独立的分区，这在非常大的数据集的场景下效果非常好。&lt;/li>
&lt;/ul>
&lt;p>MySQL的分区实现非常复杂，我们不打算介绍实现的全部细节。这里我们将专注在分区性能方面，所以如果想了解更多的关于分区的基础知识，我们建议阅读MySQL官方手册中的“分区”一节，其中介绍了很多分区相关的基础知识。另外，还可以阅读CREATE TABLE、SHOW CREATE TABLE、ALTER TABLE和INFORMATION_SCHEMA.PARTITIONS、EXPLAIN关于分区部分的介绍。分区特性使得CREATE TABLE和ALTER TABLE命令变得更加复杂了。&lt;/p>
&lt;p>分区表本身也有一些限制，下面是其中比较重要的几点：&lt;/p>
&lt;ul>
&lt;li>一个表最多只能有1024个分区。&lt;/li>
&lt;li>在MySQL 5.1中，分区表达式必须是整数，或者是返回整数的表达式。在MySQL 5.5中，某些场景中可以直接使用列来进行分区。&lt;/li>
&lt;li>如果分区字段中有主键或者唯一索引的列，那么所有主键列和唯一索引列都必须包含进来。&lt;/li>
&lt;li>分区表中无法使用外键约束。&lt;/li>
&lt;/ul>
&lt;h2 id="711分区表的原理">7.1.1　分区表的原理&lt;a class="anchor" href="#711%e5%88%86%e5%8c%ba%e8%a1%a8%e7%9a%84%e5%8e%9f%e7%90%86">#&lt;/a>&lt;/h2>
&lt;p>如前所述，分区表由多个相关的底层表实现，这些底层表也是由句柄对象（Handler object）表示，所以我们也可以直接访问各个分区。存储引擎管理分区的各个底层表和管理普通表一样（所有的底层表都必须使用相同的存储引擎），分区表的索引只是在各个底层表上各自加上一个完全相同的索引。从存储引擎的角度来看，底层表和一个普通表没有任何不同，存储引擎也无须知道这是一个普通表还是一个分区表的一部分。&lt;/p>
&lt;p>分区表上的操作按照下面的操作逻辑进行：&lt;/p>
&lt;p>SELECT查询&lt;/p>
&lt;p>当查询一个分区表的时候，分区层先打开并锁住所有的底层表，优化器先判断是否可以过滤部分分区，然后再调用对应的存储引擎接口访问各个分区的数据。&lt;/p>
&lt;p>INSERT操作&lt;/p>
&lt;p>当写入一条记录时，分区层先打开并锁住所有的底层表，然后确定哪个分区接收这条记录，再将记录写入对应底层表。&lt;/p>
&lt;p>DELETE操作&lt;/p>
&lt;p>当删除一条记录时，分区层先打开并锁住所有的底层表，然后确定数据对应的分区，最后对相应底层表进行删除操作。&lt;/p>
&lt;p>UPDATE操作&lt;/p>
&lt;p>当更新一条记录时，分区层先打开并锁住所有的底层表，MySQL先确定需要更新的记录在哪个分区，然后取出数据并更新，再判断更新后的数据应该放在哪个分区，最后对底层表进行写入操作，并对原数据所在的底层表进行删除操作。&lt;/p>
&lt;p>有些操作是支持过滤的。例如，当删除一条记录时，MySQL需要先找到这条记录，如果WHERE条件恰好和分区表达式匹配，就可以将所有不包含这条记录的分区都过滤掉。这对UPDATE语句同样有效。如果是INSERT操作，则本身就是只命中一个分区，其他分区都会被过滤掉。MySQL先确定这条记录属于哪个分区，再将记录写入对应的底层分区表，无须对任何其他分区进行操作。&lt;/p>
&lt;p>虽然每个操作都会“先打开并锁住所有的底层表”，但这并不是说分区表在处理过程中是锁住全表的。如果存储引擎能够自己实现行级锁，例如InnoDB，则会在分区层释放对应表锁。这个加锁和解锁过程与普通InnoDB上的查询类似。&lt;/p>
&lt;p>后面我们会通过一些例子来看看，当访问一个分区表的时候，打开和锁住所有底层表的代价及其带来的后果。&lt;/p>
&lt;h2 id="712分区表的类型">7.1.2　分区表的类型&lt;a class="anchor" href="#712%e5%88%86%e5%8c%ba%e8%a1%a8%e7%9a%84%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h2>
&lt;p>MySQL支持多种分区表。我们看到最多的是根据范围进行分区，每个分区存储落在某个范围的记录，分区表达式可以是列，也可以是包含列的表达式。例如，下表就可以将每一年的销售额存放在不同的分区里：&lt;/p>
&lt;pre>&lt;code> CREATE TABLE sales (
 order_date DATETIME NOT NULL,
 -- Other columns omitted
 ) ENGINE=InnoDB PARTITION BY RANGE(YEAR(order_date)) (
 PARTITION p_2010 VALUES LESS THAN (2010),
 PARTITION p_2011 VALUES LESS THAN (2011),
 PARTITION p_2012 VALUES LESS THAN (2012),
 PARTITION p_catchall VALUES LESS THAN MAXVALUE );
&lt;/code>&lt;/pre>
&lt;p>PARTITION分区子句中可以使用各种函数。但有一个要求，表达式返回的值要是一个确定的整数，且不能是一个常数。这里我们使用函数YEAR()，也可以使用任何其他的函数，如TO_DAYS()。根据时间间隔进行分区，是一种很常见的分区方式，后面我们还会再回过头来看这个例子，看看如何优化这个例子来避免一些问题。&lt;/p></description></item><item><title>第6章查询性能优化</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC6%E7%AB%A0%E6%9F%A5%E8%AF%A2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</link><pubDate>Sun, 05 Jan 2025 08:42:16 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC6%E7%AB%A0%E6%9F%A5%E8%AF%A2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</guid><description>&lt;p>第6章　查询性能优化&lt;/p>
&lt;p>前面的章节我们介绍了如何设计最优的库表结构、如何建立最好的索引，这些对于高性能来说是必不可少的。但这些还不够——还需要合理的设计查询。如果查询写得很糟糕，即使库表结构再合理、索引再合适，也无法实现高性能。&lt;/p>
&lt;p>查询优化、索引优化、库表结构优化需要齐头并进，一个不落。在获得编写MySQL查询的经验的同时，也将学习到如何为高效的查询设计表和索引。同样的，也可以学习到在优化库表结构时会影响到哪些类型的查询。这个过程需要时间，所以建议大家在学习后面章节的时候多回头看看这三章的内容。&lt;/p>
&lt;p>本章将从查询设计的一些基本原则开始——这也是在发现查询效率不高的时候首先需要考虑的因素。然后会介绍一些更深的查询优化的技巧，并会介绍一些MySQL优化器内部的机制。我们将展示MySQL是如何执行查询的，你也将学会如何去改变一个查询的执行计划。最后，我们要看一下MySQL优化器在哪些方面做得还不够，并探索查询优化的模式，以帮助MySQL更有效地执行查询。&lt;/p>
&lt;p>本章的目标是帮助大家更深刻地理解MySQL如何真正地执行查询，并明白高效和低效的原因何在，这样才能充分发挥MySQL的优势，并避开它的弱点。&lt;/p>
&lt;h1 id="61为什么查询速度会慢">6.1　为什么查询速度会慢&lt;a class="anchor" href="#61%e4%b8%ba%e4%bb%80%e4%b9%88%e6%9f%a5%e8%af%a2%e9%80%9f%e5%ba%a6%e4%bc%9a%e6%85%a2">#&lt;/a>&lt;/h1>
&lt;p>在尝试编写快速的查询之前，需要清楚一点，真正重要是响应时间。如果把查询看作是一个任务，那么它由一系列子任务组成，每个子任务都会消耗一定的时间。如果要优化查询，实际上要优化其子任务，要么消除其中一些子任务，要么减少子任务的执行次数，要么让子任务运行得更快(1)。&lt;/p>
&lt;p>MySQL在执行查询的时候有哪些子任务，哪些子任务运行的速度很慢？这里很难给出完整的列表，但如果按照第3章介绍的方法对查询进行剖析，就能看到查询所执行的子任务。通常来说，查询的生命周期大致可以按照顺序来看：从客户端，到服务器，然后在服务器上进行解析，生成执行计划，执行，并返回结果给客户端。其中“执行”可以认为是整个生命周期中最重要的阶段，这其中包括了大量为了检索数据到存储引擎的调用以及调用后的数据处理，包括排序、分组等。&lt;/p>
&lt;p>在完成这些任务的时候，查询需要在不同的地方花费时间，包括网络，CPU计算，生成统计信息和执行计划、锁等待（互斥等待）等操作，尤其是向底层存储引擎检索数据的调用操作，这些调用需要在内存操作、CPU操作和内存不足时导致的I/O操作上消耗时间。根据存储引擎不同，可能还会产生大量的上下文切换以及系统调用。&lt;/p>
&lt;p>在每一个消耗大量时间的查询案例中，我们都能看到一些不必要的额外操作、某些操作被额外地重复了很多次、某些操作执行得太慢等。优化查询的目的就是减少和消除这些操作所花费的时间。&lt;/p>
&lt;p>再次申明一点，对于一个查询的全部生命周期，上面列的并不完整。这里我们只是想说明：了解查询的生命周期、清楚查询的时间消耗情况对于优化查询有很大的意义。有了这些概念，我们再一起来看看如何优化查询。&lt;/p>
&lt;h1 id="62慢查询基础优化数据访问">6.2　慢查询基础：优化数据访问&lt;a class="anchor" href="#62%e6%85%a2%e6%9f%a5%e8%af%a2%e5%9f%ba%e7%a1%80%e4%bc%98%e5%8c%96%e6%95%b0%e6%8d%ae%e8%ae%bf%e9%97%ae">#&lt;/a>&lt;/h1>
&lt;p>查询性能低下最基本的原因是访问的数据太多。某些查询可能不可避免地需要筛选大量数据，但这并不常见。大部分性能低下的查询都可以通过减少访问的数据量的方式进行优化。对于低效的查询，我们发现通过下面两个步骤来分析总是很有效：&lt;/p>
&lt;ol>
&lt;li>确认应用程序是否在检索大量超过需要的数据。这通常意味着访问了太多的行，但有时候也可能是访问了太多的列。&lt;/li>
&lt;li>确认MySQL服务器层是否在分析大量超过需要的数据行。&lt;/li>
&lt;/ol>
&lt;h2 id="621是否向数据库请求了不需要的数据">6.2.1　是否向数据库请求了不需要的数据&lt;a class="anchor" href="#621%e6%98%af%e5%90%a6%e5%90%91%e6%95%b0%e6%8d%ae%e5%ba%93%e8%af%b7%e6%b1%82%e4%ba%86%e4%b8%8d%e9%9c%80%e8%a6%81%e7%9a%84%e6%95%b0%e6%8d%ae">#&lt;/a>&lt;/h2>
&lt;p>有些查询会请求超过实际需要的数据，然后这些多余的数据会被应用程序丢弃。这会给MySQL服务器带来额外的负担，并增加网络开销(2)，另外也会消耗应用服务器的CPU和内存资源。&lt;/p>
&lt;p>这里有一些典型案例：&lt;/p>
&lt;p>查询不需要的记录&lt;/p>
&lt;p>一个常见的错误是常常会误以为MySQL会只返回需要的数据，实际上MySQL却是先返回全部结果集再进行计算。我们经常会看到一些了解其他数据库系统的人会设计出这类应用程序。这些开发者习惯使用这样的技术，先使用SELECT语句查询大量的结果，然后获取前面的N行后关闭结果集（例如在新闻网站中取出100条记录，但是只是在页面上显示前面10条）。他们认为MySQL会执行查询，并只返回他们需要的10条数据，然后停止查询。实际情况是MySQL会查询出全部的结果集，客户端的应用程序会接收全部的结果集数据，然后抛弃其中大部分数据。最简单有效的解决方法就是在这样的查询后面加上LIMIT。&lt;/p>
&lt;p>多表关联时返回全部列&lt;/p>
&lt;p>如果你想查询所有在电影&lt;em>Academy Dinosaur&lt;/em>中出现的演员，千万不要按下面的写法编写查询：&lt;/p>
&lt;pre>&lt;code> mysql&amp;gt; ** SELECT * FROM sakila.actor** 
 -&amp;gt; ** INNER JOIN sakila.film_actor USING(actor_id)** 
 -&amp;gt; ** INNER JOIN sakila.film USING(film_id)** 
 -&amp;gt; ** WHERE sakila.film.title = 'Academy Dinosaur';** 
&lt;/code>&lt;/pre>
&lt;p>这将返回这三个表的全部数据列。正确的方式应该是像下面这样只取需要的列：&lt;/p>
&lt;pre>&lt;code> mysql&amp;gt; ** SELECT sakila.actor.* FROM sakila.actor...;** 
&lt;/code>&lt;/pre>
&lt;p>总是取出全部列&lt;/p>
&lt;p>每次看到SELECT *的时候都需要用怀疑的眼光审视，是不是真的需要返回全部的列？很可能不是必需的。取出全部列，会让优化器无法完成索引覆盖扫描这类优化，还会为服务器带来额外的I/O、内存和CPU的消耗。因此，一些DBA是严格禁止SELECT *的写法的，这样做有时候还能避免某些列被修改带来的问题。&lt;/p>
&lt;p>当然，查询返回超过需要的数据也不总是坏事。在我们研究过的许多案例中，人们会告诉我们说这种有点浪费数据库资源的方式可以简化开发，因为能提高相同代码片段的复用性，如果清楚这样做的性能影响，那么这种做法也是值得考虑的。如果应用程序使用了某种缓存机制，或者有其他考虑，获取超过需要的数据也可能有其好处，但不要忘记这样做的代价是什么。获取并缓存所有的列的查询，相比多个独立的只获取部分列的查询可能就更有好处。&lt;/p>
&lt;p>重复查询相同的数据&lt;/p>
&lt;p>如果你不太小心，很容易出现这样的错误——不断地重复执行相同的查询，然后每次都返回完全相同的数据。例如，在用户评论的地方需要查询用户头像的URL，那么用户多次评论的时候，可能就会反复查询这个数据。比较好的方案是，当初次查询的时候将这个数据缓存起来，需要的时候从缓存中取出，这样性能显然会更好。&lt;/p></description></item><item><title>第5章创建高性能的索引</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC5%E7%AB%A0%E5%88%9B%E5%BB%BA%E9%AB%98%E6%80%A7%E8%83%BD%E7%9A%84%E7%B4%A2%E5%BC%95/</link><pubDate>Sun, 05 Jan 2025 08:42:15 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC5%E7%AB%A0%E5%88%9B%E5%BB%BA%E9%AB%98%E6%80%A7%E8%83%BD%E7%9A%84%E7%B4%A2%E5%BC%95/</guid><description>&lt;p>第5章　创建高性能的索引&lt;/p>
&lt;p>索引（在MySQL中也叫做“键（key）”）是存储引擎用于快速找到记录的一种数据结构。这是索引的基本功能，除此之外，本章还将讨论索引其他一些方面有用的属性。&lt;/p>
&lt;p>索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时，索引对性能的影响愈发重要。在数据量较小且负载较低时，不恰当的索引对性能的影响可能还不明显，但当数据量逐渐增大时，性能则会急剧下降(1)。&lt;/p>
&lt;p>不过，索引却经常被忽略，有时候甚至被误解，所以在实际案例中经常会遇到由糟糕索引导致的问题。这也是我们把索引优化放在了靠前的章节，甚至比查询优化还靠前的原因。&lt;/p>
&lt;p>索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高几个数量级，“最优”的索引有时比一个“好的”索引性能要好两个数量级。创建一个真正“最优”的索引经常需要重写查询，所以，本章和下一章的关系非常紧密。&lt;/p>
&lt;h1 id="51索引基础">5.1　索引基础&lt;a class="anchor" href="#51%e7%b4%a2%e5%bc%95%e5%9f%ba%e7%a1%80">#&lt;/a>&lt;/h1>
&lt;p>要理解MySQL中索引是如何工作的，最简单的方法就是去看看一本书的“索引”部分：如果想在一本书中找到某个特定主题，一般会先看书的“索引”，找到对应的页码。&lt;/p>
&lt;p>在MySQL中，存储引擎用类似的方法使用索引，其先在索引中找到对应值，然后根据匹配的索引记录找到对应的数据行。假如要运行下面的查询：&lt;/p>
&lt;pre>&lt;code> mysql&amp;gt; ** SELECT first_name FROM sakila.actor WHERE actor_id=5;** 
&lt;/code>&lt;/pre>
&lt;p>如果在actor_id列上建有索引，则MySQL将使用该索引找到actor_id为5的行，也就是说，MySQL先在索引上按值进行查找，然后返回所有包含该值的数据行。&lt;/p>
&lt;p>索引可以包含一个或多个列的值。如果索引包含多个列，那么列的顺序也十分重要，因为MySQL只能高效地使用索引的最左前缀列。创建一个包含两个列的索引，和创建两个只包含一列的索引是大不相同的，下面将详细介绍。&lt;/p>
&lt;p>&lt;strong>如果使用的是ORM，是否还需要关心索引？&lt;/strong>&lt;/p>
&lt;p>简而言之：是的，仍然需要理解索引，即使是使用对象关系映射（ORM）工具。&lt;/p>
&lt;p>ORM工具能够生产符合逻辑的、合法的查询（多数时候），除非只是生成非常基本的查询（例如仅是根据主键查询），否则它很难生成适合索引的查询。无论是多么复杂的ORM工具，在精妙和复杂的索引面前都是“浮云”。读完本章后面的内容以后，你就会同意这个观点的!很多时候，即使是查询优化技术专家也很难兼顾到各种情况，更别说ORM了。&lt;/p>
&lt;h2 id="511索引的类型">5.1.1　索引的类型&lt;a class="anchor" href="#511%e7%b4%a2%e5%bc%95%e7%9a%84%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h2>
&lt;p>索引有很多种类型，可以为不同的场景提供更好的性能。在MySQL中，索引是在存储引擎层而不是服务器层实现的。所以，并没有统一的索引标准：不同存储引擎的索引的工作方式并不一样，也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持同一种类型的索引，其底层的实现也可能不同。&lt;/p>
&lt;p>下面我们先来看看MySQL支持的索引类型，以及它们的优点和缺点。&lt;/p>
&lt;h3 id="b-tree索引">B-Tree索引&lt;a class="anchor" href="#b-tree%e7%b4%a2%e5%bc%95">#&lt;/a>&lt;/h3>
&lt;p>当人们谈论索引的时候，如果没有特别指明类型，那多半说的是B-Tree索引，它使用B-Tree数据结构来存储数据(2)。大多数MySQL引擎都支持这种索引。Archive引擎是一个例外：5.1之前Archive不支持任何索引，直到5.1才开始支持单个自增列（AUTO_INCREMENT）的索引。&lt;/p>
&lt;p>我们使用术语“B-Tree”，是因为MySQL在CREATE TABLE和其他语句中也使用该关键字。不过，底层的存储引擎也可能使用不同的存储结构，例如，NDB集群存储引擎内部实际上使用了T-Tree结构存储这种索引，即使其名字是BTREE；InnoDB则使用的是B+Tree，各种数据结构和算法的变种不在本书的讨论范围之内。&lt;/p>
&lt;p>存储引擎以不同的方式使用B-Tree索引，性能也各有不同，各有优劣。例如，MyISAM使用前缀压缩技术使得索引更小，但InnoDB则按照原数据格式进行存储。再如MyISAM索引通过数据的物理位置引用被索引的行，而InnoDB则根据主键引用被索引的行。&lt;/p>
&lt;p>B-Tree通常意味着所有的值都是按顺序存储的，并且每一个叶子页到根的距离相同。图5-1展示了B-Tree索引的抽象表示，大致反映了InnoDB索引是如何工作的。MyISAM使用的结构有所不同，但基本思想是类似的。&lt;/p>
&lt;p>&lt;img src="img/000001.jpeg" alt="" />
&lt;strong>图5-1：建立在B-Tree结构（从技术上来说是B+Tree）上的索引&lt;/strong>&lt;/p>
&lt;p>B-Tree索引能够加快访问数据的速度，因为存储引擎不再需要进行全表扫描来获取需要的数据，取而代之的是从索引的根节点（图示并未画出）开始进行搜索。根节点的槽中存放了指向子节点的指针，存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点，这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值，要么该记录不存在。&lt;/p>
&lt;p>叶子节点比较特别，它们的指针指向的是被索引的数据，而不是其他的节点页（不同引擎的“指针”类型不同）。图5-1中仅绘制了一个节点和其对应的叶子节点，其实在根节点和叶子节点之间可能有很多层节点页。树的深度和表的大小直接相关。&lt;/p>
&lt;p>B-Tree对索引列是顺序组织存储的，所以很适合查找范围数据。例如，在一个基于文本域的索引树上，按字母顺序传递连续的值进行查找是非常合适的，所以像“找出所有以I到K开头的名字”这样的查找效率会非常高。&lt;/p>
&lt;p>假设有如下数据表：&lt;/p>
&lt;pre>&lt;code> CREATE TABLE People (
 last_name varchar(50) not null,
 first_name varchar(50) not null,
 dob date not null,
 gender enum('m', 'f') not null,
 key(last_name, first_name, dob)
 );
&lt;/code>&lt;/pre>
&lt;p>对于表中的每一行数据，索引中包含了last_name、frst_name和dob列的值，图5-2显示了该索引是如何组织数据的存储的。&lt;/p></description></item><item><title>第4章Schema与数据类型优化</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC4%E7%AB%A0Schema%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%BC%98%E5%8C%96/</link><pubDate>Sun, 05 Jan 2025 08:42:14 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC4%E7%AB%A0Schema%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%BC%98%E5%8C%96/</guid><description>&lt;p>第4章　Schema与数据类型优化&lt;/p>
&lt;p>良好的逻辑设计和物理设计是高性能的基石，应该根据系统将要执行的查询语句来设计schema，这往往需要权衡各种因素。例如，反范式的设计可以加快某些类型的查询，但同时可能使另一些类型的查询变慢。比如添加计数表和汇总表是一种很好的优化查询的方式，但这些表的维护成本可能会很高。MySQL独有的特性和实现细节对性能的影响也很大。&lt;/p>
&lt;p>本章和聚焦在索引优化的下一章，覆盖了MySQL特有的schema设计方面的主题。我们假设读者已经知道如何设计数据库，所以本章既不会介绍如何入门数据库设计，也不会讲解数据库设计方面的深入内容。这一章关注的是MySQL数据库的设计，主要介绍的是MySQL数据库设计与其他关系型数据库管理系统的区别。如果需要学习数据库设计方面的基础知识，建议阅读Clare Churcher的&lt;em>Beginning Database Design&lt;/em>（Apress出版社）一书。&lt;/p>
&lt;p>本章内容是为接下来的两个章节做铺垫。在这三章中，我们将讨论逻辑设计、物理设计和查询执行，以及它们之间的相互作用。这既需要关注全局，也需要专注细节。还需要理解整个系统以便弄清楚各个部分如何相互影响。如果在阅读完索引和查询优化章节后再回头来看这一章，也许会发现本章很有用，很多讨论的议题不能孤立地考虑。&lt;/p>
&lt;h1 id="41选择优化的数据类型">4.1　选择优化的数据类型&lt;a class="anchor" href="#41%e9%80%89%e6%8b%a9%e4%bc%98%e5%8c%96%e7%9a%84%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h1>
&lt;p>MySQL支持的数据类型非常多，选择正确的数据类型对于获得高性能至关重要。不管存储哪种类型的数据，下面几个简单的原则都有助于做出更好的选择。&lt;/p>
&lt;p>更小的通常更好。&lt;/p>
&lt;p>一般情况下，应该尽量使用可以正确存储数据的最小数据类型(1)。更小的数据类型通常更快，因为它们占用更少的磁盘、内存和CPU缓存，并且处理时需要的CPU周期也更少。&lt;/p>
&lt;p>但是要确保没有低估需要存储的值的范围，因为在schema中的多个地方增加数据类型的范围是一个非常耗时和痛苦的操作。如果无法确定哪个数据类型是最好的，就选择你认为不会超过范围的最小类型。（如果系统不是很忙或者存储的数据量不多，或者是在可以轻易修改设计的早期阶段，那之后修改数据类型也比较容易）。&lt;/p>
&lt;p>简单就好&lt;/p>
&lt;p>简单数据类型的操作通常需要更少的CPU周期。例如，整型比字符操作代价更低，因为字符集和校对规则（排序规则）使字符比较比整型比较更复杂。这里有两个例子：一个是应该使用MySQL内建的类型(2)而不是字符串来存储日期和时间，另外一个是应该用整型存储IP地址。稍后我们将专门讨论这个话题。&lt;/p>
&lt;p>尽量避免NULL&lt;/p>
&lt;p>很多表都包含可为NULL（空值）的列，即使应用程序并不需要保存NULL也是如此，这是因为可为NULL是列的默认属性(3)。通常情况下最好指定列为NOT NULL，除非真的需要存储NULL值。&lt;/p>
&lt;p>如果查询中包含可为NULL的列，对MySQL来说更难优化，因为可为NULL的列使得索引、索引统计和值比较都更复杂。可为NULL的列会使用更多的存储空间，在MySQL里也需要特殊处理。当可为NULL的列被索引时，每个索引记录需要一个额外的字节，在MyISAM里甚至还可能导致固定大小的索引（例如只有一个整数列的索引）变成可变大小的索引。&lt;/p>
&lt;p>通常把可为NULL的列改为NOT NULL带来的性能提升比较小，所以（调优时）没有必要首先在现有schema中查找并修改掉这种情况，除非确定这会导致问题。但是，如果计划在列上建索引，就应该尽量避免设计成可为NULL的列。&lt;/p>
&lt;p>当然也有例外，例如值得一提的是，InnoDB使用单独的位（bit）存储NULL值，所以对于稀疏数据(4)有很好的空间效率。但这一点不适用于MyISAM。&lt;/p>
&lt;p>在为列选择数据类型时，第一步需要确定合适的大类型：数字、字符串、时间等。这通常是很简单的，但是我们会提到一些特殊的不是那么直观的案例。&lt;/p>
&lt;p>下一步是选择具体类型。很多MySQL的数据类型可以存储相同类型的数据，只是存储的长度和范围不一样、允许的精度不同，或者需要的物理空间（磁盘和内存空间）不同。相同大类型的不同子类型数据有时也有一些特殊的行为和属性。&lt;/p>
&lt;p>例如，DATETIME和TIMESAMP列都可以存储相同类型的数据：时间和日期，精确到秒。&lt;/p>
&lt;p>然而TIMESTAMP只使用DATETIME一半的存储空间，并且会根据时区变化，具有特殊的自动更新能力。另一方面，TIMESTAMP允许的时间范围要小得多，有时候它的特殊能力会成为障碍。&lt;/p>
&lt;p>本章只讨论基本的数据类型。MySQL为了兼容性支持很多别名，例如INTEGER、BOOL，以及NUMERIC。它们都只是别名。这些别名可能令人不解，但不会影响性能。如果建表时采用数据类型的别名，然后用SHOW CREATE TABLE检查，会发现MySQL报告的是基本类型，而不是别名。&lt;/p>
&lt;h2 id="411整数类型">4.1.1　整数类型&lt;a class="anchor" href="#411%e6%95%b4%e6%95%b0%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h2>
&lt;p>有两种类型的数字：整数（whole number）和实数（real number）。如果存储整数，可以使用这几种整数类型：TINYINT，SMALLINT，MEDIUMINT，INT，BIGINT。分别使用8，16，24，32，64位存储空间。它们可以存储的值的范围从−2（N−1）到2（N−1）−1，其中N是存储空间的位数。&lt;/p>
&lt;p>整数类型有可选的UNSIGNED属性，表示不允许负值，这大致可以使正数的上限提高一倍。例如TINYINT UNSIGNED可以存储的范围是0～255，而TINYINT的存储范围是−128～127。&lt;/p>
&lt;p>有符号和无符号类型使用相同的存储空间，并具有相同的性能，因此可以根据实际情况选择合适的类型。&lt;/p>
&lt;p>你的选择决定MySQL是怎么在内存和磁盘中保存数据的。然而，整数计算一般使用64位的BIGINT整数，即使在32位环境也是如此。（一些聚合函数是例外，它们使用DECIMAL或DOUBLE进行计算）。&lt;/p>
&lt;p>MySQL可以为整数类型指定宽度，例如INT（11），对大多数应用这是没有意义的：它不会限制值的合法范围，只是规定了MySQL的一些交互工具（例如MySQL命令行客户端）用来显示字符的个数。对于存储和计算来说，INT（1）和INT（20）是相同的。&lt;/p>
&lt;p>&lt;img src="img/000000.jpeg" alt="" />一些第三方存储引擎，比如Infobright，有时也有自定义的存储格式和压缩方案，并不一定使用常见的MySQL内置引擎的方式。&lt;/p>
&lt;h2 id="412实数类型">4.1.2　实数类型&lt;a class="anchor" href="#412%e5%ae%9e%e6%95%b0%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h2>
&lt;p>实数是带有小数部分的数字。然而，它们不只是为了存储小数部分；也可以使用DECIMAL存储比BIGINT还大的整数。MySQL既支持精确类型，也支持不精确类型。&lt;/p>
&lt;p>FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算。如果需要知道浮点运算是怎么计算的，则需要研究所使用的平台的浮点数的具体实现。&lt;/p>
&lt;p>DECIMAL类型用于存储精确的小数。在MySQL 5.0和更高版本，DECIMAL类型支持精确计算。MySQL 4.1以及更早版本则使用浮点运算来实现DECIAML的计算，这样做会因为精度损失导致一些奇怪的结果。在这些版本的MySQL中，DECIMAL只是一个“存储类型”。&lt;/p>
&lt;p>因为CPU不支持对DECIMAL的直接计算，所以在MySQL 5.0以及更高版本中，MySQL服务器自身实现了DECIMAL的高精度计算。相对而言，CPU直接支持原生浮点计算，所以浮点运算明显更快。&lt;/p>
&lt;p>浮点和DECIMAL类型都可以指定精度。对于DECIMAL列，可以指定小数点前后所允许的最大位数。这会影响列的空间消耗。MySQL 5.0和更高版本将数字打包保存到一个二进制字符串中（每4个字节存9个数字）。例如，DECIMAL（18,9）小数点两边将各存储9个数字，一共使用9个字节：小数点前的数字用4个字节，小数点后的数字用4个字节，小数点本身占1个字节。&lt;/p>
&lt;p>MySQL 5.0和更高版本中的DECIMAL类型允许最多65个数字。而早期的MySQL版本中这个限制是254个数字，并且保存为未压缩的字符串（每个数字一个字节）。然而，这些（早期）版本实际上并不能在计算中使用这么大的数字，因为DECIMAL只是一种存储格式；在计算中DECIMAL会转换为DOUBLE类型。&lt;/p>
&lt;p>有多种方法可以指定浮点列所需要的精度，这会使得MySQL悄悄选择不同的数据类型，或者在存储时对值进行取舍。这些精度定义是非标准的，所以我们建议只指定数据类型，不指定精度。&lt;/p>
&lt;p>浮点类型在存储同样范围的值时，通常比DECIMAL使用更少的空间。FLOAT使用4个字节存储。DOUBLE占用8个字节，相比FLOAT有更高的精度和更大的范围。和整数类型一样，能选择的只是存储类型；MySQL使用DOUBLE作为内部浮点计算的类型。&lt;/p>
&lt;p>因为需要额外的空间和计算开销，所以应该尽量只在对小数进行精确计算时才使用DECIMAL——例如存储财务数据。但在数据量比较大的时候，可以考虑使用BIGINT代替DECIMAL，将需要存储的货币单位根据小数的位数乘以相应的倍数即可。假设要存储财务数据精确到万分之一分，则可以把所有金额乘以一百万，然后将结果存储在BIGINT里，这样可以同时避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。&lt;/p>
&lt;h2 id="413字符串类型">4.1.3　字符串类型&lt;a class="anchor" href="#413%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h2>
&lt;p>MySQL支持多种字符串类型，每种类型还有很多变种。这些数据类型在4.1和5.0版本发生了很大的变化，使得情况更加复杂。从MySQL 4.1开始，每个字符串列可以定义自己的字符集和排序规则，或者说校对规则（collation）（更多关于这个主题的信息请参考第7章）。这些东西会很大程度上影响性能。&lt;/p>
&lt;h3 id="varchar和char类型">&lt;strong>VARCHAR&lt;/strong>和CHAR类型&lt;a class="anchor" href="#varchar%e5%92%8cchar%e7%b1%bb%e5%9e%8b">#&lt;/a>&lt;/h3>
&lt;p>VARCHAR和CHAR是两种最主要的字符串类型。不幸的是，很难精确地解释这些值是怎么存储在磁盘和内存中的，因为这跟存储引擎的具体实现有关。下面的描述假设使用的存储引擎是InnoDB和/或者MyISAM。如果使用的不是这两种存储引擎，请参考所使用的存储引擎的文档。&lt;/p>
&lt;p>先看看VARCHAR和CHAR值通常在磁盘上怎么存储。请注意，存储引擎存储CHAR或者VARCHAR值的方式在内存中和在磁盘上可能不一样，所以MySQL服务器从存储引擎读出的值可能需要转换为另一种存储格式。下面是关于两种类型的一些比较。&lt;/p>
&lt;p>VARCHAR&lt;/p>
&lt;p>VARCHAR类型用于存储可变长字符串，是最常见的字符串数据类型。它比定长类型更节省空间，因为它仅使用必要的空间（例如，越短的字符串使用越少的空间）。有一种情况例外，如果MySQL表使用ROW_FORMAT=FIXED创建的话，每一行都会使用定长存储，这会很浪费空间。&lt;/p>
&lt;p>VARCHAR需要使用1或2个额外字节记录字符串的长度：如果列的最大长度小于或等于255字节，则只使用1个字节表示，否则使用2个字节。假设采用latin1字符集，一个VARCHAR（10）的列需要11个字节的存储空间。VARCHAR（1000）的列则需要1002个字节，因为需要2个字节存储长度信息。&lt;/p>
&lt;p>VARCHAR节省了存储空间，所以对性能也有帮助。但是，由于行是变长的，在UPDATE时可能使行变得比原来更长，这就导致需要做额外的工作。如果一个行占用的空间增长，并且在页内没有更多的空间可以存储，在这种情况下，不同的存储引擎的处理方式是不一样的。例如，MyISAM会将行拆成不同的片段存储，InnoDB则需要分裂页来使行可以放进页内。其他一些存储引擎也许从不在原数据位置更新数据。&lt;/p>
&lt;p>下面这些情况下使用VARCHAR是合适的：字符串列的最大长度比平均长度大很多；列的更新很少，所以碎片不是问题；使用了像UTF-8这样复杂的字符集，每个字符都使用不同的字节数进行存储。&lt;/p></description></item><item><title>第3章服务器性能剖析</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC3%E7%AB%A0%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%80%A7%E8%83%BD%E5%89%96%E6%9E%90/</link><pubDate>Sun, 05 Jan 2025 08:42:13 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC3%E7%AB%A0%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%80%A7%E8%83%BD%E5%89%96%E6%9E%90/</guid><description>&lt;p>第3章　服务器性能剖析&lt;/p>
&lt;p>在我们的技术咨询生涯中，最常碰到的三个性能相关的服务请求是：如何确认服务器是否达到了性能最佳的状态、找出某条语句为什么执行不够快，以及诊断被用户描述成“停顿”、“堆积”或者“卡死”的某些间歇性疑难故障。本章将主要针对这三个问题做出解答。我们将提供一些工具和技巧来优化整机的性能、优化单条语句的执行速度，以及诊断或者解决那些很难观察到的问题（这些问题用户往往很难知道其根源，有时候甚至都很难察觉到它的存在）。&lt;/p>
&lt;p>这看起来是个艰巨的任务，但是事实证明，有一个简单的方法能够从噪声中发现苗头。这个方法就是专注于测量服务器的时间花费在哪里，使用的技术则是性能剖析（profiling）。在本章，我们将展示如何测量系统并生成剖析报告，以及如何分析系统的整个堆栈（stack），包括从应用程序到数据库服务器到单个查询。&lt;/p>
&lt;p>首先我们要保持空杯精神，抛弃掉一些关于性能的常见的误解。这有一定的难度，下面我们一起通过一些例子来说明问题在哪里。&lt;/p>
&lt;h1 id="31性能优化简介">3.1　性能优化简介&lt;a class="anchor" href="#31%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96%e7%ae%80%e4%bb%8b">#&lt;/a>&lt;/h1>
&lt;p>问10个人关于性能的问题，可能会得到10个不同的回答，比如“每秒查询次数”、“CPU利用率”、“可扩展性”之类。这其实也没有问题，每个人在不同场景下对性能有不同的理解，但本章将给性能一个正式的定义。我们将性能定义为完成某件任务所需要的时间度量，换句话说，性能即响应时间，这是一个非常重要的原则。我们通过任务和时间而不是资源来测量性能。数据库服务器的目的是执行SQL语句，所以它关注的任务是查询或者语句，如SELECT、UPDATE、DELETE等(1)。数据库服务器的性能用查询的响应时间来度量，单位是每个查询花费的时间。&lt;/p>
&lt;p>还有另外一个问题：什么是优化？我们暂时不讨论这个问题，而是假设性能优化就是在一定的工作负载下尽可能地(2)降低响应时间。&lt;/p>
&lt;p>很多人对此很迷茫。假如你认为性能优化是降低CPU利用率，那么可以减少对资源的使用。但这是一个陷阱，资源是用来消耗并用来工作的，所以有时候消耗更多的资源能够加快查询速度。很多时候将使用老版本InnoDB引擎的MySQL升级到新版本后，CPU利用率会上升得很厉害，这并不代表性能出现了问题，反而说明新版本的InnoDB对资源的利用率上升了。查询的响应时间则更能体现升级后的性能是不是变得更好。版本升级有时候会带来一些bug，比如不能利用某些索引从而导致CPU利用率上升。CPU利用率只是一种现象，而不是很好的可度量的目标。&lt;/p>
&lt;p>同样，如果把性能优化仅仅看成是提升每秒查询量，这其实只是吞吐量优化。吞吐量的提升可以看作性能优化的副产品(3)。对查询的优化可以让服务器每秒执行更多的查询，因为每条查询执行的时间更短了（吞吐量的定义是单位时间内的查询数量，这正好是我们对性能的定义的倒数）。&lt;/p>
&lt;p>所以如果目标是降低响应时间，那么就需要理解为什么服务器执行查询需要这么多时间，然后去减少或者消除那些对获得查询结果来说不必要的工作。也就是说，先要搞清楚时间花在哪里。这就引申出优化的第二个原则：无法测量就无法有效地优化。所以第一步应该测量时间花在什么地方。&lt;/p>
&lt;p>我们观察到，很多人在优化时，都将精力放在修改一些东西上，却很少去进行精确的测量。我们的做法完全相反，将花费非常多，甚至90％的时间来测量响应时间花在哪里。如果通过测量没有找到答案，那要么是测量的方式错了，要么是测量得不够完整。如果测量了系统中完整而且正确的数据，性能问题一般都能暴露出来，对症下药的解决方案也就比较明了。测量是一项很有挑战性的工作，并且分析结果也同样有挑战性，测出时间花在哪里，和知道为什么花在那里，是两码事。&lt;/p>
&lt;p>前面提到需要合适的测量范围，这是什么意思呢？合适的测量范围是说只测量需要优化的活动。有两种比较常见的情况会导致不合适的测量：&lt;/p>
&lt;ul>
&lt;li>在错误的时间启动和停止测量。&lt;/li>
&lt;li>测量的是聚合后的信息，而不是目标活动本身。&lt;/li>
&lt;/ul>
&lt;p>例如，一个常见的错误是先查看慢查询，然后又去排查整个服务器的情况来判断问题在哪里。如果确认有慢查询，那么就应该测量慢查询，而不是测量整个服务器。测量的应该是从慢查询的开始到结束的时间，而不是查询之前或查询之后的时间。&lt;/p>
&lt;p>完成一项任务所需要的时间可以分成两部分：执行时间和等待时间。如果要优化任务的执行时间，最好的办法是通过测量定位不同的子任务花费的时间，然后优化去掉一些子任务、降低子任务的执行频率或者提升子任务的效率。而优化任务的等待时间则相对要复杂一些，因为等待有可能是由其他系统间接影响导致，任务之间也可能由于争用磁盘或者CPU资源而相互影响。根据时间是花在执行还是等待上的不同，诊断也需要不同的工具和技术。&lt;/p>
&lt;p>刚才说到需要定位和优化子任务，但只是一笔带过。一些运行不频繁或者很短的子任务对整体响应时间的影响很小，通常可以忽略不计。那么如何确认哪些子任务是优化的目标呢？这个时候性能剖析就可以派上用场了。&lt;/p>
&lt;p>&lt;strong>如何判断测量是正确的？&lt;/strong>&lt;/p>
&lt;p>如果测量是如此重要，那么测量错了会有什么后果？实际上，测量经常都是错误的。对数量的测量并不等于数量本身。测量的错误可能很小，跟实际情况区别不大，但错的终归是错的。所以这个问题其实应该是：“测量到底有多么不准确？”这个问题在其他一些书中有详细的讨论，但不是本书的主题。但是要意识到使用的是测量数据，而不是其所代表的实际数据。通常来说，测量的结果也可能有多种模糊的表现，这可能导致推断出错误的结论。&lt;/p>
&lt;h2 id="311通过性能剖析进行优化">3.1.1　通过性能剖析进行优化&lt;a class="anchor" href="#311%e9%80%9a%e8%bf%87%e6%80%a7%e8%83%bd%e5%89%96%e6%9e%90%e8%bf%9b%e8%a1%8c%e4%bc%98%e5%8c%96">#&lt;/a>&lt;/h2>
&lt;p>一旦掌握并实践面向响应时间的优化方法，就会发现需要不断地对系统进行性能剖析（profiling）。&lt;/p>
&lt;p>性能剖析是测量和分析时间花费在哪里的主要方法。性能剖析一般有两个步骤：测量任务所花费的时间；然后对结果进行统计和排序，将重要的任务排到前面。&lt;/p>
&lt;p>性能剖析工具的工作方式基本相同。在任务开始时启动计时器，在任务结束时停止计时器，然后用结束时间减去启动时间得到响应时间。也有些工具会记录任务的父任务。这些结果数据可以用来绘制调用关系图，但对于我们的目标来说更重要的是，可以将相似的任务分组并进行汇总。对相似的任务分组并进行汇总可以帮助对那些分到一组的任务做更复杂的统计分析，但至少需要知道每一组有多少任务，并计算出总的响应时间。通过性能剖析报告（&lt;em>profile report&lt;/em>）可以获得需要的结果。性能剖析报告会列出所有任务列表。每行记录一个任务，包括任务名、任务的执行时间、任务的消耗时间、任务的平均执行时间，以及该任务执行时间占全部时间的百分比。性能剖析报告会按照任务的消耗时间进行降序排序。&lt;/p>
&lt;p>为了更好地说明，这里举一个对整个数据库服务器工作负载的性能剖析的例子，主要输出的是各种类型的查询和执行查询的时间。这是从整体的角度来分析响应时间，后面会演示其他角度的分析结果。下面的输出是用Percona Toolkit中的&lt;em>pt-query-digest&lt;/em>（实际上就是著名的Maatkit工具中的&lt;em>mk-query-digest&lt;/em>）分析得到的结果。为了显示方便，对结果做了一些微调，并且只截取了前面几行结果：&lt;/p>
&lt;pre>&lt;code> Rank Response time Calls R/Call Item
 ==== ================ ===== ====== =======
 1 11256.3618 68.1% 78069 0.1442 SELECT InvitesNew
 2 2029.4730 12.3% 14415 0.1408 SELECT StatusUpdate
 3 1345.3445 8.1% 3520 0.3822 SHOW STATUS
&lt;/code>&lt;/pre>
&lt;p>上面只是性能剖析结果的前几行，根据总响应时间进行排名，只包括剖析所需要的最小列组合。每一行都包括了查询的响应时间和占总时间的百分比、查询的执行次数、单次执行的平均响应时间，以及该查询的摘要。通过这个性能剖析可以很清楚地看到每个查询相互之间的成本比较，以及每个查询占总成本的比较。在这个例子中，任务指的就是查询，实际上在分析MySQL的时候经常都指的是查询。&lt;/p>
&lt;p>我们将实际地讨论两种类型的性能剖析：基于执行时间的分析和基于等待的分析。基于执行时间的分析研究的是什么任务的执行时间最长，而基于等待的分析则是判断任务在什么地方被阻塞的时间最长。&lt;/p>
&lt;p>如果任务执行时间长是因为消耗了太多的资源且大部分时间花费在执行上，等待的时间不多，这种情况下基于等待的分析作用就不大。反之亦然，如果任务一直在等待，没有消耗什么资源，去分析执行时间就不会有什么结果。如果不能确认问题是出在执行还是等待上，那么两种方式都需要试试。后面会给出详细的例子。&lt;/p>
&lt;p>事实上，当基于执行时间的分析发现一个任务需要花费太多时间的时候，应该深入去分析一下，可能会发现某些“执行时间”实际上是在等待。例如，上面简单的性能剖析的输出显示表InvitesNew上的SELECT查询花费了大量时间，如果深入研究，则可能发现时间都花费在等待I/O完成上。&lt;/p>
&lt;p>在对系统进行性能剖析前，必须先要能够进行测量，这需要系统可测量化的支持。可测量的系统一般会有多个测量点可以捕获并收集数据，但实际系统很少可以做到可测量化。大部分系统都没有多少可测量点，即使有也只提供一些活动的计数，而没有活动花费的时间统计。MySQL就是一个典型的例子，直到版本5.5才第一次提供了Performance Schema，其中有一些基于时间的测量点(4)，而版本5.1及之前的版本没有任何基于时间的测量点。能够从MySQL收集到的服务器操作的数据大多是show status计数器的形式，这些计数器统计的是某种活动发生的次数。这也是我们最终决定创建Percona Server的主要原因，Percona Server从版本5.0开始提供很多更详细的查询级别的测量点。&lt;/p></description></item><item><title>第2章MySQL基准测试</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC2%E7%AB%A0MySQL%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95/</link><pubDate>Sun, 05 Jan 2025 08:42:12 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC2%E7%AB%A0MySQL%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95/</guid><description>&lt;p>第2章　MySQL基准测试&lt;/p>
&lt;p>基准测试（benchmark）是MySQL新手和专家都需要掌握的一项基本技能。简单地说，基准测试是针对系统设计的一种压力测试。通常的目标是为了掌握系统的行为。但也有其他原因，如重现某个系统状态，或者是做新硬件的可靠性测试。本章将讨论MySQL和基于MySQL的应用的基准测试的重要性、策略和工具。我们将特别讨论一下sysbench，这是一款非常优秀的MySQL基准测试工具。&lt;/p>
&lt;h1 id="21为什么需要基准测试">2.1　为什么需要基准测试&lt;a class="anchor" href="#21%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e5%9f%ba%e5%87%86%e6%b5%8b%e8%af%95">#&lt;/a>&lt;/h1>
&lt;p>为什么基准测试很重要？因为基准测试是唯一方便有效的、可以学习系统在给定的工作负载下会发生什么的方法。基准测试可以观察系统在不同压力下的行为，评估系统的容量，掌握哪些是重要的变化，或者观察系统如何处理不同的数据。基准测试可以在系统实际负载之外创造一些虚构场景进行测试。基准测试可以完成以下工作，或者更多：&lt;/p>
&lt;ul>
&lt;li>验证基于系统的一些假设，确认这些假设是否符合实际情况。&lt;/li>
&lt;li>重现系统中的某些异常行为，以解决这些异常。&lt;/li>
&lt;li>测试系统当前的运行情况。如果不清楚系统当前的性能，就无法确认某些优化的效果如何。也可以利用历史的基准测试结果来分析诊断一些无法预测的问题。&lt;/li>
&lt;li>模拟比当前系统更高的负载，以找出系统随着压力增加而可能遇到的扩展性瓶颈。&lt;/li>
&lt;li>规划未来的业务增长。基准测试可以评估在项目未来的负载下，需要什么样的硬件，需要多大容量的网络，以及其他相关资源。这有助于降低系统升级和重大变更的风险。&lt;/li>
&lt;li>测试应用适应可变环境的能力。例如，通过基准测试，可以发现系统在随机的并发峰值下的性能表现，或者是不同配置的服务器之间的性能表现。基准测试也可以测试系统对不同数据分布的处理能力。&lt;/li>
&lt;li>测试不同的硬件、软件和操作系统配置。比如RAID 5还是RAID 10更适合当前的系统？如果系统从ATA硬盘升级到SAN存储，对于随机写性能有什么帮助？Linux 2.4系列的内核会比2.6系列的可扩展性更好吗？升级MySQL的版本能改善性能吗？为当前的数据采用不同的存储引擎会有什么效果？所有这类问题都可以通过专门的基准测试来获得答案。&lt;/li>
&lt;li>证明新采购的设备是否配置正确。笔者曾经无数次地通过基准测试来对新系统进行压测，发现了很多错误的配置，以及硬件组件的失效等问题。因此在新系统正式上线到生产环境之前进行基准测试是一个好习惯，永远不要相信主机提供商或者硬件供应商的所谓系统已经安装好，并且能运行多快的说法。如果可能，执行实际的基准测试永远是一个好主意。&lt;/li>
&lt;/ul>
&lt;p>基准测试还可以用于其他目的，比如为应用创建单元测试套件。但本章我们只关注与性能有关的基准测试。&lt;/p>
&lt;p>基准测试的一个主要问题在于其不是真实压力的测试。基准测试施加给系统的压力相对真实压力来说，通常比较简单。真实压力是不可预期而且变化多端的，有时候情况会过于复杂而难以解释。所以使用真实压力测试，可能难以从结果中分析出确切的结论。&lt;/p>
&lt;p>基准测试的压力和真实压力在哪些方面不同？有很多因素会影响基准测试，比如数据量、数据和查询的分布，但最重要的一点还是基准测试通常要求尽可能快地执行完成，所以经常给系统造成过大的压力。在很多案例中，我们都会调整给测试工具的最大压力，以在系统可以容忍的压力阈值内尽可能快地执行测试，这对于确定系统的最大容量非常有帮助。然而大部分压力测试工具不支持对压力进行复杂的控制。务必要记住，测试工具自身的局限也会影响到结果的有效性。&lt;/p>
&lt;p>使用基准测试进行容量规划也要掌握技巧，不能只根据测试结果做简单的推断。例如，假设想知道使用新数据库服务器后，系统能够支撑多大的业务增长。首先对原系统进行基准测试，然后对新系统做测试，结果发现新系统可以支持原系统40倍的TPS（每秒事务数），这时候就不能简单地推断说新系统一定可以支持40倍的业务增长。这是因为在业务增长的同时，系统的流量、用户、数据以及不同数据之间的交互都在增长，它们不可能都有40倍的支撑能力，尤其是相互之间的关系。而且当业务增长到40倍时，应用本身的设计也可能已经随之改变。可能有更多的新特性会上线，其中某些特性可能对数据库造成的压力远大于原有功能。而这些压力、数据、关系和特性的变化都很难模拟，所以它们对系统的影响也很难评估。&lt;/p>
&lt;p>结论就是，我们只能进行大概的测试，来确定系统大致的余量有多少。当然也可以做一些真实压力测试（和基准测试有区别），但在构造数据集和压力的时候要特别小心，而且这样就不再是基准测试了。基准测试要尽量简单直接，结果之间容易相互比较，成本低且易于执行。尽管有诸多限制，基准测试还是非常有用的（只要搞清楚测试的原理，并且了解如何分析结果所代表的意义）。&lt;/p>
&lt;h1 id="22基准测试的策略">2.2　基准测试的策略&lt;a class="anchor" href="#22%e5%9f%ba%e5%87%86%e6%b5%8b%e8%af%95%e7%9a%84%e7%ad%96%e7%95%a5">#&lt;/a>&lt;/h1>
&lt;p>基准测试有两种主要的策略：一是针对整个系统的整体测试，另外是单独测试MySQL。这两种策略也被称为集成式（full-stack）以及单组件式（single-component）基准测试。针对整个系统做集成式测试，而不是单独测试MySQL的原因主要有以下几点：&lt;/p>
&lt;ul>
&lt;li>测试整个应用系统，包括Web服务器、应用代码、网络和数据库是非常有用的，因为用户关注的并不仅仅是MySQL本身的性能，而是应用整体的性能。&lt;/li>
&lt;li>MySQL并非总是应用的瓶颈，通过整体的测试可以揭示这一点。&lt;/li>
&lt;li>只有对应用做整体测试，才能发现各部分之间的缓存带来的影响。&lt;/li>
&lt;li>整体应用的集成式测试更能揭示应用的真实表现，而单独组件的测试很难做到这一点。&lt;/li>
&lt;/ul>
&lt;p>另外一方面，应用的整体基准测试很难建立，甚至很难正确设置。如果基准测试的设计有问题，那么结果就无法反映真实的情况，从而基于此做的决策也就可能是错误的。&lt;/p>
&lt;p>不过，有时候不需要了解整个应用的情况，而只需要关注MySQL的性能，至少在项目初期可以这样做。基于以下情况，可以选择只测试MySQL：&lt;/p>
&lt;ul>
&lt;li>需要比较不同的schema或查询的性能。&lt;/li>
&lt;li>针对应用中某个具体问题的测试。&lt;/li>
&lt;li>为了避免漫长的基准测试，可以通过一个短期的基准测试，做快速的“周期循环”，来检测出某些调整后的效果。&lt;/li>
&lt;/ul>
&lt;p>另外，如果能够在真实的数据集上执行重复的查询，那么针对MySQL的基准测试也是有用的，但是数据本身和数据集的大小都应该是真实的。如果可能，可以采用生产环境的数据快照。&lt;/p>
&lt;p>不幸的是，设置一个基于真实数据的基准测试复杂而且耗时。如果能得到一份生产数据集的拷贝，当然很幸运，但这通常不太可能。比如要测试的是一个刚开发的新应用，它只有很少的用户和数据。如果想测试该应用在规模扩张到很大以后的性能表现，就只能通过模拟大量的数据和压力来进行。&lt;/p>
&lt;h2 id="221测试何种指标">2.2.1　测试何种指标&lt;a class="anchor" href="#221%e6%b5%8b%e8%af%95%e4%bd%95%e7%a7%8d%e6%8c%87%e6%a0%87">#&lt;/a>&lt;/h2>
&lt;p>在开始执行甚至是在设计基准测试之前，需要先明确测试的目标。测试目标决定了选择什么样的测试工具和技术，以获得精确而有意义的测试结果。可以将测试目标细化为一系列的问题，比如，“这种CPU是否比另外一种要快？”，或“新索引是否比当前索引性能更好？”&lt;/p>
&lt;p>有时候需要用不同的方法测试不同的指标。比如，针对延迟（latency）和吞吐量（throughput）就需要采用不同的测试方法。&lt;/p>
&lt;p>请考虑以下指标，看看如何满足测试的需求。&lt;/p>
&lt;p>吞吐量&lt;/p>
&lt;p>吞吐量指的是单位时间内的事务处理数。这一直是经典的数据库应用测试指标。一些标准的基准测试被广泛地引用，如TPC-C（参考&lt;em>
&lt;a target="_blank" href="http://www.tpc.org">http://www.tpc.org&lt;/a>&lt;/em>），而且很多数据库厂商都努力争取在这些测试中取得好成绩。这类基准测试主要针对在线事务处理（OLTP）的吞吐量，非常适用于多用户的交互式应用。常用的测试单位是每秒事务数（TPS），有些也采用每分钟事务数（TPM）。&lt;/p>
&lt;p>响应时间或者延迟&lt;/p>
&lt;p>这个指标用于测试任务所需的整体时间。根据具体的应用，测试的时间单位可能是微秒、毫秒、秒或者分钟。根据不同的时间单位可以计算出平均响应时间、最小响应时间、最大响应时间和所占百分比。最大响应时间通常意义不大，因为测试时间越长，最大响应时间也可能越大。而且其结果通常不可重复，每次测试都可能得到不同的最大响应时间。因此，通常可以使用百分比响应时间（percentile response time）来替代最大响应时间。例如，如果95％的响应时间都是5毫秒，则表示任务在95％的时间段内都可以在5毫秒之内完成。&lt;/p>
&lt;p>使用图表有助于理解测试结果。可以将测试结果绘制成折线图（比如平均值折线或者95％百分比折线）或者散点图，直观地表现数据结果集的分布情况。通过这些图可以发现长时间测试的趋势。本章后面将更详细地讨论这一点。&lt;/p>
&lt;p>并发性&lt;/p>
&lt;p>并发性是一个非常重要又经常被误解和误用的指标。例如，它经常被表示成多少用户在同一时间浏览一个Web站点，经常使用的指标是有多少个会话(1)。然而，HTTP协议是无状态的，大多数用户只是简单地读取浏览器上显示的信息，这并不等同于Web服务器的并发性。而且，Web服务器的并发性也不等同于数据库的并发性，而仅仅只表示会话存储机制可以处理多少数据的能力。Web服务器的并发性更准确的度量指标，应该是在任意时间有多少同时发生的并发请求。&lt;/p>
&lt;p>在应用的不同环节都可以测量相应的并发性。Web服务器的高并发，一般也会导致数据库的高并发，但服务器采用的语言和工具集对此都会有影响。注意不要将创建数据库连接和并发性搞混淆。一个设计良好的应用，同时可以打开成百上千个MySQL数据库服务器连接，但可能同时只有少数连接在执行查询。所以说，一个Web站点“同时有50000个用户”访问，却可能只有10～15个并发请求到MySQL数据库。&lt;/p>
&lt;p>换句话说，并发性基准测试需要关注的是正在工作中的并发操作，或者是同时工作中的线程数或者连接数。当并发性增加时，需要测量吞吐量是否下降，响应时间是否变长，如果是这样，应用可能就无法处理峰值压力。&lt;/p>
&lt;p>并发性的测量完全不同于响应时间和吞吐量。它不像是一个结果，而更像是设置基准测试的一种属性。并发性测试通常不是为了测试应用能达到的并发度，而是为了测试应用在不同并发下的性能。当然，数据库的并发性还是需要测量的。可以通过&lt;em>sysbench&lt;/em>指定32、64或者128个线程的测试，然后在测试期间记录MySQL数据库的Threads_running状态值。在第11章将讨论这个指标对容量规划的影响。&lt;/p>
&lt;p>可扩展性&lt;/p>
&lt;p>在系统的业务压力可能发生变化的情况下，测试可扩展性就非常必要了。第11章将更进一步讨论可扩展性的话题。简单地说，可扩展性指的是，给系统增加一倍的工作，在理想情况下就能获得两倍的结果（即吞吐量增加一倍）。或者说，给系统增加一倍的资源（比如两倍的CPU数），就可以获得两倍的吞吐量。当然，同时性能（响应时间）也必须在可以接受的范围内。大多数系统是无法做到如此理想的线性扩展的。随着压力的变化，吞吐量和性能都可能越来越差。&lt;/p>
&lt;p>可扩展性指标对于容量规范非常有用，它可以提供其他测试无法提供的信息，来帮助发现应用的瓶颈。比如，如果系统是基于单个用户的响应时间测试（这是一个很糟糕的测试策略）设计的，虽然测试的结果很好，但当并发度增加时，系统的性能有可能变得非常糟糕。而一个基于不断增加用户连接的情况下的响应时间测试则可以发现这个问题。&lt;/p>
&lt;p>一些任务，比如从细粒度数据创建汇总表的批量工作，需要的是周期性的快速响应时间。当然也可以测试这些任务纯粹的响应时间，但要注意考虑这些任务之间的相互影响。批量工作可能导致相互之间有影响的查询性能变差，反之亦然。&lt;/p>
&lt;p>归根结底，应该测试那些对用户来说最重要的指标。因此应该尽可能地去收集一些需求，比如，什么样的响应时间是可以接受的，期待多少的并发性，等等。然后基于这些需求来设计基准测试，避免目光短浅地只关注部分指标，而忽略其他指标。&lt;/p>
&lt;h1 id="23基准测试方法">2.3　基准测试方法&lt;a class="anchor" href="#23%e5%9f%ba%e5%87%86%e6%b5%8b%e8%af%95%e6%96%b9%e6%b3%95">#&lt;/a>&lt;/h1>
&lt;p>在了解基本概念之后，现在可以来具体讨论一下如何设计和执行基准测试。但在讨论如何设计好的基准测试之前，先来看一下如何避免一些常见的错误，这些错误可能导致测试结果无用或者不精确：&lt;/p>
&lt;ul>
&lt;li>使用真实数据的子集而不是全集。例如应用需要处理几百GB的数据，但测试只有1GB数据；或者只使用当前数据进行测试，却希望模拟未来业务大幅度增长后的情况。&lt;/li>
&lt;li>使用错误的数据分布。例如使用均匀分布的数据测试，而系统的真实数据有很多热点区域（随机生成的测试数据通常无法模拟真实的数据分布）。&lt;/li>
&lt;li>使用不真实的分布参数，例如假定所有用户的个人信息（profile）都会被平均地读取(2)。&lt;/li>
&lt;li>在多用户场景中，只做单用户的测试。&lt;/li>
&lt;li>在单服务器上测试分布式应用。&lt;/li>
&lt;li>与真实用户行为不匹配。例如Web页面中的“思考时间”。真实用户在请求到一个页面后会阅读一段时间，而不是不停顿地一个接一个点击相关链接。&lt;/li>
&lt;li>反复执行同一个查询。真实的查询是不尽相同的，这可能会导致缓存命中率降低。而反复执行同一个查询在某种程度上，会全部或者部分缓存结果。&lt;/li>
&lt;li>没有检查错误。如果测试的结果无法得到合理的解释，比如一个本应该很慢的查询突然变快了，就应该检查是否有错误产生。否则可能只是测试了MySQL检测语法错误的速度了。基准测试完成后，一定要检查一下错误日志，这应当是基本的要求。&lt;/li>
&lt;li>忽略了系统预热（warm up）的过程。例如系统重启后马上进行测试。有时候需要了解系统重启后需要多长时间才能达到正常的性能容量，要特别留意预热的时长。反过来说，如果要想分析正常的性能，需要注意，若基准测试在重启以后马上启动，则缓存是冷的、还没有数据，这时即使测试的压力相同，得到的结果也和缓存已经装满数据时是不同的。&lt;/li>
&lt;li>使用默认的服务器配置。第3章将详细地讨论服务器的优化配置。&lt;/li>
&lt;li>测试时间太短。基准测试需要持续一定的时间。后面会继续讨论这个话题。&lt;/li>
&lt;/ul>
&lt;p>只有避免了上述错误，才能走上改进测试质量的漫漫长路。&lt;/p></description></item><item><title>第1章MySQL架构与历史</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC1%E7%AB%A0MySQL%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%8E%86%E5%8F%B2/</link><pubDate>Sun, 05 Jan 2025 08:42:11 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC1%E7%AB%A0MySQL%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%8E%86%E5%8F%B2/</guid><description>&lt;p>第1章　MySQL架构与历史&lt;/p>
&lt;p>和其他数据库系统相比，MySQL有点与众不同，它的架构可以在多种不同场景中应用并发挥好的作用，但同时也会带来一点选择上的困难。MySQL并不完美，却足够灵活，能够适应高要求的环境，例如Web类应用。同时，MySQL既可以嵌入到应用程序中，也可以支持数据仓库、内容索引和部署软件、高可用的冗余系统、在线事务处理系统（OLTP）等各种应用类型。&lt;/p>
&lt;p>为了充分发挥MySQL的性能并顺利地使用，就必须理解其设计。MySQL的灵活性体现在很多方面。例如，你可以通过配置使它在不同的硬件上都运行得很好，也可以支持多种不同的数据类型。但是，MySQL最重要、最与众不同的特性是它的存储引擎架构，这种架构的设计将查询处理（Query Processing）及其他系统任务（Server Task）和数据的存储/提取相分离。这种处理和存储分离的设计可以在使用时根据性能、特性，以及其他需求来选择数据存储的方式。&lt;/p>
&lt;p>本章概要地描述了MySQL的服务器架构、各种存储引擎之间的主要区别，以及这些区别的重要性。另外也会回顾一下MySQL的历史背景和基准测试，并试图通过简化细节和演示案例来讨论MySQL的原理。这些讨论无论是对数据库一无所知的新手，还是熟知其他数据库的专家，都不无裨益。&lt;/p>
&lt;h1 id="11mysql逻辑架构">1.1　MySQL逻辑架构&lt;a class="anchor" href="#11mysql%e9%80%bb%e8%be%91%e6%9e%b6%e6%9e%84">#&lt;/a>&lt;/h1>
&lt;p>如果能在头脑中构建出一幅MySQL各组件之间如何协同工作的架构图，就会有助于深入理解MySQL服务器。图1-1展示了MySQL的逻辑架构图。&lt;/p>
&lt;p>&lt;img src="img/000002.jpeg" alt="" />
&lt;strong>图1-1：MySQL服务器逻辑架构图&lt;/strong>&lt;/p>
&lt;p>最上层的服务并不是MySQL所独有的，大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。&lt;/p>
&lt;p>第二层架构是MySQL比较有意思的部分。大多数MySQL的核心服务功能都在这一层，包括查询解析、分析、优化、缓存以及所有的内置函数（例如，日期、时间、数学和加密函数），所有跨存储引擎的功能都在这一层实现：存储过程、触发器、视图等。&lt;/p>
&lt;p>第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。和GNU/Linux下的各种文件系统一样，每个存储引擎都有它的优势和劣势。服务器通过API与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异，使得这些差异对上层的查询过程透明。存储引擎API包含几十个底层函数，用于执行诸如“开始一个事务”或者“根据主键提取一行记录”等操作。但存储引擎不会去解析SQL(1)，不同存储引擎之间也不会相互通信，而只是简单地响应上层服务器的请求。&lt;/p>
&lt;h2 id="111连接管理与安全性">1.1.1　连接管理与安全性&lt;a class="anchor" href="#111%e8%bf%9e%e6%8e%a5%e7%ae%a1%e7%90%86%e4%b8%8e%e5%ae%89%e5%85%a8%e6%80%a7">#&lt;/a>&lt;/h2>
&lt;p>每个客户端连接都会在服务器进程中拥有一个线程，这个连接的查询只会在这个单独的线程中执行，该线程只能轮流在某个CPU核心或者CPU中运行。服务器会负责缓存线程，因此不需要为每一个新建的连接创建或者销毁线程(2)。&lt;/p>
&lt;p>当客户端（应用）连接到MySQL服务器时，服务器需要对其进行认证。认证基于用户名、原始主机信息和密码。如果使用了安全套接字（SSL）的方式连接，还可以使用X.509证书认证。一旦客户端连接成功，服务器会继续验证该客户端是否具有执行某个特定查询的权限（例如，是否允许客户端对world数据库的Country表执行SELECT语句）。&lt;/p>
&lt;h2 id="112优化与执行">1.1.2　优化与执行&lt;a class="anchor" href="#112%e4%bc%98%e5%8c%96%e4%b8%8e%e6%89%a7%e8%a1%8c">#&lt;/a>&lt;/h2>
&lt;p>MySQL会解析查询，并创建内部数据结构（解析树），然后对其进行各种优化，包括重写查询、决定表的读取顺序，以及选择合适的索引等。用户可以通过特殊的关键字提示（hint）优化器，影响它的决策过程。也可以请求优化器解释（explain）优化过程的各个因素，使用户可以知道服务器是如何进行优化决策的，并提供一个参考基准，便于用户重构查询和schema、修改相关配置，使应用尽可能高效运行。第6章我们将讨论更多优化器的细节。&lt;/p>
&lt;p>优化器并不关心表使用的是什么存储引擎，但存储引擎对于优化查询是有影响的。优化器会请求存储引擎提供容量或某个具体操作的开销信息，以及表数据的统计信息等。例如，某些存储引擎的某种索引，可能对一些特定的查询有优化。关于索引与schema的优化，请参见第4章和第5章。&lt;/p>
&lt;p>对于SELECT语句，在解析查询之前，服务器会先检查查询缓存（Query Cache），如果能够在其中找到对应的查询，服务器就不必再执行查询解析、优化和执行的整个过程，而是直接返回查询缓存中的结果集。第7章详细讨论了相关内容。&lt;/p>
&lt;h1 id="12并发控制">1.2　并发控制&lt;a class="anchor" href="#12%e5%b9%b6%e5%8f%91%e6%8e%a7%e5%88%b6">#&lt;/a>&lt;/h1>
&lt;p>无论何时，只要有多个查询需要在同一时刻修改数据，都会产生并发控制的问题。本章的目的是讨论MySQL在两个层面的并发控制：服务器层与存储引擎层。并发控制是一个内容庞大的话题，有大量的理论文献对其进行过详细的论述。本章只简要地讨论MySQL如何控制并发读写，因此读者需要有相关的知识来理解本章接下来的内容。&lt;/p>
&lt;p>以Unix系统的email box为例，典型的&lt;em>mbox&lt;/em>文件格式是非常简单的。一个&lt;em>mbox&lt;/em>邮箱中的所有邮件都串行在一起，彼此首尾相连。这种格式对于读取和分析邮件信息非常友好，同时投递邮件也很容易，只要在文件末尾附加新的邮件内容即可。&lt;/p>
&lt;p>但如果两个进程在同一时刻对同一个邮箱投递邮件，会发生什么情况？显然，邮箱的数据会被破坏，两封邮件的内容会交叉地附加在邮箱文件的末尾。设计良好的邮箱投递系统会通过锁（lock）来防止数据损坏。如果客户试图投递邮件，而邮箱已经被其他客户锁住，那就必须等待，直到锁释放才能进行投递。&lt;/p>
&lt;p>这种锁的方案在实际应用环境中虽然工作良好，但并不支持并发处理。因为在任意一个时刻，只有一个进程可以修改邮箱的数据，这在大容量的邮箱系统中是个问题。&lt;/p>
&lt;h2 id="121读写锁">1.2.1　读写锁&lt;a class="anchor" href="#121%e8%af%bb%e5%86%99%e9%94%81">#&lt;/a>&lt;/h2>
&lt;p>从邮箱中读取数据没有这样的麻烦，即使同一时刻多个用户并发读取也不会有什么问题。因为读取不会修改数据，所以不会出错。但如果某个客户正在读取邮箱，同时另外一个用户试图删除编号为25的邮件，会产生什么结果？结论是不确定，读的客户可能会报错退出，也可能读取到不一致的邮箱数据。所以，为安全起见，即使是读取邮箱也需要特别注意。&lt;/p>
&lt;p>如果把上述的邮箱当成数据库中的一张表，把邮件当成表中的一行记录，就很容易看出，同样的问题依然存在。从很多方面来说，邮箱就是一张简单的数据库表。修改数据库表中的记录，和删除或者修改邮箱中的邮件信息，十分类似。&lt;/p>
&lt;p>解决这类经典问题的方法就是并发控制，其实非常简单。在处理并发读或者写时，可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种类型的锁通常被称为共享锁（shared lock）和排他锁（exclusive lock），也叫读锁（read lock）和写锁（write lock）。&lt;/p>
&lt;p>这里先不讨论锁的具体实现，描述一下锁的概念如下：读锁是共享的，或者说是相互不阻塞的。多个客户在同一时刻可以同时读取同一个资源，而互不干扰。写锁则是排他的，也就是说一个写锁会阻塞其他的写锁和读锁，这是出于安全策略的考虑，只有这样，才能确保在给定的时间里，只有一个用户能执行写入，并防止其他用户读取正在写入的同一资源。&lt;/p>
&lt;p>在实际的数据库系统中，每时每刻都在发生锁定，当某个用户在修改某一部分数据时，MySQL会通过锁定防止其他用户读取同一数据。大多数时候，MySQL锁的内部管理都是透明的。&lt;/p>
&lt;h2 id="122锁粒度">1.2.2　锁粒度&lt;a class="anchor" href="#122%e9%94%81%e7%b2%92%e5%ba%a6">#&lt;/a>&lt;/h2>
&lt;p>一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定需要修改的部分数据，而不是所有的资源。更理想的方式是，只对会修改的数据片进行精确的锁定。任何时候，在给定的资源上，锁定的数据量越少，则系统的并发程度越高，只要相互之间不发生冲突即可。&lt;/p>
&lt;p>问题是加锁也需要消耗资源。锁的各种操作，包括获得锁、检查锁是否已经解除、释放锁等，都会增加系统的开销。如果系统花费大量的时间来管理锁，而不是存取数据，那么系统的性能可能会因此受到影响。&lt;/p>
&lt;p>所谓的锁策略，就是在锁的开销和数据的安全性之间寻求平衡，这种平衡当然也会影响到性能。大多数商业数据库系统没有提供更多的选择，一般都是在表上施加行级锁（row-level lock），并以各种复杂的方式来实现，以便在锁比较多的情况下尽可能地提供更好的性能。&lt;/p>
&lt;p>而MySQL则提供了多种选择。每种MySQL存储引擎都可以实现自己的锁策略和锁粒度。在存储引擎的设计中，锁管理是个非常重要的决定。将锁粒度固定在某个级别，可以为某些特定的应用场景提供更好的性能，但同时却会失去对另外一些应用场景的良好支持。好在MySQL支持多个存储引擎的架构，所以不需要单一的通用解决方案。下面将介绍两种最重要的锁策略。&lt;/p>
&lt;h3 id="表锁table-lock">表锁（table lock）&lt;a class="anchor" href="#%e8%a1%a8%e9%94%81table-lock">#&lt;/a>&lt;/h3>
&lt;p>表锁是MySQL中最基本的锁策略，并且是开销最小的策略。表锁非常类似于前文描述的邮箱加锁机制：它会锁定整张表。一个用户在对表进行写操作（插入、删除、更新等）前，需要先获得写锁，这会阻塞其他用户对该表的所有读写操作。只有没有写锁时，其他读取的用户才能获得读锁，读锁之间是不相互阻塞的。&lt;/p>
&lt;p>在特定的场景中，表锁也可能有良好的性能。例如，READ LOCAL表锁支持某些类型的并发写操作。另外，写锁也比读锁有更高的优先级，因此一个写锁请求可能会被插入到读锁队列的前面（写锁可以插入到锁队列中读锁的前面，反之读锁则不能插入到写锁的前面）。&lt;/p>
&lt;p>尽管存储引擎可以管理自己的锁，MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如，服务器会为诸如ALTER TABLE之类的语句使用表锁，而忽略存储引擎的锁机制。&lt;/p>
&lt;h3 id="行级锁row-lock">行级锁（row lock）&lt;a class="anchor" href="#%e8%a1%8c%e7%ba%a7%e9%94%81row-lock">#&lt;/a>&lt;/h3>
&lt;p>行级锁可以最大程度地支持并发处理（同时也带来了最大的锁开销）。众所周知，在InnoDB和XtraDB，以及其他一些存储引擎中实现了行级锁。行级锁只在存储引擎层实现，而MySQL服务器层（如有必要，请回顾前文的逻辑架构图）没有实现。服务器层完全不了解存储引擎中的锁实现。在本章的后续内容以及全书中，所有的存储引擎都以自己的方式显现了锁机制。&lt;/p>
&lt;h1 id="13事务">1.3　事务&lt;a class="anchor" href="#13%e4%ba%8b%e5%8a%a1">#&lt;/a>&lt;/h1>
&lt;p>在理解事务的概念之前，接触数据库系统的其他高级特性还言之过早。事务就是一组原子性的SQL查询，或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句，那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行，那么所有的语句都不会执行。也就是说，事务内的语句，要么全部执行成功，要么全部执行失败。&lt;/p>
&lt;p>本节的内容并非专属于MySQL，如果读者已经熟悉了事务的ACID的概念，可以直接跳转到1.3.4节。&lt;/p>
&lt;p>银行应用是解释事务必要性的一个经典例子。假设一个银行的数据库有两张表：支票（checking）表和储蓄（savings）表。现在要从用户Jane的支票账户转移200美元到她的储蓄账户，那么需要至少三个步骤：&lt;/p>
&lt;ol>
&lt;li>检查支票账户的余额高于200美元。&lt;/li>
&lt;li>从支票账户余额中减去200美元。&lt;/li>
&lt;li>在储蓄账户余额中增加200美元。&lt;/li>
&lt;/ol>
&lt;p>上述三个步骤的操作必须打包在一个事务中，任何一个步骤失败，则必须回滚所有的步骤。&lt;/p></description></item><item><title>第13章云端的MySQL</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC13%E7%AB%A0%E4%BA%91%E7%AB%AF%E7%9A%84MySQL/</link><pubDate>Sun, 05 Jan 2025 08:42:07 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC13%E7%AB%A0%E4%BA%91%E7%AB%AF%E7%9A%84MySQL/</guid><description>&lt;p>第13章　云端的MySQL&lt;/p>
&lt;p>许多人在云中使用MySQL，有时候规模还非常庞大，这并不奇怪。从我们的经验来看，大多数人使用的是Amazon Web Services平台（AWS）：特别是Amazon的弹性计算云（Elastic Compute Cloud，EC2），弹性块存储（Elastic Block Store，EBS），以及更小众的关系数据库服务（Relational Database Service，RDS）。&lt;/p>
&lt;p>为了便于讨论MySQL在云中的应用，可以将其粗略分为两类。&lt;/p>
&lt;p>IaaS（基础设施即服务）&lt;/p>
&lt;p>&lt;em>Iaas&lt;/em>是用于托管自有的MySQL服务器的云端基础架构。可以在云端购买虚拟的服务器资源来安装运行MySQL实例。也可以根据需求随意配置MySQL和操作系统，但没有权限也无法看到处于底层的物理硬件设备。&lt;/p>
&lt;p>&lt;em>DBaaS&lt;/em>（数据库即服务）&lt;/p>
&lt;p>MySQL本身作为由云端管理的资源。用户需要先收到MySQL服务器的访问许可（通常是一个连接串）才能访问。也可以配置一些MySQL选项，但没有权限去控制或查看底层的操作系统或虚拟服务器实例。例如 Amazon运行MySQL的RDS。其中一些服务器并非真的使用MySQL，但它们能兼容MySQL协议和查询语言。&lt;/p>
&lt;p>我们讨论的重点主要集中在第一类：云托管平台，例如AWS、Rackspace Cloud以及Joyent(1)。有许多很好的资源介绍如何部署和管理MySQL及其运行所需要的资源，并且也有非常多的平台来完全满足这样的需求，所以我们不会展示代码样例或讨论具体的操作技术。因此，本章关注的重点是，在云端运行MySQL还是在传统服务器上部署MySQL，它们在最终经济上和性能特性上的关键区别是什么。我们假定你对云计算很熟悉。这里不是对云计算概念的简单介绍，我们的目的只是帮助那些还不熟悉在云端部署MySQL的用户在使用时避免一些可能遇到的陷阱。&lt;/p>
&lt;p>一般来说，MySQL能够在云中很好地运行。在云中运行MySQL并不比在其他平台困难，但有一些非常重要的差别。你需要注意这些差别并据此设计应用和架构来获得好的效果。某些场景下在云端托管MySQL并不是非常适合，有时候则很适合，但大多数时候云仅仅是另外一个部署平台而已。&lt;/p>
&lt;p>云是一个部署平台，而不是一种架构，理解这一点很重要。架构会受平台的影响，但平台和架构明显不同。如果你把架构和平台搞混了，就可能会做出不合适的选择而给以后带来麻烦。这也正是我们要花时间讨论云端的MySQL到底有什么不同的原因。&lt;/p>
&lt;h1 id="131云的优点缺点和相关误解">13.1　云的优点、缺点和相关误解&lt;a class="anchor" href="#131%e4%ba%91%e7%9a%84%e4%bc%98%e7%82%b9%e7%bc%ba%e7%82%b9%e5%92%8c%e7%9b%b8%e5%85%b3%e8%af%af%e8%a7%a3">#&lt;/a>&lt;/h1>
&lt;p>云计算有许多优点，但很少是为MySQL特别设计。有一些书籍已经介绍了相关的话题(2)，这里我们不再赘述。不过我们会列出一些比较重要的条目供参考，因为接下来会讨论到云计算的缺点，我们不希望你认为我们是在过分苛求云计算。&lt;/p>
&lt;ul>
&lt;li>云是一种将基础设施外包出去无须自己管理的方法。你不需要寻找供应商购买硬件，也不需要维护和供应商之间的关系，更无须替换失效的硬盘驱动器等。&lt;/li>
&lt;li>云一般是按照即用即付的方式支付，可以把前期的大量资本支出转换为持续的运营成本。&lt;/li>
&lt;li>随着供应商发布新的服务和成本降低，云提供的价值越来越大。你自己无须做任何事情（例如升级服务器），就可以从这些提升中获益；随着时间推移你会很容易地获得更多更好的选择并且费用更低。&lt;/li>
&lt;li>云能够帮助你轻松地准备好服务器和其他资源，在用完后直接将其关闭，而无须关注怎么处理它们，或者怎么卖掉它们收回成本。&lt;/li>
&lt;li>云代表了对基础设施的另一种思考方式——作为通过API来定义和控制的资源——支持更多的自动化操作。从“私有云”中也可以获得这些好处。&lt;/li>
&lt;/ul>
&lt;p>当然，不是所有跟云相关的东西都是好的。这里有一些缺点可能会构成挑战（在本章稍后部分我们会列出MySQL特有的缺点）。&lt;/p>
&lt;ul>
&lt;li>资源是共享并且不可预测的，实际上你可以获得比你支付的更多的资源。这听起来很不错，但却导致容量规划很难做。如果你在不知情的情况下获得了比理应享受到的更多的计算资源，那么就存在这样的风险：别人也许会索要他们应得的资源，这会使你的应用性能退化到应有的水平。一般来说，很难确切地知道本来应该得到多少（资源），大多数云托管服务提供商不会对此给出确切的答案。&lt;/li>
&lt;li>无法保证容量和可用性。你可能以为还可以获得新实例，但如果供应商已经超额销售了呢？这在有很多共享资源的情况下会发生，同样也会发生在云中。&lt;/li>
&lt;li>虚拟的共享资源导致排查故障更加困难，特别是在无法访问底层物理硬件的情况下无法检查并弄清到底发生了什么。例如，我们曾经看到过一些系统的&lt;em>iostat&lt;/em>显示的I/O很正常或者&lt;em>vmstat&lt;/em>显示的CPU很正常，而当实际衡量完成一个任务需要的时间时，资源却被系统上的其他东西严重占用了。如果在云平台上出现了性能问题，尤其需要去仔细地分析检测。如果对此并不擅长，可能就无法确认到底是底层系统性能差，还是你做了什么事情导致应用出现不合理的资源需求。&lt;/li>
&lt;/ul>
&lt;p>总的来说，云平台上对性能、可用性和容量的透明性和控制力都有所下降。最后，还有一些对云的误解需要记住。&lt;/p>
&lt;p>云天生具备更好的可扩展性&lt;/p>
&lt;p>应用、云的架构，以及管理云服务的组织是不是都是可扩展的。云并不是天生可扩展的，云也仅仅是云而已，选择一个可扩展的平台并不能自动使应用变得可扩展。的确，如果云托管提供商没有超售，那么你可以根据需求来购买资源，但在需要时能够获得资源仅仅是扩展性的一个方面而已。&lt;/p>
&lt;p>云可以自动改善甚至保证可用时间&lt;/p>
&lt;p>一般来说，个别在云端托管的服务器比那些经过良好设计的专用基础设施更容易发生故障或运行中断。但是许多人并没有意识到这一点。例如，有人这样写道：“我们将基础设施升级到基于云构建的系统以保证100％的可用时间和可扩展性”。而就在这之前AWS遭受了两次大规模的运行中断故障，导致很大一部分用户受影响。好的架构能够用不可靠的组件设计出可靠的系统，但通常更可靠的基础设施可以获得更高的可用性。（当然不可能有100％的可用时间的系统。）&lt;/p>
&lt;p>另一方面，购买云计算服务，实际上是购买一个由专家构建的平台。他们已经考虑了许多底层的东西，这意味着你可以更专注于上层工作。如果构建自己的平台而对其中的那些细枝末节并不精通，就可能犯一些初学者的错误，早晚会导致一些宕机时间。从这一点来说，云计算能够帮助改善可用时间。&lt;/p>
&lt;p>云是唯一能提供[这里填入任意的优点]的东西&lt;/p>
&lt;p>事实上，许多云的优点是继承自构建云平台所用到的技术，即使不使用云也可以获得(3)。例如，通过管理得当的虚拟化和容量规划，可以像任何一个云平台那样简单快速地启动（spin up）一台新的机器。完全没必要专门使用云来做到这一点。&lt;/p>
&lt;p>云是一个“银弹”（silver bullet）&lt;/p>
&lt;p>虽然大部分人会认为这很荒谬，但确实有人会这么认为。实际上完全没有这回事。&lt;/p>
&lt;p>无可否认，云计算提供了独特的优点，随着时间的推移，关于云计算是什么，以及它们在什么情况下会有帮助，我们会获得更多的共识。但有一点非常肯定：它是全新的，我们现在所做的任何预测都未必经得起时间的考验。我们会在本书讨论相对安全的部分，而将剩下的部分留给读者讨论。&lt;/p>
&lt;h1 id="132mysql在云端的经济价值">13.2　MySQL在云端的经济价值&lt;a class="anchor" href="#132mysql%e5%9c%a8%e4%ba%91%e7%ab%af%e7%9a%84%e7%bb%8f%e6%b5%8e%e4%bb%b7%e5%80%bc">#&lt;/a>&lt;/h1>
&lt;p>在一些场景下云托管比传统的服务器部署方式更经济。以我们的经验来看，云托管比较适合尚处于初级阶段的企业，或者那些持续接触新概念并且本质上是以适用为主的企业，例如移动应用开发者或游戏开发者。这些技术的市场随着移动计算的扩张出现了爆炸式增长，并且仍然是快速发展的领域。在许多情况下，成功的因素并不为开发者所控制，例如口口相传的推荐或者恰逢重要国际事件的时机。&lt;/p>
&lt;p>我们已经帮助很多公司在云中构建移动应用、社交网络以及游戏应用。其中一个他们大量使用的策略是尽可能又快又便宜地开发和发布应用。如果一个应用碰巧变得流行了，公司将投入资源扩大其规模；否则就会很快终结这些应用。一些公司构建并发布的应用的生命周期甚至只有几个星期，在这样的环境下，可以毫不犹豫地选择云托管。&lt;/p>
&lt;p>如果是一个小规模的公司，可能无法提供足够的硬件来自建数据中心以满足一个非常流行的Facebook应用的发展曲线。我们也协助过一些大型的Facebook应用进行扩展，它们能够以今人惊讶的速度增长——有时甚至会快到让一个主机托管公司耗尽资源。更为严重的是，这些应用的增长是完全无法预测的；它们可能只有极少量的用户（也可能突然有了爆炸性的用户数量增长）。我们在数据中心和云中都遇到过这样的应用。如果是一个小公司，云可以帮你避免前期快速注入大量的资金来获得更快更大规模的风险。&lt;/p>
&lt;p>云的另一种潜在的大用途是运行不是很重要的基础设施，例如集成环境、开发测试平台，以及评估环境。假设部署周期是两个星期。你会每天每个小时都测试部署一次，还是只在项目最后的冲刺时测试？许多用户只是偶尔需要筹划和部署测试环境。在这种场景下，云可以帮助节约不少钱。&lt;/p>
&lt;p>以下是我们使用云的两种方式。第一个是作为我们对技术职员面试的一部分，我们会询问如何解决一些实际的问题。我们使用AMI（Amazon Machine Images）来模拟一些被“破坏”的机器，然后让求职者登录并在服务器上执行一系列任务。我们不必开放他们到内部网络的授权，这种方案显然要方便得多。另一个是作为新项目的工作平台和开发服务器。有一个这样的项目已经在一台云端开发服务器上运行了数个月，而花费不足一美元！这在我们自己的基础设施上是不可能做到的。单是发送一封邮件给系统管理员申请开发服务器的时间价值就不止一美元。&lt;/p>
&lt;p>但是另一方面，云托管对于长期项目而言可能会更加昂贵。如果打算长远地使用云，就需要花时间来计算一下（它是否划算）。除了猜想未来的创新能给云计算和商用硬件带来什么，还需要做基准测试以及一个完整的总体持有成本（TCO）账单。为了理清事情的本质并考虑全面所有相关的细节，你需要把所有的事情最终归结为一个数字：每美元的业务交易数。事情变化得太快，所以我们将这个留给读者思考。&lt;/p>
&lt;h1 id="133云中的mysql的可扩展性和高可用性">13.3　云中的MySQL的可扩展性和高可用性&lt;a class="anchor" href="#133%e4%ba%91%e4%b8%ad%e7%9a%84mysql%e7%9a%84%e5%8f%af%e6%89%a9%e5%b1%95%e6%80%a7%e5%92%8c%e9%ab%98%e5%8f%af%e7%94%a8%e6%80%a7">#&lt;/a>&lt;/h1>
&lt;p>正如我们之前提到的，MySQL并不会在云端自动变得更具扩展性。事实上，如果机器的性能较差，会导致过早使用横向扩展策略。况且云托管服务器相比专用的硬件可靠性和可预测性要更差些，所以想在云端获得高可用性需要更多的创新。&lt;/p>
&lt;p>但是总的来说，在云端中扩展MySQL和在其他地方扩展没有太多的差别。最大的不同就是按需提供服务器的能力。但是也有某些限制会导致扩展和高可用实现起来有点麻烦，至少在有些云环境中是这样的。例如，在AWS云平台中，无法使用类似虚拟IP地址的功能来完成快速原子故障转移。像这种对资源的有限控制意味着你需要使用其他办法，例如代理。（ScaleBase也值得去看看。）&lt;/p>
&lt;p>云另外一个迷惑人的地方是梦想中的自动扩展——就是根据需求的增加或减少来启动或关闭实例。尽管对于诸如Web服务器这样的无状态部分是可行的，但对于数据库服务器而言则很难做到，因为它是有状态的。对于一些特定的场景，例如以读为主的应用，可以通过增加备库的方式来获得有限的自动扩展(4)，但这并不是一个通用的解决方案。实际上，虽然许多应用在Web层使用了自动扩展，但MySQL并不具备在一个无共享（Shared Nothing）集群中的对等角色服务器之间迁移的能力。你可以通过分片架构来自动重新分片并自动增长或收缩(5)，但MySQL本身是无法自动扩展的。&lt;/p>
&lt;p>事实上，因为数据库通常是一个应用系统中主要或唯一的有状态并且持久化的组件，所以把应用服务迁移到云端是很普遍的事情，因为除数据库之外的所有部分都可以从云中收益——Web服务器、工作队列服务器、缓存等——而MySQL只需要处理剩下的东西。毕竟，数据库并非世界的中心。如果应用系统其他部分获得的好处，超过了让MySQL运行得足够好而投入的额外开销和必需的工作量，那这不是一个是否会发生的问题，而是怎么发生的问题。要回答这个问题，最好先了解你在云中可能碰到的额外的挑战。这些通常围绕着数据库服务器的可用资源。&lt;/p>
&lt;h1 id="134四种基础资源">13.4　四种基础资源&lt;a class="anchor" href="#134%e5%9b%9b%e7%a7%8d%e5%9f%ba%e7%a1%80%e8%b5%84%e6%ba%90">#&lt;/a>&lt;/h1>
&lt;p>MySQL需要四种基础资源来完成工作：CPU周期、内存、I/O，以及网络。这四种资源的特性和重要程度在不同的云平台上各不相同。可以通过了解它们的不同之处和对MySQL的影响，以决定是否选择在云中托管MySQL。&lt;/p>
&lt;ul>
&lt;li>CPU通常很少且慢。在写作本书时最大的标准EC2实例提供8个虚拟CPU核心。EC2提供的虚拟CPU比高端CPU的速度明显要慢很多（可以查看本章稍后的基准测试结果）。虽然可能略有不同，但很可能在大多数云托管平台中这都是一种普遍现象。EC2提供使用多个CPU资源的实例，但它们的最大可用内存却更低。在写作本书时商用服务器能提供几十个CPU核心——甚至更多，如果按硬件线程算的话。(6)&lt;/li>
&lt;li>内存大小受限制。最大的EC2实例当前能提供68.4GB的内存。与此相比，商用服务器能提供512GB～1TB的内存。&lt;/li>
&lt;li>I/O的吞吐量、延迟以及一致性受到限制。在AWS云中有两个存储选项。&lt;br>
第一个选择是使用EBS卷，这有点类似云中的SAN。AWS的最佳实践是在用EBS组建的RAID10卷上建立服务器。但是EBS是一个共享资源，就像EC2服务器和EBS服务器之间的网络连接。延迟可能会很高并且不可预测，即使是在适量的吞吐量需求下也是如此。我们已经测得EBS设备的I/O延迟可以达到十几分之一秒。相比之下，直接插在本机的商用硬盘驱动器只需几个毫秒，而闪存设备比硬盘驱动器的速度又要高出几个数量级。但另一方面，EBS卷也有许多很好的特性，例如和其他AWS服务、快照等结合起来使用。&lt;br>
第二个选择是实例的本地存储。每个EC2服务器有一定数量的本地存储，实际安装在底层服务器上。它能够比EBS提供更多的一致性性能(7)，但如果实例停止了就无法做到持久化。正是由于这样的特性导致其不适合大多数的数据库服务器场景。&lt;/li>
&lt;li>尽管网络通常是一个变化多端的共享资源，但是性能通常比较好。虽然使用商用硬件可以获得更快更持续的网络性能，但CPU、RAM和I/O更容易成为主要的性能瓶颈，在AWS云中我们还没有遇到过网络性能问题。&lt;/li>
&lt;/ul>
&lt;p>正如你所看到的，四种基础资源中有三种在AWS云中是受限的，在某些场景下尤其明显。总的来说，这些基础资源并没有商业硬件那样的性能。下一节我们会讨论这些确切的结论。&lt;/p></description></item><item><title>第12章高可用性</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC12%E7%AB%A0%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7/</link><pubDate>Sun, 05 Jan 2025 08:42:06 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC12%E7%AB%A0%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7/</guid><description>&lt;p>第12章　高可用性&lt;/p>
&lt;p>本章将讲述我们提到的复制、可扩展性以及高可用性三个主题中的第三个。归根结底，高可用性实际上意味着“更少的宕机时间”。然而糟糕的是，高可用性经常和其他相关的概念混淆，例如冗余、保障数据不丢失，以及负载均衡。我们希望之前的两章已经为清楚地理解高可用性做了足够的铺垫。跟其他两章一样，这一章也不仅仅是关注高可用性的内容，一些相关的话题也会综合阐述。&lt;/p>
&lt;h1 id="121什么是高可用性">12.1　什么是高可用性&lt;a class="anchor" href="#121%e4%bb%80%e4%b9%88%e6%98%af%e9%ab%98%e5%8f%af%e7%94%a8%e6%80%a7">#&lt;/a>&lt;/h1>
&lt;p>高可用性实际上有点像神秘的野兽。它通常以百分比表示，这本身也是一种暗示：高可用性不是绝对的，只有相对更高的可用性。100％的可用性是不可能达到的。可用性的“9”规则是表示可用性目标最普遍的方法。你可能也知道，“5个9”表示99.999％的正常可用时间。换句话说，每年只允许5分钟的宕机时间。对于大多数应用这已经是令人惊叹的数字，尽管还有一些人试图获得更多的“9”。&lt;/p>
&lt;p>每个应用对可用性的需求各不相同。在设定一个可用时间的目标之前，先问问自己，是不是确实需要达到这个目标。可用性每提高一点，所花费的成本都会远超之前；可用性的效果和开销的比例并不是线性的。需要保证多少可用时间，取决于能够承担多少成本。高可用性实际上是在宕机造成的损失与降低宕机时间所花费的成本之间取一个平衡。换句话说，如果需要花大量金钱去获得更好的可用时间，但所带来的收益却很低，可能就不值得去做。总的来说，应用在超过一定的点以后追求更高的可用性是非常困难的，成本也会很高，因此我们建议设定一个更现实的目标并且避免过度设计。幸运的是，建立2个9或3个9的可用时间的目标可能并不困难，具体情况取决于应用。&lt;/p>
&lt;p>有时候人们将可用性定义成服务正在运行的时间段。我们认为可用性的定义还应该包括应用是否能以足够好的性能处理请求。有许多方法可以让一个服务器保持运行，但服务并不是真正可用。对一个很大的服务器而言，重启MySQL之后，可能需要几个小时才能充分预热以保证查询请求的响应时间是可以接受的，即使服务器只接收了正常流量的一小部分也是如此。&lt;/p>
&lt;p>另一个需要考虑的问题是，即使应用并没有停止服务，但是否可能丢失了数据。如果服务器遭遇灾难性故障，可能多少都会丢失一些数据，例如最近已经写入（最新丢失的）二进制日志但尚未传递到备库的中继日志中的事务。你能够容忍吗？大多数应用能够容忍；因为替代方案大多非常昂贵且复杂，或者有一些性能开销。例如，可以使用同步复制，或是将二进制日志放到一个通过DRBD进行复制的设备上，这样就算服务器完全失效也不用担心丢失数据。（但是整个数据中心也有可能会掉电。）&lt;/p>
&lt;p>一个良好的应用架构通常可以降低可用性方面的需求，至少对部分系统而言是这样的，良好的架构也更容易做到高可用。将应用中重要和不重要的部分进行分离可以节约不少工作量和金钱，因为对于一个更小的系统改进可用性会更容易。可以通过计算“风险敞口（risk exposure）”，将失效概率与失效代价相乘来确认高优先级的风险。画一个简单的风险计算表，以概率、代价和风险敞口作为列，这样很容易找到需要优先处理的项目。&lt;/p>
&lt;p>在前一章我们通过讨论如何避免导致糟糕的可扩展性的原因，来推出如何获得更好的可扩展性。这里也会使用相似的方法来讨论可用性，因为我们相信，理解可用性最好的方法就是研究它的反面——宕机时间。接下来的小节我们会讨论为什么会出现宕机。&lt;/p>
&lt;h1 id="122导致宕机的原因">12.2　导致宕机的原因&lt;a class="anchor" href="#122%e5%af%bc%e8%87%b4%e5%ae%95%e6%9c%ba%e7%9a%84%e5%8e%9f%e5%9b%a0">#&lt;/a>&lt;/h1>
&lt;p>我们经常听到导致数据库宕机最主要的原因是编写的SQL查询性能很差，真的是这样吗？2009年我们决定分析我们客户的数据库所遇到的问题，以找出那些真正引起宕机的问题，以及如何避免这些问题(1)。结果证实了一些我们已有的猜想，但也否定了一些（错误的）认识，我们从中学到了很多。&lt;/p>
&lt;p>我们首先对宕机事件按表现方式而非导致的原因进行分类。一般来说，“运行环境”是排名第一的宕机类别，大约35％的事件属于这一类。运行环境可以看作是支持数据库服务器运行的系统和资源集合，包括操作系统、硬盘以及网络等。性能问题紧随其后，也是约占35％；然后是复制，占20％；最后剩下的10％包含各种类型的数据丢失或损坏，以及其他问题。&lt;/p>
&lt;p>我们对事件按类型进行分类后，确定了导致这些事件的原因。以下是一些需要注意的地方：&lt;/p>
&lt;ul>
&lt;li>在运行环境的问题中，最普遍的问题是磁盘空间耗尽。&lt;/li>
&lt;li>在性能问题中，最普遍的宕机原因确实是运行很糟糕的SQL，但也不一定都是这个原因，比如也有很多问题是由于服务器Bug或错误的行为导致的。&lt;/li>
&lt;li>糟糕的Schema和索引设计是第二大影响性能的问题。&lt;/li>
&lt;li>复制问题通常由于主备数据不一致导致。&lt;/li>
&lt;li>数据丢失问题通常由于DROP TABLE的误操作导致，并总是伴随着缺少可用备份的问题。&lt;/li>
&lt;/ul>
&lt;p>复制虽然常被人们用来改善可用时间，但却也可能导致宕机。这主要是由于不正确的使用导致的，即便如此，它也阐明了一个普遍的情况：许多高可用性策略可能会产生反作用，我们会在后面讨论这个话题。&lt;/p>
&lt;p>现在我们已经知道了主要宕机类别，以及有什么需要注意，下面我们将专门介绍如何获得高可用性。&lt;/p>
&lt;h1 id="123如何实现高可用性">12.3　如何实现高可用性&lt;a class="anchor" href="#123%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e9%ab%98%e5%8f%af%e7%94%a8%e6%80%a7">#&lt;/a>&lt;/h1>
&lt;p>可以通过同时进行以下两步来获得高可用性。首先，可以尝试避免导致宕机的原因来减少宕机时间。许多问题其实很容易避免，例如通过适当的配置、监控，以及规范或安全保障措施来避免人为错误。第二，尽量保证在发生宕机时能够快速恢复。最常见的策略是在系统中制造冗余，并且具备故障转移能力。这两个维度的高可用性可以通过两个相关的度量来确定：平均失效时间（MTBF）和平均恢复时间（MTTR）。一些组织会非常仔细地追踪这些度量值。&lt;/p>
&lt;p>第二步——通过冗余快速恢复——很不幸，这里是最应该注意的地方，但预防措施的投资回报率会很高。接下来我们来探讨一些预防措施。&lt;/p>
&lt;h2 id="1231提升平均失效时间mtbf">12.3.1　提升平均失效时间（MTBF）&lt;a class="anchor" href="#1231%e6%8f%90%e5%8d%87%e5%b9%b3%e5%9d%87%e5%a4%b1%e6%95%88%e6%97%b6%e9%97%b4mtbf">#&lt;/a>&lt;/h2>
&lt;p>其实只要尽职尽责地做好一些应做的事情，就可以避免很多宕机。在分类整理宕机事件并追查导致宕机的根源时，我们还发现，很多宕机本来是有一些方法可以避免的。我们发现大部分宕机事件都可以通过全面的常识性系统管理办法来避免。以下是从我们的白皮书中摘录的指导性建议，在白皮书中有我们详细的分析结果。&lt;/p>
&lt;ul>
&lt;li>测试恢复工具和流程，包括从备份中恢复数据。&lt;/li>
&lt;li>遵从最小权限原则。&lt;/li>
&lt;li>保持系统干净、整洁。&lt;/li>
&lt;li>使用好的命名和组织约定来避免产生混乱，例如服务器是用于开发还是用于生产环境。&lt;/li>
&lt;li>谨慎安排升级数据库服务器。&lt;/li>
&lt;li>在升级前，使用诸如Percona Toolkit中的&lt;em>pt-upgrade&lt;/em>之类的工具仔细检查系统。&lt;/li>
&lt;li>使用InnoDB并进行适当的配置，确保InnoDB是默认存储引擎。如果存储引擎被禁止，服务器就无法启动。&lt;/li>
&lt;li>确认基本的服务器配置是正确的。&lt;/li>
&lt;li>通过skip_name_resolve禁止DNS。&lt;/li>
&lt;li>除非能证明有效，否则禁用查询缓存。&lt;/li>
&lt;li>避免使用复杂的特性，例如复制过滤和触发器，除非确实需要。&lt;/li>
&lt;li>监控重要的组件和功能，特别是像磁盘空间和RAID卷状态这样的关键项目，但也要避免误报，只有当确实发生问题时才发送告警。&lt;/li>
&lt;li>尽量记录服务器的状态和性能指数，如果可能就尽量久地保存。&lt;/li>
&lt;li>定期检查复制完整性。&lt;/li>
&lt;li>将备库设置为只读，不要让复制自动启动。&lt;/li>
&lt;li>定期进行查询语句审查。&lt;/li>
&lt;li>归档并清理不需要的数据。&lt;/li>
&lt;li>为文件系统保留一些空间。在GNU/Linux中，可以使用-&lt;em>m&lt;/em>选项来为文件系统本身保留空间。还可以在LVM卷组中留下一些空闲空间。或者，更简单的方法，仅仅创建一个巨大的空文件，在文件系统快满时，直接将其删除。(2)&lt;/li>
&lt;li>养成习惯，评估和管理系统的改变、状态以及性能信息。&lt;/li>
&lt;/ul>
&lt;p>我们发现对系统变更管理的缺失是所有导致宕机的事件中最普遍的原因。典型的错误包括粗心的升级导致升级失败并遭遇一些Bug，或是尚未测试就将Schema或查询语句的更改直接运行到线上，或者没有为一些失败的情况制定计划，例如达到了磁盘容量限制。另外一个导致问题的主要原因是缺少严格的评估，例如因为疏忽没有确认备份是否是可以恢复的。最后，可能没有正确地监控MySQL的相关信息。例如缓存命中率报警并不能说明出现问题，并且可能产生大量的误报，这会使监控系统被认为不太有用，于是一些人就会忽略报警。有时候监控系统失效了，甚至没人会注意到，直至你的老板质问你，“为什么Nagios没有告诉我们磁盘已经满了”。&lt;/p>
&lt;h2 id="1232降低平均恢复时间mttr">12.3.2　降低平均恢复时间（MTTR）&lt;a class="anchor" href="#1232%e9%99%8d%e4%bd%8e%e5%b9%b3%e5%9d%87%e6%81%a2%e5%a4%8d%e6%97%b6%e9%97%b4mttr">#&lt;/a>&lt;/h2>
&lt;p>之前提到，可以通过减少恢复时间来获得高可用性。事实上，一些人走得更远，只专注于减少恢复时间的某个方面：通过在系统中建立冗余来避免系统完全失效，并避免单点失效问题。&lt;/p>
&lt;p>在降低恢复时间上进行投资非常重要，一个能够提供冗余和故障转移能力的系统架构，则是降低恢复时间的关键环节。但实现高可用性不单单是一个技术问题，还有许多个人和组织的因素。组织和个人在避免宕机和从宕机事件中恢复的成熟度和能力层次各不相同。&lt;/p>
&lt;p>团队成员是最重要的高可用性资产，所以为恢复制定一个好的流程非常重要。拥有熟练技能、应变能力、训练有素的雇员，以及处理紧急事件的详细文档和经过仔细测试的流程，对从宕机中恢复有巨大的作用。但也不能完全依赖工具和系统，因为它们并不能理解实际情况的细微差别，有时候它们的行为在一般情况下是正确的，但在某些场景下却会是个灾难！&lt;/p>
&lt;p>对宕机事件进行评估有助于提升组织学习能力，可以帮助避免未来发生相似的错误，但是不要对“事后反思”或“事后的调查分析”期待太高。后见之明被严重曲解，并且一味想找到导致问题的唯一根源，这可能会影响你的判断力(3)。许多流行的方法，例如“五个为什么”，可能会被过度使用，导致一些人将他们的精力集中在找到唯一的替罪羊。很难去回顾我们解决的问题当时所处的状况，也很难理解真正的原因，因为原因通常是多方面的。因此，尽管事后反思可能是有用的，但也应该对结论有所保留。即使是我们给出的建议，也是基于长期研究导致宕机事件的原因以及如何预防它们所得，并且只是我们的观点而已。&lt;/p>
&lt;p>这里我们要反复提醒：所有的宕机事件都是由多方面的失效联合在一起导致的。因此，可以通过利用合适的方法确保单点的安全来避免。整个链条必须要打断，而不仅仅是单个环节。例如，那些向我们求助恢复数据的人不仅遭受数据丢失（存储失效，DBA误操作等），同时还缺少一个可用的备份。&lt;/p>
&lt;p>这样说来，当开始调查并尝试阻止失效或加速恢复时，大多数人和组织不应太过于内疚，而是要专注于技术上的一些措施——特别是那些很酷的方法，例如集群系统和冗余架构。这些是有用的，但要记住这些系统依然会失效。事实上，在本书第二版中提到的MMM复制管理，我们已经失去了兴趣，因为它被证明可能导致更多的宕机时间。你应该不会奇怪一组Perl脚本会陷于混乱，但即使是特别昂贵并精密设计的系统也会出现灾难性的失效——是的，即使是花费了大量金钱的SAN也是如此。我们已经见过太多的SAN失效。&lt;/p>
&lt;h1 id="124避免单点失效">12.4　避免单点失效&lt;a class="anchor" href="#124%e9%81%bf%e5%85%8d%e5%8d%95%e7%82%b9%e5%a4%b1%e6%95%88">#&lt;/a>&lt;/h1>
&lt;p>找到并消除系统中的可能失效的单点，并结合切换到备用组件的机制，这是一种通过减少恢复时间（MTTR）来改善可用性的方法。如果你够聪明，有时候甚至能将实际的恢复时间降低至0，但总的来说这很困难。（即使一些非常引人注目的技术，例如昂贵的负载均衡器，在发现问题并进行反馈时也会导致一定的延迟。）&lt;/p>
&lt;p>思考并梳理整个应用，尝试去定位任何可能失效的单点。是一个硬盘驱动器，一台服务器，一台交换或路由器，还是某个机架的电源？所有数据都在一个数据中心，或者冗余数据中心是由同一个公司提供的吗？系统中任何不冗余的部分都是一个可能失效的单点。其他比较普遍的单点失效依赖于一些服务，例如DNS、单一网络提供商(4)、单个云“可用区域”，以及单个电力输送网，具体有哪些取决于你的关注点。&lt;/p>
&lt;p>单点失效并不总是能够消除。增加冗余或许也无法做到，因为有些限制无法避开，例如地理位置，预算，或者时间限制等。试着去理解每一个影响可用性的部分，采取一种平衡的观点来看待风险，并首先解决其中影响最大的那个。一些人试图编写一个软件来处理所有的硬件失效，但软件本身导致的宕机时间可能比它节约的还要多。也有人想建立一种“永不沉没”的系统，包括各种冗余，但他们忘记了数据中心可能掉电或失去连接。或许他们彻底忘记了恶意攻击者和程序错误的可能性，这些情况可能会删除或损坏数据——一个不小心执行的DROP TABLE也会产生宕机时间。&lt;/p>
&lt;p>可以采用两种方法来为系统增加冗余：增加空余容量和重复组件。增加容量余量通常很简单——可以使用本章或前一章讨论的任何技术。一个提升可用性的方法是创建一个集群或服务器池，并使用负载均衡解决方案。如果一台服务器失效，其他服务器可以接管它的负载。有些人有意识地不使用组件的全部能力，这样可以保留一些“动态余量”来处理因为负载增加或组件失效导致的性能问题。&lt;/p>
&lt;p>出于很多方面的考虑会需要冗余组件，并在主要组件失效时能有一个备件来随时替换。冗余组件可以是空闲的网卡、路由器或者硬盘驱动器——任何能想到的可能失效的东西。完全冗余MySQL服务器可能有点困难，因为一个服务器在没有数据时毫无用处。这意味着你必须确保备用服务器能够获得主服务器上的数据。共享或复制存储是一个比较流行的办法，但这真的是一个高可用性架构吗？让我们深入其中看看。&lt;/p>
&lt;h2 id="1241共享存储或磁盘复制">12.4.1　共享存储或磁盘复制&lt;a class="anchor" href="#1241%e5%85%b1%e4%ba%ab%e5%ad%98%e5%82%a8%e6%88%96%e7%a3%81%e7%9b%98%e5%a4%8d%e5%88%b6">#&lt;/a>&lt;/h2>
&lt;p>共享存储能够为数据库服务器和存储解耦合，通常使用的是SAN。使用共享存储时，服务器能够正常挂载文件系统并进行操作。如果服务器挂了，备用服务器可以挂载相同的文件系统，执行需要的恢复操作，并在失效服务器的数据上启动MySQL。这个过程在逻辑上跟修复那台故障的服务器没什么两样，不过更快速，因为备用服务器已经启动，随时可以运行。当开始故障转移时，检查文件系统、恢复InnoDB以及预热(5)是最有可能遇到延迟的地方，但检测失效本身在许多设置中也会花费很长时间。&lt;/p></description></item><item><title>第11章可扩展的MySQL</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC11%E7%AB%A0%E5%8F%AF%E6%89%A9%E5%B1%95%E7%9A%84MySQL/</link><pubDate>Sun, 05 Jan 2025 08:42:05 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC11%E7%AB%A0%E5%8F%AF%E6%89%A9%E5%B1%95%E7%9A%84MySQL/</guid><description>&lt;p>第11章　可扩展的MySQL&lt;/p>
&lt;p>本章将展示如何构建一个基于MySQL的应用，并且当规模变得越来越庞大时，还能保证快速、高效并且经济。&lt;/p>
&lt;p>有些应用仅仅适用于一台或少数几台服务器，那么哪些可扩展性建议是和这些应用相关的呢？大多数人从不会维护超大规模的系统，并且通常也无法效仿在主流大公司所使用的策略。本章会涵盖这一系列的策略。我们已经建立或者协助建立了许多应用，包括从单台或少量服务器的应用到使用上千台服务器的应用。选择一个合适的策略能够大大地节约时间和金钱。&lt;/p>
&lt;p>MySQL经常被批评很难进行扩展，有些情况下这种看法是正确的，但如果选择正确的架构并很好地实现，就能够非常好地扩展MySQL。但是扩展性并不是一个很好理解的主题，所以我们先来理清一些容易混淆的地方。&lt;/p>
&lt;h1 id="111什么是可扩展性">11.1　什么是可扩展性&lt;a class="anchor" href="#111%e4%bb%80%e4%b9%88%e6%98%af%e5%8f%af%e6%89%a9%e5%b1%95%e6%80%a7">#&lt;/a>&lt;/h1>
&lt;p>人们常常把诸如“可扩展性”、“高可用性”以及“性能”等术语在一些非正式的场合用作同义词，但事实上它们是完全不同的。在第3章已经解释过，我们将性能定义为响应时间。我们也可以很精确地定义可扩展性，稍后将完整讨论。简要地说，可扩展性表明了当需要增加资源以执行更多工作时系统能够获得划算的等同提升（equal bang for the buck）的能力。缺乏扩展能力的系统在达到收益递减的转折点后，将无法进一步增长。&lt;/p>
&lt;p>容量是一个和可扩展性相关的概念。系统容量表示在一定时间内能够完成的工作量(1)，但容量必须是可以有效利用的。系统的最大吞吐量并不等同于容量。大多数基准测试能够衡量一个系统的最大吞吐量，但真实的系统一般不会使用到极限。如果达到最大吞吐量，则性能会下降，并且响应时间会变得不可接受地大且非常不稳定。我们将系统的真实容量定义为在保证可接受的性能的情况下能够达到的吞吐量。这就是为什么基准测试的结果通常不应该简化为一个单独的数字。&lt;/p>
&lt;p>容量和可扩展性并不依赖于性能。以高速公路上的汽车来类比的话：&lt;/p>
&lt;ul>
&lt;li>性能是汽车的时速。&lt;/li>
&lt;li>容量是车道数乘以最大安全时速。&lt;/li>
&lt;li>可扩展性就是在不减慢交通的情况下，能增加更多车和车道的程度。&lt;/li>
&lt;/ul>
&lt;p>在这个类比中，可扩展性依赖于多个条件：换道设计得是否合理、路上有多少车抛锚或者发生事故，汽车行驶速度是否不同或者是否频繁变换车道——但一般来说和汽车的引擎是否强大无关。这并不是说性能不重要，性能确实重要，只是需要指出，即使系统性能不是很高也可以具备可扩展性。&lt;/p>
&lt;p>从较高层次看，可扩展性就是能够通过增加资源来提升容量的能力。&lt;/p>
&lt;p>即使MySQL架构是可扩展的，但应用本身也可能无法扩展，如果很难增加容量，不管原因是什么，应用都是不可扩展的。之前我们从吞吐量方面来定义容量，但同样也需要从较高的层次来看待容量问题。从有利的角度来看，容量可以简单地认为是处理负载的能力，从不同的角度来考虑负载很有帮助。&lt;/p>
&lt;p>数据量&lt;/p>
&lt;p>应用所能累积的数据量是可扩展性最普遍的挑战，特别是对于现在的许多互联网应用而言，这些应用从不删除任何数据。例如社交网站，通常从不会删除老的消息或评论。&lt;/p>
&lt;p>用户量&lt;/p>
&lt;p>即使每个用户只有少量的数据，但在累计到一定数量的用户后，数据量也会开始不成比例地增长且速度快过用户数增长。更多的用户意味着要处理更多的事务，并且事务数可能和用户数不成比例。最后，大量用户（以及更多的数据）也意味着更多复杂的查询，特别是查询跟用户关系相关时（用户间的关联数可以用N×（N−1）来计算，这里N表示用户数）。&lt;/p>
&lt;p>用户活跃度&lt;/p>
&lt;p>不是所有的用户活跃度都相同，并且用户活跃度也不总是不变的。如果用户突然变得活跃，例如由于增加了一个吸引人的新特性，那么负载可能会明显提升。用户活跃度不仅仅指页面浏览数，即使同样的页面浏览数，如果网站的某个需要执行大量工作的部分变得流行，也可能导致更多的工作。另外，某些用户也会比其他用户更活跃：他们可能比一般人有更多的朋友、消息和照片。&lt;/p>
&lt;p>相关数据集的大小&lt;/p>
&lt;p>如果用户间存在关系，应用可能需要在整个相关联用户群体上执行查询和计算，这比处理一个一个的用户和用户数据要复杂得多。社交网站经常会遇到由那些人气很旺的用户组或朋友很多的用户所带来的挑战(2)。&lt;/p>
&lt;h2 id="1111正式的可扩展性定义">11.1.1　正式的可扩展性定义&lt;a class="anchor" href="#1111%e6%ad%a3%e5%bc%8f%e7%9a%84%e5%8f%af%e6%89%a9%e5%b1%95%e6%80%a7%e5%ae%9a%e4%b9%89">#&lt;/a>&lt;/h2>
&lt;p>有必要探讨一下可扩展性在数学上的定义了，这有助于在更高层次的概念上清晰地理解可扩展性。如果没有这样的基础，就可能无法理解或精确地表达可扩展性。不过不用担心，这里不会涉及高等数学，即使不是数学天才，也能够很直观地理解它。&lt;/p>
&lt;p>关键是之前我们使用的短语：“划算的等同提升（equal bang for the buck）”。另一种说法是，可扩展性是当增加资源以处理负载和增加容量时系统能够获得的投资产出率（ROI）。假设有一个只有一台服务器的系统，并且能够测量它的最大容量，如图11-1所示。&lt;/p>
&lt;p>&lt;img src="img/000001.jpeg" alt="" />
&lt;strong>图11-1：一个只有一台服务器的系统&lt;/strong>&lt;/p>
&lt;p>假设现在我们增加一台服务器，系统的能力加倍，如图11-2所示。&lt;/p>
&lt;p>&lt;img src="img/000002.jpeg" alt="" />
&lt;strong>图11-2：一个线性扩展的系统能由两台服务器获得两倍容量&lt;/strong>&lt;/p>
&lt;p>这就是线性扩展。我们增加了一倍的服务器，结果增加了一倍的容量。大部分系统并不是线性扩展的，而是如图11-3所示的扩展方式。&lt;/p>
&lt;p>&lt;img src="img/000003.jpeg" alt="" />
&lt;strong>图11-3：一个非线性扩展的系统&lt;/strong>&lt;/p>
&lt;p>大部分系统都只能以比线性扩展略低的扩展系数进行扩展。越高的扩展系数会导致越大的线性偏差。事实上，多数系统最终会达到一个最大吞吐量临界点，超过这个点后增加投入反而会带来负回报——继续增加更多工作负载，实际上会降低系统的吞吐量。(3)&lt;/p>
&lt;p>这怎么可能呢？这些年产生了许多可扩展性模型，它们有着不同程度的良好表现和实用性。我们这里所讲的可扩展性模型是基于某些能够影响系统扩展的内在机制。这就是Neil J. Gunther博士提出的通用可扩展性定律（Universal Scalability Law，USL）。Gunther博士将这些详尽地写到了他的书中，包括&lt;em>Guerrilla Capacity Planning&lt;/em> （Springer）。这里我们不会深入到背后的数学理论中，如果你对此感兴趣，他撰写的书籍以及由他的公司Performance Dynamics提供的训练课程可能是比较好的资源。(4)&lt;/p>
&lt;p>简而言之，USL说的是线性扩展的偏差可通过两个因素来建立模型：无法并发执行的一部分工作，以及需要交互的另外一部分工作。为第一个因素建模就有了著名的Amdahl定律，它会导致吞吐量趋于平缓。如果部分任务无法并行，那么不管你如何分而治之，该任务至少需要串行部分的时间。&lt;/p>
&lt;p>增加第二个因素——内部节点间或者进程间的通信——到Amdahl定律就得出了USL。这种通信的代价取决于通信信道的数量，而信道的数量将按照系统内工作者数量的二次方增长。因此最终开销比带来的收益增长得更快，这是产生扩展性倒退的原因。图11-4阐明了目前讨论到的三个概念：线性扩展、Amdahl扩展，以及USL扩展。大多数真实系统看起来更像USL曲线。&lt;/p>
&lt;p>&lt;img src="img/000004.jpeg" alt="" />
&lt;strong>图11-4：线性扩展、AmdahI扩展以及USL扩展定律&lt;/strong>&lt;/p>
&lt;p>USL可以应用于硬件和软件领域。对于硬件，横轴表示硬件的数量，例如服务器数量或CPU数量。每个硬件的工作量、数据大小以及查询的复杂度必须保持为常量(5)。对于软件，横轴表示并发度，例如用户数或线程数。每个并发的工作量必须保持为常量。&lt;/p>
&lt;p>有一点很重要，USL并不能完美地描述真实系统，它只是一个简化模型。但这是一个很好的框架，可用于理解为什么系统增长无法带来等同的收益。它也揭示了一个构建高可扩展性系统的重要原则：在系统内尽量避免串行化和交互。&lt;/p>
&lt;p>可以衡量一个系统并使用回归来确定串行和交互的量。你可以将它作为容量规划和性能预测评估的最优上限值。也可以检查系统是怎么偏离USL模型的，将其作为最差下限值以指出系统的哪一部分没有表现出它应有的性能。这两种情况下，USL给出了一个讨论可扩展性的参考。如果没有USL，那即使盯着系统看也无法知道期望的结果是什么。如果想深入了解这个主题，最好去看一下对应的书籍。Gunther博士已经写得很清楚，因此我们不会再深入讨论下去。&lt;/p>
&lt;p>另外一个理解可扩展性问题的框架是约束理论，它解释了如何通过减少依赖事件和统计变化（statistical variation）来改进系统的吞吐量和性能。这在Eliyahu M. Goldratt所撰写的&lt;em>The Goal&lt;/em>（North River）一书中有描述，其中有一个关于管理制造业设备的延伸的比喻。尽管这看起来和数据库服务器没有什么关联，但其中包含的法则和排队理论以及其他运筹学方面是一样的。&lt;/p></description></item><item><title>第10章复制</title><link>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC10%E7%AB%A0%E5%A4%8D%E5%88%B6/</link><pubDate>Sun, 05 Jan 2025 08:42:04 +0800</pubDate><guid>https://blog.liuyi2026.asia/zh/docs/technology/MySQL/_%E9%AB%98%E6%80%A7%E8%83%BDMySQL_/%E7%AC%AC10%E7%AB%A0%E5%A4%8D%E5%88%B6/</guid><description>&lt;p>第10章　复制&lt;/p>
&lt;p>MySQL内建的复制功能是构建基于MySQL的大规模、高性能应用的基础，这类应用使用所谓的“水平扩展”的架构。我们可以通过为服务器配置一个或多个备库(1)的方式来进行数据同步。复制功能不仅有利于构建高性能的应用，同时也是高可用性、可扩展性、灾难恢复、备份以及数据仓库等工作的基础。事实上，可扩展性和高可用性通常是相关联的话题，我们会在接下来的三章详细阐述。&lt;/p>
&lt;p>本章将阐述所有与复制相关的内容，首先简要介绍复制如何工作，然后讨论基本的复制服务搭建，包括与复制相关的配置以及如何管理和优化复制服务器。虽然本书的主题是高性能，但对于复制来说，我们同样需要关注其准确性和可靠性，因此我们也会讲述复制在什么情况下会失败，以及如何使其更好地工作。&lt;/p>
&lt;h1 id="101复制概述">10.1　复制概述&lt;a class="anchor" href="#101%e5%a4%8d%e5%88%b6%e6%a6%82%e8%bf%b0">#&lt;/a>&lt;/h1>
&lt;p>复制解决的基本问题是让一台服务器的数据与其他服务器保持同步。一台主库的数据可以同步到多台备库上，备库本身也可以被配置成另外一台服务器的主库。主库和备库之间可以有多种不同的组合方式。&lt;/p>
&lt;p>MySQL支持两种复制方式：基于行的复制和基于语句的复制。基于语句的复制（也称为逻辑复制）早在MySQL 3.23版本中就存在，而基于行的复制方式在5.1版本中才被加进来。这两种方式都是通过在主库上记录二进制日志(2)、在备库重放日志的方式来实现异步的数据复制。这意味着，在同一时间点备库上的数据可能与主库存在不一致，并且无法保证主备之间的延迟。一些大的语句可能导致备库产生几秒、几分钟甚至几个小时的延迟。&lt;/p>
&lt;p>MySQL复制大部分是向后兼容的，新版本的服务器可以作为老版本服务器的备库，但反过来，将老版本作为新版本服务器的备库通常是不可行的，因为它可能无法解析新版本所采用的新的特性或语法，另外所使用的二进制文件的格式也可能不相同。例如，不能从MySQL 5.1复制到MySQL 4.0。在进行大的版本升级前，例如从4.1升级到5.0，或从5.1升级到5.5，最好先对复制的设置进行测试。但对于小版本号升级，如从5.1.51升级到5.1.58，则通常是兼容的。通过阅读每次版本更新的ChangeLog可以找到不同版本间做了什么修改。&lt;/p>
&lt;p>复制通常不会增加主库的开销，主要是启用二进制日志带来的开销，但出于备份或及时从崩溃中恢复的目的，这点开销也是必要的。除此之外，每个备库也会对主库增加一些负载（例如网络I/O开销），尤其当备库请求从主库读取旧的二进制日志文件时，可能会造成更高的I/O开销。另外锁竞争也可能阻碍事务的提交。最后，如果是从一个高吞吐量（例如5000或更高的TPS）的主库上复制到多个备库，唤醒多个复制线程发送事件的开销将会累加。&lt;/p>
&lt;p>通过复制可以将读操作指向备库来获得更好的读扩展，但对于写操作，除非设计得当，否则并不适合通过复制来扩展写操作。在一主库多备库的架构中，写操作会被执行多次，这时候整个系统的性能取决于写入最慢的那部分。&lt;/p>
&lt;p>当使用一主库多备库的架构时，可能会造成一些浪费，因为本质上它会复制大量不必要的重复数据。例如，对于一台主库和10台备库，会有11份数据拷贝，并且这11台服务器的缓存中存储了大部分相同的数据。这和在服务器上有11路RAID 1类似。这不是一种经济的硬件使用方式，但这种复制架构却很常见，本章我们将讨论解决这个问题的方法。&lt;/p>
&lt;h2 id="1011复制解决的问题">10.1.1　复制解决的问题&lt;a class="anchor" href="#1011%e5%a4%8d%e5%88%b6%e8%a7%a3%e5%86%b3%e7%9a%84%e9%97%ae%e9%a2%98">#&lt;/a>&lt;/h2>
&lt;p>下面是复制比较常见的用途：&lt;/p>
&lt;p>数据分布&lt;/p>
&lt;p>MySQL复制通常不会对带宽造成很大的压力，但在5.1版本引入的基于行的复制会比传统的基于语句的复制模式的带宽压力更大。你可以随意地停止或开始复制，并在不同的地理位置来分布数据备份，例如不同的数据中心。即使在不稳定的网络环境下，远程复制也可以工作。但如果为了保持很低的复制延迟，最好有一个稳定的、低延迟连接。&lt;/p>
&lt;p>负载均衡&lt;/p>
&lt;p>通过MySQL复制可以将读操作分布到多个服务器上，实现对读密集型应用的优化，并且实现很方便，通过简单的代码修改就能实现基本的负载均衡。对于小规模的应用，可以简单地对机器名做硬编码或使用DNS轮询（将一个机器名指向多个IP地址）。当然也可以使用更复杂的方法，例如网络负载均衡这一类的标准负载均衡解决方案，能够很好地将负载分配到不同的MySQL服务器上。Linux虚拟服务器（Linux Virtual Server，LVS）也能够很好地工作，第11章将详细地讨论负载均衡。&lt;/p>
&lt;p>备份&lt;/p>
&lt;p>对于备份来说，复制是一项很有意义的技术补充，但复制既不是备份也不能够取代备份。&lt;/p>
&lt;p>高可用性和故障切换&lt;/p>
&lt;p>复制能够帮助应用程序避免MySQL单点失败，一个包含复制的设计良好的故障切换系统能够显著地缩短宕机时间，我们将在第12章讨论故障切换。&lt;/p>
&lt;p>MySQL升级测试&lt;/p>
&lt;p>这种做法比较普遍，使用一个更高版本的MySQL作为备库，保证在升级全部实例前，查询能够在备库按照预期执行。&lt;/p>
&lt;h2 id="1012复制如何工作">10.1.2　复制如何工作&lt;a class="anchor" href="#1012%e5%a4%8d%e5%88%b6%e5%a6%82%e4%bd%95%e5%b7%a5%e4%bd%9c">#&lt;/a>&lt;/h2>
&lt;p>在详细介绍如何设置复制之前，让我们先看看MySQL实际上是如何复制数据的。总的来说，复制有三个步骤：&lt;/p>
&lt;ol>
&lt;li>在主库上把数据更改记录到二进制日志（Binary Log）中（这些记录被称为二进制日志事件）。&lt;/li>
&lt;li>备库将主库上的日志复制到自己的中继日志（Relay Log）中。&lt;/li>
&lt;li>备库读取中继日志中的事件，将其重放到备库数据之上。&lt;/li>
&lt;/ol>
&lt;p>以上只是概述，实际上每一步都很复杂，图10-1更详细地描述了复制的细节。&lt;/p>
&lt;p>&lt;img src="img/000002.jpeg" alt="" />
&lt;strong>图10-1：MySQL复制如何工作&lt;/strong>&lt;/p>
&lt;p>第一步是在主库上记录二进制日志（稍后介绍如何设置）。在每次准备提交事务完成数据更新前，主库将数据更新的事件记录到二进制日志中。MySQL会按事务提交的顺序而非每条语句的执行顺序来记录二进制日志。在记录二进制日志后，主库会告诉存储引擎可以提交事务了。&lt;/p>
&lt;p>下一步，备库将主库的二进制日志复制到其本地的中继日志中。首先，备库会启动一个工作线程，称为I/O线程，I/O线程跟主库建立一个普通的客户端连接，然后在主库上启动一个特殊的二进制转储（&lt;em>binlog dump&lt;/em>）线程（该线程没有对应的SQL命令），这个二进制转储线程会读取主库上二进制日志中的事件。它不会对事件进行轮询。如果该线程追赶上了主库，它将进入睡眠状态，直到主库发送信号量通知其有新的事件产生时才会被唤醒，备库I/O线程会将接收到的事件记录到中继日志中。&lt;/p>
&lt;p>&lt;img src="img/000001.jpeg" alt="" />MySQL 4.0之前的复制与之后的版本相比改变很大，例如MySQL最初的复制功能没有使用中继日志，所以复制只用到了两个线程，而不是现在的三个线程。目前大部分人都是使用的最新版本，因此在本章我们不会去讨论关于老版本复制的更多细节。&lt;/p>
&lt;p>备库的SQL线程执行最后一步，该线程从中继日志中读取事件并在备库执行，从而实现备库数据的更新。当SQL线程追赶上I/O线程时，中继日志通常已经在系统缓存中，所以中继日志的开销很低。SQL线程执行的事件也可以通过配置选项来决定是否写入其自己的二进制日志中，它对于我们稍后提到的场景非常有用。&lt;/p>
&lt;p>图10-1显示了在备库有两个运行的线程，在主库上也有一个运行的线程：和其他普通连接一样，由备库发起的连接，在主库上同样拥有一个线程。&lt;/p>
&lt;p>这种复制架构实现了获取事件和重放事件的解耦，允许这两个过程异步进行。也就是说I/O线程能够独立于SQL线程之外工作。但这种架构也限制了复制的过程，其中最重要的一点是在主库上并发运行的查询在备库只能串行化执行，因为只有一个SQL线程来重放中继日志中的事件。后面我们将会看到，这是很多工作负载的性能瓶颈所在。虽然有一些针对该问题的解决方案，但大多数用户仍然受制于单线程。&lt;/p>
&lt;h1 id="102配置复制">10.2　配置复制&lt;a class="anchor" href="#102%e9%85%8d%e7%bd%ae%e5%a4%8d%e5%88%b6">#&lt;/a>&lt;/h1>
&lt;p>为MySQL服务器配置复制非常简单。但由于场景不同，基本的步骤还是有所差异。最基本的场景是新安装的主库和备库，总的来说分为以下几步：&lt;/p>
&lt;ol>
&lt;li>在每台(3)服务器上创建复制账号。&lt;/li>
&lt;li>配置主库和备库。&lt;/li>
&lt;li>通知备库连接到主库并从主库复制数据。&lt;/li>
&lt;/ol>
&lt;p>这里我们假定大部分配置采用默认值即可，在主库和备库都是全新安装并且拥有同样的数据（默认MySQL数据库）时这样的假设是合理的。接下来我们将展示如何一步步配置复制：假设有服务器server1（IP地址192.168.0.1）和服务器server2（IP地址192.168.0.2），我们将解释如何给一个已经运行的服务器配置备库，并探讨推荐的复制配置。&lt;/p>
&lt;h2 id="1021创建复制账号">10.2.1　创建复制账号&lt;a class="anchor" href="#1021%e5%88%9b%e5%bb%ba%e5%a4%8d%e5%88%b6%e8%b4%a6%e5%8f%b7">#&lt;/a>&lt;/h2>
&lt;p>MySQL会赋予一些特殊的权限给复制线程。在备库运行的I/O线程会建立一个到主库的TCP/IP连接，这意味着必须在主库创建一个用户，并赋予其合适的权限。备库I/O线程以该用户名连接到主库并读取其二进制日志。通过如下语句创建用户账号：&lt;/p>
&lt;pre>&lt;code> mysql&amp;gt; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.*
 -&amp;gt; TO repl@'192.168.0.%' IDENTIFIED BY 'p4ssword',;
&lt;/code>&lt;/pre>
&lt;p>我们在主库和备库都创建该账号。注意我们把这个账户限制在本地网络，因为这是一个特权账号（尽管该账号无法执行select或修改数据，但仍然能从二进制日志中获得一些数据）。&lt;/p></description></item></channel></rss>