MVCC
查看隔离级别
1 2 3 4 5 6 7
| select @@global.tx_isolation,@@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+
|
默认为RR
(可重复读)
修改隔离级别
1 2 3 4 5 6 7 8
| MariaDB sia@localhost:dev> set global transaction isolation level read committed; Query OK, 0 rows affected Time: 0.001s
MariaDB sia@localhost:dev> set session transaction isolation level read committed; Query OK, 0 rows affected Time: 0.001s
|
现在都应该是RC
了
READ-UNCOMMITTED
读未提交
事务一
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
| MariaDB sia@localhost:dev> select @@global.tx_isolation,@@tx_isolation; +-----------------------+------------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+------------------+ | READ-UNCOMMITTED | READ-UNCOMMITTED | +-----------------------+------------------+ 1 row in set Time: 0.005s MariaDB sia@localhost:dev> select * from `user` MariaDB sia@localhost:dev> begin Query OK, 0 rows affected Time: 0.001s MariaDB sia@localhost:dev> select * from `user`; +----+----------+---------+ | id | name | balance | +----+----------+---------+ | 1 | xiaoming | 0 | +----+----------+---------+ 1 row in set Time: 0.005s MariaDB sia@localhost:dev> update `user` set balance=100 where id=1; Query OK, 1 row affected Time: 0.001s MariaDB sia@localhost:dev>
|
事务二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| MariaDB sia@localhost:dev> begin Query OK, 0 rows affected Time: 0.001s MariaDB sia@localhost:dev> select * from `user`; +----+----------+---------+ | id | name | balance | +----+----------+---------+ | 1 | xiaoming | 0 | +----+----------+---------+ 1 row in set Time: 0.005s MariaDB sia@localhost:dev> select * from `user`; +----+----------+---------+ | id | name | balance | +----+----------+---------+ | 1 | xiaoming | 100 | +----+----------+---------+ 1 row in set Time: 0.004s MariaDB sia@localhost:dev>
|
很显然 事务二读取到了事务一未提交的数据
READ-COMMITTED
左边为事务一右边为事务二 事务二读取到了事务一提交的内容
可能导致脏读
REPEATABLE-READ
SERIALIZABLE
串行化
每个select 都会加上s锁 每个update insert delete 都会加上x 锁
其实际执行流程类似这样
事务问题:
脏读
如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象。
不可重复读
在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象。
幻读
在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象。
不可重复读与幻读的最本质的区别是
不可重复读是对于一条内容多次读取到的内容不一样,幻读是多次读取到的数据的条数不一样
MVCC 实现原理
当前读:会加上锁 next-key lock 行锁和gap锁的集合
1 2 3 4 5
| select * lock in share mode; select * for update update delete insert
|
快照读:
readview
决定返回那一个版本链的内容 对于读已提交的来说,是每个语句都生成一个readview 对于可重复读来说每个事务只生成一个readview
事务开启
1 2
| begin/start transaction --- 只有在执行了第一条命令后才算是开启了事务 start transaction with consistent snapshot ---马上开启了事务
|
Read View 结构
- m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务。
- min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值。
- max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;
- creator_trx_id :指的是创建该 Read View 的事务的事务 id
对于每条记录来说,每个里面都会有两个固定的隐藏字段,要是没有主建的话,也会生成一个虚拟的主键:
隐藏字段:
1 2
| trx_id , roll_pointer ---事务id 回滚指针
|
*undo log
*维护了多个版本链
每一个记录里记录着修改该记录的事务的id,和一个回滚指针,可以根据回滚指针,回滚记录,或者借助readview 执行并发控制。