程序园

MySQL锁

2020-08-07  本文已影响0人  小马过河R

本文非小马原创,为学习总结笔记,作为日后复盘回顾,感谢原作者分享,文末已注明出处,侵删。

1、锁的颗粒

行级锁

表锁

间隙锁

间隙锁是一个在索引记录之间的间隙上的锁。

间隙锁的作用

保证某个间隙内的数据在锁定情况下不会发生任何变化。比如mysql默认隔离级别下的可重复读(RR)。

当使用唯一索引来搜索唯一行的语句时,不需要间隙锁定。如下面语句的id列有唯一索引,此时只会对id值为10的行使用记录锁。

select * from t where id = 10 for update;// 注意:普通查询是快照读,不需要加锁

如果,上面语句中id列没有建立索引或者是非唯一索引时,则语句会产生间隙锁。

2、锁的类型

读锁(共享锁 / S锁) : 一个事务加了s锁后, 其他事务只能读不能写, 其他事务可以再加读锁

select ... from table lock in share mode ;

写锁(排他锁 / X锁 ) : 一个事务加了x锁后, 其他事务只能读不能写,不能再加锁

update ...

insert ...

delete ...

select ... for update ;

无锁 : select 语句直接查完全不受锁影响, 既不触发加锁也不被已有的锁限制。但如果select ... from table lock in share mode 或者select ... for update 就是手动加读锁和写锁了。

select ... from table ;

2.1、读锁总结 :

select ... lock in share mode 加的是行级读锁

多个读锁是可以重复加的

读锁是可读不可写的

事务结束(commit / rollback)后锁自动解除

2.2、写锁总结:

for update 加的是行级写锁

加上写锁后不能再加读锁 / 写锁

写锁是可读不可写的

事务结束(commit / rollback)后锁自动解除

锁冲突结论: 只有读锁之间不冲突

3、sql 语句手动加锁

加读锁 select ... lock in share mode;

加写锁 select ... for update ;

注意 : 如果select 的条件中未使用到索引, for update 将会变成表锁 ; 不仅是select...for update ,update 也是一样。

案例:

转账操作中需要判断余额是否足够

begin;

select balance from account where id=1;  ①

(对余额进行判断,余额不做的不更新操作)

update account set balance=balance-100 where id=1; ②

update account set balance=balance+100 where id=2;

commit;

多线程下,会有多个事务并发访问数据库。

假设余额150,事务a可以执行到①判断余额充足,切换线程执行事务b到①并判断余额充足,最后都提交后,id为1的账户余额为-50;所以必须让整个事务操作保持原子性。

修改①为:select balance from account where id=1 for update;对该记录加行锁。当事务a执行完提交释放行锁时事务b才能获得行锁 再查询判断。

4、死锁的排查和解除

小马曾经遇到自建MySQL使用innodb存储引擎的写操作导致死锁问题,排查后杀死进程可以解决,但一旦写操作又会出现死锁,后来直接换成MYISAM后死锁不再出现,至今未知原因。

5、MyISAM 和InnoDB 比较

是否支持行级锁: MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。

是否支持外键:MyISAM不支持,而InnoDB支持。

是否支持MVCC:仅InnoDB支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在READ COMMITTED和REPEATABLE READ两个隔离级别下工作;MVCC可以使用乐观(optimistic)锁和悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。

参考文献:

mysql锁

什么是间隙锁?

Mysql 查看死锁,解除死锁 方式

上一篇 下一篇

猜你喜欢

热点阅读