前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL-锁02

MySQL-锁02

作者头像
端碗吹水
发布2020-09-23 10:40:19
2950
发布2020-09-23 10:40:19
举报

l 表级锁

l 悲观锁

l 乐观锁

l 脏读简介

l 不可重复读简介

l 幻读简介

表级锁:

之前我们介绍了行级锁,顾名思义行级锁就只是锁住一行或多行数据,因为针对的是行去锁的,因为一个表格内会有很多行数据,要在这些数据中去锁定其中几行数据,是比较耗费资源。而表级锁则是可以锁住整个表,所以相对于行级来说没那么耗费资源,表级锁有两个模式:只读模式和只写模式,这和文件权限里的只读只写有点类似。

在一般情况下表格锁并不经常使用,在这里只是介绍一下如何使用表级锁,和解锁表级锁,而且表级锁的资料都可以在网络上查找到,所以了解一下即可,在mysql官方也有表格锁语法的文档:

https://dev.mysql.com/doc/refman/5.6/en/lock-tables.html

使用只读模式的表级锁,语法:

LOCK TABLES 表名 READ

示例:

15c3f548b169f7e3847145d31f3f91c1.png
15c3f548b169f7e3847145d31f3f91c1.png

  我们再打开一个客户端来看看能否使用SELECT语句查询这个带有表级锁的表格的数据:

8ae6b16dba6d08aedb44a6df5bbc3b19.png
8ae6b16dba6d08aedb44a6df5bbc3b19.png

  因为我们使用的是只读模式的表级锁,自然每个用户都可以读取、查询这个表格的数据,那么我们可以尝试一下update这会对表格数据修改的语句能否执行:

9f65323647f99ad55f165cccbc5941b8.png
9f65323647f99ad55f165cccbc5941b8.png

在行级锁里即便某些行数据被上锁了也还是能够使用insert语句插入数据的,那么我们试一下在表格锁里是否能行得通:

85e9fc68ede7486da60201fe5402c338.png
85e9fc68ede7486da60201fe5402c338.png

从结果可以得知在表级锁的只读模式下,是不允许任何用户对上锁的表格进行任何的修改的。

自然的delete语句也无法使用:

150689baad83c72a60a9c434acb0d41d.png
150689baad83c72a60a9c434acb0d41d.png

那么如何解锁呢?看看解锁时会发生什么,解锁表级锁的语法很简单:

UNLOCK TABLES

示例:

58255dc48f34931d2d7b712db08a09c1.png
58255dc48f34931d2d7b712db08a09c1.png

使用只写模式的表级锁,语法:

LOCK TABLES 表名 WRITE

示例:

8db004ef80fc94e050ff6e2128e8bb1b.png
8db004ef80fc94e050ff6e2128e8bb1b.png

在表级锁的只写模式里,只有上锁用户可以对表格进行写入数据,其他用户是不可以写入数据的,其他用户就连使用SELECT语句查询数据都不可以:

206f88930f9927a5b8189e3dae0566bd.png
206f88930f9927a5b8189e3dae0566bd.png

上锁用户可以使用insert语句插入数据,其他用户则不允许这个操作:

fb2b5234c71690da2d9791a5035c060d.png
fb2b5234c71690da2d9791a5035c060d.png

update语句也是一样的:

19832fb3c2cbb30b3b000573a0c9b4f1.png
19832fb3c2cbb30b3b000573a0c9b4f1.png

还有delete语句:

4386edd1682cab852a3dad885d81835a.png
4386edd1682cab852a3dad885d81835a.png

如果用户给一张表格上了表级锁,那么这个用户在给这个表格解锁之前就只能操作这个表格,数据库里的其他表格均不可以进行任何的操作,如果操作就会报错:

554ea98d98d424e59138d704bcb8ad82.png
554ea98d98d424e59138d704bcb8ad82.png

只写模式的解锁语法是一样的,都是UNLOCK TABLES:

61c5b2293bdcb5fb48faf60a58c0388d.png
61c5b2293bdcb5fb48faf60a58c0388d.png

总结一下表级锁,表级锁就是针对表格进行锁定,相对于行级锁没那么耗资源,表级锁有两个模式,只读模式和只写模式,只读模式下上锁用户和其他用户都只能查询数据不能写入数据,只写模式下上锁用户可以进行查询数据和写入数据,其他用户既不能查询数据,也不能写入数据,执行任何SQL语句都会进入等待状态,一直等到表格解锁为止,当表格解锁的时候在等待中的事务会马上被执行。某个用户对某个表格使用了表级锁的话,就只能操作那个表格,数据库里的其他表格均不可进行任何操作。

悲观锁:

悲观锁(Pessimistic Lock)是一种概念、解决某些问题的模式,并不是一种特定的机制,悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

所以简单来说悲观锁就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会

block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

例如之前我们做的火车票务系统的小案例,就是使用的悲观锁的方式,在我们的代码里都是借助于数据库自带的锁机制完成的,当用户A在购票时用户B就不能够购票,或者购票失败,这就是在整个数据处理过程中,将数据处于锁定状态,具有很明显的排他性(悲观)。

  代码:

b0c8930097b8d9e3fd43b3f027c6e35d.png
b0c8930097b8d9e3fd43b3f027c6e35d.png
4ea1ec2084bb8004a91efa8df48ea1fd.png
4ea1ec2084bb8004a91efa8df48ea1fd.png

悲观锁的优点与不足:

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那数据。

乐观锁:

乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

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

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

所以实际上乐观锁和悲观锁一样也是一种概念、解决某些业务需求的模式,并不是一种特定的机制,乐观锁的主要实现方式是我们开发人员自己通过在数据库中增加一条存储数据版本的列,然后通过代码来判断这些数据的版本,而不是借助数据库自带的锁来完成的一种锁定数据的模式。

例如我们假设一种情况:用户A和用户B同时在ATM机里往一个账户余额2000元的账户取款,两个用户都查询到账户还有2000元,所以用户A要取款1500,用户B要取款1000,然后这两个取款事务会同时提交,如果这个银行的系统逻辑写得不够好的话,就会出现2000-1500-1000=-500的结果,账户余额就会出现负数。

示意图:

e2bdf28570de0a15b9adfb2e0bb068ed.png
e2bdf28570de0a15b9adfb2e0bb068ed.png

在这种取款的情况下,如果使用悲观锁来锁住数据的话,由于其排他性,那么另外一个用户就无法查询账户余额,只能处于等待状态,因为在悲观锁里在事务结束之前数据都是处于锁定状态,而且在银行在这种数据量大的地方,使用共享锁这种行级锁也耗费资源。所以就需要用到乐观锁了,乐观锁只有在操作提交的时候才会去锁定数据。在乐观锁中我们可以给数据设定一个版本号,一旦这个数据发生修改,版本号就会发生变化,每一个操作都会先判断版本号是否是最新的版本号,不是的话就不允许操作,在乐观锁的实现过程中我们并不会使用到数据库自带的锁,所以用户们都可以任意的查询或提交操作。

  示意图:

8f12ad511ba4d020565bb864015232e3.png
8f12ad511ba4d020565bb864015232e3.png

下面我们做一个简单的取款系统来演示如何实现乐观锁:

先准备一个表格里面填充一行数据:

f412781b401fe836be2c157b81d9d055.png
f412781b401fe836be2c157b81d9d055.png

代码示例:

4e1af1ca1c95ee543e7d51edbf696ba6.png
4e1af1ca1c95ee543e7d51edbf696ba6.png

运行结果:

1468c50e8b9a07a77ecd889cc86b1a1e.png
1468c50e8b9a07a77ecd889cc86b1a1e.png

乐观锁的优点与不足:

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

脏读简介:

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。

不可重复读:

在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。

幻读:

幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样.一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-10-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档