【悲观锁/乐观锁/排它锁/共享锁/表级锁/行级锁/死锁】

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 【悲观锁/乐观锁/排它锁/共享锁/表级锁/行级锁/死锁】

文章目录



本文内容:


悲观锁/乐观锁/排它锁/共享锁/表级锁/行级锁/死锁


2b28d86e64e34c2ba0b5c0bc447e58be.png

悲观锁


每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。适用于写为居多的场景下。比如行锁,表锁等,读锁,写锁,syncronized实现的锁等。sql中实现悲观锁,使用for update对数据加锁,例如:select num from goods where id = 1 for update;


乐观锁


每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,在表中增加一个版本(version)或时间戳(timestamp)来实现。适用于读为居多的场景下。乐观锁适用于多读的应用类型,这样可以提高吞吐量。


工作流程


获取当前数据版本

更新操作版本号+1

提交更新时,获取版本号

比较提交时的版本号与第一次获取的版本号,如果一致,那么认为资源是最新的,可以更新

否则回滚或者抛出异常


案例:


事务一开启,男柜员先执行读操作,取出金额和版本号,执行写操作,此时金额改为 120,版本号为1,事务还没有提交

事务二开启,女柜员先执行读操作,取出金额和版本号,执行写操作,此时金额改为 50,版本号变为 1,事务未提交


现在提交事务一,金额改为 120,版本变为1,提交事务。理想情况下应该变为 金额 = 50,版本号 = 2,但是实际上事务二 的更新是建立在金额为 100 和 版本号为 0 的基础上的,所以事务二不会提交成功,应该重新读取金额和版本号,再次进行写操作。这样,就避免了女柜员 用基于 version=0 的旧数据修改的结果覆盖男操作员操作结果的可能。


乐观锁和悲观锁的使用:(高性能、高可用、高并发)来说,悲观锁的实现用的越来越少了,但是一般多读的情况下还是需要使用悲观锁的,因为虽然加锁的性能比较低,但是也阻止了像乐观锁一样,遇到写不一致的情况下一直重试的时间。使用悲观锁锁住某个数据后,再遇到其他需要修改数据的操作,那么此操作就无法完成金额的修改,对产品来说是灾难性的一刻,使用乐观锁的版本号机制能够解决这个问题。


行锁和表锁


MySQL常用引擎有MyISAM和InnoDB,而InnoDB是mysql默认的引擎。MyISAM不支持行锁,而InnoDB支持行锁和表锁。


如何加锁?MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。


显式加锁:

上共享锁(读锁)的写法:lock in share mode,例如:select math from zje where math>60 lock in share mode;

上排它锁(写锁)的写法:for update,例如:select math from zje where math >60 for update;


表级锁


表锁:不会出现死锁,发生锁冲突几率高,并发低。


MySQL的表级锁有两种模式:

表共享读锁

表独占写锁


读锁会阻塞写,写锁会阻塞读和写

对MyISAM表的读操作,不会阻塞其它进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。

对MyISAM表的写操作,会阻塞其它进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。


MyISAM不适合做写为主表的引擎,因为写锁后,其它线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞

行锁:会出现死锁,发生锁冲突几率低,并发高。


行锁


在MySQL的InnoDB引擎支持行锁,与Oracle不同,MySQL的行锁是通过索引加载的,也就是说,行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描,行锁则无法实现,取而代之的是表锁,此时其它事务无法对当前表进行更新或插入操作。


for update:如果在一条select语句后加上for update,则查询到的数据会被加上一条排它锁,其它事务可以读取,但不能进行更新和插入操作。


行锁的实现需要注意:

行锁必须有索引才能实现,否则会自动锁全表,那么就不是行锁了。

两个事务不能锁同一个索引。

insert,delete,update在事务中都会自动默认加上排它锁。

行锁场景:

A用户消费,service层先查询该用户的账户余额,若余额足够,则进行后续的扣款操作;这种情况查询的时候应该对该记录进行加锁。否则,B用户在A用户查询后消费前先一步将A用户账号上的钱转走,而此时A用户已经进行了用户余额是否足够的判断,则可能会出现余额已经不足但却扣款成功的情况。为了避免此情况,需要在A用户操作该记录的时候进行for update加锁。


死锁


产生死锁的四个必要条件

互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。

请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有


处理死锁的方法:

预防死锁:通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个条件,来防止死锁的发生。

避免死锁:在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免死锁的发生。

检测死锁:允许系统在运行过程中发生死锁,但可设置检测机构及时检测死锁的发生,并采取适当措施加以清除。

解除死锁:当检测出死锁后,便采取适当措施将进程从死锁状态中解脱出来。


避免死锁的技术:

加锁顺序(线程按照一定的顺序加锁)

加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

死锁检测 (首先为每一个进程和每一个资源指定一个唯一的号码;而后创建资源分配表和进程等待表)


死锁检测工具:

Jstack命令:用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,生成java虚拟机当前时刻的线程快照。生成线程快照的主要目的是定位线程出现长时间停顿的缘由,如线程间死锁、死循环、请求外部资源致使的长时间等待等。 线程出现停顿的时候经过jstack来查看各个线程的调用堆栈,就能够知道没有响应的线程到底在后台作什么事情,或者等待什么资源。

JConsole工具:用于链接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。


什么是死锁?锁等待?如何优化这类问题?通过数据库哪些表可以监控?


死锁是指两个或多个事务在同一资源上互相占用,并请求加锁时,而导致的恶性循环现象。当多个事务以不同顺序试图加锁同一资源时,就会产生死锁。当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。InnoDB的并发写操作会触发死锁,InnoDB也提供了死锁检测机制,可以通过设置innodb_deadlock_detect参数可以打开或关闭死锁检测。打开死锁检测数据库发生死锁时自动回滚(默认选项)。关闭死锁检测,发生死锁的时候,用锁超时来处理,通过设置锁超时参数innodb_lock_wait_timeout可以在超时发生时回滚被阻塞的事务


锁等待:mysql数据库中,不同session在更新同行数据中,会出现锁等待


重要的三张锁的监控表innodb_trx,innodb_locks,innodb_lock_waits


如何优化锁:

1、尽可能让所有的数据检索都通过索引来完成,从而避免Innodb因为无法通过索引键加锁而升级为表级锁定

2、合理设计索引。不经常使用的列最好不加锁

3、尽可能减少基于范围的数据检索过滤条件


总结

以上就是今天要讲的内容,还希望各位读者大大能够在评论区积极参与讨论,给文章提出一些宝贵的意见或者建议📝,合理的内容,我会采纳更新博文,重新分享给大家。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
5月前
乐观锁、悲观锁、互斥锁、读写锁
乐观锁、悲观锁、互斥锁、读写锁
41 3
|
2月前
|
数据库 索引 关系型数据库
乐观锁和悲观锁+行锁和表锁
【8月更文挑战第2天】
47 8
|
2月前
|
SQL 关系型数据库 MySQL
临键锁引发的死锁
【8月更文挑战第4天】
33 0
临键锁引发的死锁
|
2月前
|
关系型数据库 MySQL 数据库
共享锁与排它锁 + 意向锁+记录锁
【8月更文挑战第3天】
21 0
|
2月前
|
数据库 SQL
间隙锁+临键锁
【8月更文挑战第1天】
34 0
|
5月前
|
存储 关系型数据库 MySQL
分布式事物【悲观锁、乐观锁、读锁、写锁、间隙锁、临键锁 、 表锁、行锁、页面锁、 如何避免死锁】(二)-全面详解(学习总结---从入门到深化)
分布式事物【悲观锁、乐观锁、读锁、写锁、间隙锁、临键锁 、 表锁、行锁、页面锁、 如何避免死锁】(二)-全面详解(学习总结---从入门到深化)
144 0
|
4月前
|
SQL 关系型数据库 MySQL
MySQL数据库——锁-行级锁(行锁、间隙锁和临键锁)
MySQL数据库——锁-行级锁(行锁、间隙锁和临键锁)
55 0
|
4月前
|
SQL 存储 关系型数据库
MySQL数据库——锁-表级锁(表锁、元数据锁、意向锁)
MySQL数据库——锁-表级锁(表锁、元数据锁、意向锁)
107 0
|
5月前
|
数据库 数据安全/隐私保护
什么是行级锁和表级锁
什么是行级锁和表级锁
86 0
共享锁(读锁)和排他锁(写锁)
共享锁(读锁)和排他锁(写锁)
74 0