MySQL--锁机制详解

什么是锁?

锁是计算机协调多个进程并发访问贡献资源的一种机制,MySQL数据库的锁机制简单来说,就是数据库为了保证访问共享数据的一致性的一种机制,有锁存在就一定存在锁冲突,锁冲突也是影响数据库并发访问性能的一个重要因素,因此锁对数据库而言显得尤其重要。

MySQL锁的分类?

在这里插入图片描述
InnoDB存储引擎和MyISAM对锁支持?
InnoDB支持丰富的锁类型,行级锁、表级锁、间隙锁、临键锁等,MyISAM只支持表级锁,故一下对锁的分析基本都是针对InnoDB引擎。

表级锁:表级锁是MySQL数据库中最大粒度的锁机制,每次加锁就锁定全表。

  • 优点:加锁逻辑简单,所以加锁释放锁都非常快。
  • 缺点:表级锁有一个致命的缺点就是并发性能差,因为锁全表导致我访问一条数据也要把全表锁定,导致并发性能很差。

行级锁:行级锁,见名知意,就是一次锁定一行数据,加锁粒度小。

  • 优点:加锁粒度小,并发性能好。
  • 缺点:加锁较为复杂,所冲突的概率变大,甚至可能出现死锁。

页级锁:页级锁,介于表级锁和行级锁之间的一种锁机制。

  • 优点:加锁解锁消耗时间介于表级锁和行级锁中间,并发性能也介于两者之间,会发生死锁。

  • 缺点:加锁较为复杂,所冲突的概率变大,甚至可能出现死锁。

    共享锁与排他锁

  • 共享锁:共享锁又叫S锁,不会阻塞其他连接对加锁数据的读操作,但是会阻塞对加锁数据的写做操,因此共享锁又叫读锁。

  • 排他锁:排他锁又叫X锁,排他顾名思义就是排斥,对于其他连接既不允许读操作也不允许写操作,因此又叫写锁。

共享锁与排他锁的兼容情况:
在这里插入图片描述

乐观锁与悲观锁

  • 乐观锁:假设多用户在并发事务时候相互不影响,在更改数据的时候进行数据校验,如果数据发生了变化就回滚操作,乐观锁只是一种思想,MySQL并没有帮我们实现,一般是通过版本号机制来实现乐观锁。
  • 悲观锁:悲观锁的思想是默认数据会被其他事务影响,在获取数据的时候就对数据进行显式加锁,悲观锁依赖MySQL的锁机制,要使用悲观锁,必须关闭MySQL的自动提交,其常用的加锁方式是 select * from table where … for update。

记录锁:记录锁就是对表中的记录加锁,其实就是行锁,一次锁定一行,否则退化为间隙锁。
记录锁加锁语法:

#对id=1的记录加记录锁,id列必须是唯一索引列或者主键索引列,否则退化为间隙锁
select * from  user where id=1 for update;
#查询结果只能是一条数据,否则退化为间隙锁,如下加锁方法就会退化为间隙锁
select * from  user where id>1 and id<3  for update;

间隙锁:间隙锁是MySQL InnoDB引擎在可重复读的隔离级别下解决幻读引入的一种锁机制,间隙锁不仅仅锁住间隙内的数据,同时不允许其他客户端往这个间隙内插入数据。

#对id大于10小于15之间加间隙锁
select * from  user where id>10 and id<15 for update;

对于上面的加锁sql语句,假设表中只有id=11、id=12、id=13三条数据,在当前加锁的客户端事务没有提交的情况下,其他客户端试图往user表中插入一条14的数据是插入不了的。

临键锁:Next-key称之为临键锁,临键锁锁住的是索引本身及索引之前的间隙,是一个左开右闭的区间,当SQL使用到非唯一索引时候,会给匹配到的数据加上临键锁,临键锁也只有在隔离级别为RR(REPEATABLE-READ:可重复读)级别才生效。
如下user表数据,age字段有非唯一索引,隔离级别为RR。

idagename
112张三
215李四
318王五
420赵六
521刘七

在事务A中执行如下SQL:

select * from user age<18 for update;

此时user表中存在临键锁(15, 18]。
在事务A还没有提交的情况下,事务B中执行如下SQL,会被阻塞,因为临键锁锁定了区间(15, 18]。

insert into user  values (6,17,'胡九');

意向锁:意向锁是表级锁,分为意向共享锁和意向排他锁,它的存在是为了协调行级锁和表级锁的关系,InnoDB允许行锁和表锁同时存在,意向锁不会与行级锁冲突。

  • 意向锁是一种不与行级锁冲突的表锁,事务如果想要获得某些行数据的共享锁,就必须先获得表的意向共享锁,同理事务如果想要获取某些数据的排他锁,也必须先获取该表的意向排他锁。
  • 意向锁相互之间是互相兼容的,与行级共享锁、排他锁也是兼容的,但是和表级共享锁、排他锁不兼容。
  • 当事务想对表加锁的时候,会先看表中是否有意向锁,如果有意向锁就会等锁释放,再去尝试加锁,减少了锁的查询。

开发常用的SQL的加锁情况

SQL类型加锁类型加锁方式
insert into …排它锁MySQL自动加锁
update …排它锁MySQL自动加锁
delete …排它锁MySQL自动加锁
select * from table无锁MySQL自动加锁
select * from table for update排它锁手动加锁
select * from table lock in share mode共享锁手动加锁

查询正在被阻塞的SQL语句:

SELECT * FROM information_schema.INNODB_TRX;

什么是死锁?
死锁就是两个或两个以上的线程互相持有对方想要的资源,且各自处于阻塞等待获取资源的情况,造成线程无限制的阻塞等待下去,以至于程序无法执行下去,这就是死锁。

死锁产生的条件?

  • 互斥条件:资源同一时刻只能被一个进程使用。
  • 请求与保持条件:一个进程占有一个资源,且在等待另外一个资源,但是另外一个资源被其他进程占用。,
  • 不可剥夺条件:占有的资源不能被强行抢占,除非自己释放资源。
  • 循环等待条件:多个进程相互持有对方的资源,且相互等待获取对方的资源,形成了环路。

以上是产生死锁的四个必要条件,缺一不可,那如果想要打破死锁,就只需要破坏其中任何一个条件即可。

MySQL避免死锁的一些方法:

  • 尽量基于主键或者唯一索引更新数据。
  • 控制单次事务中操作的数据量。
  • 控制单次事务中设计的表数量。
  • 一个事务中涉及到多个表加锁的时候,加锁解锁尽量保证顺序一致。
  • 数据检索时候尽量能够用到索引,尽量避免无索引导致的表级锁。

锁分析的方法
SQL如下:

show status like 'innodb_row_lock%';

执行结果:
在这里插入图片描述

  • Innodb_row_lock_current_waits:当前正在等待锁定的数量。
  • Innodb_row_lock_time:等待总时长,从系统启动到现在锁定总时间长度。
  • Innodb_row_lock_time_avg:平均等待时长,每次等待所花平均时间。
  • Innodb_row_lock_time_max:最大等待时长,从系统启动到现在等待最长的一次所花的时间。
  • Innodb_row_lock_waits:等待此时,统计系统启动后到现在总共等待的次数。

如有不正确的地方请各位指出纠正。

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值