《高性能MySQL》读书笔记(一)

第1章:MySQL架构与历史

    MySQL拥有分层的架构。上层是服务器层的服务和查询执行引擎,下层则是存储引擎。虽然有很多不同作用的插件API,但存储引擎API还是最重要的。如果能理解MySQL在存储引擎和服务层之间处理查询时如何通过API来回交互,就能抓住MySQL的核心基础架构的精髓。
    MySQL最初基于ISAM构建(后来别MyISAM取代),其后陆续添加了更多的存储引擎和事务支持。MySQL有一些怪异的行为是由于历史遗留导致的。例如,在执行ALTER TABLE时,MySQL提交事务的方式是由于存储引擎的架构直接导致的,并且数据字典也保存在.frm文件中(这并不是说InnoDB会导致ALTER变成非事务型的。对于InnoDB来说,所有的操作都是事务)。
    当然,存储引擎API的架构也有一些缺点。有时候选择多并非好事,而在MySQL5.0和MySQL5.1中有太多的存储引擎可以选择。InnoDB对于95%以上的用户来说都是最佳选择,所以其他存储引擎只是让事情变得复杂难搞,当然也不可否认某些情况下某些存储引擎能更好地满足需求。
    Oracle一开始收购了InnoDB,之后又收购了MySQL,在同一屋檐下对于两者都是有利的。InnoDB和MySQL服务器之间可以更快地协同发展。MySQL依然基于GPL协议开放全部源代码,社区和客户都可以获得坚固而稳定的数据库,MySQL正在变得越来越可扩展和有用。

第2章:MySQL基准测试

    每个MySQL的使用者都应该了解一些基准测试的知识。基准测试不仅仅是用来解决业务问题的一种实践行动,也是一种很好的学习方法。学习如何将问题分解成可以通过基准测试来获得答案的方法,就和在数学课上从文字题目中推导出方程式一样。首先正确地描述问题,之后选择合适的基准测试来回答问题,设置基准测试的持续时间和参数,运行测试,收集数据,分析结果数据,这一系列的训练可以帮助你成为更好地MySQL用户。
    如果你还没有做过基准测试,那么建议至少要熟悉sysbench。可以先学习如何使用oltp和fileio测试。oltp基准测试可以很方便地比较不同系统的性能。另一方面,文件系统和磁盘基准测试,则可以在系统出现问题时有效地诊断和隔离异常的组件。
    如果经常执行基准测试,那么制定一些原则是很有必要的。选择一些合适的测试工具并深入学习。可以建立一个脚本库,用于配置基准测试,收集测试结果。

第3章:服务器性能剖析

    本章给出了一些基本的思路和技术,有助于你成功地进行性能优化。正确地思维方式是开启系统的全部潜力和应用本书其他章节提供的知识的关键。下面是我们试图演示的一些基本知识点:
1).我们认为定义性能最有效的方法时响应时间。
2).如果无法测量就无法有效的优化,所以性能优化工作需要基于高质量、全方位及完整的响应时间测量。
3).测量的最佳开始点是应用程序,而不是数据库。即使问题出在底层的数据库,借助良好的测量也可以容易的发现问题。
4).大多数系统无法完整地测量,测量有时候也会有错误的结果。但也可以想办法绕过一些限制,并得到好的结果(但是要能意识到所使用的方法的缺陷和不确定性在哪里)。
5).完整的测量会产生大量需要分析的数据,所以需要用到剖析器。这是最佳的工具,可以帮助将重要的问题冒泡到前面,这样就可以决定从哪里开始分析会比较好。
6).剖析报告是一种汇总信息,掩盖和丢弃了太多细节。而且它不会告诉你缺少了什么,所以完全依赖剖析报告也是不明智的。
7).有两种消耗时间的操作:工作和等待。大多数剖析器只能测量因为工作而消耗的时间,所以等待分析有时候是很有用的补充,尤其是当CPU利用率很低但工作却一直无法完成的时候。
8).优化和提升是两回事。当继续提升的成本超过收益的时候,应当停止优化。
9).注意你的直觉,但应该只根据直觉来指导解决问题的思路,而不是用于确定系统的问题。决策应当尽量基于数据而不是感觉。
    总体来说,我们认为解决问题的方法,首先是要澄清问题,然后选择合适的技术来解答这些问题。如果你想尝试提升服务器的总体性能,那么一个比较好的起点是将所有查询记录到日志中,然后利用pt-query-digest工具生成系统级别的剖析报告。如果是要追查某些性能低下的查询,记录和剖析的方法也会有帮助。可以把精力放在寻找那些消耗时间最多的、导致了糟糕的用户体验的,或者那些高度变化的,抑或有奇怪的响应时间直方图的查询。当找到了这些“坏”查询时,要钻取pt-query-digest报告中包含的该查询的详细信息,或者使用show profile及其他诸如explain这样的工具。
    如果找不到这些查询性能低下的原因,那么也可能是遇到了服务器级别的性能问题。这时,可以较高精度测量和绘制服务器状态计数器的细节信息。如果通过这样的分析重现了问题,则应该通过同样的数据来指定一个可靠的触发条件,来收集更多的诊断数据。多花费一点时间来确定可靠的触发条件,尽量避免漏检或误报。如果已经可以捕捉故障活动期间的数据,但还是无法找到其根本原因,则要么尝试捕获更多的数据,要么尝试寻求帮助。

第4章:Schema与数据类型优化

    良好的schema设计原则是普遍适用的,但MySQL有它自己的实现细节要注意。概括来说,尽可能保持任何东西小而简单总是好的。MySQL喜欢简单,需要使用数据库的人应该也同样会喜欢简单的原则:
1).尽量避免过度设计,例如会导致极其复杂查询的schema设计,或者有很多列的表设计(很多的意思是介于有点多和非常多之间);
2).使用小而简单的合适数据类型,除非真实数据模型中有确切的需要,否则应该尽可能地避免使用NULL值;
3).尽量使用相同的数据类型存储相似或相关的值,尤其是要在关联条件中使用的列;
4).注意可变长字符串,其在临时表和排序时可能导致悲观的按最大长度分配内存;
5).尽量使用整形定义标识列;
6).避免使用MySQL已经遗弃的特性,例如指定浮点数的精度,或者整数的显示宽度;
7).小心使用ENUM和SET。虽然它们用起来很方便,但是不要滥用,否则有时候会变成陷阱,最好避免使用BIT。
    范式是好的,但是反范式(大多数情况下意味着重复数据)有时也是必需的,并且能带来好处。预先计算、缓存或生成汇总表也可能获得很大的好处。
    最后,ALTER TABLE是让人痛苦的操作,因为在大部分情况下,它都会锁表并重建整张表。我们展示了一些特殊的场景可以使用骇客方法,但大部分场景,必须使用其他更常规的方法,例如在备机执行ALTER并在完成后把它切换为主库。本书后续章节会有更多这方面的内容。

 第5章:创建高性能的索引

    通过本章可以看到,索引是一个非常复杂的话题!MySQL和存储引擎访问数据的方式,加上索引的特性,使得索引成为一个影响数据访问的有力而灵活的工作(无论数据是在磁盘中还是内存中)。
    在MySQL中,大多数情况下都会使用B-tree索引。其他类型的索引大多只适合特殊的目的。如果在合适的场景中使用索引,将大大提高查询数据的响应时间。
    在选择索引和编写利用这些索引的查询时,有如下三个原则始终需要记住:
1).单行访问时很慢的。特别是在机械硬盘存储中(SSD的随机I/O要快很多,不过这一点仍然成立)。如果服务器从存储中读取一个数据块只是为了获取其中一行,那么久浪费了很多工作。最好读取的块中能包含尽可能多所需要的行。使用索引可以创建位置引用以提升效率;
2).按顺序访问范围数据是很快的,这有两个原因。第一,顺序I/O不需要多次磁盘寻道,所以比随机I/O要快很多(特别是对机械硬盘)。第二,如果服务器能够按需要顺序读取数据,那么久不再需要额外的排序操作,并且GROUP BY查询也无须再做排序和将行按组进行聚合计算了;
3).索引覆盖查询时很快的。如果一个索引包含了查询需要的所有列,那么存储引擎就不需要再回表查找行。这避免了大量单行访问,而上面的第1点已经写明单行访问时很慢的。
    总的来说,编写查询语句时应该尽可能选择合适的索引以避免单行查找,尽可能地使用数据原生顺序从而避免额外的排序操作,并尽可能使用索引覆盖查询。
如果表上的每一个查询都能有一个完美的索引来满足当然是最好的。但不幸的是,哟啊这么做有时可能需要创建大量的索引。还有一些时候对某些查询时不可能创建一个达到“三星”的索引的(例如查询要按照两个列排序,其中一列正序,另一列倒序)。这时必须有所取舍以创建最合适的索引,或者寻求替代策略(例如反范式化,或者提前计算汇总表等)。
理解索引是如何工作的非常重要,应该根据这些理解来创建最合适的索引,而不是根据一些诸如“在多列索引中将选择性最高的列放在第一列”或“应该为WHERE子句中出现的所有列创建索引”之类的经验法则及其推论。
    那如何判断一个系统创建的索引是否合理?一般来说,我们建议按响应时间来对查询进行分析。找出那些消耗最长时间的查询或者那些给服务器带来最大压力的查询,然后检查这些茶歇schema、SQL和索引结构,判断是否有查询扫描了太多的行,是否做了额外的排序或者使用了临时表,是否使用随机I/O访问数据,或者是由太多回表查询那些不在索引中的列的操作。
    如果一个查询无法从所有可能的索引中获益,则应该看看是否可以创建一个更合适的索引来提升性能。如果不行,也可以看看是否可以重写该查询,将其转化成一个能够高效利用现有索引或新创建索引的查询。这也是下一章要介绍的内容。
    如果根据第3章介绍的基于响应时间的分析不能找出有问题的查询呢?是否可能有我们没有注意到的“很糟糕”的查询,需要一个更好地索引来获取更高的性能?一般来说,不可能。对于诊断时抓不到的查询,那就不是问题。但是,这个查询未来可能成为问题,因为应用程序、数据和负载都在变化。如果仍然想找到哪些索引不是很合适的查询,并在它们成为问题前进行优化,则可以使用pt-query-digest的查询审查“review”功能,分析其EXPLAIN出来的执行计划。

 第6章:查询性能优化

    如果把创建高性能应用程序比作是一个环环相扣的“难题”,除了前面介绍的schema、索引和查询语句设计之外,查询优化应该是解开“难题”的最后一步了。要想写一个好的查询,你必须要理解schema设计、索引设计等,反之亦然。
    理解查询是如何被执行的以及时间都消耗在哪些地方,这依然是前面我们介绍的相应时间的一部分。再加上一些诸如解析和优化过程的知识,就可以更进一步地理解上一章讨论的MySQL如何访问表和索引的内容了。这也从另一个维度帮助读者理解MySQL在访问表和索引时查询和索引的关系。
    优化通常都要三管齐下:不做、少做、快速地做。我们希望这里的案例能够帮助你将理论和实践联系起来。
    除了这些基础的手段,包括查询、表结构、索引等,MySQL还有一些高级的特性可以帮助你优化应用,例如分区,分区和索引有些类似但是原理不同。MySQL还支持查询缓存,它可以帮你缓存查询结果,当完全相同的查询再次执行时,直接使用缓存结果(回想一下,“不做”)。我们将在下一章中介绍这些特性。

第7章:MySQL高级特性

    本章详细介绍了前面各个章节中提到的一些MySQL特性。这里我们将再来回顾一下其中的一些重点内容。
分区表:分区表是一种粗粒度的、简易的索引策略,适用于大数据量的过滤场景。
视图:对好几个表的复杂查询,使用视图有时候会大大简化问题。当视图使用临时表时,无法将WHERE条件下推到各个具体的表,也不能使用任何索引,需要特别注意这类查询的性能。
外键:外键限制会将约束放到MySQL中,这对于必须维护外键的场景,性能会更高。不过这也会带来额外的复杂性和额外的索引消耗,还会增加多表之间的交互,会导致系统中更多的锁和竞争。
存储过程:MySQL本身实现了存储过程、触发器、存储函数和事件,老实说,这些特征并没什么特别的。而且对于基于语句的复制还有很多问题。
绑定变量:当查询语句的解析和执行计划生成消耗了主要的时间,那么绑定变量可以在一定程度上解决问题。
插件:使用C或者C++编写的插件可以让你最大程度地扩展MySQL功能,如编写了很多UDF和插件,解决了MySQL中的很多问题。
字符集:字符集是一种字节到字符之间的映射,而校对规则是指一个字符集的排序方法。
全文索引:目前只有MyISAM支持全文索引,但InnoDB通常使用Sphinx来解决全文索引的问题。
XA事务:使用XA事务是要开启参数innodb_support_xa,InnoDB和二进制日志也是需要使用XA事务来做协调的,从而确保在系统崩溃时,数据能够一直地恢复。
查询缓存:完全相同的查询在重复执行的时候,查询缓存可以立即返回结果,而无须在数据库中重新执行一次,但在高并发压力环境中查询缓存会导致系统性能的下降,甚至僵死。如果一定要使用查询缓存,那么不要设置太大内存,而且只有在明确收益的时候才使用。

posted on 2020-01-22 08:49  阿泰555  阅读(119)  评论(0编辑  收藏  举报

导航