Mysql有多种存储引擎,他们的锁机制也不尽相同。例如MyISAM不支持行锁只有表琐,所以本文主要讨论InnoDB引擎的锁机制。
一、前置问题
- 为什么要有锁?锁对性能影响大吗?
- 锁有几种逻辑分类?
- Innodb的锁是公平锁吗?
- 如何检测和避免死锁?Innodb会对死锁自动处理吗?
- 行锁是锁的什么?表琐是在哪一层实现的,服务器层还是存储引擎层?
- 表琐有几种?意向锁是什么?
- mysql有锁升级吗?什么是锁升级?
二、锁是维护数据一致性的必要条件
数据库在处理并发访问共享资源时,需要锁来保护资源数据的一致性。
三、锁分类
3.1 锁在层级上分为:1)服务器级别锁;2)存储引擎级别锁;
服务器锁:在mysql服务器层实现并发控制,会忽略存储引擎的锁;
存储引擎锁:在存储引擎层实现,锁的粒度一般比服务器层要更细;
3.2 锁从粒度上可分为:1)行锁;2)表琐;
两者都可以表现为共享性或排他性;
行锁
在存储引擎层实现;一般通过锁索引来实现;
表琐
1)Mysql服务器层;
LOCK TABLE t READ;
LOCK TABLE t WRITE;
2)InnoDB存储引擎也实现了一种表琐,意向锁(意向共享锁,意向排它锁);
select * from t where id < 3 lock in share mode; // 表上加意向共享锁
select * from t where id < 3 lock for update; // 表上加意向排它锁
update t set name = 'test' where id = 3; // 表上加意向排它锁
delete from t where id = 3; // 表上加意向排它锁
意向锁是InnoDB在增加行锁时,同时在表上标记的一个信息。用于多个事务对表资源进行竞争获取锁时,避免去查看表中记录的行锁信息,提高了性能。
例如:事务A,执行语句update t set name = 'test' where id = 3;
时会加两把锁:
a. 在id=3的索引上加行锁;
b. 在t表上增加意向排他锁:IX LOCK
此时,事务B要执行LOCK TABLE t READ;
,需要在表上增加X锁。此时由于X,IX锁的不兼容,该事务被阻塞;
意向锁间兼容性
由于意向锁实际上是行锁的一种表级别的提示,他们都是要操作行,所以意向锁之间不会导致事务(在表级别)相互阻塞。如果是操作的相同行时通过行锁阻塞来实现并发控制。
IX | IS | |
---|---|---|
IX | 兼容 | 兼容 |
IS | 兼容 | 兼容 |
读写表琐兼容性
X | S | |
---|---|---|
X | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 |
意向锁和读写锁的兼容性
X | S | |
---|---|---|
IX | 不兼容 | 不兼容 |
IS | 不兼容 | 兼容 |
3.3 锁在访问性上可分为:1)共享锁;2)排它锁
共享锁:也称为读锁,多个用户可以获取某资源的共享锁,互不阻塞;
排它锁:也称为写锁,多个用户尝试去获取排他锁时,只有一个会成功,其他的被阻塞;
四、死锁
在InnoDb中两个或多个事务并发访问多个资源时,双发都请求对方占用的资源时,会发生死锁。
死锁原因
多个事务以不同的顺序去锁定资源时,就可能会发生死锁。
如何避免死锁
以相同的顺序去请求资源,并且可以设置等待超时时间避免一直等待下去;
如何解决死锁
InnoDB引擎检测到死锁时,强制回滚其中的一方事务,解锁争抢资源;
五、锁升级
一般数据库把锁作为一种稀缺资源,当多个小粒度的锁开销较大时,会升级为一个粗粒度的锁来减小开销。类似java中的锁粗化;
举个栗子
1)Mysql中多个行锁升级为页锁,或页锁升级为表琐。
2)拿Java的锁粗化来举例:
public void doSomethingMethod(){
synchronized(lock){
//do some thing
}
//这是还有一些代码,做其它不需要同步的工作,但能很快执行完毕
synchronized(lock){
//do other thing
}
}
会被优化为以下形式;
public void doSomethingMethod(){
//进行锁粗化:整合成一次锁请求、同步、释放
synchronized(lock){
//do some thing
//做其它不需要同步但能很快执行完的工作
//do other thing
}
}
InnoDB没有锁升级
在InnoDB中锁通过位图实现,开销是一致的,无需升级。
网友评论