Mysql事务详解

一、ACID

1、事务及其ACID属性

  • 原子性:事务是一个原子操作单位,其对数据的修改,要么全部执行,要么全部不执行。
  • 一致性:在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到
  • 隔离性:数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
  • 持久性:事务完成之后,它对于数据的修改是永久性的。这意味着事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚、

二、事务的并发问题

  • 脏读:一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。也就是说,读取到了未提交的数据;意味着在事务A中,事务B虽然没有提交,但它任何一条数据变化,事务A都可以看到。

  • 不可重复读:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变或某些记录已经被删除了!比如说, 在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的

  • 幻读:一个事务按相同的查询条件重新读取以前检索过得数据,却发现其他事务插入了满足其查询条件的新数据

不可重复读和幻读的区别应该在于:

不可重复读是主要是说多次读取一条记录, 发现该记录中某些列值被修改过。

幻读是主要是说多次读取一个范围内的记录(包括直接查询所有记录结果或者做聚合统计), 发现结果不一致(标准档案一般指记录增多, 记录的减少应该也算是幻读)

解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁住表

三、事务的隔离机制

(1)命令

修改隔离级别:

set tx_isolation='read-commited/read-uncommited/repeatable-read/serializable'

查看隔离级别:

select @@tx_isolation

(2)事务隔离级别

分为四个隔离级别:未提交读、已提交读、可重复读(MySQL默认)、可序列化

隔离级别 读数据一致性 脏读 不可重复读 幻读
未提交读(read-uncommitted) 最低级别,只能保证不读取物理上损坏的数据
已提交读(read-committed) 语句级
可重复读(repeatable-read) 事务级
可序列化(serializable) 最高级别,事务级

(3)隔离级别示例讲解

查看mysql默认的事务隔离级别为repeatable-read

未提交读

(1)打开一个客户端A,并设置当前事务模式为read uncommitted(未提交读),查询user表的pwd字段

(2)在客户端A事务提交之前,打开另一个客户端B,更新user表的pwd字段

(3)这时,虽然客户端B的事务还没提交,但是客户端A就可以查询到客户端B已经更新的数据了

(4)一旦客户端B的事务以为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据

已提交读

(1)打开一个客户端A,并设置当前事务模式为read committed(未提交读),查询表user的所有记录:

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表user:

(3)这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题:

(4)客户端B的事务提交

(5)客户端A执行与上一步相同的查询,结果 与上一步不一致,即产生了不可重复读的问题

可重复读

(1)打开一个客户端A,并设置当前事务模式为repeatable read,查询表user的所有记录

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表user并提交

(3)在客户端A查询表user的所有记录,与步骤(1)查询结果一致,没有出现不可重复读的问题

(4)重新打开客户端B,插入一条新数据后提交

(5)在客户端A查询表account的所有记录,没有 查出 新增数据,所以没有出现幻读 (疑惑的地方)

可序列化

(1)打开一个客户端A,并设置当前事务模式为serializable,查询表user的初始值:

(2)打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

注意事项

  • 事务隔离级别为读提交时,写数据只会锁住相应的行
  • 事务隔离级别为串行化时,读写数据都会锁住整张表
  • 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
posted @ 2018-09-04 17:38  StoneGeek  阅读(402)  评论(0编辑  收藏  举报