《MySQL实战45讲》(20-35讲)学习总结

Posted 月亮的-影子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《MySQL实战45讲》(20-35讲)学习总结相关的知识,希望对你有一定的参考价值。

注明:极客时间《mysql实战45讲》个人学习总结

目录

第二十一讲:为什么我只改一行的语句,锁这么多?

CREATE TABLE `t` (
 `id` int(11) NOT NULL,
 `c` int(11) DEFAULT NULL,
 `d` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

加锁规则

  1. 原则1:加锁的基本单位是next-key lock
  2. 原则2:访问到的对象都要加锁
  3. 优化1:索引等值查询,唯一索引加锁,next-key lock退化成行锁
  4. 优化2:索引等值查询,向右遍历而且最后最后一个不符合规则,next-key lock退化成间隙锁(不代表一开始加上的next-key lock是可以被优化的,而是要往右移动到第一个不符合规则的才是能够进行优化的)
  5. bug:唯一索引范围查询会访问到不满足的第一个值为止

案例一:等值查询间隙锁

  • 根据原则1,现在就要给id=7加上next-key lock,因为id=7这个记录并不存在。加锁的范围是(5,10]
  • 然后就是优化2,现在遍历5-10,发现10很明显就是不符合规则,因为id=10记录是存在的而且id不等于7。所以退化为间隙锁(5,10)。
  • 这也就是为什么B被阻塞,但是C可以修改的原因了。(测试成功)

案例二:非唯一索引等值锁

分析

  • 根据原则1给(0,5]加上next-key lock
  • 由于c是普通索引根据原则2需要继续向右遍历找到不符合规则的记录,刚好找到id=10的记录,并且c不符合规则等于5,那么就要给(5,10]加上next-key lock。因为要给访问到的对象加上锁。
  • 还需要配合优化2使用,因为id=10的位置不符合规则所以(5-10)退化成了间隙锁
  • 由于只有访问到的对象才会加锁,这里访问的只是普通索引的记录,所以主键索引不会受到任何影响。所以下面是成立的。

案例三:主键索引范围锁

  • 对于这里的第一条语句是从(5-10]退化成行锁,只是锁住id=10这条记录
  • 但是对于第二条就不一样的,因为是唯一索引而且还是范围查找,那么还需要继续去查找后面第一个符合规则的记录。也就是给(10-15]加上next-key lock,而且由于不是等值查询所以不会退化。
  • 所以B和C都会被阻塞。但是8这条并不会被阻塞。
 select * from t where id=10 for update;
 select * from t where id>=10 and id<11 for update;

案例四:非唯一索引范围锁

  • 首先是找到c=10的位置,并且加上(5-10]的next-key lock。
  • 然后就是要继续往后面找到不符合范围的记录刚好就是15,所以还要加上一个(10,15]的next-key lock。由于不是一个范围查询所以不能进行优化。

案例五:唯一索引范围锁 bug

  • 这个案例正常来说是直接锁上(10,15]但是现在多了一个bug,就是需要拓展到(15,20].

案例六:非唯一索引上存在"等值"的例子

insert into t values(30,10,30);
  • 假设现在有两个c=10是相等的,那么他们的一个间隙是什么?他们的间隙就是id之间的一个间隙。10和30

  • 对于下面的过程其实就是delete和select的规则是一样的,先找到第一个c的位置,然后给c=5,id=5到c=10、id=10加上一个next-key lock。接着就是向右遍历到c=15 、id=15。也就是覆盖范围和一个的时候相似。

案例七:limit 语句加锁

  • 对于下面的场景加上了limit 2,虽然结果相同但是加锁的范围不同,加锁会直接结束,找到第二个记录的时候。
  • 所以删除的时候限制删除的条目,能够缩短锁的范围。那样插入各方面或者update的并发度能够得到更好的提升。

案例八:一个死锁的例子

  • 过程是A先给(5,10]上next-key lock然后给在(10,15)上间隙锁
  • 这个时候B要进行更新,所以要给(5,10]上next-key lock
  • 但是A要再c=8的位置插入数据但是B的间隙锁让A无法插入,而A的间隙锁也让B无法修改。
  • B的上锁过程是间隙锁(5,10)然后再给c=10上一个行锁。

总结

对于这个地方需要思考清除规则。

  • 第一个规则是只要是访问加锁,那么基本上都是间隙锁+行锁
  • 第二个规则是只要访问到了的地方就要加锁
  • 第三个规则是唯一索引如果是等值比较,而且符合规则就可以改成行锁。
  • 第四个规则,制造了第一个间隙锁+行锁之后仍然需要往右边遍历,找到第一个不符合的为止才能够结束。而且还能优化为间隙锁
  • 最后一个就是bug,对于唯一索引来说就是已经找到了第一个间隙,但是仍然要去找第一个不符合规则的,并且加上间隙锁+行锁。

问题

对于这里为什么会锁上(5,10]的原因?

  • 第一个要解释的地方就是为什么加上锁之后仍然需要扫描?原因就是你不知道右边是不是还有和当前的c相同的值,所以必须要找到一个不同的值的记录并且进行锁定。如果没有锁的问题就是可能会通过修改,把c相同的一些记录修改了,或者是插入了一些新的记录导致出现的幻读问题。

  • 原因其实就是因为他是一个desc,所以首先锁住的是next-key lock(15-20],然后加上间隙锁(20,25)

  • 然后向左边进行遍历,扫描到10的时候停下。所以给10也加上了next-key lock范围刚好就是(5,10]。向左边遍历的原因就是因为desc导致的遍历方向不同,开始的位置不同。如果没有desc,那么正常的加锁状态应该是,(10-15]加next锁,然后往右边遍历,发现了20不符合,然后给加上(15-20)的间隙锁(等值比较),接着就是给20加上一个(15-20]的next锁,然后往右遍历直到25。但是加上desc之后关系相反,现在从20开始进行加锁,先加上next-key lock(15-20],然后往右遍历(20-25)间隙锁,这个时候仍然需要往左边去遍历范围,然后遍历到15加上一个next-key lock(10,15],接着就是往右边扫描发现已经有间隙锁,由于这个时候15还是符合的,所以需要接着需要往左边的记录进行遍历,扫描到10终于不符合了,所以在这个时候给10加上一个next-key lock刚好锁住(5,10]。这里是个人理解,可能会不对。但是我觉得是按照规则进行的一个扫描。

  • 而且select * 说明需要回表访问,所以主键索引这里也有锁,不过被优化成了行锁。

第二十二讲:MySQL有哪些“饮鸩止渴”提高性能的方法?

短连接风暴

短连接的问题就是如果并发量起来就会导致服务器大部分时间在做登录验证,权限查询等。所以需要限制每次的连接数。

第一种方法:先处理掉那些占着连接但是不工作的线程。

  • 优先断开事务外空闲连接,避免断开事务中的连接不然就只能回滚事务了
  • 要是不行才断开事务内的连接太久的连接。

第二种方法:减少连接过程的消耗。

  • 可以选择减少权限授予这部分,但是风险太大

慢查询性能问题

  • 索引问题
  • sql语句问题
  • mysql选错了索引

导致慢查询的第一种可能是,索引没有设计好。

这种只能通过紧急创建索引的方式建立索引

  1. 备库B执行set sql_log_bin=off,不写binlog,然后alter table添加索引
  2. 执行主备切换
  3. 主库是B,备库是A。A执行set sql_log_bin=off,然后alter table来添加索引。

导致慢查询的第二种可能是,语句没写好。

  • 比如语句的索引列使用了函数
  • 没有使用到索引等。

选错了索引

  • 统计错误

QPS 突增问题

第二十三讲:MySQL是怎么保证数据不丢的?

binlog 的写入机制

  • 写到binlog cache然后事务提交之后再写入到binlog文件中

  • 一个事务的binlog不能被拆开,每个事务的binlog都要保证一次性写入。

  • 所以每个事务的线程都对应着一份binlog cache,如果binlog cache不够用那么就要使用到临时文件先存入到磁盘。这样是为了保证每个事务的binlog的完整性。

  • 而且整个过程是binlog cache写到文件系统缓存,然后才是同步到磁盘。

  • 关于写的时机

    • sync_binlog=0只write到文件系统缓存但是不同步到磁盘
    • sync_binlog=1每次提交事务都会同步到磁盘
    • sync_binlog=2每次提交事务都会先写到文件缓存,然后写多几次才会同步到磁盘。
  • 对于0和2来说主机宕机都会导致binlog的几次写的数据丢失。

redo log 的写入机制

  • 对于redo log cache来说不需要每次生成都同步到磁盘,而且如果mysql重启那么redo log cache就会丢失,问题是事务没有提交,所以没有关系。
  • 三种redo log状态
    • 在redo log cache中
    • 在page cache中
    • 持久化到磁盘中
  • redo log的写入策略,innodb_flush_log_at_trx_commit 参数控制。
    • 0每次事务提交都只是写到redo log buffer
    • 1事务提交直接持久化到磁盘
    • 2每次事务提交都只是把redo log同步到page cache中
  • 有一个后台线程会把redo log buffer的数据同步到磁盘。也就是未提交的redo log也可能会比存储到磁盘。

还有两种场景会把未提交的redo log也存储到磁盘。

  • redo log buffer的大小等于这个参数innodb_log_buffer_size的一半,后台线程会主动写盘。这里只是写到page cache中。
  • 并行事务提交会顺带把redo log buffer持久化到磁盘。

对于redo log来说在prepare的时候就需要进行一次持久化,如果发生崩溃,那么恢复就需要依靠redo log+binlog进行恢复。

  • 如果是mysql的双1,也就是sync_binlog和innodb_flush_log_at_trx_commit 都是1,那么就是每次事务提交都需要刷两次盘。但是为什么有时候mysql的TPS是每秒2w,相当于是刷4w磁盘,但是磁盘io只有2w,那么如何实现?

  • 他依靠的是组提交,可以看看上面的图,lsn日志逻辑序列号,主要是对应redo log的写入点。
    • trx1是组长
    • trx1写的时候已经有了3个事务,所以会连带这些事务的redo log 写盘,并且lsn已经写到了160。
    • 现在所有lsn<=160都被写盘了
    • trx2和3都可以直接返回了。
  • 并发场景,组员多,fync晚调用,积累更多写盘,那么节约IOPS的效果就越好。

  • 下面的意思是redo log和binlog都先写盘,然后等待他们都写盘之后再进行一次fync同步到磁盘。大大节约了IO的成本。

WAL的好处

  • redo log和binlog都是顺序写,顺序写速度比随机写快
  • 组提交机制能够更好降低磁盘的IOPS

问题

如果你的 MySQL 现在出现了性能瓶颈,而且瓶颈 在 IO 上,可以通过哪些方法来提升性能呢?

  • 通过组提交,减少binlog写盘次数。
  • 设置sync_binlog>1意思就是写盘多次,fync一次,但是如果主机挂了会丢失数据。
  • 将 innodb_flush_log_at_trx_commit=2也是主机挂的时候导致数据全部丢失。

总体:为了保证mysql的redo log和binlog的完整,主要还是通过各种策略。比如双1是比较稳定的策略,事务提交就写盘,fync。但是这样导致的问题就是并发度不够高,如果在系统相对稳定可以写到page cache多次,然后再集中fync到磁盘。并且可以通过组提交和lsn的减少IOPS,减少的原因就是减少了写磁盘的频率,把redo log和binlog集中起来刷盘。

为什么 binlog cache 是每个线程自己维护的,而 redo log buffer 是全局共用 的?

  • 原因就是binlog需要保证完整写完一次事务,但是对于redo log并不是。而且redo log buffer的连续能够让并行事务同时刷新到磁盘。

事务执行期间,还没到提交阶段,如果发生 crash 的话,redo log 肯定丢了,这 会不会导致主备不一致呢?

  • 不会因为binlog也还在binlog buffer所以没有推送到备库。因为binlog在事务提交才会进行写盘和刷新。

第24讲:MySQL是怎么保证主备一致的?

MySQL 主备的基本原理

  • 对备库需要设置readonly
  • 防止切换时的双写问题。

readonly对超级用户权限无效,仍然可以写,所以这就可以进行主从配置的同步。

  • 主库接收到客户端的请求,更新并且写binlog
    • 备库B能够通过change master得知如何请求A主库的binlog
    • 备库B执行start slave,启动一个io线程和一个sql线程,io线程和主库进行连接
    • 主库A给B发送binlog
    • B读binlog写到本地文件,这个就是文件就是relay log
    • sql线程读取relay 日志并且解析命令和执行。

binlog 的三种格式对比

CREATE TABLE `t` (
 `id` int(11) NOT NULL,
 `a` int(11) DEFAULT NULL,
 `t_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 KEY `a` (`a`),
 KEY `t_modified`(`t_modified`)
) ENGINE=InnoDB;
insert into t values(1,1,'2018-11-13');
insert into t values(2,2,'2018-11-12');
insert into t values(3,3,'2018-11-11');
insert into t values(4,4,'2018-11-10');
insert into t values(5,5,'2018-11-09');

binlog如果是statement的时候就是直接保存原文的。

mysql> show binlog events in 'master.000001';

delete from t /comment/ where a>=4 and t_modified<=‘2018-11-10’ limit 1;

delete语句会造成一个warnings,因为delete+limit可能会造成主从设备不一致的问题

  1. 如果使用索引a,那么先找到a=4的情况
  2. 如果是索引t.modified那么第一个找到就是2018-11-10的情况

这个是statement格式的问题。这样就会导致主库可能使用的是a索引,但是从库使用的是t_modified索引。那么删除的顺序就有可能不同。

  • 所以现在把binlog格式改成row格式
    • 发现这里的原文语句变成了两个活动语句
    • table_map:说明要操作test.t表
    • delete_rows:说明定义删除行为。

下面展示的是上面图的具体信息

  • server id说明事务在server id=1的库上面执行
  • table map event对应了number 226,每个表都有对应的table_map event对应不同表
  • 然后就是语句的具体细节@1=4,@2=4这种参数。对应id=4,a=4完全具体到了一条语句上
  • binlog_row_image默认null,但是在这里设置为MINIMAL,为了记录必要的删除信息。那么就不会像statement的语句造成使用不同索引,删除顺序不同导致主从不一致。

为什么会有 mixed 格式的 binlog?

  • row很占用空间,写binlog耗费IO资源
  • statement容易造成主从不一致,但是占用空间小
  • mixed可以根据语句是否造成主从不一致灵活调控两种格式的应用。

为什么大部分时候mysql场景要求使用row格式?

恢复数据很方便

  • row记录了整行数据包括各种参数,如果要恢复那么直接重新插入即可
  • insert记录了整行数据,如果插入错了,可以直接通过binlog删除
  • update会记录前和后的数据,只要查一下就可以恢复过来。

insert into t values(10,10, now());这条语句使用的是statement还是row?

  • 答案是使用statement,可能你会认为如果使用statement那么从库调用now是不是就和主库不一样,但是这里有个好处就是能够直接SET TIMESTAMP=1546103491,先设置好这个timestamp那么从库也就能够使用它了。
  • 但是有的就不能这么做,你会发现直接把statement拿出来恢复整个数据库,可能有些函数是依赖上下文等,比如时间,随机数,这些都会造成最后恢复的不正确。

循环复制问题

这个问题就是双M结构的问题,因为两个库互为主从,导致的问题就是可能会有重复的binlog执行。比如A更新一条记录发送binlog给B,但是B更新之后也写binlog发送给A。那么要如何避免?

  • 两个库的server id不同
  • 备库获取binlog重放,生成server id和原binlog相同的。
  • 收到主库发来的binlog检查server id是否和自己相同,相同就不执行了。

问题

什么情况会造成循环复制

  • 执行事务之后修改了server id,导致备库认为这是它的binlog然后发送给主库,主库就发现server id不同又执行,又把server id改为备库的。

第25讲:MySQL是怎么保证高可用的?

主备延迟

同步延迟,和数据同步相关的三个时间点

  • 主库A完成事务写入binlog是T1
  • 传给备库B,备库B接收完这个binlog的时刻是T2
  • 备库B执行完成事务,这个时刻是T3

主备延迟指的就是T3-T1

seconds_behind_master也就是备库的一个status用来记录主库和自己的一个延迟,计算方式

  • 每个事务的binlog都有一个时间,记录主库写入时间

  • 备库取出这个时间和当前时间计算差值就是seconds_behind_master

  • 如果主库设置系统时间不一致,备库能够通过SELECT UNIX_TIMESTAMP() 函数进行对比,并且减去差值保持时间的准确

主备延迟最大的问题就是备库消费relay日志比主库写入binlog日志要慢。对于T2和T1的速度是非常快的,关键就是T3.

主备延迟的来源

1.有些部署条件下,备库所在机器的性能要比主库所在的机器性能差。

把备库放到一台机器,但是主库放到多台机器,问题就是多个备库可能会产生争夺主机资源来读取主库送过来的binlog。但是现在基本使用对称部署,也就是主库和备库都是使用性能差不多的机器。

2.但是,做了对称部署以后,还可能会有延迟。这是为什么呢?

从库的查询压力大

  • 可以使用一主多从进行处理分担读压力
  • binlog输出到外部系统,让别的系统分担读压力

3.采用了一主多从,保证备库的压力不会超过主库,还有什么情况可能导致主备延迟 吗?

大事务场景

  • 如果一个事务执行时间很长就会导致主备延迟,假如要删除百万条数据,延时就会影响整个业务,所以通常是晚上处理,而且要分批删除。
  • 大表 DDL

4.如果主库上也不做大事务了,还有什么原因会导致主备延迟吗?

  • 备库并行复制能力不行。

可靠性优先策略

双M结构从状态1到状态2的转换

  1. 判断备库B的seconds_behind_master如果小于某个值继续,不然就要重试这一步
  2. 把主库A改为只读,readonly=true
  3. 判断B的seconds_behind_master变为0
  4. B改为可读写,readonly=false
  5. 业务请求切换到B

SBM就是seconds_behinds_master

  • 这里从步骤2开始A和B都是只读导致系统不可用,这就是为什么需要判断SBM足够小才进行切换,而且步骤3需要一点时间,可能会比较慢。
  • 但是保证了主备数据的一致性。

可用性优先策略

把上面4,5放到最前面。

  • 那么就没有不可用的时间了。
  • 但是牺牲的就是主备数据的一致性。
> CREATE TABLE `t` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `c` int(11) unsigned DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(c) values(1),(2),(3);

insert into t(c) values(4);
insert into t(c) values(5);

分析切换流程

  • A在步骤2完成插入4之后开始切换。
  • 步骤3由于主备延迟是5s,所以没来的及完成relay log转存insert 4(因为是一个大事务,除了insert4还有前面操作别的表)的动作,就先执行insert 5了。
  • 步骤4中B插入了(4,5),并且把binlog发送给A。
  • 步骤5的时候B才开始执行insert 4的中转日志,所以插入了(5,4).这个插入语句通过binlog交给了A,A也插入5.
  • 所以可以发现A和B之所以出现数据不一致,就是因为插入的语句顺序改变了,改变的原因就是因为主备延迟,由于直接修改B为主库,导致上一次的relay log没有写完,就要插入一条新的语句,这个时候A和B插入语句的顺序就反了。

  • 但是改一下格式binlog_format=row
  • 使用row的时候可以记录行的具体信息,所以数据不一致问题可以更好地被发现。
  • 所以还是建议使用可靠性优先策略。通过两个只读一段时间,防止主备不一致。之所以要等待SMB为0原因是如果不等,那么就可能导致B接收业务,但是之前的事务没有解决导致上面的插入错序问题。

有没有哪种情况数据的可用性优先级更高呢?

A宕机,只能是B备库切换为主库。虽然延迟很大,但是仍然需要先切换,保持可用,然后让中转日志慢慢恢复数据,这段时间造成数据不一致,而且客户端发现B是暂时丢失数据的状态。

第26讲:备库为什么会延迟好几个小时?

  • 主库写入binlog能力和备库的并行复制能力很重要,如果他们速度不匹配就会导致延迟能够一直累加。
  • 对于客户端写入到主库的并行度是远远大于sql_thread写入到备库的速度,这也就是为什么主库和备库之间存在这么大的延迟问题。那么怎么解决?

  • 下面的coordinator就是之前的sql_thread现在不会写了,而是读取所有的中转日志和分发任务到各个线程进行处理。分配给work,slave_parallel_workers 决定worker个数,通常分配8-16个,剩下还是要用于查询的。
  • 是否能用轮询方式分配事务?不行,因为两个事务之间可能会有顺序排列,如果不按照顺序执行可能导致数据库的不一致问题。
  • 同一个事务多个更新是否能够分配给不同worker?不行因为要保证事务的完整性,如果t1表改给w1处理,t2给w2处理,t1完成修改刚好查询发现更新一半,那么破坏了事务逻辑的隔离性。

分发事务要满足要求

  1. 不能更新覆盖,修改同一行记录交给同一个worker
  2. 同一个事务不能拆分。

MySQL 5.5 版本的并行复制策略

按表分发策略

  • 如果两个事务是更新两个表,而且互不关联那么就可以分发不同的worker,但是如果表之间有关联那么仍然是不可以分配给两个worker的。
  • worker对应一个hash表,存储库名.表名,value是多少个事务在修改这张表。

  • 事务T的分配流程
  1. 事务T要修改t1,但是worker_1队列有事务在修改表t1,事务和队列某个事务修改同一个表的数据,这种就是T和worker_1冲突。
  2. 顺序判断发现worker_2也冲突
  3. 如果冲突worker多于一个那么进入到coordinator等待
  4. 每个worker执行并且修改hash table,如果事务执行完就从table去掉。worker_2去掉t3
  5. 这个时候coordinator发现事务T只和worker_1发生冲突,所以可以分配给worker_1
  6. 继续分配事务。

冲突关系的三种情况

  1. 和所有worker不冲突,分配给空闲worker
  2. 和一个worker冲突,分配给这个worker
  3. 和多于1个worker冲突,那么就要继续阻塞。

相当于一个事务要分配给worker,如果worker有修改相同的表,为了防止更新覆盖,所以要分配给这个worker。如果事务发现多个worker和自己冲突,为了防止更新覆盖所以只能阻塞。

按行分发策略

  • binlog必须是row

  • 而且两个并行事务没有更新同一行。

  • 现在的key是库+名表名+唯一键的值,value还是多少个事务。

CREATE TABLE `t1` (
 `id` int(11) NOT NULL,
 `a` int(11) DEFAULT NULL,
 `b` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB;
insert into t1 values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5);

  • 上面这个场景的问题就是B无法修改的原因就是可能id=1的还没修改完,导致唯一键冲突。前提是分配到worker并且是B先执行。

  • 所以key应该改为库名 + 表名 + 索引 a 的名字 +a 的值

  • 所以对于hash表来说这个语句需要分析三个项

    • key=db1+t1+id+2,value=2
    • key=db1+t1+a+2,value=1
    • key=db1+t1+a+1,value=1

    然后就会发现a=1这里冲突,所以只能加入到A的worker上去。等待A执行完再到B

  • 行策略消耗更多计算资源

  1. binlog解析出表名,主键,唯一索引的值,binlog还要是row
  2. 表必须有主键
  3. 不能有外键。

但是如果操作行很多的大事务

  1. 耗费内存
  2. 耗费CPU

所以如果并行度超过阈值就要转换为单线程

  1. coordinator先hold住事务
  2. 等待worker执行完,变成空队列
  3. coordinator直接执行事务
  4. 恢复并行。

MySQL 5.6 版本的并行复制策略

  • 直接用库为单位的分配,但是表都存在一个库就没什么用了

MariaDB 的并行复制策略

  1. 同一组提交的事务,不会修改同一行
  2. 主库并行的事务,备库也可以并行执行

mariaDB这么做

  1. 每组提交都会有一个commit_id并且会自增
  2. commit_id写入到binlog中。
  3. 相同commit_id的事务分发到多个worker中执行
  4. 然后取下一批。

原因就是假设同一组能够修改同一行,但是反例就是修改同一行一定有一个会先被阻塞,所以同一组提交那么只能是修改不同行的事务。

  • 主库的执行情况

  • 备库执行情况,很明显可以看到每次都是一组执行完,才能到下一组。如果其中有一个大事务,就导致同一组事务需要等待这个大事务执行完。所以没有真正模拟主库的并发度。

MySQL 5.7 的并行复制策略

slave-parallel-type控制执行策略

  • 配置为 DATABASE,按库并行执行
  • 配置LOGICAL_CLOCK,也就是MariaDB的执行方式。但是做了优化

同时处于“执行状态”的所有事务,是不是可以并行?

  • 当然是不行的
  • 对于MariaDB的核心就是事务都可以提交,也就是不会造成锁冲突问题。

  • 其实到redo log prepare阶段也是说明组事务已经解决锁冲突检测

所以5.7的复制策略

  1. 同时处于prepare的事务,在备库可以并行执行
  2. 处于prepare和commit之间的事务也可以并行执行。

binlog_group_commit_sync_delay:延迟多少微妙才fsync到磁盘

binlog_group_commit_sync_no_delay_count:累积多少次才调用fsync。

  • 拉长write之后fsync的时间,但是能够减少fsync的次数。
  • 制造更多同时处于prepare事务,相当于增加了备库复制的并行度。

MySQL 5.7.22 的并行复制策略

基于writeset的并行复制

binlog-transaction-dependency-tracking参数控制并行策略

  • COMMIT_ORDER:同时进入prepare和commit来判断是否可以并行
  • WRITESET:计算事务的更新行的hash,如果两个事务更新行的hash没有交集说明可以并行
  • WRITESET_SESSION:保证事务的执行顺序。

hash值是通过库名 + 表名 + 索引名 + 值来计算的。

优势

  1. writeset主库生成后直接写入到binlog,备库就不需要解析了
  2. 不需要扫描binlog所有的事务,可以通过writeset的交集就能够判断是不是可以分配。
  3. 分发策略不依赖binlog,所以statement格式也可以使用。之前需要扫描事务状态,mariaDB需要扫描事务是不是在同一个组,5.5版本需要扫描行并且去到worker判断是不是冲突。但是这里只需要在writeset中记录更新行,并且在判断是不是可以并行只需要判断writeset有没有交集就可以了。

问题

你为了更快地让备库追上主库,要开并行复制。在 binlog-transactiondependency-tracking 参数的 COMMIT_ORDER、WRITESET 和 WRITE_SESSION 这三 个取值中,你会选择哪一个呢?

  • 选择WRITESET
  • 对于commit_order来说如果每个commit_id不同导致的就是单线程模式,相当于就是没有组事务
  • WRITESET_SESSION如果要求事务是顺序执行,那么并行事务就无效了。

总结:主从复制的策略,包括行和表,行很明显由于需要row格式,并且还需要注意唯一索引所以导致的问题就是binlog占用的内存很大,而且CPU计算成本也很高。另一种方案就是maria复制,这种利用同时提交组事务,并行地执行,但是问题就是无法模拟主库并发度,也就是run和commit在从库必须是同时执行完才能换下一组。对于现在的5.7来创新了,①行和表②组提交并行,到现在的writeset实际上就是根据每行数据执行一个hash函数得到hash值,也就是每行数据有自己的hash,如果事务要修改,那么自然就会加上这个hash到writeset中,好处很明显,判断是否能执行只需要判断writeset是否有交集,没有那么就可以并行执行。否则只能等待。①需要通过hash表计算,遍历行②需要判断每个事务的状态,并且判断是不是同一组事务关键是不是同时commit或者是prepare。但是③不需要扫描,能够直接判断是否可以并行执行。

第27讲: 主库出问题了,从库怎么办?

基于位点的主备切换

  • 如果下面的主库A出现了故障,这个时候从库都需要修改对应的主库为A1
  • master_log_name和master_log_pos是修改主库的时候必要的参数,那么如何设置?
CHANGE MASTER TO 
MASTER_HOST=$host_name 
MASTER_PORT=$port 
MASTER_USER=$user_name 
MASTER_PASSWORD=$password 
MASTER_LOG_FILE=$master_log_name 
MASTER_LOG_POS=$master_log_pos 

  • 第一个要找的是位点,但是这个同步位点不是准确的。
  1. 等待主库A1把中转日志完成
  2. 在A1中执行show master status,获取file和position
  3. 获取原主库A故障的时刻T
  4. 使用mybinlog工具解析A1的file,得到T时刻的位点。

以上是关于《MySQL实战45讲》(20-35讲)学习总结的主要内容,如果未能解决你的问题,请参考以下文章

MySQL实战45讲 第一课总结&My SQL VS SQL Server

每日推荐MySQL实战45讲

极客时间MySQL实战45讲(完结)百度网盘

精选好课分享MySQL实战45讲完结

Mysql实战45讲学习详情----一条SQL更新语句是如何执行的?

《MySQL45讲》基础篇总结