高性能 MySQL(第 4 版)-Silvia Botros Jeremy Tinley
高性能 MySQL(第 4 版)-Silvia Botros Jeremy Tinley
程序员朱永胜元数据
[!abstract] 高性能 MySQL(第 4 版)
- 书名:高性能 MySQL(第 4 版)
- 作者:Silvia Botros Jeremy Tinley
- 简介:《高性能 MySQL》一直是 MySQL 领域的经典之作,影响了一代又一代的 DBA 和技术人员,从第 3 版出版到第 4 版出版过去了近十年,MySQL 也从 5.5 版本更新到了 8.0 版本。第 4 版中增加了大量对 MySQL 5.7 和 8.0 版本新特性的介绍,删除了一些在新版本中已经废弃或者不再常用的功能,还增加了对云数据库的介绍,减少了在官方文档中已有的基础使用和配置相关的内容。这些年,MySQL 经过在大量大规模互联网场景中的应用验证,使得本书在继续关注高性能之外,还用了较多的篇幅来介绍如何实现 MySQL 的大规模可扩展应用和合规性问题,这是相比第 3 版最大的不同,也是本书封面上所写的 “ 经过大规模运维验证的策略 “ 的体现。本书适合数据库管理员(DBA)阅读,也适合系统运维和开发人员参考学习。不管你是数据库新手还是专家,相信都能从本书中有所收获。
- 出版时间:2022-09-01 00:00:00
- ISBN:9787121442575
- 分类:计算机 - 数据库
- 出版社:电子工业出版社
- PC 地址:https://weread.qq.com/web/reader/00a32b70813ab746fg018ec7
高亮划线
第 1 章 MySQL 架构
📌 默认情况下,每个客户端连接都会在服务器进程中拥有一个线程,该连接的查询只会在这个单独的线程中执行,该线程驻留在一个内核或者 CPU 上。服务器维护了一个缓存区,用于存放已就绪的线程,因此不需要为每个新的连接创建或者销毁线程
⏱ 2023-04-29 20:55:36 ^3300035678-11-1847-1956
📌 当客户端(应用)连接到 MySQL 服务器时,服务器需要对其进行身份验证。身份验证基于用户名、发起的主机名和密码。如果以跨传输层安全 (TLS) 的方式连接,还可以使用 X.509 证书认证。客户端连接成功后,服务器会继续验证该客户端是否具有其发出的每个查询的权限(例如,是否允许客户端对 world 数据库中的 Country 表执行 SELECT 语句)。
⏱ 2023-04-29 20:55:13 ^3300035678-11-2139-2307
📌 MySQL 可以使用内部查询缓存 (query cache) 来查看是否可以直接提供结果。但是,随着并发性的增加,查询缓存成为一个让人诟病的瓶颈。从 MySQL 5.7.20 版本开始,查询缓存已经被官方标注为被弃用的特性,并在 8.0 版本中被完全移除。
⏱ 2023-04-29 20:57:35 ^3300035678-11-2814-2935
📌 更理想的方式是,只对需要修改的数据片段进行精确的锁定
⏱ 2023-04-29 20:59:42 ^3300035678-11-4597-4623
📌 表锁 (table lock) 是 MySQL 中最基本也是开销最小的锁策略。表锁非常类似于前文描述的电子表格的锁机制:它会锁定整张表。当客户端想对表进行写操作(插入、删除、更新等)时,需要先获得一个写锁,这会阻塞其他客户端对该表的所有读写操作。只有没有人执行写操作时,其他读取的客户端才能获得读锁,读锁之间不会相互阻塞。
⏱ 2023-04-29 21:01:12 ^3300035678-11-5242-5399
📌 使用行级锁 (row lock) 可以最大程度地支持并发处理(也带来了最大的锁开销)
⏱ 2023-04-29 21:01:51 ^3300035678-11-5672-5712
📌 行级锁是在存储引擎而不是服务器中实现的。
⏱ 2023-04-29 21:02:11 ^3300035678-11-5875-5895
📌 事务就是一组 SQL 语句,作为一个工作单元以原子方式进行处理。如果数据库引擎能够成功地对数据库应用整组语句,那么就执行该组语句。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么整组语句都不执行。也就是说,作为事务的一组语句,要么全部执行成功,要么全部执行失败。
⏱ 2023-04-29 21:04:09 ^3300035678-11-6236-6368
📌 为什么存在高度复杂且缓慢的两阶段提交系统的典型原因:为了应对各种失败场景。
⏱ 2023-04-29 21:05:20 ^3300035678-11-7481-7518
📌 ACID 代表原子性 (atomicity)、一致性 (consistency)、隔离性 (isolation) 和持久性 (durability)
⏱ 2023-04-29 21:05:38 ^3300035678-11-7577-7645
📌 原子性 (atomicity) 一个事务必须被视为一个不可分割的工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
⏱ 2023-04-29 21:05:58 ^3300035678-11-7705-7831
📌 一致性 (consistency) 数据库总是从一个一致性状态转换到下一个一致性状态。在前面的例子中,一致性确保了,即使在执行第 3、4 条语句之间时系统崩溃,支票账户中也不会损失 200 美元。如果事务最终没有提交,该事务所做的任何修改都不会被保存到数据库中。
⏱ 2023-04-29 21:06:14 ^3300035678-11-7860-8014
📌 隔离性 (isolation) 通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的,这就是隔离性带来的结果。在前面的例子中,当执行完第 3 条语句、第 4 条语句还未开始时,此时有另外一个账户汇总程序开始运行,其看到的支票账户的余额并没有被减去 200 美元。后面我们讨论隔离级别 (isolation level) 的时候,会发现为什么我们要说 “ 通常来说 “ 是不可见的。
⏱ 2023-04-29 21:06:27 ^3300035678-11-8043-8254
📌 持久性 (durability) 一旦提交,事务所做的修改就会被永久保存到数据库中。此时即使系统崩溃,数据也不会丢失。持久性是一个有点模糊的概念,实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有 100% 的持久性保障(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢?)。
⏱ 2023-04-29 21:06:46 ^3300035678-11-8283-8478
📌 READ UNCOMMITTED(未提交读)在 READ UNCOMMITTED 级别,在事务中可以查看其他事务中还没有提交的修改。这个隔离级别会导致很多问题,从性能上来说,READ UNCOMMITTED 不会比其他级别好太多,却缺乏其他级别的很多好处,除非有非常必要的理由,在实际应用中一般很少使用。读取未提交的数据,也称为脏读 (dirty read)。
⏱ 2023-04-29 21:07:52 ^3300035678-11-9337-9571
📌 READ COMMITTED(提交读)大多数数据库系统的默认隔离级别是 READ COMMITTED(但 MySQL 不是)。READ COMMITTED 满足前面提到的隔离性的简单定义:一个事务可以看到其他事务在它开始之后提交的修改,但在该事务提交之前,其所做的任何修改对其他事务都是不可见的。这个级别仍然允许不可重复读 (nonrepeatable read),这意味着同一事务中两次执行相同语句,可能会看到不同的数据结果。
⏱ 2023-04-29 21:08:21 ^3300035678-11-9600-9838
📌 REPEATABLE READ(可重复读)REPEATABLE READ 解决了 READ COMMITTED[插图] 级别的不可重复读问题,保证了在同一个事务中多次读取相同行数据的结果是一样的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读 (phantom read) 的问题。
⏱ 2023-04-29 21:09:22 ^3300035678-11-9867-10151
📌 幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行 (phantom row)。
⏱ 2023-04-29 21:09:43 ^3300035678-11-10153-10234
📌 InnoDB 和 XtraDB 存储引擎通过多版本并发控制 (MVCC,Multiversion Concurrency Control) 解决了幻读的问题。
⏱ 2023-04-29 21:10:04 ^3300035678-11-10234-10308
📌 REPEATABLE READ 是 MySQL 默认的事务隔离级别。
⏱ 2023-04-29 21:10:23 ^3300035678-11-10351-10382
📌 死锁是指两个或多个事务相互持有和请求相同资源上的锁,产生了循环依赖。当多个事务试图以不同的顺序锁定资源时会导致死锁。当多个事务锁定相同的资源时,也可能会发生死锁。
⏱ 2023-04-29 21:12:17 ^3300035678-11-11024-11105
📌 InnoDB 存储引擎,检测到循环依赖后会立即返回一个错误信息。
⏱ 2023-04-29 21:13:06 ^3300035678-11-11867-11898
📌 InnoDB 目前处理死锁的方式是将持有最少行级排他锁的事务回滚(这是一种最容易回滚的近似算法)。
⏱ 2023-04-29 21:13:33 ^3300035678-11-11964-12012
📌 死锁的产生有双重原因:有些是因为真正的数据冲突,这种情况通常很难避免,但有些则完全是由于存储引擎的实现方式导致的
⏱ 2023-04-29 21:14:13 ^3300035678-11-12087-12143
📌 事务日志有助于提高事务的效率。存储引擎只需要更改内存中的数据副本,而不用每次修改磁盘中的表,这会非常快。然后再把更改的记录写入事务日志中,事务日志会被持久化保存在硬盘上。因为事务日志采用的是追加写操作,是在硬盘中一小块区域内的顺序 I/O,而不是需要写多个地方的随机 I/O,所以写入事务日志是一种相对较快的操作。最后会有一个后台进程在某个时间去更新硬盘中的表。因此,大多数使用这种技术(write-ahead logging,预写式日志)的存储引擎修改数据最终需要写入磁盘两次。
⏱ 2023-04-29 21:16:19 ^3300035678-11-12524-12762
📌 默认情况下,单个 INSERT、UPDATE 或 DELETE 语句会被隐式包装在一个事务中并在执行成功后立即提交,这称为自动提交 (AUTOCOMMIT) 模式。通过禁用此模式,可以在事务中执行一系列语句,并在结束时执行 COMMIT 提交事务或 ROLLBACK 回滚事务。
⏱ 2023-04-29 21:37:38 ^3300035678-11-13148-13277
📌 在当前连接中,可以使用 SET 命令设置 AUTOCOMMIT 变量来启用或禁用自动提交模式。启用可以设置为 1 或者 ON,禁用可以设置为 0 或者 OFF。
⏱ 2023-04-29 21:37:58 ^3300035678-11-13306-13376
📌 MySQL 可以通过执行 SET TRANSACTION ISOLATION LEVEL 命令来设置隔离级别。新的隔离级别会在下一个事务开始的时候生效。
⏱ 2023-04-29 21:38:42 ^3300035678-11-13762-13835
📌 MySQL 不在服务器层管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中,混合使用多种存储引擎是不可靠的。
⏱ 2023-04-29 21:39:16 ^3300035678-11-14246-14303
📌 最好不要在应用程序中混合使用存储引擎。失败的事务可能导致不一致的结果,因为某些部分可以回滚,而其他部分不能回滚。
⏱ 2023-04-29 21:40:02 ^3300035678-11-14791-14847
📌 InnoDB 使用两阶段锁定协议 (two-phase locking protocol)。在事务执行期间,随时都可以获取锁,但锁只有在提交或回滚后才会释放,并且所有的锁会同时释放。前面描述的锁定机制都是隐式的。InnoDB 会根据隔离级别自动处理锁。
⏱ 2023-04-29 21:40:26 ^3300035678-11-14916-15039
📌 LOCK TABLES 命令和事务之间的交互非常复杂,并且在一些服务器版本中存在意想不到的行为。因此,本书建议,除了在禁用 AUTOCOMMIT 的事务中可以使用之外,其他任何时候都不要显式地执行 LOCK TABLES,不管使用的是什么存储引擎。
⏱ 2023-04-29 21:41:31 ^3300035678-11-15875-15995
📌 MVCC 的工作原理是使用数据在某个时间点的快照来实现的。这意味着,无论事务运行多长时间,都可以看到数据的一致视图,也意味着不同的事务可以在同一时间看到同一张表中的不同数据!
⏱ 2023-04-29 21:42:51 ^3300035678-11-16390-16476
📌 我们可以通过图 1-2 所示的序列图解释 InnoDB 的行为 [插图],以此来展示 MVCC 的一种实现方式。[插图] 图 1-2:跨不同事务处理同一行多个版本的序列图 InnoDB 通过为每个事务在启动时分配一个事务 ID 来实现 MVCC。该 ID 在事务首次读取任何数据时分配。在该事务中修改记录时,将向 Undo 日志写入一条说明如何恢复该更改的 Undo 记录,并且事务的回滚指针指向该 Undo 日志记录。这就是事务如何在需要时执行回滚的方法。当不同的会话读取聚簇主键索引记录时,InnoDB 会将该记录的事务 ID 与该会话的读取视图进行比较。如果当前状态下的记录不应可见(更改它的事务尚未提交),那么 Undo 日志记录将被跟踪并应用,直到会话达到一个符合可见条件的事务 ID。这个过程可以一直循环到完全删除这一行的 Undo 记录,然后向读取视图发出这一行不存在的信号。事务中的记录可以通过在记录的 “info flags” 中设置 “deleted” 位来删除。这在 Undo 日志中也被作为 “ 删除标记 “ 进行跟踪。值得注意的是,所有 Undo 日志写入也都会写入 Redo 日志,因为 Undo 日志写入是服务器崩溃恢复过程的一部分,并且是事务性的。
⏱ 2023-04-29 21:46:55 ^3300035678-11-16593-17552
📌 MVCC 仅适用于 REPEATABLE READ 和 READ COMMITTED 隔离级别。READ UNCOMMITTED 与 MVCC 不兼容 [插图],是因为查询不会读取适合其事务版本的行版本,而是不管怎样都读最新版本。SERIALIZABLE 与 MVCC 也不兼容,是因为读取会锁定它们返回的每一行。
⏱ 2023-04-29 21:48:04 ^3300035678-11-17895-18150
📌 在 8.0 版本中,MySQL 将表的元数据重新设计为一种数据字典,包含在表的.ibd 文件中。这使得表结构上的信息支持事务和原子级数据定义更改。
⏱ 2023-04-29 21:52:16 ^3300035678-11-19064-19161
📌 InnoDB 使用 MVCC 来实现高并发性,并实现了所有 4 个 SQL 标准隔离级别。InnoDB 默认为 REPEATABLE READ 隔离级别,并且通过间隙锁 (next-key locking) 策略来防止在这个隔离级别上的幻读:InnoDB 不只锁定在查询中涉及的行,还会对索引结构中的间隙进行锁定,以防止幻行被插入。
⏱ 2023-04-29 21:53:19 ^3300035678-11-20128-20281
📌 InnoDB 内部做了很多优化。其中包括从磁盘预取数据的可预测性预读、能够自动在内存中构建哈希索引以进行快速查找的自适应哈希索引 (adaptive hash index),以及用于加速插入操作的插入缓冲区 (insert buffer)
⏱ 2023-04-29 21:54:46 ^3300035678-11-20509-20625
📌 从 MySQL 5.6 开始,InnoDB 引入了在线 DDL,它最初只支持有限的使用场景,但在 5.7 和 8.0 版本中进行了扩充。就地 (in-place) 更改 schema 的机制允许在不使用完整表锁和外部工具的情况下进行特定的表更改操作,这大大提高了 MySQL InnoDB 表的可操作性。
⏱ 2023-04-29 21:55:37 ^3300035678-11-20995-21133
📌 JSON 类型在 5.7 版本被首次引入 InnoDB,它实现了 JSON 文档的自动验证,并优化了存储以允许快速读
⏱ 2023-04-29 21:56:19 ^3300035678-11-21284-21336
📌 MySQL 8.0.7 的进一步改进增加了在 JSON 数组上定义多值索引的能力。将常用访问模式匹配到可以映射 JSON 文档值的函数这一特性可以进一步加快对 JSON 类型的读取访问查询
⏱ 2023-04-29 21:57:12 ^3300035678-11-21416-21503
📌 MySQL 8.0 引入了原子数据定义更改。这意味着数据定义语句现在要么全部成功完成,要么全部失败回滚。这是通过创建 DDL 特定的 Undo 日志和 Redo 日志来实现的,InnoDB 便依赖这两种日志来跟踪变更——这是 InnoDB 经过验证的设计,已经扩展到 MySQL 服务器的操作中。
⏱ 2023-04-29 21:58:24 ^3300035678-11-21989-22125
第 2 章 可靠性工程世界中的监控
📌 [插图]
⏱ 2023-04-29 22:53:28 ^3300035678-12-4302-4303
📌 经常会听到 “ 生产中测试 “ 的鼓声,这让很多人畏缩。实际情况是,在生产中进行测试具有很大的价值。在生产中,你可以发现这种变化是如何影响系统的其他部分、规模和实际客户流量的。也可以查看对相邻系统的影响。
⏱ 2023-04-30 08:57:10 ^3300035678-12-7364-7462
📌 MySQL 中有一个 Threads_running 状态计数器可以作为可用性问题的关键指标,这个计数器跟踪的是给定数据库主机上当前正在运行的查询数量。当运行的线程快速增长且没有任何下降迹象时,说明查询完成得不够快,因此正在堆积和消耗资源。如果允许这个指标增长,通常会导致数据库主机出现完全的 CPU 锁定或严重的内存负载,从而导致操作系统关闭整个 MySQL 进程。
⏱ 2023-04-30 09:00:50 ^3300035678-12-9249-9426
📌 首先要检查有多少个 CPU 核,如果 Threads_running 超过了 CPU 核数,则可能表明服务器正处于不稳定状态。与此相结合,你还可以监控 Thread_running 与 max_connections 的差距,将此差距作为另一个数据点,以检查正在进行的工作是否过载。
⏱ 2023-04-30 09:01:24 ^3300035678-12-9471-9602
📌 应用程序层打开了大量未使用的连接,导致产生了毫无理由的连接过多的风险。一个明显的迹象是连接的线程数 (threads_connected) 很高,但运行的线程数 (threads_running) 仍然很低。
⏱ 2023-04-30 09:06:50 ^3300035678-12-13816-13916
📌,还需要对数据库主机的繁忙程度进行跟踪并设置告警,正如前面解释的,这可以从 threads_running 的值中看出。通常,如果这个值增长到 100 以上,就会开始看到 CPU 使用率和内存使用率的增加,这是数据库主机上高负载的普遍迹象
⏱ 2023-04-30 09:07:47 ^3300035678-12-14238-14351
📌 像 iostat 这样的工具可以监控 I/O 等待。
⏱ 2023-04-30 09:10:08 ^3300035678-12-16225-16275
读书笔记
本书评论
书评 No.1
^280435523-7HQbX7sOJ
⏱ 2023-04-29 21:25:30