langyue 2020-04-29
数据库事务的特性包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabilily),简称 ACID。
在数据库执行中,如果多个事务同时对同一份数据进行读写就容易出现数据不一致的情况,如下:
是指一个事务中访问到了另外一个事务未提交的数据。例如事务 T1 中修改的数据项在尚未提交的情况下被其他事务(T2)读取到,如果 T1 进行回滚操作,则 T2 刚刚读取到的数据实际并不存在。
是指一个事务读取同一条记录 2 次,得到的结果不一致。例如事务 T1 第一次读取数据,接下来 T2 对其中的数据进行了更新或者删除,并且 Commit 成功。这时候 T1 再次读取这些数据,那么会得到 T2 修改后的数据,发现数据已经变更,这样 T1 在一个事务中的两次读取,返回的结果集会不一致。
是指一个事务读取 2 次,得到的记录条数不一致。例如事务 T1 查询获得一个结果集,T2 插入新的数据,T2 Commit 成功后,T1 再次执行同样的查询,此时得到的结果集记录数不同。
如果发生了脏读,那么幻读和不可重复读都有可能出现。如果出现了不可重复读,则也可能出现幻读。
隔离级别(Isolation Level)和数据库的性能呈反比,隔离级别越低,数据库性能越高;而隔离级别越高,数据库性能越差,四种隔离级别如下:
(1)Read uncommitted 读未提交
在该级别下,一个事务对数据修改的过程中,不允许另一个事务对该行数据进行修改,但允许另一个事务对该行数据进行读,读未提交会出现脏读,不可重复读的情况。
举例:数据库有一条行数据 id=1,name=jack;事务1对该行数据进行修改,事务2对改行数据执行读。
事务1将name更改为lucas,并且还未提交,就被事务2读取到,则事务2读到脏数据,另外如果事务1执行了回滚,事务2对该行数据执行重复读,即又读取一次,则事务2读取的结果会跟第一次不一样,即出现了不可重复读情况。
(2)Read committed 读已提交
在该级别下,未提交的写事务不允许其他事务访问该行,不会出现脏读,但是读取数据的事务允许其他事务访问该行数据,因此会出现不可重复读的情况。
举例:数据库有一条行数据 id=1,name=jack;事务1对该行数据进行修改,事务2对改行数据执行读。
事务1将name改为lucas,但是并未提交。事务2由于只能读到已经提交的数据,所以事务2读到的数据是jack。但是如果事务1执行了commit,事务2再去读取就会发现两次读取的结果不一致。所以产生了不可重复读的情况
(3)Repeatable read 可重复读
在该级别下,在同一个事务内的查询都是和事务开始时刻一致的,保证对同一字段的多次读取结果都相同,除非数据是被本身事务自己所修改,不会出现同一事务读到两次不同数据的情况。但是因为没有约束其他事务的Insert操作,所以会出现幻读。
举例:数据库有一条行数据 id=1,name=jack;事务1对该行数据进行修改,事务2对改行数据执行读。
是这样的,事务2在开始事务的那一刻起,就已经限制了事务2能读到的数据,不管这里事务1对该行数据怎么修改(不管修改后提交不提交),事务2读到的就一直是jack,直到事务2结束。但是...如果还有其他事务执行了insert操作,比如又来个了事务3 insert了一条id=2,name=kevin的数据。事务2如果再去读取一次,会发现读到的数据不是原来的一条数据了,而是变成了2条,这就发生了幻读。
MySQL InnoDB 引擎默认的隔离级别就是可重复读,并且 MySQL 额外添加了间隙锁(Gap Lock),以避免幻读。
(4)Serializable 序列化
要求所有事务都必须串行执行,可以避免各种并发引起的问题,效率也最低。