【MySql】- 3.MySQL的锁

MySql的锁介绍

在这里插入图片描述

a.读锁和写锁

InnoDB实现了两种标准的行级锁。

  • 读锁:共享锁,Shared Locks,S锁。允许多个事务读取同一行数据
  • 写锁:排它锁,Exclusive Locks ,X锁。某一时刻只允许一个事务去增删改一行数据/

二者的关系:
在这里插入图片描述

读操作加锁

对普通的Select操作,InnoDB不会加任何锁。

select ... lock in share mode

select语句结尾加lock in share mode会给select查询的行加一个S锁,该锁允许其他事务继续获取这些记录的S锁,而不能获取这些行的X锁(S锁会阻塞X锁)

select ... for update

给select语句结尾添加for update会给被查询的记录加X锁,不允许其他事务获取这些记录的S锁和X锁。

写操作加锁
  • delete:删除一条数据时,会先对记录加X锁,在这些删除操作
  • Insert:插入一条记录时,会先加隐式锁保护新插入的数据不被其他事务访问
  • Update:
    • 如果被更新的列,修改前后没有导致存储空间变化,那么会先给记录加X锁,再直接对记录进行修改。
    • 如果被更新的列,修改前后导致存储空间发生了变化,那么会先给记录加X锁,然后将记录删掉,再Insert一条新记录。

隐式锁:一个事务插入一条记录后,还未提交,这条记录会保存本次事务id,而其他事务如果想来读取这个记录会发现事务id不对应,所以相当于在插入一条记录时,隐式的给这条记录加了一把隐式锁。

b.行锁与表锁

行锁
行锁的范围
  • LOCK_REC_NOT_GAP:单个行记录上的锁。
  • LOCK_GAP:间隙锁,锁定一个范围,但不包括记录本身。 GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
  • LOCK_ORDINARY:锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
表锁
表锁级别的S锁,X锁

在对某个表执行SELECT、 INSERT、 DELETE、 UPDATE语句时, InnoDB存储引擎是不会为这个表添加表级别的S锁或者X锁的。

对某个表执行ALTER TABLE、 DROP TABLE这些DDL语句时,其他事务对这个表执行SELECT、 INSERT、DELETE、 UPDATE的语句会发生阻塞,或者,某个事务对某个表执行SELECT、 INSERT、 DELETE、 UPDATE语句时,其他事务对这个表执行DDL语句也会发生阻塞。这个过程是通过使用的元数据锁(英文名: MetadataLocks,简称MDL)来实现的,并不是使用的表级别的S锁和X锁。

  • LOCK TABLES t1 READ:对表t1加表级别的S锁。
  • LOCK TABLES t1 WRITE:对表t1加表级别的S锁。

尽量不用这两种方式去加锁,因为InnoDB的优点就是行锁,所以尽量使用行锁,性能更高。

IS锁,IX锁
  • IS锁:意向共享锁、 Intention Shared Lock。当事务准备在某条记录上加S锁时,需要先在表级别加一个IS锁。
  • IX锁,意向排他锁、 Intention Exclusive Lock。当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。

IS、 IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录。

AUTO-INC锁
  • 在执行插入语句时就在表级别加一个AUTO-INC锁,然后为每条待插入记录的AUTO_INCREMENT修饰的列分配递增的值,在该语句执行结束后,再把AUTO-INC锁释放掉。这样一个事务在持有AUTO-INC锁的过程中,其他事务的插入语句都要被阻塞,可以保证一个语句中分配的递增值是连续的。

  • 采用一个轻量级的锁,在为插入语句生成AUTO_INCREMENT修饰的列的值时获取一下这个轻量级锁,然后生成本次插入语句需要用到的AUTO_INCREMENT列的值之后,就把该轻量级锁释放掉,并不需要等到整个插入语句执行完才释放锁。

系统变量innodb_autoinc_lock_mode:

  • innodb_autoinc_lock_mode值为0:采用AUTO-INC锁。
  • innodb_autoinc_lock_mode值为2:采用轻量级锁。
  • 当innodb_autoinc_lock_mode值为1:当插入记录数不确定是采用AUTO-INC锁,当插入记录数确定时采用轻量级锁。

c.悲观锁

悲观锁用的就是数据库的行锁,认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至提交了当前事务。

d.乐观锁

乐观锁其实是一种思想,认为不会锁定的情况下去更新数据,如果发现不对劲,才不更新(回滚)。在数据库中往往添加一个version字段来实现。

相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳

乐观锁使用CAS(Compare And Swep)操作保证数据一致性

使用版本号实现乐观锁

使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。

优点与不足

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。

e.死锁

死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种相互等待的现象。

解决死锁问题最简单的一种方法是超时。但是不够理想。

除了超时机制,当前数据库还都普遍采用 wait-for graph(等待图)的方式进行死锁检测。

Mysql会自动检查死锁并根据一定的算法释放循环等待中的某一个锁,使另一个加锁的事务可以正常执行。

避免死锁
  • 以固定的顺序访问表和行
  • 大事务拆小,大事务更容易产生死锁
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率
  • 降低隔离级别(下下签)
  • 为表添加合理的索引

MySQL各存储引擎所支持的锁

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值