mysql 事务笔记

143 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

前言

mysql的事务隔离是个老生常谈的话题了,之前一直也没有系统的去学习这个东西。最近项目有空闲了,打算好好学习一下,整理成笔记,以供参考。

ACID

首先我们要理解这个概念。ACID是原子性、一致性、隔离性、持久性的缩写。这四个特点与数据库的事务概念密切相关。

原子性

指的是在SQL会话中事务是最小单元,是无法再分割的。比如我们在执行update前开启事务,执行后提交事务。那么要么update执行成功,要么失败回滚。不会存在比如原来 update table set price = 10,age =20 where id = 9 出现price字段update成功而age字段update失败。

一致性

指的是数据库在每次提交或回滚以及事务进行期间。如果我们操作了多张表,那么要么所有修改的表都更新,不存在只更新部分表,新的值和旧的值混合的结果。

隔离性

指的是数据库在锁机制下,不同的事务之间在操作进行中不能相互干扰或查看彼此未提交的数据。

持久性

指的是一旦我们的事务提交成功了,那么这个结果是持久的。不会应为电源故障、系统崩溃等情况所影响修改。

事务的隔离级别

mysql中拥有四个事务的隔离级别。分别是1、读未提交2、读已提交3、可重复读4、可序列化。默认的事务隔离级别是可重复读。

我们可以通过set transaction语句来调整数据库的事务隔离级别,验证我们的操作

读未提交

read uncommitted是最低的隔离级别,会产生脏读的问题。首先我们创建一条数据

idnameamount
1张三100

然后我们分别开启两个数据库连接执行开启事务的命令。start transaction; 在事务1中我们更新张三的余额为200,然后还未提交事务;

update account set amount = 200 where name = '张三'; 然后我们在事务2中读取张三的余额

select * from account where name = '张三'; 发现变为了200。

idnameamount
1张三200

假设此时事务2判断张三的余额充足,购买了190的商品,

update account set amount = amount - 190 where name = '张三';

那么现在读取到张三的余额就是10。然后事务2提交事务,即购买商品流程走完。此时事务1发生了回滚。那么余额就会发生异常,本来张三的余额并不足以支付190,但是由于出现脏读,读取了未提交的数据,执行了update。

读已提交

读已提交指的是即使是在同一个事务中,每次的读取都会使用自己的最新快照。在当前的事务隔离级别是读已提交的情况下,同一个事务内多次读取,可能会读取到不一样的结果,因为每次查询都会读取最新的快照。

我们设定事务隔离级别为读已提交。分别开启两个事务,首先我们在表中有这样的数据数据。 两个事务查询可以返回一样的结果。

idnameamount
1张三200
2李四100

事务一执行一次更新李四的amount的操作,设置为500,然后commit事务。这个时候事务2查询(事务未提交)返回了,

idnameamount
1张三200
2李四500

也就是在读已提交情况下会出现不可重复读。即在一个事务中,两次相同的查询,返回的结果缺不一样。

可重复读

顾名思义它和读已提交的区别在于同一事务中的一致读取读取由第一次读取建立的快照,而不是每次select都会读取最新快照。那么这种隔离级别下,事务内同一个查询执行多次返回的结果也是相同的。但是如果在一个事务中执行两次相同查询中间,另一个事务插入新行或更新行,就会使两次读取返回的记录数不同。

查询网上资料得到的原因是行锁只能锁住行,但是新插入的行更新的是记录的间隙并没有被锁住。比如在事务一中where条件末尾增加for update,对查询增加行锁,不允许别的事务读写。但是事务二新增了一条可以被事务一查询匹配的记录,在提交事务一后,事务二再次执行查询,会发现多返回了一条记录。

可序列化

这个是事务隔离的最高级别,安全性最好但是并发性能最差。它意味着在当前事务下,查询第一次后,无论别的事务进行何种读写都不会影响当前事务,即再进行相同查询还是可以得到一样的结果。当autocommit关闭时,所以读操作会加上for share,其它事务允许读取但不允许写入。

以上就是一部分对于mysql数据库事务的笔记,事务的隔离和锁机制息息相关,所以后面还会再学习下锁的知识帮助更好理解事务。