答疑文章(一):日志与索引相关问题读后总结

日志相关的问题:
mysql 利用 binlog与redo log做 崩溃恢复。mysql利用了二阶段提交维护了主备数据一致性
(PS:对mysql的崩溃恢复,以及异常数据恢复不是一样的情况需要注意)

两阶段提交:
取ID=2这一行 =》判断数据页是否在内存中,不在就从磁盘中读入内存中返回数据。=》将这一行的c值加1,写入新行=》新行更新到内存=》写入redo log处于prepare阶段。=》写入binlog=》提交事务redo log 处于commit状态。

在两阶段提交的过程中,mysql异常重启后产生的情况:

  1. 处于redo log prepare状态,并在写binlog之前。redo log 状态没有commit,以及bin log 没有写。在崩溃恢复的时候,事务会回滚。bin log 没有写,所以也不会传到备库上。
    2.binlog写完后,redo log状态commit 之前。这里提供一下崩溃恢复的判断规则
  2. redo log中的事务状态已经是commit,则直接提交
  3. redo log中的事务只有完整的prepare,则判断对应的binlog事务是否完整
  4. 完整则提交事务
  5. 否则,回滚事务

Mysq怎么知道binlog是完整的?
一个事务的binlog是有完整格式:
statement格式中,每一个事务最后都会有一个commit
row格式中,每个事务最后都会有一个XID event
mysql5.6以后 用binlog-checksum参数检查binlog是否完整。

redo log 与bin log是如何联系起来的?
redolog与binlog有一个公共字段,XID。
恢复崩溃时,会顺序扫描redolog。
1.遇到既有prepare与commit的redolog直接提交
2. 遇到只有prepare的事务,拿着该事务的XID去binlog中查对应的事务是否完整。

处于prepare的redo与完整的binlog,就可以重启恢复,mysql为什么要这么设计?
这时候binlog已经写入,所以就可能同步到从库中,所以主库恢复的时候需要将事务提交。这样就保证了主从数据一致。

为什么还需要两阶段提交,保证redolog与binlog数据完整,做崩溃异常
两阶段提交是所有分布式系统的经典数据一致性问题。
而mysql的redo log写完之后就不能回滚了。所以如果不使用两阶段提交的话,redolog写成功,但是binlog写失败了,由于redolog的事务无法回滚,导致主从数据不一致。

不引入两个日志,就可以不考虑两阶段提交的问题。只用binlog来支持崩溃恢复,又能支持归档,不就可以了?
首先innodb一开始是以插件的身份接入mysql的,binlog没有崩溃恢复的能力。
binlog无法现实数据页的恢复。

那能不能反过来,只要redo log,不要binlog?
binlog是mysql高可用的基础组件,binlog复制,以及归档等等作用是redo log无法实现的。

redo log 一般设置多大?
redo log 如果设置太小的话,会导致经常刷redo log,刷脏页,影响磁盘性能,并且不能发挥WAL的最大的作用。一般几T硬盘直接设4个redo log,一个文件一个G大小。

正常运行的实例,数据写入后端最终落盘,是从redolog更新过来的还是从buffer pool更新过来的?
脏页被刷到磁盘的过程与redolog没有关系。redolog中没有记录有关数据页的数据,根本没有能力修改磁盘数据页。

  1. 正常运行的实例,在之前提过的四种情况下,1.redolog满了,2.内存不足,3.mysql认为系统空闲,4.mysql重启时触发刷脏页。将内存页数据刷入磁盘。redolog中记录的更改记录,并没有源数据。
    2.在崩溃恢复的场景中,innodb判定一个数据页在崩溃中可能丢失的更新,就会将它读入内存,从redolog读取更新操作,更新该数据页使其成为脏页。回到了第一种情况。redo log 与数据落盘没有直接关系。

redo log buffer是什么?是先修改内存,还是先写redo log 文件

redo log buffer是一块内存,用来先存redo log的。一个事务中,更新语句执行完,先更新内存,在将redo log 写入 redo log buffer 中,到最后commit的再写入redo log 文件。
事务执行过程中一般不会中主动刷盘,以减少不必要的IO消耗。但是也会有被动刷盘的情况发生比如内存不够,有其他事务提交的情况。

业务设计问题
背景:用户相互关注自动成为好友,有两张表一张liker表记录了谁关注谁,另一张表friend记录哪些用户成为好友。用户关注流程,当用户A关注B的时候,先查看B是否关注了A,是则AB自动成为好友,向friend插入一条数据,在liker表中插入A关注B的记录,否则只在liker表中插入A关注B的记录。
问题在并发相互关注的时候,会引起双方无法成为好友的情况。原因是在双方查询的时候对方关注的记录还没有与插入,导致形成了只向liker表中插入了AB相互关注了对方,但是friend表中并没有记录AB成为好友。
根本原因没有记录所以你想用锁也无用武之地。向liker表中加一个字段relation ,1表示A关注B,2表示B关注A,3表示相互关注。

CREATE TABLE like (
id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11) NOT NULL,
liker_id int(11) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_user_id_liker_id (user_id,liker_id)
) ENGINE=InnoDB;

CREATE TABLE friend (
idint(11) NOT NULL AUTO_INCREMENT,friend_1_idint(11) NOT NULL,firned_2_idint(11) NOT NULL, UNIQUE KEYuk_friend(friend_1_id,firned_2_id) PRIMARY KEY (id`)
) ENGINE=InnoDB;

mysql> begin; /* 启动事务 /
insert into like(user_id, liker_id, relation_ship) values(A, B, 1) on duplicate key update relation_ship=relation_ship | 1;
select relation_ship from like where user_id=A and liker_id=B;
/
代码中判断返回的 relation_ship,
如果是 1,事务结束,执行 commit
如果是 3,则执行下面这两个语句:
*/
insert ignore into friend(friend_1_id, friend_2_id) values(A,B);
commit;

mysql> begin; /* 启动事务 /
insert into like(user_id, liker_id, relation_ship) values(B, A, 2) on duplicate key update relation_ship=relation_ship | 2;
select relation_ship from like where user_id=B and liker_id=A;
/
代码中判断返回的 relation_ship,
如果是 2,事务结束,执行 commit
如果是 3,则执行下面这两个语句:
*/
insert ignore into friend(friend_1_id, friend_2_id) values(B,A);
commit;

这个设计里,liker表中数据保证user_id <liker_id,不管A关注B,还是B关注A,都一条记录,这样就可以用锁锁定记录。
|与 insert ignore,是保证了重复调用时的幂等性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值