0%

Mysql-事务复习

Mysql-行列转换

Mysql-事务复习

四大特性:原子性,一致性,持久性,隔离性。

事务实现

单个事务如何保证事务特性?关键点:1.事务回滚,2.事务结束提交。

  • 事务回滚通过undo log保证,undo log 记录了事务修改前的日志数据。

  • 事务提交通过redo log file保证,事务 commit后,数据会持久化到redo log file(磁盘)中。 redo log file 快满时,持久化undo log中的数据,清空redo log file。

具体的执行情况:

img

redo log file: 是顺序循环存储的日志文件,不像undo log是存储有关联关系的数据。redo log file 达到75%时,异步持久化undo log,redo log file达到90%时,使用同步操作持久化undo log。

单个事务

mysql中用undo log 和redo log file解决持久性问题。

事务进行一半系统崩溃,重启后如何保证事务的特性?

事务并发

多个事务之间产生什么样的影响?

不同的隔离级别下影响不同。

隔离级别

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)RU
不可重复读(read-committed)RC
可重复读(repeatable-read)RR
串行化(serializable)

mysql-mvcc

解决了什么

​ mysql中RR隔离级别实现了,单事务内重复读一条数据,结果不变。

​ mysql中RC隔离级实现了,单事务内不会读取其他事务未提交的数据。

SQL实例

RR隔离级SQL实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# session1
begin;
select * from tb_user where user_id = 1;

user_id user_name pass_word create_time last_edit_time enable_status
1 135**752195 nie16226 2018-07-02 22:39:50 2018-07-02 22:39:50 1

# session2
begin;
update tb_user set pass_word = 123 where user_id = 1;

# session1
select * from tb_user where user_id = 1;#单个事务内查询结果不变

user_id user_name pass_word create_time last_edit_time enable_status
1 135**752195 nie16226 2018-07-02 22:39:50 2018-07-02 22:39:50 1

# session2
commit;

# session1
select * from tb_user where user_id = 1;#单个事务内查询结果不变

user_id user_name pass_word create_time last_edit_time enable_status
1 135**752195 nie16226 2018-07-02 22:39:50 2018-07-02 22:39:50 1

如何保证可重复读:开始事务时拷贝readview。

RC隔离级SQL实例

1
2
# 设置当前会话隔离级别为 读已提交
set session transaction isolation level read COMMITTED;

RU 隔离级SQL实例

1
2
3
4
5
6
7
8
9
10
11
12
13

# session1
set session transaction isolation level READ UNCOMMITTED;# 设置当前会话隔离级别为 读未提交
begin;
update tb_user set pass_word = 12345 where user_id = 1;

# session2
set session transaction isolation level READ UNCOMMITTED;
begin;
select * from tb_user where user_id = 1;#读取到session未提交的数据

user_id user_name pass_word create_time last_edit_time enable_status
1 135**752195 12345 2018-07-02 22:39:50 2018-07-02 22:39:50 1

原理实现

三个关键点:undo log,ReadView,版本号。

事务流程(涉及undo log 和版本号):每个事务以系统版本号为事务id,修改记录行时将事务id记录到隐藏列trx_id.隐藏列roll_pointer指向未修改的记录行。事务开始时copy read_view。

read_view记录当前进行中的事务id, 事务id大的可以看到事务id小的记录。

undo log

聚簇索引记录中了隐藏列trx_id、roll_pointer

版本号
readview
  • 结构:
  • 判断可见性

    • 行记录事务id > readview 最大事务id 不可见,通过回滚指针找上一个记录
    • 行记录事务id < readview 最小事务id 可见
    • 行记录事务id = readview 事务数组中id 不可见
    • 行记录事务id > readview 最小事务id < readview 最大事务id != 事务数组中的id 可见
  • readview建立时机:

    • RR 事务开始时建立。第二次执行select不会重新拷贝readview,事务B提交事务后,事务B的事务id仍存在于事务A的readview中。事务A不会读取到事务B提交的数据。
    • RC在事务中的每一条SQL语句执行时建立。其他事务未提交时,事务id存在于readview的事务数组中,不可见。

幻读

什么是幻读?因为事务B在事务A多次查询中间插入了数据,事务A读取结果不一致。

RR隔离级是否解决了幻读? 解决了。

如何解决的?MVCC+next key

RR下的幻读:

事务A update 全表后,查询到事务B insert的数据。

1
2
3
#session1
begin;
select * from user;
1
2
3
4
id	username	birthday	sex	address
1 123 2019-11-28 男 北京
3 han 0000-00-00 nan bei
4 hxr234 0000-00-00 nan bei
1
2
3
4
5
#session2
insert into user values(5,'zala',0000-00-00,'nan','beijing');
#session1
update user set username = '123' where id > 1;
select * from user;
1
2
3
4
5
id	username	birthday	sex	address
1 zs 2019-11-28 男 北京
3 123 0000-00-00 nan bei
4 123 0000-00-00 nan bei
5 123 0000-00-00 nan beijing

问题

RR为什么单事务读取数据一致?事务开始时拷贝readview。不再更新readview。

RC为什么出现重复读一条数据结果不一致?每一条语句执行时,拷贝readview。

RU为什么不会读取其他事务未提交的数据?其他事务id存在于readview中。select语句执行时 拷贝readview看到了其他事务的id。

RC可处理并发量大于RR?RR增加了间隙锁。