锁是数据库中的一个非常重要的概念,当多个用户同时对数据库并发操作时,会带来数据不一致的问题,所以,锁主要用于多用户环境下保证数据库完整性和一致性。
按照不同的方式可以得到不同的数据库锁的分类,按照锁的模式划分,可分为悲观锁、乐观锁;按照锁的范围划分,可以分为表锁、行锁;按照锁的算法划分,可分为临键锁、间隙锁、记录锁等;按照锁的属性划分,可以分为共享锁、排他锁等;按照锁的状态划分,可以分为意向共享锁、意向排他锁等。
这里对于几种经常出现的锁进行介绍:
悲观锁(Pessimistic Lock):正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
乐观锁( Optimistic Locking ):相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。这里的数据版本的含义:为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
行锁:行锁就是一锁锁一行或者多行记录,MySQL的行锁是基于索引加载的,所以行锁是要加在索引响应的行上,即命中索引。行锁冲突概率低,并发性高,但是会有死锁的情况出现。
表锁:表锁就是一锁锁一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作。表锁响应的是非索引字段,即全表扫描,全表扫描时锁定整张表,SQL语句可以通过执行计划看出扫描了多少条记录。由于表锁每次都是锁一整张表,所以表锁的锁冲突几率特别高,表锁不会出现死锁的情况。
记录锁:记录锁锁的是表中的某一条记录,记录锁的出现条件必须是精准命中索引并且索引是唯一索引,如主键id。
间隙锁:又称之为区间锁,每次锁定都是锁定一个区间,隶属行锁。既然间隙锁隶属行锁,那么,间隙锁的触发条件必然是命中索引的,当我们查询数据用范围查询而不是相等条件查询时,查询条件命中索引,并且没有查询到符合条件的记录,此时就会将查询条件中的范围数据进行锁定(即使是范围库中不存在的数据也会被锁定)。
临键锁:MySQL的行锁默认就是使用的临键锁,临键锁是由记录锁和间隙锁共同实现的。间隙锁的触发条件是命中索引,范围查询没有匹配到相关记录。而临键锁恰好相反,临键锁的触发条件也是查询条件命中索引,不过,临键锁有匹配到数据库记录。
资料来源:
深入理解数据库行锁与表锁 https://zhuanlan.zhihu.com/p/52678870
数据库锁分类和总结 https://blog.csdn.net/weixin_39651041/article/details/79985715
数据库锁相关 https://www.cnblogs.com/joeysh/p/10748511.html