MySQL_锁

事务并发访问同一数据资源的情况主要就分为读-读写-写读-写三种。

  1. 读-读 即并发事务同时访问同一行数据记录。由于两个事务都进行只读操作,不会对记录造成任何影响,因此并发读完全允许。
  2. 写-写 即并发事务同时修改同一行数据记录。这种情况下可能导致脏写问题,这是任何情况下都不允许发生的,因此只能通过加锁实现,也就是当一个事务需要对某行记录进行修改时,首先会先给这条记录加锁,如果加锁成功则继续执行,否则就排队等待,事务执行完成或回滚会自动释放锁。
  3. 读-写 即一个事务进行读取操作,另一个进行写入操作。这种情况下可能会产生脏读不可重复读幻读。最好的方案是读操作利用多版本并发控制(MVCC),写操作进行加锁

锁的粒度

按锁作用的数据范围进行分类的话,锁可以分为行级锁表级锁

  1. 行级锁:作用在数据行上,锁的粒度比较小。
  2. 表级锁:作用在整张数据表上,锁的粒度比较大。

锁的分类

为了实现读-读之间不受影响,并且写-写读-写之间能够相互阻塞,Mysql使用了读写锁的思路进行实现,具体来说就是分为了共享锁排它锁

  1. 共享锁(Shared Locks):简称S锁,在事务要读取一条记录时,需要先获取该记录的S锁S锁可以在同一时刻被多个事务同时持有。我们可以用select ...... lock in share mode;的方式手工加上一把S锁
  2. 排他锁(Exclusive Locks):简称X锁,在事务要改动一条记录时,需要先获取该记录的X锁X锁在同一时刻最多只能被一个事务持有。X锁的加锁方式有两种,第一种是自动加锁,在对数据进行增删改的时候,都会默认加上一个X锁。还有一种是手工加锁,我们用一个FOR UPDATE给一行数据加上一个X锁

还需要注意的一点是,如果一个事务已经持有了某行记录的S锁,另一个事务是无法为这行记录加上X锁的,反之亦然。

除了共享锁(Shared Locks)排他锁(Exclusive Locks)Mysql还有意向锁(Intention Locks)

意向锁是由数据库自己维护的,一般来说,

  • 当我们给一行数据加上共享锁之前,数据库会自动在这张表上面加一个意向共享锁(IS锁)
  • 当我们给一行数据加上排他锁之前,数据库会自动在这张表上面加一个意向排他锁(IX锁)

意向锁可以认为是S锁X锁在数据表上的标识,通过意向锁可以快速判断表中是否有记录被上锁,从而避免通过遍历的方式来查看表中有没有记录被上锁,提升加锁效率。例如,我们要加表级别的X锁,这时候数据表里面如果存在行级别的X锁或者S锁的,加锁就会失败,此时直接根据意向锁就能知道这张表是否有行级别的X锁或者S锁

InnoDB中的行级锁

我们知道,通过MVCC可以解决脏读不可重复读幻读这些读一致性问题,但实际上这只是解决了普通select语句的数据读取问题。

事务利用MVCC进行的读取操作称之为快照读,所有普通的SELECT语句在READ COMMITTEDREPEATABLE READ隔离级别下都算是快照读

除了快照读之外,还有一种是锁定读,即在读取的时候给记录加锁,在锁定读的情况下依然要解决脏读不可重复读幻读的问题。由于都是在记录上加锁,这些锁都属于行级锁

InnoDB的行锁,是通过锁住索引来实现的,如果加锁查询的时候没有使用过索引,会将整个聚簇索引都锁住,相当于锁表了

根据锁定范围的不同,行锁可以使用记录锁(Record Locks)间隙锁(Gap Locks)临键锁(Next-Key Locks)的方式实现。

  • 记录锁(Record Locks) :所谓记录,就是指聚簇索引中真实存放的数据。记录锁就是直接锁定某行记录。当使用唯一性的索引(包括唯一索引和聚簇索引)进行等值查询且精准匹配到一条记录时,此时就会直接将这条记录锁定。

  • 间隙锁(Gap Locks) 间隙指的是两个记录之间逻辑上尚未填入数据的部分。同理,间隙锁就是锁定某些间隙区间的。当我们使用用等值查询或者范围查询,并且没有命中任何一个record,此时就会将对应的间隙区间锁定。

  • 临键锁(Next-Key Locks) 临键指的是间隙加上它右边的记录组成的左开右闭区间。是记录锁(Record Locks)和间隙锁(Gap Locks)的结合,即除了锁住记录本身,还要再锁住索引之间的间隙。当我们使用范围查询,并且命中了部分record记录,此时锁住的就是临键区间。注意,临键锁锁住的区间会包含最后一个record的右边的临键区间。当使用唯一性索引,等值查询匹配到一条记录的时候,临键锁(Next-Key Locks)会退化成记录锁;没有匹配到任何记录的时候,退化成间隙锁。

间隙锁(Gap Locks)临键锁(Next-Key Locks)都是用来解决幻读问题的

常见面试点:

范围

  • 表级锁:开销小、加锁快,锁粒度小发生锁冲突的概率大、并发度低,不会出现死锁;
  • 行级锁:开销大,加锁慢,锁粒度大发生锁冲突的概率小,并发度高,会出现死锁;

类型

  • 共享锁(S):行锁,读取一行
  • 排它锁(X):行锁,更新一行
  • 意向共享锁(IS):表级,准备加共享锁
  • 意向拍他锁(IX):表级,准备加排他锁
  • 间隙锁(NX):行级,使用范围条件时

注意:对范围内不存在的记录加锁,一是为了防止幻读,二是为了满足恢复和复制的需要;

加锁

  • 增加行级锁之前,InnoDB会自动给表加意向锁
  • 执行DML(数据操作语句)语句时,InnoDB会自动给数据加排他锁
  • 执行DQL(数据查询语句)语句时:
    • 共享锁:SEIECT。。。FROM。。。WHERE。。。LOCK IN SHARE MODE;
    • 排他锁:SEIECT。。。FROM。。。WHERE。。。FOR UPDATE;
    • 间隙锁:上述SQL采用范围条件时,InnoDB对不存在的记录自动增加间隙锁;

死锁

  • 解决方案(已经产生了死锁)
    • 一般InnoDB会自动检测到死锁,然后使一个事务回滚释放占有的资源,另一个事务继续执行;
    • 设置超时等待参数:innodb_lock_wait_timeout;
  • 避免死锁
    • 不同的业务并发访问多个表时,应该约定以相同的顺序来访问这些表;
    • 以批量的方式处理数据时,应事先对数据排序,保证线程按固定的顺序来处理数据;
    • 在事务中,如果要更新记录,应直接申请足够级别的锁,即排他锁

MySQL乐观锁(CAS算法/自旋锁)

  • CAS算法(Compare and swap):是一种无锁的算法,该算法设计三个操作数:内存值V,旧值A,新值B

    • 当V等于A时,采用原子方式用B得值更新V的值;

    • 否则,

      该算法通常采用自旋操作,也就叫做了自旋锁;它的缺点是:

      • ABA问题:某线程将A改为B,再改回A,则CAS会误认为A没有修改过;
      • 自旋操作采用循环的方式实现,若加锁时间太长,则会给CPU带来巨大的开销;
      • CAS只能保证一个共享变量的院子操作;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值