站长资讯网
最全最丰富的资讯网站

一起聊聊MySQL事务与MVCC如何实现的隔离级别

本篇文章给大家带来了关于MySQL事务与MVCC如何实现的隔离级别的相关问题,希望对大家有帮助。

一起聊聊MySQL事务与MVCC如何实现的隔离级别

数据库事务介绍

事务的四大特性(ACID)

  1. 原子性(atomicity): 事务的最小工作单元,要么全成功,要么全失败。

  2. 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏。

  3. 隔离性(isolation): 不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。

  4. 持久性(durability): 事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。

事务的隔离级别

读未提交(Read UnCommitted/RU)

又称为脏读,一个事务可以读取到另一个事务未提交的数据。这种隔离级别岁最不安全的一种,因为未提交的事务是存在回滚的情况。

读已提交(Read Committed/RC)

又称为不可重复读,一个事务因为读取到另一个事务已提交的修改数据,导致在当前事务的不同时间读取同一条数据获取的结果不一致。

举个例子,在下面的例子中就会发现SessionA在一个事务期间两次查询的数据不一样。原因就是在于当前隔离级别为 RC,SessionA的事务可以读取到SessionB提交的最新数据。

发生时间 SessionA SessionB
1 begin;
2 select * from user where id=1;(张三)
3 update user set name='李四' where id=1;(默认隐式提交事务)
4 select * from user where id=1;(李四)
5 update user set name='王二' where id=1;(默认隐式提交事务)
6 select * from user where id=1;(王二)

可重复读(Repeatable Read/RR)

又称为幻读,一个事物读可以读取到其他事务提交的数据,但是在RR隔离级别下,当前读取此条数据只可读取一次,在当前事务中,不论读取多少次,数据任然是第一次读取的值,不会因为在第一次读取之后,其他事务再修改提交此数据而产生改变。因此也成为幻读,因为读出来的数据并不一定就是最新的数据。

举个例子:在SessionA中第一次读取数据时,后续其他事务修改提交数据,不会再影响到SessionA读取的数据值。此为可重复读

发生时间 SessionA SessionB
1 begin;
2 select * from user where id=1;(张三)
3 update user set name='李四' where id=1; (默认隐式提交事务)
4 select * from user where id=1;(张三)
5 update user set name='王二' where id=1;(默认隐式提交事务)
6 select * from user where id=1;(张三)

串行化(Serializable)

所有的数据库的读或者写操作都为串行执行,当前隔离级别下只支持单个请求同时执行,所有的操作都需要队列执行。所以种隔离级别下所有的数据是最稳定的,但是性能也是最差的。数据库的锁实现就是这种隔离级别的更小粒度版本。

发生时间 SessionA SessionB
1 begin;
2 begin;
3 update user set name='李四' where id=1;
4 select * from user where id=1;(等待、wait)
5 commit;
6 select * from user where id=1;(李四)

事务和MVCC原理

不同事务同时操作同一条数据产生的问题

示例:

发生时间 SessionA SessionB
1 begin;
2 begin;
3 查询余额 = 1000元
4 查询余额 = 1000元
5 存入金额 100元,修改余额为 1100元
6 取出现金100元,此时修改余额为900元
8 提交事务(余额=1100)
9 提交事务(余额=900)
发生时间 SessionA SessionB
1 begin;
2 begin;
3 查询余额 = 1000元
4 查询余额 = 1000元
5 存入金额 100元,修改余额为 1100元
6 取出现金100元,此时修改余额为900元
8 提交事务(余额=1100)
9 撤销事务(余额恢复为1000元)

上面的两种情况就是对于一条数据,多个事务同时操作可能会产生的问题,会出现某个事务的操作被覆盖而导致数据丢失。

LBCC 解决数据丢失

LBCC,基于锁的并发控制,Lock Based Concurrency Control。

使用锁的机制,在当前事务需要对数据修改时,将当前事务加上锁,同一个时间只允许一条事务修改当前数据,其他事务必须等待锁释放之后才可以操作。

MVCC 解决数据丢失

MVCC,多版本的并发控制,Multi-Version Concurrency Control。

使用版本来控制并发情况下的数据问题,在B事务开始修改账户且事务未提交时,当A事务需要读取账户余额时,此时会读取到B事务修改操作之前的账户余额的副本数据,但是如果A事务需要修改账户余额数据就必须要等待B事务提交事务。

MVCC使得数据库读不会对数据加锁,普通的SELECT请求不会加锁,提高了数据库的并发处理能力。借助MVCC,数据库可以实现READ COMMITTED,REPEATABLE READ等隔离级别,用户可以查看当前数据的前一个或者前几个历史版本,保证了ACID中的I特性(隔离性)。

InnoDB的MVCC实现逻辑

InnoDB存储引擎保存的MVCC的数据

InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。一个保存了行的事务ID(DB_TRX_ID),一个保存了行的回滚指针(DB_ROLL_PT)。每开始一个新的事务,都会自动递增产 生一个新的事务id。事务开始时刻的会把事务id放到当前事务影响的行事务id中,当查询时需要用当前事务id和每行记录的事务id进行比较。

下面看一下在REPEATABLE READ隔离级别下,MVCC具体是如何操作的。

SELECT

InnoDB 会根据以下两个条件检查每行记录:

  1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的事务编号小于或等于当前事务的事务编号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。

  2. 删除的行要事务ID判断,读取到事务开始之前状态的版本,只有符合上述两个条件的记录,才能返回作为查询结果。

INSERT

InnoDB为新插入的每一行保存当前事务编号作为行版本号。

DELETE

InnoDB为删除的每一行保存当前事务编号作为行删除标识。

UPDATE

InnoDB为插入一行新记录,保存当前事务编号作为行版本号,同时保存当前事务编号到原来的行作为行删除标识。

保存这两个额外事务编号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做

赞(0)
分享到: 更多 (0)