ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

数据库的悲观锁、乐观锁

2020-03-25 20:53:36  阅读:248  来源: 互联网

标签:加锁 数据库 乐观 version 线程 悲观 quantity


 

并发控制

并发情况下,需要做一些控制(一般是加锁),保证共享数据的一致性。   并发操作数据库时,需要给数据库中的数据加锁,确保数据库中数据的一致性。        

数据库锁的常见分类

按使用方式来分:悲观锁、乐观锁

按锁级别来分:共享锁、排它锁(主要是这2种,当然还有其他的)

按锁粒度来分:行级锁、表级锁、页级锁

 

 

 

悲观锁  Pessimistic Lock

悲观的,假设是最坏的情况,认为其它线程一定会修改当前线程使用的数据库数据,当前线程一定要给使用的数据库数据加锁。   悲观锁只是个统称,并不是指某一种具体的锁。悲观锁主要包括:
  • 共享锁(S锁,share),又称为读锁,所有线程都可以访问,但都只能读
  • 排它锁(X锁),又称为写锁,是排它的,同一时刻只能有一个线程来访问,这个线程可对加锁的数据进行读写。
Java中的synchronized、ReentrantLock等独占锁就是悲观锁思想的实现。

 

悲观锁一般要借助数据库本身提供的锁机制来实现。

以mysql最常用的InnoDB引擎为例:加排它锁

begin;  //开始事务
select * from tb_user where id=1 for update;  //给选中的行加锁
update tb_user set username='chy',password='abcd' where id=1;  //修改数据
commit;  //提交事务

要开启事务,不一定非要用mysql的begin、commit,比如可以使用Spring的事务管理。

先select...for update 锁定要使用的行,再修改数据。

InnoDB默认使用行级锁,锁定要使用的行;但行级锁是基于索引的,如果sql语句用不到索引,会使用表级锁将整张表锁住。

 

 

 

 

乐观锁   Optimistic Lock

乐观的,假设是很好的情况,认为一般不会发生冲突,只在提交更新时进行冲突检测。


乐观锁不需要借助数据库自身的锁机制来实现。乐观锁常见的实现方式:

1、CAS方式

select quantity from tb_goods where id=1;  //先查询该种商品的库存,假设为10
update tb_goods set quantity=quantity-1 where id=1 and quantity=10;  //提交修改时带上条件库存等于10,确保数据没有被修改

CAS 即 Compare and Swap,先和数据库中的quatity比较,如果quantity等于先前查询到的值(10),说明记录没有被修改,执行操作。

 

CAS方式可能会产生ABA问题:

开始查到库存为10,有一个线程将库存改为了9(比如售出1件),然后又有一个线程将库存改回了10(比如买家不满意,退货了),库存还是原来的值,但数据已经被改过了。

 

且选择作为比较的那些字段不一定能标识这条记录是否已被修改。

 

 

2、版本号机制(推荐)

select version from tb_goods where id=1;  //查询这条记录的数据版本号,假设为5
update tb_goods set quantity=quantity-1,version=version+1 where id=1 and version=5;  //提交更新时检测版本号是否一致

设计表时额外增加version列,每次更新一条记录时都将这条记录的version+1,执行更新操作时先查询这条记录的version,提交更新时比较version是否和查询到的相同,相同就说明数据未被修改,就会提交更新。

 

 

 

 

悲观锁、乐观锁的比较、选择

比较:悲观锁是一定要加锁,乐观锁实际上并没有加锁。

 

乐观锁的2种实现都有个问题:

乐观锁是假设在本线程访问数据库数据时,其它线程不会修改这部分数据。

而并发量大的时候,你查到version=5,其他线程往往会修改当前线程使用的数据库数据,修改version,因为没加锁,其他线程也可以访问当前线程使用的数据库数据。更新的时候很容易出现更新不了的情况。

 

就是说乐观锁适合并发量小的情况使用,那为什么在高并发的情况下也会使用乐观锁?因为效率|性能。

悲观锁是每次都要加锁,悲观锁保证了数据的一致性,更新成功概率高,但效率低下。

乐观锁实际没有加锁,更新成功概率要低一些,尤其是高并发的时候,但每次都不加锁,效率高、性能好。

 

面对高并发,首先要能扛住,抗都扛不住,很多请求都不能及时处理,谈什么操作成功率。

抗住了,就算更新失败,好歹用户知道请求处理了、只是操作失败了;没抗住,用户请求半天没响应,连处理都还没处理。

 

选择:

并发量小,悲观锁、乐观锁的更新成功率都高,但悲观锁加了锁,更新成功率更高,优先使用悲观锁;

并发量大,使用乐观锁,优先考虑性能。

 

 

 

 

乐观锁的优化写法

update tb_goods set quantity=quantity-1 where id=1 and quantity-1>=0; 

只要库存够就行,管它其他线程修不修改,反正只有一条sql语句,不涉及事务。

 

这种写法有要求:

数据库操作要只有一条sql语句,如果有多条sql语句,执行起来需要时间,这期间可能其他线程修改了当前线程要使用的数据。

 



   

标签:加锁,数据库,乐观,version,线程,悲观,quantity
来源: https://www.cnblogs.com/chy18883701161/p/12569870.html

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

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

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

ICode9版权所有