前言
MySql数据库锁大致分类:
表级锁:加锁快;不会出现死锁;锁冲突概率高;锁粒度大;并发度低。
行级锁:开销大,加锁慢;会出现死锁;锁冲突概率低;锁粒度小;并发度高。
页面锁:介于表锁和行锁之间。
锁的机制取决于使用的数据库引擎。MyISAM支持表级锁,不支持事务。InnoDB支持行级锁和表级锁,并且支持事务。
MyISAM引擎锁分类
表共享锁(Table Read Lock)和表独占锁(Table Write Lock),读和写是串行的,写和写是串行的,读和读不串行。当一个线程获取一个表的写锁时, 只有持有锁的线程可以对表操作,其他线程都会等待,直到锁被释放。
MyISAM加锁方式
SELECT:自动加入读锁
UPDATE、DELETE、INSERT:加入写锁
MyISAM的并发机制
通过MyISAM系统变量concurrent_insert决定:
concurrent_insert = 0 不允许并发 。
concurrent_insert = 1 一个线程在读时,允许其他线程在表尾出入记录。
concurrent_insert = 2 时,总是允许并发插入,通过定期在系统空闲时段执行OPTIONMIZE TABLE语句来整理空间碎片,收集因删除记录而产生的中间空洞。
InnoDB锁和事务
InnoDB引入了事务和行锁,同时引入了很多并发问题。对于事务,有四个ACID属性:原子性,一致性,隔离性,持久性。
关于ACID前面的文章已经提到过,这里不做细述。要着重提到的一点是一致性,此处有一篇文章,我觉理解得很好。
https://www.zhihu.com/question/31346392
InnoDB的行锁
InnoDB的行锁是通过索引的索引项来加锁的,如果通过非索引条件检索,则是行锁。如果使用非索引检索可能导致大量的锁冲突。
共享锁(S):xxx lock in share mode 即读锁
一个事务T读取一行数据集A,不允许其他事务获取该数据集的排他锁,即不允许其他事务修改。
排他锁(X):xxx for update 即写锁
一个事务T读取一行数据集A,不允许其他事务获取该数据集的共享读锁和排他写锁。即不允许其他事务读和写。
共享锁主要用于查询某行记录是否存在时,确定此时该记录没有被其他事务修改。排他锁主要用于要对某行数据集进行一个修改操作的完整性,确保数据此时不会被其他事务修改。
特别地,在InnoDB事务中,行锁是事务结束后才会释放。
InnoDB的间隙锁
例子:SELECT * FROM emp WHERE empid > 100 FOR UPDATE
此时如果大于100的记录不存在,会在这个间隙加上间隙锁。如果没有间隙锁,此时其他事务可能会插入大于100的记录,导致当前事务发生幻读。
特别地,间隙锁会发生死锁:
加入id=5的记录不存在,执行select * from table_name where id = 5 for update即为间隙锁。两个session之间的间隙锁是不互斥的,因此for update可并发执行,导致insert时出现死锁。
死锁以及解决
MyISAM表锁是deadlock free的,不会死锁。
InnoDB的行锁会发生死锁。
概念:多个线程争夺资源造成的僵局。
线程A持有锁A等待获取锁B,线程B持有锁B等待获取锁A,此时两个事务均未提交,互相进入一个等待状态,任意一方都无法释放锁,陷入僵局,即为死锁。
死锁的解决方案:
方案一:统一执行时序,不同的线程获取不到锁只会处于阻塞等待状态,永远不会死锁。
方案二:设施超时机制,不同的线程获取不到锁,在一定时间后放弃获取该锁,以保证其他线程可以获取该锁。
扫码了解更多
不要认为开学与你无关,
我们一直都是学生。
关注公众号,回复”资料“领取更多阿里腾讯网易等大厂面试题。