面试必备的数据库悲观锁与乐观锁

悲观锁与乐观锁

前言

在上一个章节5分钟带你读懂事务隔离性与隔离级别 的最后,其实我们已经提到了锁的概念。本章节接下来将主要介绍以下数据库悲观锁与乐观锁的相关知识。如有错误还请大家及时指出~

本文已同步至 GitHub/Gitee/公众号,感兴趣的同学帮忙点波关注~

问题:

  • 为什么需要锁?
  • 什么是悲观锁?
  • 什么是乐观锁?
  • 悲观锁与乐观锁区别与联系?
  • 悲观锁与乐观锁的使用场景?

为什么需要锁?

在并发环境下,如果多个客户端访问同一条数据,此时就会产生数据不一致的问题,如何解决,通过加锁的机制,常见的有两种锁,乐观锁和悲观锁,可以在一定程度上解决并发访问。

1. 悲观锁(Pessimistic Lock)

1.1 定义

百度百科:

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

其他知识点

悲观锁主要是共享锁排他锁
共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

1.2 案例分析

使用场景举例:以MySQL InnoDB为例

作为演示,我们继续使用之前的数据库表:product表

productId productName productPrice productCount
1 小米 1999 100
2 魅族 1999 100

首先我们需要set autocommit=0,即不允许自动提交

有看过上一篇文章5分钟带你读懂事务隔离性与隔离级别 的同学,可以看到最后我们使用事务隔离级别时,所引申出来的根本问题就是可以通过锁机制解决。

问题

在并发情况下回导致数据一致性的问题:
如果有A、B两个用户需要抢productId =1的小米手机,A、B用户都查询小米手机数量是100,A购买后修改商品的数量为99,B购买后修改数量为99。

用法

每次获取小米手机时,对该商品加排他锁。也就是在用户A获取获取 id=1 的小米手机信息时对该行记录加锁,期间其他用户阻塞等待访问该记录。代码如下:

start transaction;
 
select p.productCount from product p where p.productId = 1 for update;
 
update product p set p.productCount=p.productCount-1 where p.productId=1 ;
 
commit;

操作

下面同时打开两个窗口模拟2个用户并发访问数据库

时间轴 事务A 事务B
T1 start transaction;
T2 select p.productCount from product p where p.productId = 1 for update;
T3 start transaction;
T4 select p.productCount from product p where p.productId = 1 for update;(等待中...)

流程说明

  1. 用户A start transaction开启一个事物。前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交。
  2. 在获得小米手机信息(productId = 1 )时,进行数据加锁操作(for update)。与普通查询方式不同,我们使用了select…for update的方式,这样就通过数据库实现了悲观锁。在这个update事务提交之前其他外界是不能修改这条数据的,但是这种处理方式效率比较低,一般不推荐使用。
  3. 用户B start transaction开启一个事物。
  4. 用户B 也进行查询操作,此时处于等待中(阻塞状态)。ps:需要等待用户A事务提交后,才会执行。

注意:在事务中,只有select…for update(排他锁) 或lock in share mode(共享锁) 操作同一个数据时才会等待其它事务结束后才执行,一般select... 则不受此影响。例如在 T3中执行select p.productCount from product p where p.productId = 1;则能正常查询出数据,不会受第一个事务的影响。

2. 乐观锁(Optimistic Lock)

2.1 定义

百度百科:

乐观锁机制采取了更加宽松的加锁机制。乐观锁是相对悲观锁而言,也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。

其他知识点

实现乐观锁一般来说有以下2种方式:

  • 使用版本号
    使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

  • 使用时间戳
    乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

2.2 案例分析

使用场景举例:以MySQL InnoDB为例

作为演示,我们继续使用之前的数据库表:product表

productId productName productPrice productCount version
1 小米 1999 100 1
2 魅族 1999 100 2

我们以版本号实现的方式进行说明。

操作

查询当前事务隔离级别:


SELECT @@tx_isolation;


结果:
REPEATABLE-READ

下面同时打开两个窗口模拟2个用户并发访问数据库

第一种测试

时间轴 用户A 用户B
T1 start transaction;
T2 select * from product p where p.productId = 1;(productCount=100)
T3 update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影响的行: 1)
T4 start transaction;
T5 select * from product p where p.productId = 1;(productCount=100)
T6 update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(等待中...)
T7 commit;
T8 T6执行(受影响的行: 0)
T9 commit;

流程说明

  1. 事务A开启事务。
  2. 事务A查询当前小米手机数量为100。
  3. 事务A购买小米手机,小米手机数量更新为99。(此时并未提交事务)。
  4. 事务B开启事务。
  5. 事务B查询当前小米手机数量为100。
  6. 事务B购买小米手机,小米手机数量更新为99。注意:此时处于阻塞状态。
  7. 事务A提交事务。
  8. 此时第六步执行完毕,但并未成功(受影响的行: 0)。
  9. 事务B提交事务。

第二种测试

时间轴 用户A 用户B
T1 select * from product p where p.productId = 1;(productCount=100)
T2 update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影响的行: 1)
T3 select * from product p where p.productId = 1;(productCount=100)
T4 update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影响的行: 0)

乐观锁小结

  • 用户B修改数据的时候,受影响行数为0,对业务来说,及更新失败。这时候我们只需要告诉用户购买失败,重新查询一遍即可。
  • 对比第一种和第二种测试,我们会发现第一种测试,将update语句放入事务中会出现阻塞的情况,而第二种测试不会出现阻塞情况。这是为什么呢?update其实在不在事务中都无所谓,在内部是这样的:update是单线程的,及如果一个线程对一条数据进行update操作,会获得锁,其他线程如果要对同一条数据操作会阻塞,直到这个线程update成功后释放锁。

乐观锁不需要数据库底层的支持!

3. 适用场景

悲观锁

比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

乐观锁

比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

文末

本章节主要简单介绍了数据库中乐观锁与悲观锁的相关知识,后续我们将会继续介绍数据库中的其他锁以及相关知识。例如行锁、表锁、死锁、

欢迎关注公众号:Coder编程
获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识!

参考文章:

https://chenzhou123520.iteye.com/blog/1860954

https://chenzhou123520.iteye.com/blog/1863407

微信公众号

推荐阅读

带你了解数据库中JOIN的用法

带你了解数据库中事务的ACID特性

5分钟带你读懂事务隔离性与隔离级别

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容