MYSQL事务的实现---隔离性实现原理

10 篇文章 0 订阅
9 篇文章 0 订阅

背景介绍

事务的特点是ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性).

隔离性的意思就是两个事务各执行各的。

谈到隔离性必须提数据库的四个问题和解决对应问题的隔离级别,

场景/问题                                                                   解决方案/隔离级别

                                                                                     读未提交(read uncommitted )

脏读(dirty read)                                                       读提交(read committed)

不可重复读(non-repeatable read)                           可重复读(repeatable read)

幻读(phantom read)                                                串行化(serializable )

查看当前数据库的隔离级别

Oracle 默认是读提交

MySQL 是可重复读 

查看当前的隔离级别:

show variables like 'tx_isolation';  5.6之前用这个

show variables like 'transaction_isolation';  5.7及以后

设置MYSQL的隔离级别

set session transaction isolation level read uncommitted;

set session transaction isolation level read committed;

set session transaction isolation level repeatable read;

set session transaction isolation level serializable;

读提交和当前读又是什么意思

当前如果有一个A事务要更新某一行,并且另外一个B事务拥有这一行的行锁,那当前A事务就会被锁住,等等到B事务提交才能继续。

等B事务已经提交了,理论上B事务的提交时间是A启动之后的,应该看不到B的修改(一致性读),但是实际上某些场景,可以读到B的修改。这就是当前读。

一个观察当前读的小实验

0.先确认当前数据库的隔离级别

设置成读提交

set session transaction isolation level read committed;

1. 建表语句

 CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2),(3,3);

2. 执行顺序

ABC

set session transaction isolation level read committed;

start transaction with consistent snapshot ;

select k from t where id =1;

set session transaction isolation level read committed;

start transaction with consistent snapshot ;

update t set k=k+1 where id =1;

update t set k=k+1 where id =1;

select k from t where id =1; //??

select k from t where id =1; //???

commit;

select k from t where id =1;

select k from t where id =1 for update ;

commit;

3. 实验分析

实验完成了吧,说明C 的 commit 在AB读之前,所以对他们都有影响,AB在读的时候都会进行当前读。

另外B的提交后于A的读,所以B的update 对A 不可见。

但是如果 select   for update ; 就会触发当前读。

哈哈哈

可重复读是什么

可重复读是,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据
是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的(这一点读提交级别就能保证了)。

可重复读的使用场景大致就是 类似对账操作,当一个事务开始的时候要获取一个逻辑上不变的静态快照,让这个操作不受操作启动后其他的修改操作的影响。

可重复读的复现和实验观察

把数据的隔离级别设置成为可重复读,把上面的执行流程重新操作一遍,就会有不一样的结果

update t set k=id;

ABC

set session transaction isolation level repeatable read;

start transaction with consistent snapshot ;

set session transaction isolation level repeatable read;

start transaction with consistent snapshot ;

update t set k=k+1 where id =1;

update t set k=k+1 where id =1;

select k from t where id =1; //??

select k from t where id =1; //???

commit;

select k from t where id =1;

select k from t where id =1 for update ;

commit;

可重复读隔离级别的实现逻辑

MYSQL 里面有一个MVCC 多版本并发控制的解决方案。

首先,InnoDB 里面每一个事务都会有一个全局唯一,按顺序单调递增的事务ID  transaction id。

每一行数据是有多个版本的,每次事务更新数据,都有一个数据版本,并关联上本次修改操作的事务ID,计做 row trx_id。

另外,当前事务A启动的时候,会对整个数据库的所有事务进行分类,分别是:

基本原则是,事务启动时已经commit的就可见,还没commit的就不可见。

用逻辑来实现就是:

1. A (id=15)启动时就已经commit过的事务,(<=10 的,和13),肯定可见的。

2. A (id=15)启动时已经启动,但是还没有commit的事务,组成了一个数组,最低点的事务Id叫低水位,最大的叫做高水位。[ 11 ,12,14 ]

3. 如果A查询到的数据的版本在高低水位之间,那就看在不在集合里面,在上面集合里的

A启动之后开始的事务,大于高水位,肯定不可见。(>15的所有事务)

解析: 如果当前A读取到的某行记录上面的有10,11,12,13,14,16,17的更改,那么 10小于低水位11肯定可见;17大于高水位14 肯定不可见;11,12在集合里面,不可见,13不在集合里面,可见。

undo log

那么,如果确定了哪些应该可见那些不可见又怎么加载数据呢:

undo log就是每次数据更新的时候生成的回滚日志,是逻辑日志,可以关联数据的多个版本,用于版本回退。

innoDB在事务提交的时候,先将事务的所有日志写入磁盘(redo log file 和undo log file)。

启动时确定的这个所有事务的分类就是形成了一个稳定的一致性视图 read view。

也就是说,不同时候启动的事务是通过回滚日志来实现的,回滚日志会一直保留到日志对应时间之前的开始的事务都已经全部提交才能进行清理。

所以说长事务不仅会增加锁竞争,还会导致数据库里会有大量的回滚日志 undo log.

MYSQL 5.5之前回滚日志是和数据放在一起的(ibdata文件),有可能数据几十G,回滚段几百G.

当前读是什么

可重复读隔离级别中的可重复读本身是一致性读。

但是就算是可重复读隔离级别,更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)

如果selecte 加锁的话也是当前读。当前读会读到所有已经commit的数据。

select k from t where id=1 lock in share mode; //共享锁

select k from t where id=1 for update; //排他锁

幻读是什么

幻读也在可重复读的隔离级别才会发生。

同一次事务,但是前后两次查询同一范围,后一次看到第一次没有看到的行。

可重复读在普通查询进行快照读,只有在当前读财汇出现幻读。

幻读专门指新插入的行。

怎么解决幻读

幻读之所以会发生是因为 ,行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录。解决幻读就需要使用到 间隙锁。

解决幻读就要用升级到串行化隔离级别,写有写锁,读有读锁,读写冲突时后访问的事务需要等待(前一个事务提交)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL事务实现的原理主要涉及ACID特以及锁机制。 ACID是指事务的四个特:原子(Atomicity)、一致(Consistency)、隔离(Isolation)和持久(Durability)。原子表示事务的所有操作要么全部成功,要么全部失败回滚;一致保证了事务执行前后数据库的完整隔离确保并发执行的事务相互之间是隔离的,互不干扰;持久保证了一旦事务提交,其结果将永久保存在数据库中。 MySQL使用锁机制来实现事务隔离。当多个事务同时对数据库进行读写操作时,为了避免数据的不一致MySQL会使用不同的锁来保证事务隔离MySQL提供两种类型的锁:共享锁(S锁)和排他锁(X锁)。共享锁允许事务进行读操作,但不允许进行写操作;排他锁允许事务进行读写操作。当一个事务需要对某个数据进行修改时,它会先申请排他锁,其他事务需要读取该数据时,需等待排他锁释放。 MySQL中还存在多个隔离级别,包括读未提交、读提交、可重复读和串行化。每个隔离级别会对并发事务的读写操作进行不同程度的锁定控制,从而保证事务隔离。 总结来说,MySQL事务实现原理是通过ACID特和锁机制来保证事务的原子、一致隔离和持久。锁机制通过共享锁和排他锁来控制对数据的读写访问,不同的隔离级别则决定了锁的粒度和控制方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值