0
点赞
收藏
分享

微信扫一扫

#yyds干货盘点#MySQL学习-行锁的介绍

witmy 2022-02-27 阅读 148

前言

行锁是针对行记录的锁,相比较于表级别锁,行锁的颗粒度更细,更加灵活;

目录

  1. 行锁的介绍
  2. 上锁和释放锁的时机
  3. 死锁问题

正文

1. 行锁的介绍

行锁的实现是在引擎层面,比如我们常用的InnoDB就是支持行锁的;但是MyISAM却不支持;

像我们之前介绍的表锁,是把整个表锁住,这种方式最大的缺点就是效率低,如果有一个线程在操作表,那么其他线程都只能等;

但是行锁不一样,它是针对行的索引进行上锁,如果有一个线程在操作这行数据,那么其他线程也只是这行记录不能操作,但是表里面的其他记录还是可以同步进行的,这样效率就会高很多;

如果表没有索引,那么行锁会锁住整个表

2. 上锁和释放锁的时机

上锁是在事务启动时(即第一条语句开始时),而释放锁是在事务提交时;

这两个阶段也叫做两阶段锁协议;

下面我们用例子看一下,开启两个线程,一个先执行更新语句,另一个执行接着也执行更新语句;

image-20220213154016450

可以看到,第一个线程可以顺利完成,但是第二个线程再执行更新操作就会卡住;

这里的上锁就是在第一个线程执行更新操作时上的,接着第二个线程也去更新同一条数据(索引id=1);

此时线程1获得的行锁(id=1)还没释放,所以线程2就会被阻塞;

如果阻塞时间过长,会报错如下:

1205 - Lock wait timeout exceeded; try restarting transaction

这里对应的超时参数是innodb_lock_wait_timeout,默认50S,即超过50S还是阻塞的,就直接报错退出了;

如果在超时时间内,线程1提交了事务,那么线程2就可以继续执行了;

3. 死锁问题

因为行锁是针对行记录的锁,所以多个线程可以同时锁住不同的行;

但是如果两个线程各持有一个行锁,还想要获取对方的行锁,那么此时就会导致死锁;

复现:

我们可以开启两个线程,线程1更新id=1,线程2更新id=2,然后再互调更新操作;

如下所示,现在两个线程各自持有id=1的行锁和id=2的行锁;

image-20220213154923053

此时我们让线程1再去获取id=2的行锁,会被锁住;

然后再让线程2去获取id=1的行锁,此时会提示出现了死锁,如下所示:

image-20220213201541050

可以看到,默认的死锁处理方式,就是重启后面的事务,以此来释放一个行锁,然后让前面的事务可以继续执行;

这个就是死锁检测机制:如果有发现死锁,则回滚死锁事务链中的某个事务,使得其他事务可以继续执行;

上面的例子就是线程2中的事务被强制重启,此时线程2中的行锁就会被释放,线程1中的事务会被继续执行;

我们可以通过设置参数:innodb_deadlock_detect,来开启和关闭死锁检测机制,默认on;

总结

行锁是针对行记录的锁,在事务启动时(第一条SQL语句执行时)申请锁,在事务提交时释放锁;

死锁就是多个线程各自拥有行锁,然后又想要获取对方的锁,此时就会造成行锁;

默认的死锁检测机制会自动检测是否有死锁,如果有发现死锁,则回滚死锁事务链中的某个事务,使得其他事务可以继续执行;

举报

相关推荐

0 条评论