事务的四大特性
一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性: 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的数据必须完全符合所有的预设规则,这包含数据的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
事务的隔离级别事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
隔离级别
脏读
不可重复读
幻读
未提交(Read uncommitted)
是
是
是
不可重复读(read-committed)
否
是
是
可重复读(repeatable-read)
否
否
是
串行化(serializable)
否
否
否
下面通过实例来说明这几种的区别
读未提交
进入mysql,并设置当前事务模式为read uncommitted(未提交读)
set session transaction isolation level read uncommitted;
查看表user的初始值
打开客户端B,开启事务,更新user表的age字段,然后在客户端A中查询数据
此时会发现虽然客户端B的事务没有提交,但是A却能读到B更新后的的数据,一旦客户端B的数据因为某种原因回滚的话,那么A读到的其实就是脏数据。
在客户端执行update user set age = age - 5 where id = 1;发现zhangsan的年龄并没有变成想象中的 15-5 = 10,居然是5,是不是很奇怪,数据不一致啊,如果你这么想就太天真 了,在应用程序中,我们会用15-5=10,并不知道其他会话回滚了,要想解决这个问题可以采用读已提交(read committed)的隔离级别。
读已提交
打开客户端A,设置当前事务模式为 read committed (读已提交)查询表user的数据;
在客户端A提交之前打开客户端B并更新数据;
此时客户端B的事务还没提交,客户端A并不能查询到客户端B更新的内容,这解决了脏读的问题;
然后我们提交客户端B的事务;
客户端A执行相同的查询,但是结果却不一致,这就产生了不可重复读的问题
可重复读
打开客户端A,设置当前事务模式为 repeatable read (重复读)查询表user的数据;
在客户端A的事务提交之前打开客户端B更新表user的值并提交;
在客户端A中查询user表的数据,发现与之前查询的相同,没有出现不可重复读的问题。当我们将当前会话的隔离级别设置为可重复读的时候,当前会话可以重复读,就是每次读取的结果 集都相同,而不管其他事务有没有提交
在客户端执行update user set age = age - 10 where id = 1 ; 这个时候我们发现age字段并没有 变成 30-10 = 20 ,而是按照客户端B里面的 50-10 = 40 来计算的 ,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
打开客户端B插入一条数据,然后提交。
在客户端A中查询所有的记录,发现并没有出现新增的数据。
串行化
打开一个客户端A,并设置当前事务模式为serializable(串行化),开启事务,并查询user表数据
在客户端B中插入一条数据,可以发现并不能立即插入,可以看出,如果一个事务,使用了SERIALIZABLE——可串行化隔离级别时,在这个事务没有被提交之前其他的线程,只能等到当前操作完成之后,才能进行操作,这样会非常耗时,而且,影响数据库的性能,通常情况下,不会使用这种隔离级别
本作品采用《CC 协议》,转载必须注明作者和本文链接