第一章MySQL架构
MySQL提供了多种锁的颗粒度,每种MySQL存储引擎都可以实现自己的锁策略和锁力度。
行级锁是在存储引擎而不是在服务器中实现的。
隔离界别
READ UNCOMMITTED - 脏读
在事务中可以可以查看到其他事务中还没有提交的修改。实际中很少用。
READ COMMITTED - 提交读
同一事务中两次执行相同语句,可能会看到不同的数据结果。
REPEATABLE READ - 可重复读
MySQL的默认隔离级别。
serializable - 可串行化
最高的隔离级别。强制事务按顺序执行。实际中很少用到。
死锁
InnoDB如果检测到循环依赖(死锁),会立刻返回一个错误信息。
InnoDB目前处理死锁的方式是将持有最少行级排他所的事务回滚。
事务
在同一个事务中,混合多种存储引擎是不可靠的。
第二章
MySQL中有一个Threads running状态计数器可以作为可用性问题的关键指标,这个计数器跟踪的是给定数据库主机上当前正在运行的查询数量。
如果Threads running超过了CPU核数,则可能表明服务器整处于不稳定状态。
thread_connected/max_connections的比值显示了应用程序节点数量的增长与数据库允许的最大连接池有多接近。
第三章 Performance Schema
Performance Schema介绍
Performance Schema同了有关MySQL服务器内部运行的操作上的底层指标。
Performance Schema有两个概念:
程序插桩:程序插桩在MySQL代码中插入探测代码,以获取我么你想了解的信息。
消费者表:指存储关于程序插桩代码信息的表。如果我们为查询模块添加插桩,相应的消费者表将记录诸如执行总数、未使用索引的次数、花费的时间等信息。消费者表是插桩发送信息的目的地。
第四章 操作系统和硬件优化
MySQL需要的四种基本资源是:CPU、内存、磁盘、网络资源。
MySQL为了加快IO,许多事务系统使用提前写日志的策略。提前写日志允许在内存中更改页面,而不用将更改刷新到硬盘上。
写入放大用来描述由于部分块写而导致的数据从一处移动到另一处、多次写入数据和元数据所产生的额外操作。
当设备被填满时,垃圾收集器必须更努力的保持某些块的清洁,因此写放大因子会增加。因此,许多设备在空间块满时会变慢。
raid控制器的内存是一种稀缺资源,应该明智的使用它。将其用于读操作通常是一种浪费,将其用于写操作时提高IO性能的一种重要方式。
建议生产环境启用:skip_name_resolve 解决dns问题。
MySQL的文件系统建议选择ext4、XFS或ZFS。
某些特殊情况,OOM Killer进程将启动并终止某些进程,例如终止SSH进程,为了防止这种情况,需要更改SSH进程的oom_adj或oom_score_adj。
查看操作系统状态一般用:iostat 或 vmstat
第五章 优化服务器设置
MySQL有很多可以更改但是不需要更改的设置。通过建议正确的配置基本设置(在大多数情况下,只有少数设置是重要的),并将更多的时间花在schema优化、索引和查询设计上。
首先应该确保InnoDB缓冲池和日志文件大小等基本设置是合适的。
如果非要改进配置,应该在查询响应时间中体现出来。最好从查询及响应时间开始分析,而不是从配置选项开始。
配置文件位置通常是:/etc/my.cnf或/etc/mysql/my.cnf
需要永久使用的任何设置都应该写入到全局配置文件,而不是在命令行中指定。
如果不知道服务器会读取哪些配置文件,可以通过以下命令查询:
服务器一般读取mysqld部分。
配置文件中,配置设置全部用小写字母书写,单词之间可以用下划线或中横线分隔。
如果在服务器运行时设置变量的全局值,则当前回话与其他现有会话的值将不受影响。如果客户端依赖数据库长连接,请务必记住这一点。在每次更改后应该检查SHOW GLOBAL VARIABLES的输出,以确保达到预期效果。
可以用SET命令为变量设置一个特殊值DEFAULT。将会话作用域变量设置成DEFAULT会将该变量设置为相应的全局作用域变量的值。
8.0新语法;SET PRESIST允许在运行时设置一次值,MySQL将把这个设置写入磁盘,在下次重启后继续使用该值。
请注意:在默认情况下,只更改配置文件,实际上不会做任何事情,还必须更改运行时设置。
缓存命中率和缓存大小无关。
尽量避免使用大型临时表或复杂的存储过程。
操作系统有足够内存的最佳判断依据是:它没有主动将虚拟内存交换(分页)到磁盘。
InnoDB的配置
最重要的选项是:innodb_log_file_size和innodb_buffer_pool_size
日志配置:innodb_log_file_size、innodb_log_file_in_group,建议使用innodb_dedicated_server,日志文件的大小会根据系统内存来自动管理。
如何刷新日志到缓冲区:innodb_flash_log_at_trx_commit来控制日志以缓冲区的刷新位置和刷新频率。把它设置成 1 ,是最安全的。
InnoDB表空间
使用innodb_data_home_dir和innodb_data_file_path 配置指定表空间文件。
建议使用innodb_file_per_table并限制共享表空间的大小。
MySQL并发
将innodb_thread_concurrency设置为与可用CPU核数相同的值,然后根据需要调整大小。
MySQL安全设置
max_connect_error
max_connections:可以设置的足够高。 同时可以关注max_used_connections。
skip_name_resolve:强烈建议设置此选项
read_only和super_read_only:强烈建议启用。super_read_only会防止意外地使用管理员账户将数据写入只读副本,从而引起数据不同步。
第六章 schema设计与管理
整数类型:int(11)对大多数应用毫无意义,它不会限制值的合法范围。它只是规定了MySQL的一些交互工具用来显示字符的个数。
实数类型:我们建议只指定数据类型,不指定精度。MySQL会使用double进行浮点类型的内部计算。
字符串类型:CHAR在存储时,MySQL会删除所有尾随空格。如果需要进行比较,值会用空格填充。字符串长度定义的是字符数,不是字节数。
BIT:MySQL会在处理时将BIT视为字符串类型,而不是数字类型。不建议使用BIT类型。
SET:FIND_IN_SER() 、FIELD()
太多联接的查询会很慢,每个查询最好少于十几个的表。
小心使用ENUM和SET。
第七章 创建高性能的索引
索引基础
如果索引包含很多列,那么列的顺序也很重要。MySQL智能有效的使用索引的最左前缀列。
b-tree索引的限制:
- 如果不是按照索引的最左列开始查找,则无法使用索引。
- 不能跳过索引中的列。
- 如果查询中有某列的范围查询,则其右边所有列都无法使用索引优化查找。
对于BLOB、TEXT、很长的VARCHAR类型的列,必须使用前缀索引。
创建前缀索引的语句:
ALTER TABLE city_demo ADD KEY (city(7));
通常使用UNION改写查询,往往是最好的办法。
使用索引排序
只有当索引的顺序和order by子句的顺序完全一致,并且所有列的排序方向都一样时,MySQL才能使用索引才对结果做排序。
如果查询需要联接多张表,则只有当order by子句引用的字段全部在一个表时,才能使用索引做排序。
order by子句和查找型查询的限制是一样的:需要满足索引的最左前缀的要求,否则,MySQL需要执行排序操作,而无法利用索引排序。
有种特殊情况,如果前导列是常量的时候,order by子句中的列也可以不满足索引的最左前缀的要求。
修复损坏的表
CHECK TABLE通常能找出大多数的表和索引的错误。
也可以使用REPAIR TABLE命令来修复损坏的表,但是同样不是所有的存储引擎都支持该命令。
更改表的存储引擎: ALTER TABLE<table> ENGINE=INNODB;
更新索引统计信息
其目的是使存储引擎向优化器提供正确的表信息,以防止优化器做出错误的判断。
语句:SHOW INDEX,只要SHOW INDEX查看表索引统计信息,就会出发统计信息的更新。
减少索引的碎片
语句:OPTIMIZE TABLE
注意:单行访问时最慢的。同时,我们建议按响应时间来对查询进行分析。找出那些消耗最长时间的查询或者那些给服务器带来最大压力的查询。
第八章 查询性能优化
MySQL是否在扫描额外的记录
衡量MySQL查询开销的三个指标:
- 响应时间
- 扫描的行数
- 返回的行数
EXPLAIN语句中的type列反应了访问类型。
查询请求的流程
当向MySQL发送一个请求的时候,MySQL到底做了什么?
- 客户端你给服务器发送一条sql语句;
- 服务器进行sql语句解析、预处理,再由优化器生成对应的执行计划;
- MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;
- 将结果返给客户端。
查询优化器
优化策略有2中:静态优化和动态优化。
并行执行
MySQL无法利用多核特性来并行执行查询。
在同一个表中查询和更新
MySQL不允许对一张表同时进行查询和更新。
count()
count()有两种作用:它可以统计某列的值的数量,也可以统计行数。
在统计列值时,要求列值时非空的(不统计NULL)。如果在count()的括号中指定了列或列的表达式,则统计的就是这个表达式有值的结果数。
在统计结果集的行数时,当MySQL确认括号内的表达式不可能为空时,实际上就是在统计行数。最简单的就是当我们使用count(*)时,这种情况通配符*并不会向我们猜想的那样扩展成所有列,实际上它会忽略所有列而直接统计所有的行数。
如果想要知道结果集中的行数,应该使用count(*)。
优化联接查询
除非有其他理由,否则只需要再联接顺序中的第二个表的响应列上创建索引。例如A表和B表通过C列联接查询时,如果优化器的联接顺序是B、A,则不需要再B表的对应列上建索引。
优化limit
select id,desc from film order by title limit 50,5
如果该表很大,那么可以改成下面的样子:
select id,desc from film
inner join
(select id,desc from film order by title limit 50,5) as lim
using(id);
优化offset
select id,desc from film where id < 16030 order by title desc limit 20;
第九章 复制
MySQL提供了三种不同的二进制日志格式用于复制,建议使用基于行的复制。
强烈建议启用GTID全局事务标识符。
第十章 备份与恢复
在生产实践中,对于大数据库来说,裸文件备份是必须的。对于较小的数据库,逻辑备份可以很好的胜任。
保存二进制日志用于基于故障时间点的恢复,应该将expire_logs_days参数的值设置的足够大。
差异备份:是对自上次全备份后所有更改的部分而多的备份。
增量备份:是对自任意类型的上次备份后的所有修改做的备份。
裸文件恢复用:PerconaXtraBackup
逻辑备份恢复用:mydumper
第十一章 扩展MySQL
第十二章 云端MySQL
选择正确的机器类型
vCPU数量的计算公式:(物理机CPU核的数量 X 95%CPU总使用量) X 2
建议将50%作为常规的使用率目标,最高可达65%-70%。如果维持在70%或更高的CPU使用率,将可能看到延迟增加,此时应该考虑增加更多CPU。