本文是个人学习极客时间专栏《MySQL实战45讲》过程中所记录的一些笔记,内容来源于专栏
MySQL高可用
主备延迟
在备库上执行 show slave status 命令,它的返回结果里面会显示 seconds_behind_master,用于表示当前备库延迟了多少秒。主备延迟的来源可能
备库所在机器的性能要比主库所在的机器性能差
备库的压力大
大事务,如一次性地用 delete 删除太多数据,大表 DDL 等
主备切换策略
可靠性优先策略(有不可用时间)
判断备库 B 现在的 seconds_behind_master,如果小于某个值(比如 5 秒)继续下一步,否则持续重试这一步;
把主库 A 改成只读状态,即把 readonly 设置为 true;
判断备库 B 的 seconds_behind_master 的值,直到这个值变成 0 为止;
把备库 B 改成可读写状态,也就是把 readonly 设置为 false;
把业务请求切到备库 B。
可用性优先策略(有可能出现数据不一致)
- 不等主备数据同步,直接把连接切到备库 B,并且让备库 B 可以读写
MySQL Join
Index Nested-Loop Join
- **可以用上被驱动表的索引。**先遍历驱动表(全表扫描),然后从驱动表中取出每行数据中的关联值,去被驱动表中查找满足条件的记录(树搜索)
Block Nested-Loop Join
- 被驱动表上无索引可用。把驱动表的数据读入线程内存 join_buffer 中(全表扫描,存不下则分段取),把被驱动表的每一行取出来(全表扫描),跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回
Join 该怎么用
能不能使用 join 语句?
如果可以使用 Index Nested-Loop Join 算法,也就是说可以用上被驱动表上的索引,其实是没问题的
如果使用 Block Nested-Loop Join 算法,扫描行数就会过多。尤其是在大表上的 join 操作,这样可能要扫描被驱动表很多次,会占用大量的系统资源。所以这种 join 尽量不要用
如果要使用 join,应该选择大表做驱动表还是选择小表做驱动表?
- 总是应该使用小表做驱动表(在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与 join 的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表)
常见配置参数
- join_buffer_size:控制Join Buffer的大小,调大后可以避免多次的被驱动表扫描,从而提高性能。默认值为
257KB
自增主键为什么不是连续的
自增值保存在哪
MyISAM 引擎的自增值保存在数据文件中
InnoDB 引擎的自增值,其实是保存在了内存里,并且到了 MySQL 8.0 版本后,才有了“自增值持久化”的能力
在 MySQL 5.7 及之前的版本,自增值保存在内存里,并没有持久化。每次重启后,第一次打开表的时候,都会去找自增值的最大值 max(id),然后将 max(id)+1 作为这个表当前的自增值
在 MySQL 8.0 版本,将自增值的变更记录在了 redo log 中,重启的时候依靠 redo log 恢复重启之前的值
自增值修改机制
假设,某次要插入的值是 X,当前的自增值是 Y
如果 X < Y,那么这个表的自增值不变;
如果 X ≥ Y,那么需要把当前自增值修改为新的自增值
新的自增值生成算法是:从 auto_increment_offset 开始,以 auto_increment_increment 为步长,持续叠加,直到找到第一个大于 X 的值,作为新的自增值。其中,auto_increment_offset 和 auto_increment_increment 是两个系统参数,分别用来表示自增的初始值和步长,默认值都是 1
自增值修改时机
- 表的自增值修改是在真正执行插入数据的操作之前,且自增值不会回退(MySQL 这么设计是为了提升性能,回退需要多一次判断id存不存在或需要扩大自增id锁的范围),所以如果插入数据操作失败(如唯一键冲突)或事务回滚,那么自增主键将出现不连续的情况(但是递增的)
自增主键不连续的原因
数据插入时发生唯一索引冲突
数据插入事务回滚
自增主键的批量申请。对于批量插入语句(如 insert … select、replace … select、load data 语句),MySQL 有一个批量申请自增 id 的策略,第一次申请分配1个自增id,后续每次申请的 id 都是上一次的两倍,当 id 用不到时会浪费掉