mysql 锁机制
乐观锁
数据版本(Version)记录机制实现是乐观锁最常用的一种实现方式。即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加1。当提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
select id,value,version from TABLE where id = #{id}
2、每次更新表中的value字段时,为了防止发生冲突,需要这样操作
update TABLE set value=2,version=version+1 where id=#{id} and version=#{version}
共享锁
共享锁又称读锁 (read lock),是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。当如果事务对读锁进行修改操作,很可能会造成死锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获得共享锁的事务只能读数据,不能修改数据。
SELECT * from TABLE where id = 1 lock in share mode;
LOCK IN SHARE MODE ,会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。 其他线程也可以读取使用共享锁的表,而且这些线程读取的是同一个版本的数据。加上共享锁后,对于update,insert,delete语句会自动加排它锁。
排他锁
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。 排它锁是悲观锁的一种实现
读取为什么要加读锁呢?防止数据在被读取的时候被别的线程加上写锁,排他锁使用方式:在需要执行的语句后面加上for update就可以了 select status from TABLE where id=1 for update;
行锁
多个事务操作同一行数据时,后来的事务处于阻塞等待状态。可以避免了脏读等数据一致性的问题。后来的事务可以操作其他行数据,解决了表锁高并发性能低的问题。
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。
间隙锁
用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,
InnoDB使用间隙锁的目的,为了防止幻读;在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件
特别说明的是,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!
如果索引列是唯一索引,那么只会锁住这条记录(只加行锁),而不会锁住间隙。
对于联合索引且是唯一索引,如果 where 条件只包括联合索引的一部分,那么依然会加间隙锁。
next-key lock
next-key lock 实际上就是 行锁+这条记录前面的 gap lock 的组合。假设有索引值10,11,13和 20,那么可能的 next-key lock 包括:
(负无穷,10],(10,11],(11,13],(13,20],(20,正无穷)
在 RR 隔离级别下,InnoDB 使用 next-key lock 主要是防止幻读问题产生
分析行锁定
通过检查InnoDB_row_lock 状态变量分析系统上中行锁的争夺情况
mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 0 |
| Innodb_row_lock_time_avg | 0 |
| Innodb_row_lock_time_max | 0 |
| Innodb_row_lock_waits | 0 |
+-------------------------------+-------+
- innodb_row_lock_current_waits: 当前正在等待锁定的数量
- innodb_row_lock_time: 从系统启动到现在锁定总时间长度;非常重要的参数,
- innodb_row_lock_time_avg: 每次等待所花平均时间;非常重要的参数,
- innodb_row_lock_time_max: 从系统启动到现在等待最常的一次所花的时间;
- innodb_row_lock_waits: 系统启动后到现在总共等待的次数;非常重要的参数。直接决定优化的方向和策略。
行锁优化
1、尽可能让所有数据检索都通过索引来完成,避免无索引行或索引失效导致行锁升级为表锁。
2、尽可能避免间隙锁带来的性能下降,减少或使用合理的检索范围。
3、尽可能减少事务的粒度,比如控制事务大小,而从减少锁定资源量和时间长度,从而减少锁的竞争等,提供性能。
4、尽可能低级别事务隔离,隔离级别越高,并发的处理能力越低。
属性值重复率
当“值重复率”低时,甚至接近主键或者唯一索引的效果,“普通索引”依然是行锁;当“值重复率”高时,MySQL 不会把这个“普通索引”当做索引,即造成了一个没有索引的 SQL,此时引发表锁。
意向锁
参考:
https://juejin.im/post/5b82e0196fb9a019f47d1823
https://www.cnblogs.com/crazylqy/p/7611069.html
浙公网安备 33010602011771号