mysql 锁机制

乐观锁

  数据版本(Version)记录机制实现是乐观锁最常用的一种实现方式。即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加1。当提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

  1、数据库表三个字段,分别是id、value、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,此时引发表锁。

意向锁

       为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks)
  意向共享锁(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
  意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

        

参考:

https://juejin.im/post/5b82e0196fb9a019f47d1823

https://www.cnblogs.com/crazylqy/p/7611069.html

    

 

posted on 2021-11-07 19:51  溪水静幽  阅读(80)  评论(0)    收藏  举报