mysql——事务

mysql——事务

四大特性

原子性,一致性,隔离性,持久性简称ACID
以转账的例子解释ACID,A向B转1000元

原子性: A扣1000元,B加1000元这两个操作必须全部成功或者全部失败

一致性: 若没有其他人干扰和忽略手续费,转账前后二人总余额不变

隔离性: 多个事务间不可以相互干扰,如在事务1不可以读取到事务2新增或更新的数据

持久性: 一旦转账完成,A扣1000,B加1000就持久化到硬盘里了

概念

保证原子性、隔离性、一致性和持久性的一个或多个数据库操作

使用事务

开启事务: BEGIN; 或 START TRANSACTION;
提交事务: COMMIT;
回滚事务: ROLLBACK;
自动提交: 默认情况下,如果我们不显式的使用START TRANSACTION或者BEGIN语句开启一个事务,那么每一条语句都算是一个独立的事务,这种特性称之为事务的自动提交(相当于mysql帮你COMMIT)

查看是否自动提交:SHOW VARIABLES LIKE ‘autocommit’;

如果要关闭自动提交功能,可以使用下边两种方法之一:

方法一: 显式的的使用START TRANSACTION或者BEGIN语句开启一个事务。这样在本次事务提交或者回
滚前会暂时关闭掉自动提交的功能。

方法二: SET autocommit = OFF; 写入的多条语句就算是属于同一个事务了,直到我们显式的写出COMMIT语句来把这个事务提交掉,或者显式的写出ROLLBACK语句来把这个事务回滚掉

保存点: 就像我们玩游戏时可以设置一些暂存点,死亡后可以到最近设置的暂存点

BEGIN
update user set age = 100 where id = 1
SAVEPOINT p1
update user set age = 200 where id = 1
ROLLBACK TO p1
commit

回滚到p1,最终age=100,如果想要释放保存点:RELEASE SAVEPOINT p1

四大隔离级别

未提交读(READ UNCOMMITED)
一个事务可以读到其他事务还没有提交的数据,出现脏读,不可重复读,幻读

脏读:一个事务可以读到其他事务还没有提交的数据

不可重复读:A事务根据给定条件查出记录,B事务修改了A事务查出的记录,导致A事务再次按相同条件查出的数据不相同

幻读:A事务根据给定条件查出记录,B事务新增了符合A事务查询条件的记录,导致A事务再次按相同条件查出的数据不相同

已提交读(READ COMMITED)
一个事务可以读到其他事务修改提交的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,会出现不可重复读、幻读

可重复读(REPEATABLE READ)
一个事务第一次读过某条记录后,即使其他事务修改了该记录的值并且提交,该事务之后再读该条记录时,读到的仍是第一次读到的值,而不是每次都读到不同的数据,这就是可重复读,这种隔离级别解决了不可重复,但是还是会出现幻读,mysql利用innodb锁可以在这种隔离级别解决幻读

序列化读(SERIALIZABLE)
以上3种隔离级别都允许对同一条记录同时进行读-读、读-写、写-读的并发操作,如果我们不允许读-写、写-读的并发操作,可以使用SERIALIZABLE,这种隔离级别因为对同一条记录的操作都是串行的,所以不会出现脏读、幻读等现象

对于上述四种隔离级别,第一种和最后一种实现较为简单,第一种啥也不干,最后一种串行化处理

主要是已提交读是如何解决脏读和可重复读是如何解决不可重复读的?

在解释上面两个问题前先介绍两个概念版本链和ReadView读视图

版本链

对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列:

trx_id:每次对某条记录进行改动时,都会把对应的事务id赋值给该记录的trx_id

roll_pointer:每次对某条记录进行改动时,这个隐藏列会存一个指针,可以通过这个指针找到该记录修改前的信息

推荐阅读:innodb行格式
在这里插入图片描述

ReadView读视图

ReadView中主要包含4个比较重要的内容:

  1. m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。

  2. min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小
    值。

  3. max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。

  4. creator_trx_id:表示生成该ReadView的事务的事务id。

注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。

有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。

  • 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。

  • 如果被访问版本的trx_id属性值大于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。

  • 如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

有了版本链和ReadView的知识就可以解释下面两个问题了
已提交读隔离级别是如何解决脏读的?
在这里插入图片描述
还是以这张图做解释,张三记录版本链如上图,ReadView的m_ids=[102,101], 即代表版本链的事务102和101是未提交事务,100是已提交事务。

此时若事务101去读取张三的年龄,他去遍历版本链,发现第一个事务未提交,并且此事务不是自己,他就知道18这个年龄我不能拿,接着遍历到了年龄为20的记录,发现此事务就是自己,就查出来年龄=20。

同理,若事务102去查张三的年龄直接第一条记录是自己的事务直接查出来18。

若已提交事务100去查,他遍历版本链式,发现事务102和101都未提交,只能查出来25

综上所述:已提交读是可以隔离未提交数据的

可重复读隔离级别是如何解决不可重复读的?
在已提交读的隔离级别下,设想如下场景:
事务101在ReadView的m_ids=[102,101]的时候读取出age=20,正确

此时事务102提交了更新,ReadView的m_ids更新为[101]

事务101再去读取age,此时ReadView的m_ids=[101],代表事务101未提交,102和100都已提交,这样由于事务102已经提交了,所以读到age=18

这就有问题了,在同一个事务101下两次竟然读取到了不同的数据,这就是不可重复读

那么在可重复读的隔离级别是如何解决的呢?
其实非常简单,在这种隔离级别下,同一个事务只会读取一次ReadView,即最开始进行select的这次,再看看上面提出的场景,事务101两次读取时m_ids都是[102,101],那么不管怎么读取,都是age=20

综上所述:就是已提交读每次读取都会生成ReadView,可重复读只有第一次读取会生成ReadView

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值