关于死锁

最近在项目中遇到了死锁的问题,在解决死锁的同时,也比较系统的理解了相关的锁,这里记录一下。
了解一下innodb中的锁
基本锁:
共享锁(S锁):读锁:事务T对数据A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

排他锁(X锁):写锁。事务T对数据A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的X锁之前不能再读取和修改A。

注意:如果2个JOB同时获取到了数据A(就是同时给数据A加上了S锁),因为jobA给数据A添加S锁后,jobB也可以对数据A添加S锁。那么当2个JOB都需要开始对数据A进行修改时,那么都要同时升级到X锁,在这种情况下就会出现死锁(jobA等待jobB释放S锁,jobB等待jobA释放S锁)。

锁与索引的关系
主键本身就是索引。innodb对于主键使用了聚簇索引,这是一种数据存储方式,表数据是和主键一起存储。主键索引的叶结点存储行数据。普通索引,其叶结点存储的是主键值(普通索引锁住了,也会锁住相对应的的主键左右)。

分析下索引和锁的关系
id:主键      token:二级索引(非唯一)
1)update table_1 set msg='a' where id=1;
由于id是主键,所以锁定table_1表中的此行记录。(X锁)

2)update table_1 set msg='a' where token='b';
由于token是二级索引,会先锁定这个二级索引(表中的记录token的值为b的行,有几行锁定几行),然后锁定相应主键所对应的记录。(X锁)

3)update table_1 set msg='a' where msg='b';
没有索引,是全表扫描,会对表中的各个记录加上X锁。

以上的是在数据库事务隔离级别是RC(已提交读)级别下的锁。

数据库事务隔离级别:1)未提交读(Read uncommitted);2)已提交读(Read committed(RC));3)可重复读(Repeatable read(RR));4)可串行化(Serializable)
提交读(RC):只能读取到已经提交的数据。
可重复读(RR):在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。

在RC的级别下的锁,可以防止不同事务数据修改提交时造成数据冲突的情况。但是在做insert的时候会出现幻读。



innodb在RR隔离级别中引入了GAP锁,可以避免幻读发生。
在事务A执行update语句的时候,innodb首先会和RC级别一样,给索引上的记录添加上X锁,此外,还在非唯一索引token值与相邻两个索引的区间加上锁。这样在事务B执行insert语句,然后commit;时会首先检查这个区间是否被锁上,如果被锁上,则不能立即执行,需要等待该gap锁被释放。这样就能避免幻读问题。

造成死锁的原因:
1)不同表相同记录行锁冲突



2)相同表记录行锁冲突:2个job做更新操作


3)不同索引锁冲突:事务A在执行时,除了了在二级索引加锁,还会在主键上加锁。事务B只会在主键上加锁。


4)GAP锁冲突



猜你喜欢

转载自jishuaige.iteye.com/blog/2364497