mysql事物隔离与死锁

一、事物

0、事物的ACID:原子性、一致性、隔离性(不受干扰)、持久性(提交后不可回滚)

1、对数据库的所有操作,内部全部封装成事物(transaction)来运行。事物要满足ACID特性,数据库的恢复和管理全部基于事物。

2、数据库内的数据是所有人共享的,为了保证事物的ACID特性,必须在事物之间进行同步

3、最安全的同步策略是事物之间串行执行,但是效率最低、并发度最低,所以都会使用并发的方式

4、并发调度执行时,导致数据不一致。类型包括:

1》 丢失修改(Lost Update)2》 读“脏”数据(Dirty Read):事物结束前,别的事物回滚了所读的数据

3》 不可重复读(Non-repeatable Read) 事物结束前,有别的事物改数据

4》 幻读:事物结束前,有别的事物插入和删除

5、数据库内部保证事物隔离性的方式,加锁

1》写锁(排斥所有别的)、2》读锁 别的事物只能加读锁

6、锁的粒度

物理层面:页(索引或数据)、记录

逻辑:属性值 属性值集 元组 表 库 索引项 索引 

7、加锁的影响

活锁:一直没有获取到锁 ,(解决方案,先来先服务)

死锁:循环依赖

8、乐观锁

类似于内核的自旋锁

概括:事务并发-- 数据不一致/死锁

二、mysql的事物隔离级别(不影响写,影响读的行为)

事物本身并发执行,为了提高吞吐率,一定程度上牺牲了隔离性。

1、mysql提供的事物隔离级别

1》 读未提交(read-uncommitted) 2》 不可重复读(read-committed) 3》 可重复读(repeatable-read) 4》 串行化(serializable)

2、不同并发级别DMS隐含加的锁

    1》 读未提交 写事物没有锁住行到结束(允许此事物结束前,别的事物来读这一行) 2》 RC 写事物锁住一行到结束,但是当前读事物,读完之后没有锁住行(允许别的事物在此读事物结束前修改此行) 3》 RR 读事物一开始就锁行,一直到事物结束(不允许别的事物更改)。因为没有锁表,所以别的事物可以删除和插入新的行。

   4》串行 读事物直接锁住表。在此读事物结束前,整个表都不允许别的事物写。

3、内部实现

写加悲观锁

读使用缓存来模拟乐观锁

三、RC级别事物隔离的问题

1、数据不一致

本事物内数据可能被修改多次,所以当写依赖读时,会产生问题。解决方案:1》java代码里使用分布式锁,进行资源的锁定 2》事物执行完之后,java代码里加个查询检查条件是否变化。

2、死锁

innodb写的时候加悲观锁,所以多个事物并发时会有死锁的可能性。

3、可能死锁的原因和解决方案

1》 A和B 操作2张表,但是顺序相反,结果产生循环依赖。解决方案:涉及多表操作时,保持一样的顺序

2》 A查询一条纪录,然后修改该条纪录;在修改之前,用户B修改该条纪录,这时A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升到独占锁也就不可能释放共享锁,于是出现了死锁。简单说,B等A释放,结果A因为B排在前面得不到排它锁,反而释放不了,导致死锁。

解决方案:按钮点击后改变状态,不要一个事物有很多副本在运行(自己锁住自己)

3》 不满足条件的update和delete等导致全表扫描情况出现时,容易导致循环依赖。解决方案:干掉全表扫描(尽量干掉join),加索引,查询条件尽量能定位到行

4》事物太长容易引起循环依赖。解决方案:变成短事物。

四、jdbc逻辑

1、jdbc 默认是 autocommit,每条sql一个单独的事物(永远不会死锁),但是需要多个操作保持一致性时,需要把多个操作放入一个事物中。

2、spring的事物传播机制默认是 PROPAGATION_REQUIRED:外层有了,内层就不起作用了

3、加悲观锁的方式,在语句后面添加 for update,例如 select * from t_user for update;

五、总结

写操作加锁不会导致不一致,但是会导致死锁。读操作可能不满足强一致性,事物隔离级别的本质是容忍哪种程度的读不一致。

相关推荐