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

MySQL-锁

作者头像
端碗吹水
发布2020-09-23 10:40:39
4940
发布2020-09-23 10:40:39
举报
文章被收录于专栏:程序猿的大杂烩

锁的概念:

任何关系型数据库里都会有锁的机制,锁是基于事务的一种机制,锁主要是用来保护数据的安全,不被破坏,因为在数据量大并且高并发的情况下,就容易出现多个用户同时对同一个数据操作的情况,在这种情况下没有锁来保护数据的话,就会导致数据的混乱,数据就会遭到破坏。

所以锁的作用就是当一个用户操作某些数据的时候,可以锁住这些数据,其他用户就不能对这些数据进行操作了。数据库锁的概念和编程中线程的概念是差不多的,都是让用户能够排队地去操作数据。

在数据库里锁分两大类,行级锁和表级锁。这里主要是介绍行级锁里的两种锁:排他锁和共享锁。

在数据库中使用锁:

排他锁:

排他锁,顾名思义就是拒绝其他用户操作的锁,A用户对某行数据使用了排他锁,那么除了A用户之外的其他用户都无法对这行数据进行操作。在数据库中insert、update、delete语句是自动带有排他锁的,在事务没有被提交前,其他用户都无法对上了排他锁的数据进行操作,锁会随着事务的结束而消失。

在数据库里想要使用锁的话,得先把数据的默认的自动事务,改为手动事务,命令:set autocommit=0;

13badfbb9ffcdc73af6cb4b66a44396f.png
13badfbb9ffcdc73af6cb4b66a44396f.png

改为手动事务后,每次写SQL语句对数据库做出操作都需要使用commit提交或者rollback回滚,不然的话默认是回滚的。

  示例使用insert插入一条数据:

3c3a3f99f7b51bb7f1173784dffcfd29.png
3c3a3f99f7b51bb7f1173784dffcfd29.png

在没使用commit前,这条语句是不会写入数据到表格中的:

ec734bf6e35016eeb93c957ecc218b9e.png
ec734bf6e35016eeb93c957ecc218b9e.png

commit提交之后才会将数据写入到表格中:

8735852d59d020dda803ac645f08b1ad.png
8735852d59d020dda803ac645f08b1ad.png
f8834125c53f4ba5cf19a34cf2b3a12f.png
f8834125c53f4ba5cf19a34cf2b3a12f.png

现在我们开启两个MySQL客户端来做一下排他锁的实验,现在用户A要使用update语句对一行数据进行更新,但是用户B也要使用update语句对一行数据进行更新:

f1bd0d565e6c6cde266f3b2b15793752.png
f1bd0d565e6c6cde266f3b2b15793752.png
bdf5857f6dc298c956731bf2281e98f8.png
bdf5857f6dc298c956731bf2281e98f8.png
2208d2d20297ce3b23a6211992d196be.png
2208d2d20297ce3b23a6211992d196be.png

从以上实验可以得知系统是自带排他锁的,而且某行数据一旦被锁上了,其他的用户就不能进行操作了,要等锁解除后也就是事务结束后才能进行操作。

同样的insert和delete语句也是一样,自带排他锁:

  Insert:

3fb36434af8a951227edd82066d25a4b.png
3fb36434af8a951227edd82066d25a4b.png
7ede5ccb94db611209c6dfd08637d0cd.png
7ede5ccb94db611209c6dfd08637d0cd.png

Delete:

513e9d62f2bc6d0871223b802f2641ee.png
513e9d62f2bc6d0871223b802f2641ee.png

人为给指定的数据加锁:

以上这些都是系统自带的排他锁示例,但是我们如何自己给某行数据加上一个排他锁呢?答案是在select语句末尾加上for update,例如我要给sid为40的那行数据加上排他锁:

c654304036f44b84e5a421d7a26afacd.png
c654304036f44b84e5a421d7a26afacd.png
88e75998e8e5286055691caafcf6726b.png
88e75998e8e5286055691caafcf6726b.png
513005193882c59d61621b477a12f4d1.png
513005193882c59d61621b477a12f4d1.png

从以上实验可以知道,人为给数据加上排他锁,需要在查询语句末尾加上for update,锁定的行数由where条件决定。

共享锁:

共享锁属于行级锁这一类,共享锁是需要自己加的,数据库不会自动带有这个锁,所谓共享锁就是用户之间可以同时锁定某行、某些行数据,就像共享单车一样,谁都可以去骑,但是谁先骑走就归谁,后面的人就只能等着别人骑完之后才能骑了。共享锁就是这样,任何用户都可以同时锁定同一行数据,但是谁先进行了操作剩余的其他用户的操作均会失败。

当两个用户使用共享锁锁定某行数据时,都可以对这行数据进行操作,先操作的用户会进入等待状态,后操作的用户就会直接失败,当后操作的用户失败后先操作的用户就会被判定为成功。但是为什么先进行的操作才会成功?这是因为不判定其中一方执行成功的话,就会进入到互相等待的死锁状态,所以数据库会判定先操作的执行成功,然后将后操作的判定为失败,并将此事务杀掉,以免进入到无限等待的死锁状态中。

  示例:

879d73d00ca0de0d0a2163279236b65b.png
879d73d00ca0de0d0a2163279236b65b.png
7278934e279028857378441644a1c98b.png
7278934e279028857378441644a1c98b.png

示意图:

5ccd7de8c9f22b6ea8824705493bbfa1.png
5ccd7de8c9f22b6ea8824705493bbfa1.png

在共享锁中可以加上人为定义的排他锁,但是加上排他锁之后就会解除共享锁,加上排他锁之后其他用户就不能操作了:

120717a8223bf5a9624d2e479eaa334e.png
120717a8223bf5a9624d2e479eaa334e.png

实际上我们在对加有共享锁的数据进行insert、update、delete操作时,就是在从共享锁转换为排他锁,因为这些语句是自带排他锁的,所以系统才会判定先执行操作的用户成功。

通过JDBC使用锁:

在JDBC里使用锁是和数据库里的语法是一样的,排他锁在查询语句末尾加for update,共享锁在查询语句末尾加lock in share mode,下面我们做一些实验就明白了。

  排他锁示例,需要用两个一样的类做实验,因为需要模拟两个客户端:

333834c089722684221a52072ba7b66f.png
333834c089722684221a52072ba7b66f.png
186c1a0d2024c37cc499d73c147aa038.png
186c1a0d2024c37cc499d73c147aa038.png

运行结果:

ccf8fbf7ac2496f2776e28a81a18cc43.png
ccf8fbf7ac2496f2776e28a81a18cc43.png

因为TestFor1类先运行的,所以它先加入了排他锁,TestFor2再运行的话就无法访问student表的数据,所以不会有任何打印,相当于进入到了等待状态。

0f0dacd7ff62a842095172af6f0676c0.png
0f0dacd7ff62a842095172af6f0676c0.png

TestFor1类将事务提交后,TestFor2类紧接着就把数据打印出来了,因为这个表的排他锁解除了。

共享锁示例,同样的需要两个类一样的类:

c890ca319d8831aa18172688621b486c.png
c890ca319d8831aa18172688621b486c.png
379ddeb3b33a37e8b89fc2ece2fd5c1c.png
379ddeb3b33a37e8b89fc2ece2fd5c1c.png

运行结果:

4b864fb4be9506c7cecaa23d6ab6309f.png
4b864fb4be9506c7cecaa23d6ab6309f.png

可以看到两个客户端都打印的数据,先执行操作的用户同样会进入到等待状态。

4bdab18e3e59257d82129faa023387b6.png
4bdab18e3e59257d82129faa023387b6.png

最后用户B的操作执行失败,打印出错误,而用户A自然是执行成功了,打印出这次操作影响的行数。

在日常生活中其实也能经常看到共享锁,例如自助售票、网络售票、网购下单等等,在这种情况下一旦别人先一步锁定了票之后,那你就无法购票或显示购票失败。这种就是锁的实际应用,这只是其中一种例子,因为锁有很多种,以便于不同的应用。

下面做一个售票的小例题:

先在数据库里创建一张表,填充数据:

c2f516992eb36df82b21ce76c64a331b.png
c2f516992eb36df82b21ce76c64a331b.png

代码示例:

c714fba886ac06a5b038f9072b210b2b.png
c714fba886ac06a5b038f9072b210b2b.png
fea04ffdc2135e28f72e97fe8bf1c402.png
fea04ffdc2135e28f72e97fe8bf1c402.png

运行结果:

100df307fdc080e592832cb9e8e6e823.png
100df307fdc080e592832cb9e8e6e823.png

票被购买后state列的值就改为0,表示被购买的票:

f9227e99d6172106b6e5209a374be12d.png
f9227e99d6172106b6e5209a374be12d.png

再声明一个类,把代码复制一下,模拟两个用户同时购票的情况:

d8b26d02947327f48d904245d4264f2f.png
d8b26d02947327f48d904245d4264f2f.png

运行结果:

e6b2e7b9c7862a8da5c3cf930d0793fa.png
e6b2e7b9c7862a8da5c3cf930d0793fa.png

从运行结果可以看出和我们平时购票的情况一样,票的信息大家都可以浏览,但是被人抢先买了的话,后面的人就买不到了,就会显示出票失败或者票已售出了,这就是共享锁的应用之一。

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

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

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

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

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