mysql事务和日志讲解
本文讲解的是mysql的事务和日志之间的关联,并有部分MVCC的讲解。有和理解错的话请指正。
事务
事务的特性(ACID):
- 原子性(A):在同一个事务中存在的n多条sql语句,要么全部成功,要么全部失败。如果有部分sql失败了,则所有sql结果通过undolog回滚日志来恢复初始状态
- 一致性(C):事务执行之前和执行之后数据库均处于一致性状态。它是由 原子性、隔离性、持久性来实现的
- 隔离性(I):多个事务操作同一张表的时候,每个事务互不干扰。锁、MVCC多版本并发控制来实现 数据安全问题用RR
- 持久性(D):当事务被提交以后数据库中的数据就会被写入到磁盘中,数据改变就是永久性的了。通过redolog来实现
日志
日志类型:
- undolog:回滚日志 原子性和MVCC的支撑
- redolog:前滚日志 将数据持久化
- binlog: 主从复制
- relaylog:中继日志 在进行主从复制时 从机里先存储的日志
- slowlog:慢查询日志 超过指定时长的sql语句
- errorlog:错误日志
undolog、redolog归属于Innodb 存储引擎
binlog、relaylog、slowlog、errorlog归属于 mysqlserver
binlog、relaylog(主从复制)
- 多线程进行操作master数据库,将数据存放到数据库中,并生成binlog记录,主从数据库要保证数据的同步一致性,主从直接会由IO Thread线程读取 master的binlog同步到slave内,并生成relaylog,然后由SQL Thread线程回放数据,保证主从两个数据库数据一致
redolog(两阶段提交)
- redolog 是顺序读写机制,数据库数据属于随机读写机制,因为顺序读写效率远高于随机读写,所以操作数据库时,先将数据存到redolog中记录,然后再从redolog中放到数据库磁盘中(采用了WAL技术)。如果写到数据库的时候断电或宕机之类的,恢复后会从redolog中继续同步;如果写到redolog时出现断电或宕机则数据丢失。
WAL:write ahead log 预写日志
两段提交
- 为了保证redolog和binlog的数据同步一致性,则采用了两段提交的方式。
数据想写入redolog中其状态为perpare状态(此状态的数据不能进行恢复),然后将数据写到binlog中,最后将redolog的数据提交。 如果再步骤1出现问题,在服务器恢复后检测到redolog有perpare状态的数据,则去binlog中找对应的数据,若未找到则删除redolog的此数据记录,在binlog中找到了,则将redolog中此数据commit;如果步骤2出现问题,先检测redolog记录,然后再去binlog中找对应的,找到commit,未找到删除。
MVCC (多版本并发控制)
MVCC 被称为多版本并发控制,它是为了提高数据库并发性能,以不加锁的方式来解决读写操作并发问题。
场景1
场景2
- 当前读:读取当前的数据,即最新的数据(update、delete、insert、select…for update、select … lock in share mode可触发)
- 快照读:读取历史数据(select 可触发)
mysql 存在的并发场景
- 读读 不存在数据安全问题
- 读写 脏读、幻读、不可重复读(MVCC解决的问题)
- 写写 更新丢失
MVCC的组成
-
隐藏字段 数据库的表在定义的时候除了我们自己声明的字段之外,还会有其他隐藏的字段
- DB_TRX_ID 最近事务的id。创建这条记录或者最新更改这条数据的事务id
- DB_ROLL_PRT 回滚指针,指向了记录上一个版本
- DB_ROW_ID 隐藏主键,如果表没有主键,则会生成一个6字节的rowid
-
undolog 在进行 insert、delete、update操作时生成的方便回滚到历史数据的日志
因此undolog会形成一个链表,链首是最新的旧记录,链尾是最旧的旧记录,
当undolog的日志记录达到一定值时,后台有一个purge线程进行删除操作 -
readview 读视图,在事务进行快照读的时候产生的读视图,保存的并不是实际的数据,而是事务的相关信息。
- trx_list 当前系统活跃的事务id列表
- up_limit_id 活跃列表中事务id最小的值
- low_limit_id 系统尚未分配的下一个事务id
案例分析
-
案例1图案:
readview视图字段对照
-
案例二图案:
readview视图字段对照
经过两次案例结果发现,readview值是一样的,可见性算法也是一样的,但是最后的结果不一致,可以猜测案例二的蓝色的readview和橙色的readview是否是一样的?
根据可见性算法判断后发现和案例二的结果一致,因此可以确定猜测是对的。
因此可以得出一个结论:
第一次查询的时候生成了readview,第二次查询的时候沿用了第一次的readview
(RC:不可重复读 RR:幻读)
1、如果是RC(读后提交)隔离级别,那么每次进行快照读的时候都会产生新的readview
2、如果是RR(可重复读)隔离级别,那么只有在当前事务进行第一次快照读的时候产生readview,之后的都是沿用第一次的readview
可见性算法
1、首先比较DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断
2、然后判断DB_TRX_ID >= low_limit_id,如果大于等于则表示DB_TRX_ID 所在的记录在readview生成后才出现,那么对于当前事务不可见,如果小于进去下一个判断
3、然后判断DB_TRX_ID是否在活跃事务中,如果在 则表示在readview生成时刻,这个事务还在活跃状态,还没有commit,修改的数据当前事务也看不到;如果不在,则说明这个事务在readview生成之前就开始commit,那么修改的结果是可见的
幻读
其根本原因是:当前读和快照读一起使用,如果同一个事务中只有快照读则不会出现幻读问题。
MVCC解决不了幻读问题,幻读是加锁解决的
比如:select * from table for update;