行锁原理
主键加锁
id为主键索引 update t set name='x' where id =10; 加锁行为仅在id=10的主键索引记录上加X锁(记录锁)
id为唯一键索引 name为主键索引 先在唯一索引id上加id=10的x锁 再在id=10的主键索引记录上加x锁
非唯一键加锁
name是主键 id是索引(二级索引) 加锁行为:对满足id=10条件的记录和主键分别加x锁 然后在 (6,c)~(10,b) (10,b)~(10,d) (10,d)~(11,f) 间隙分别加Gap锁
有了间隙锁 这些区间内不允许其他事务做插入操作
无索引加锁
name是逐渐 id没有索引 select * from t where id =10; 会导致全表扫描 加锁行为:表里所有行和间隙均加x锁 由于InnoDB引擎行锁机制基于索引实现记录锁定 因为没有索引时会导致全表锁定
死锁
死锁现象
- 表锁死锁
- 行级锁死锁
- 共享锁转换为排他锁
表级锁死锁
用户A先访问表A 对表A加了锁 然后再访问表B 用户B先访问表B 对表B加了锁 然后再访问表A 因表B被加了锁 所以用户A需等着用户B释放了表B的锁才可以对表B加锁 因表A被加了锁 所以用户B需等着用户A释放了表A的锁才可以对表A加锁 所以2者互相等待 从而死锁
解决方案
- 调整程序的逻辑
把表A和表B当成同一个资源
用户A访问完表A和表B之后 用户B再来访问表A和表B
- 尽量避免同时锁定2个资源
行级锁死锁
产生原因1
在事务中执行了一条不满足for update的操作 则执行全表扫描 把行级锁上升为表级锁 多个这样的事务执行后 就很容易产生死锁和阻塞
解决方案
SQL语句中不要使用太复杂的关联表的查询 优化索引
产生原因2
表中的2个数据id1和id2 事务1先访问id1 对id1加行锁 再访问id2 事务2先访问id2 对id2加行锁 再访问id1 事务1访问id2等待事务2释放id2的行锁 事务2访问id1等待事务1释放id1的行锁 所以事务1和事务2互相等待 阻塞 从而产生死锁 类似于2个表死锁 这个是表中的2个记录行级死锁
解决方案
- 同一个事务中 尽可能做到一次性锁定所有资源
- 按照id对资源排序 然后按顺序进行处理
- 采用MVCC机制处理 普通读 不会使用锁
共享锁转排他锁
事务A查询一条记录 加共享读锁 事务A更新这条记录 事务B也更新这条记录 需要排他锁 事务B需等待事务A释放了共享锁 才可以获得排他锁进行更新 所以事务B进入了排队等待 事务A也需要排他锁进行更新操作 但是无法授予该 锁请求 因为事务B已经有了一个排他锁请求 并且等待事务A释放其共享锁
解决方案
- 避免引发对同一条记录的反复操作
- 使用乐观锁进行控制