MySQL 锁
- 发布
- 分类
- MySQL
- 篇幅
- 1491 字 / 3 分钟
- 阅读
- 409
锁



每个层级的锁数量是有限制的,因为锁会占用内存空间,锁空间的大小是有限的。
当某个层级的锁数量超过了这个层级的阈值时,就会进行锁升级。
锁升级就是用更大粒度的锁替代多个更小粒度的锁,比如InnoDB中行锁升级为表锁,这样做的好处是占用的锁空间降低了,但同时数据的并发度也下降了。
避免死锁
如果事务涉及多个表,操作比较复杂,那么可以尽量一次锁定所有的资源,而不是逐步来获取,这样可以减少死锁发生的概率;
如果事务需要更新数据表中的大部分数据,数据表又比较大,这时可以采用锁升级的方式,比如将行级锁升级为表级锁,从而减少死锁产生的概率;
不同事务并发读写多张数据表,可以约定访问表的顺序,采用相同的顺序降低死锁发生的概率。
当然在数据库中,也有一些情况是不会发生死锁的,比如采用乐观锁的方式。另外在MySQL MyISAM存储引擎中也不会出现死锁,这是因为MyISAM总是一次性获得全部的锁,这样的话要么全部满足可以执行,要么就需要全部等待。
共享锁和排它锁
按照数据库管理的角度对锁进行划分为共享锁和排它锁
共享锁
表上加共享锁
| Session A | Session B |
|---|---|
| LOCK TABLE product_comment READ; | |
| UPDATE product_comment SET product_id = 10002 WHERE user_id = 912178; | |
| UNLOCK TABLE; |
行上加共享锁
SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 LOCK IN SHARE MODE`
排它锁
排它锁也叫独占锁、写锁或X锁。排它锁锁定的数据只允许进行锁定操作的事务使用,其他事务无法对已锁定的数据进行查询或修改。
当我们对数据进行更新的时候,也就是INSERT、DELETE或者UPDATE的时候,数据库会自动使用排它锁,防止其他事务对该数据行进行操作。
数据表添加排它锁
LOCK TABLE product_comment WRITE;
UNLOCK TABLE;
数据行上添加排它锁
SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 FOR UPDATE;
意向锁(Intent Lock)
意向锁是表级锁。简单来说就是给更大一级别的空间示意里面是否已经上过锁。
在事务中, 如果我们给某一行数据加上了排它锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了,这样当其他人想要获取数据表排它锁的时候,只需要了解是否有人已经获取了这个数据表的意向排他锁即可。
如果事务想要获得数据表中某些记录的共享锁,就需要在数据表上添加意向共享锁。
同理,事务想要获得数据表中某些记录的排他锁,就需要在数据表上添加意向排他锁。这时,意向锁会告诉其他事务已经有人锁定了表中的某些记录,不能对整个表进行全表扫描。
乐观锁
乐观锁的版本号机制
在表中设计一个版本字段version,第一次读的时候,会获取version字段的取值。
然后对数据进行更新或删除操作时,会执行UPDATE … SET version=version+1 WHERE version=version。
此时如果已经有事务对这条数据进行了更改,修改就不会成功。
乐观锁的时间戳机制
时间戳和版本号机制一样,也是在更新提交的时候,
将当前数据的时间戳和更新之前取得的时间戳进行比较,
如果两者一致则更新成功,否则就是版本冲突。
悲观锁
悲观锁(Pessimistic Locking)也是一种思想,对数据被其他事务的修改持保守态度,
会通过数据库自身的锁机制来实现,从而保证数据操作的排它性。
InnoDB中有三种行锁的方式
记录锁:针对单个行记录添加锁。
间隙锁(Gap Locking):可以帮我们锁住一个范围(索引之间的空隙),但不包括记录本身。采用间隙锁的方式可以防止幻读情况的产生。
Next-Key锁:帮我们锁住一个范围,同时锁定记录本身,相当于间隙锁+记录锁,可以解决幻读的问题。