乐观锁

mysql悲观锁与乐观锁

隐身守侯 提交于 2021-01-09 05:22:07
最近学习了一下数据库的悲观锁和乐观锁,根据自己的理解和网上参考资料总结如下: 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中, 将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了 加锁机制,也无法保证外部系统不会修改数据)。 使用场景举例 :以MySQL InnoDB为例 商品goods表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。 1如果不采用锁,那么操作方法如下: //1.查询出商品信息 select status from t_goods where id=1; //2.根据商品信息生成订单 insert into t_orders (id,goods_id) values (null,1); //3.修改商品status为2 update t_goods set status=2; 上面这种场景在高并发访问的情况下很可能会出现问题。 前面已经提到,只有当goods status为1时才能对该商品下单,上面第一步操作中

乐观锁与悲观锁——解决并发问题

て烟熏妆下的殇ゞ 提交于 2020-04-08 06:48:59
引言 为什么需要锁(并发控制)?   在 多用户环境 中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。 典型的冲突 有: 丢失更新 :一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。 脏读 :当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。 为了解决这些并发带来的问题。 我们需要引入并发控制机制。 并发控制机制   悲观锁:假定会发生并发冲突, 屏蔽一切可能违反数据完整性的操作。[1]   乐观锁:假设不会发生并发冲突, 只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。 乐观锁应用 乐观锁介绍:   乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。那么我们如何实现乐观锁呢,一般来说有以下2种方式:   1.使用 数据版本 (Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的

分布式系统---幂等性设计

 ̄綄美尐妖づ 提交于 2020-04-02 15:00:38
最近做的项目的性能调优中关于幂等设计的一些总结 场景:假设有这样一个方法,包含了一些DB操作,check if existing then update else save. 如果两个线程同时去执行这个方法,并且他们处理的是同一条数据,期望应该是其中一个线程是save,另外一个是update。但是有可能线程的处理时间相当重合,线程A在check的时候,线程B也在check,这时A和B都认为数据不存在,都去save,在 数据库 有unique 约束的情况下其中一个操作会失败,而我们期望的可能是后面一个操作应该update(取决于具体业务)。 这是很典型的多线程问题,check - then do something,在单系统环境中这很容易用线程同步来处理(syncronised). 但是如果是分布式系统,这两个线程在不同的server上面,syncronised 是不会起效的,而且同步往往降低效率,并不是我们想要的。 拥有相同参数的多次请求对系统造成的副作用应该是相同的,这就是幂等性。在这个例子里面就是说保证相同的ID组合只会插入一条数据到DB里面,如果一个请求是save,后续的都应该update这条。在单系统中也可以用幂等的设计来规避使用syncronized,因为那会降低效率。一般情况下数据库就能保证这种幂等性--用unique关键字,以上面的场景为例

数据库的悲观锁、乐观锁

半腔热情 提交于 2020-03-25 21:00:48
并发控制 并发情况下,需要做一些控制(一般是加锁),保证共享数据的一致性。 并发操作数据库时,需要给数据库中的数据加锁,确保数据库中数据的一致性。 数据库锁的常见分类 按使用方式来分:悲观锁、乐观锁 按锁级别来分:共享锁、排它锁(主要是这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; //提交事务

面试必备之乐观锁与悲观锁

醉酒当歌 提交于 2020-03-25 19:09:34
何谓悲观锁与乐观锁 乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。 悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 两种锁的使用场景 从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候

乐观锁与悲观锁

不打扰是莪最后的温柔 提交于 2020-03-25 17:26:53
乐观锁 悲观锁——是一种思想。可以用在很多方面。 数据库方面: 悲观锁就是for update(锁定查询的行) 乐观锁就是 version字段(比较跟上一次的版本号,如果一样则更新,如果失败则要重复读-比较-写的操作。) JDK方面: 悲观锁就是sync 乐观锁就是原子类(内部使用CAS实现) 本质来说,就是悲观锁认为总会有人抢我的。 乐观锁就认为,基本没人抢。 乐观锁 -不加锁 总认为不会发生并发问题,每一次取数据时总认为其他线程不会对该数据进行更改,但是在更新时会判断其他线程在这之前有没有对该数据进行修改, 数据库当中常用方案:版本号控制      乐观并发控制相信事务之间的数据竞争概率非常小,因此尽可能直接操作,提交的时候才去锁定,不会产生任何锁和死锁。 悲观锁 总是假设最坏的情况,每次取数据时,都会认为其他线程会对该数据进行修改,所以会进行加锁 其他线程访问的时候会阻塞等待,例如在数据库当中可以使用行锁,表锁以及读写锁等方式实现    在Java中synchronized就是悲观锁的表现 在效率上,处理加锁的机制会让数据库产生额外的开销,还会有死锁的可能性。降低并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。 悲观锁的实现方式:悲观锁的实现,依靠数据库提供的锁机制。在数据库中,悲观锁的流程如下: 在对数据修改前,尝试增加排他锁。

说说Java中的那些锁

自闭症网瘾萝莉.ら 提交于 2020-03-22 03:54:22
  在学习Java锁的时候,总觉的比较含糊,感觉一直没有系统的消化理解。所以决定重新梳理一下java相关的锁。     本质来说只有两种锁,乐观锁和悲观锁,其他所谓的可重入、自旋、偏向/轻量/重量锁等,都是锁具有的一些特点或机制。目的就是在数据安全的前提下,提高系统的性能。 乐观锁    乐观锁,顾名思义,就是说在操作共享资源时,它总是抱着乐观的态度进行,它认为自己可以成功地完成操作。但实际上,当多个线程同时操作一个共享资源时,只有一个线程会成功,那么失败的线程呢?它们不会像悲观锁一样在操作系统中挂起,而仅仅是返回,并且系统允许失败的线程重试,也允许自动放弃退出操作。所以,乐观锁相比悲观锁来说,不会带来死锁、饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小。更为重要的是,乐观锁没有因竞争造成的系统开销,所以在性能上也是更胜一筹。   CAS 是实现乐观锁的核心算法,它包含了 3 个参数:V(需要更新的变量)、E(预期值)和 N(最新值)。只有当需要更新的变量等于预期值时,需要更新的变量才会被设置为最新值,如果更新值和预期值不同,则说明已经有其它线程更新了需要更新的变量,此时当前线程不做操作,返回 V 的真实值。    如何实现原子操作   在 JDK 中的 concurrent 包中,atomic 路径下的类都是基于 CAS 实现的。AtomicInteger 就是基于

悲观锁和乐观锁浅谈

守給你的承諾、 提交于 2020-03-18 08:36:06
乐观锁和悲观锁的比较和探究 平时我们在多线程并发编程的情况下经常要使用到锁机制,本文主要讨论了常用的悲观锁和乐观锁机制,同时乐观锁中使用的CompareAndSet(CAS)跟踪了源码并进行一定的分析。 悲观锁(Pessimistic Lock) 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 我们认为系统中的并发更新会非常频繁,并且事务失败了以后重来的开销很大,这样以来,我们就需要采用真正意义上的锁来进行实现。悲观锁的基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁。 实现方式: 平时大多在数据库层面实现加锁操作,比如JDBC方式:在JDBC中使用悲观锁,需要使用select for update语句 e.g. Select * from Account where ...(where condition).. for update. 乐观锁(Optimistic Lock) 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据

理解乐观锁与悲观锁

坚强是说给别人听的谎言 提交于 2020-03-16 08:05:44
  DBMS中并发控制的任务是确保多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性   乐观锁和悲观锁是并发控制主要采用的技术手段。   无论是乐观锁还是悲观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库中有乐观锁和背锅所的盖面,其他很多地方都有类似的概念。   针对不同的业务场景,应该选用不同的并发控制方式,所以,不要把乐观锁和悲观锁下一的理解为DBMS中的概念,更不要把他们和数据库中提供的锁机制混为一谈。其实,在DBMS中,悲观锁正式利用数据库本身的锁机制来实现的。   悲观锁   在关系数据库中,悲观锁是一种并发控制的方法,它可以组织一个事务一影响其他用户的方式来修改数据。如果一个事务执行的操作嗾使某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。   悲观锁主要用于数据征用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。   悲观锁指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往一句数据库提供的锁机制(也只有数据库层提供的锁机制擦能真正保证数据访问的排他性,否则,及时在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)   在数据库中

乐观锁与悲观锁

早过忘川 提交于 2020-03-14 07:55:19
一乐观锁 总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。 version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 核心SQL代码: update table set x=x+1, version=version+1 where id=#{id} and version=#{version}; CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。 二悲观锁 总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中