数据库的悲观锁、乐观锁

并发控制

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

并发操作数据库时,需要给数据库中的数据加锁,确保数据库中数据的一致性。

数据库锁的常见分类

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

按锁级别来分:共享锁、排它锁(主要是这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语句,执行起来需要时间,这期间可能其他线程修改了当前线程要使用的数据。

原文地址:https://www.cnblogs.com/chy18883701161/p/12569870.html

时间: 2024-08-26 17:55:56

数据库的悲观锁、乐观锁的相关文章

悲观锁&乐观锁

最近意外发现之前对悲观锁乐观锁的理解有误,所以重新学习了一下. 1.悲观锁 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据). 使用场景举例:以MySQL InnoDB为例 商品goods表中有一个字段

最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 独享锁/共享锁 乐观锁/悲观锁 分段锁 自旋锁 01.乐观锁 vs 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用. 1.乐观锁 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制. 乐观锁适用于多

黑马day11 悲观锁&乐观锁

悲观锁:悲观锁悲观的认为每一次操作都会造成更新丢失问题,在每次查询时就加上排他锁 乐观锁:乐观锁会乐观的认为每次查询都不会造成更新丢失.利用一个版本字段进行控制 查询非常多,修改非常少,使用乐观锁 修改非常多,查询非常少,使用悲观锁 第一张图的解释: 小zhang想在一个游戏网站买装备,此时游戏网站会去重定向到银行(假设是建设银行),然后银行再重定向会这个游戏网站. 但是小zhang点击充值的时候由于网页很慢,点击了好几下.....这个时候为了防止银行的钱过多的充值到小zhang的用户,银行会

php使用数据库的并发问题(乐观锁与悲观锁)

在php与数据库的交互中,如果并发量大,并且都去进行数据库的修改的话,就有一个问题需要注意.数据的锁问题.就会牵扯数据库的事务跟隔离机制 数据库事务依照不同的事务隔离级别来保证事务的ACID特性,也就是说事务不是一开启就能解决所有并发问题.通常情况下,这里的并发操作可能带来四种问题: 更新丢失:一个事务的更新覆盖了另一个事务的更新,这里出现的就是丢失更新的问题. 脏读:一个事务读取了另一个事务未提交的数据. 不可重复读:一个事务两次读取同一个数据,两次读取的数据不一致. 幻象读:一个事务两次读取

Hibernate 悲观锁,乐观锁

业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的"锁",即给我们选定的目标数据上锁,使其无法被其它程序修改. Hibernate 支持两种锁机制: 1. 悲观锁(Pessimistic Locking) 从加载对象就开始锁定.修改过程中一直是锁.直到事务commit()提交后再解锁. session.load(Info.class,"p003",LockOp

【MySQL】悲观锁&乐观锁

悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的特点是先获取锁,再进行业务操作,即"悲观"的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作.通常所说的"一锁二查三更新"即指的是使用悲观锁.通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select - for update操作来实现

多线程之 悲观锁,乐观锁

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

谈谈mysql的悲观和乐观锁

悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.之前有写过一篇文章关于并发的处理思路和解决方案,这里我单独将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍一次吧. 悲观锁(Pessimistic Lock) 悲观锁的特点是先获取锁,再进行业务操作,即"悲观"的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作.通常所说的"一锁二查三更新"即指的是使用悲观锁.通常来讲在数据库上的悲观锁需要数据库本身提供支持,

悲观锁乐观锁简单整理

一:介绍 悲观锁,正如其名,具有强烈的独占和排他特性.它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据).乐观锁机制采取了更加宽松的加锁机制.悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性.但随之而来的就是数据库 性能的大

Mysql:行锁 表锁 乐观锁 悲观锁 读锁 写锁

锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足.在DBMS中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎).表级锁(MYISAM引擎)和页级锁(BDB引擎 ). 行锁 锁定整个行数据,开销大,加锁慢,会出现死锁.锁定粒度小,发生锁冲突的概率低,并发度高. 表锁 锁定整个表数据,开销小,加锁快,不会出现死锁.锁定粒度大,发生锁冲突概率高,并发度低. 悲观锁 每次取数据时都认为别人会修改,所以每次取数据的时候都会上锁,这样别人想拿这个数据就会被阻