ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

乐观锁与悲观锁

2019-08-29 23:50:21  阅读:169  来源: 互联网

标签:事务 goods num 乐观 version 悲观 数据 id


(Lock)

锁是一种保证数据安全的机制和手段,其并不是特定于某项技术的,其主要是通过在并发下控制多个操作的顺序执行,以此来保证数据安全地变动

例如在程序中,当多个线程修改共享变量时,可以给修改操作上锁(syncronized);在数据库中,当多个用户修改表中同一数据时,我们可以给该行数据上锁

悲观锁(Pessimistic Concurrency Control)

总是假设最坏的情况,每次取数据的时候都认为别人会修改,所以每次取数据都会加锁。这样别人在操作这条数据的时候,如果没有拿到锁,就会发生阻塞,操作就无法执行。

数据库中的行锁,表锁,读锁,写锁等都是悲观锁

乐观锁(Optimistic Concurrency Control)

总是假设最好的情况,每次取数据的时候都认为别人不会修改数据,所以不对数据加锁。但是会在更新的时候判断一下在此期间别人有没有更新这个数据,判断可以使用版本号机制和CAS算法实现。

乐观锁通常是通过在表中增加一个版本(version)或时间戳(timestamp)来实现,其中版本最为常用.

事务从数据库中取数据时,会将该数据的版本也取出来(v1),当事务对数据变动完毕需要提交至数据库时,会将之前取出的v1与数据的最新版本v2进行对比

  • v1 = v2:说明数据变动期间没有其他事务对该数据进行修改,此时允许事务对表中数据进行修改,且修改后version会加1

  • v1 != v2:说明有其他事务修改数据,此时不允许数据更新至表中,一般情况下是通知用户让其重新操作

锁的实现

实现场景

AB用户都需要购买一本小说,两者打开了同一家书店,该店商品表goods结构和表中数据如下:

idnamenum
1 小说 1
2 童话 1

可以看出,小说只有1本,如果AB同时下单,在不加锁的情况下有可能导致超卖

悲观锁实现

解决思路

认为数据修改产生冲突的概率较大,所以在更新之前显式地对需要修改的记录加锁,直到修改完之后再释放锁

实现过程:

  • A下单时先给小说这条记录加锁,此时该行数据只能由A进行操作,B需要买的话需要等A操作结束

  • A买完之后,B查询发现数量已经为0,放弃购买

数据库演示

开启两个MySQL会话,即两个命令行,分别代表事务A和事务B

事务A事务B
BEGIN  
SELECT num FROM goods WHERE id=1 FOR UPDATE;  
num=1  
  BEGIN
  SELECT num FROM goods WHERE id=1 FOR UPDATE;
UPDATE goods SET num=num-1 WHERE id=1; waiting
COMMIT waiting
  num=0
  COMMIT

注意点:

  • 由于MySQL默认自动提交,所以此处显式地使用BEGIN开启事务

  • 使用FOR UPDATE给需要修改的数据加锁

  • 对于事务B来说,waiting状态表示在尝试获取数据的锁,由于数据的锁被事务A持有,所以此时事务B阻塞

  • 直到事务A释放锁资源之后,事务B才能获取数据,此时拿到的是事务A修改之后的数据

乐观锁实现

解决思路

乐观锁可以通过版本号机制来实现,所以需要给表goods加上version字段,表变动之后结构如下:

idnamenumversion
1 小说 1 0
2 童话 1 0

乐观锁认为数据修改产生冲突的概率不大,多个事务在修改数据之前先查出版本号,在修改时将版本号作为修改条件,所以只会有一个事务修改成功

实现过程:

  • AB同时将小说的数据查出来,然后A先买,以id=1 and version=0作为条件进行数据更新

  • A操作完成,小说数量减1,版本号加1

  • B开始购买,也将id=1 and version=0作为条件进行更新

  • B更新完之后发现更新的行数为0,说明已经有人更改过数据,此时提醒用户重新查看最新数据再进行购买

数据库演示

开启两个MySQL会话,即两个命令行,分别代表事务A和事务B

事务A事务B
SELECT num, version FROM goods WHERE id=1;  
num=1, version=0  
  SELECT num, version FROM goods WHERE id=1;
  num=1, version=0
UPDATE goods SET num=num-1, version=version+1 WHERE id=1 and version=0;  
SELECT num, version FROM goods WHERE id=1;  
num=0, version=1  
  UPDATE goods SET num=num-1, version=version+1 WHERE id=1 and version=0;
  SELECT num, version FROM goods WHERE id=1;
  num=0, version=1

对于事务B来说,可以看出更新的行数为0,所以应该提醒用户重新处理

分析

优缺点

悲观锁

优点:能够利用锁机制实现数据变化的顺序执行,保证数据安全性

缺点:一个事务对数据进行加锁之后,其他事务需要一直等待,如果持有锁的事务执行时间过长,会显著影响系统的吞吐量

乐观锁

优点:不在数据库上进行加锁,只在更新时进行校验,能够避免悲观锁带来的吞吐量下降的问题

缺点:由于乐观锁是人为实现的,所以仅仅适用于自己的业务当中,如果有外来事务插入,可能发生错误

应用场景

悲观锁:适用于多写的应用类型,这样可以防止数据出错。

乐观锁:适用于多读的应用类型,这样可以提高吞吐量。

标签:事务,goods,num,乐观,version,悲观,数据,id
来源: https://www.cnblogs.com/jeemzz/p/11432707.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有