ICode9

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

使用悲观锁还是乐观锁

2021-06-17 13:58:03  阅读:168  来源: 互联网

标签:读取 小张 乐观 并发 冲突 悲观 使用


一、悲观锁和乐观锁

读取频繁使用乐观锁,写入频繁使用悲观锁。

乐观锁想成一种检测冲突的手段,而悲观锁是一种避免冲突的手段。

 

1. 悲观锁(Pessimistic Lock)

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

 

实际生产环境里边,如果并发量不大,完全可以使用悲观锁定的方法,这种方法使用起来非常方便和简单。但是如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以就要选择乐观锁定的方法。

悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好。

 

2. 乐观锁(Optimistic Lock)

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁不能解决脏读的问题。

 

乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。

但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。

 

以版本控制系统为例,来说说两种最基本的并发性问题。

 

二、冲突导致的并发性问题

1. 丢失更新:

写-写冲突

一个事务的更新覆盖了其它事务的更新结果

 

小张想修改源代码里面的a方法。

正在她修改的同时,小李打开了这个文件,修改了b方法并且保存了文件。

等小张修改完成后,保存文件,小李所做的修改就被覆盖了。

 

2. 不一致的读(脏读):

写-读冲突

当一个事务读取其它完成一半事务的记录时,就会发生脏读取。

 

小张想要知道包里面一共有多少个类,包分了a,b两个子包。

小张打开a包,看到了7个类。

突然小张接到老婆打来的电话,在小张接电话的时候,小李往a包中加了2个类,b包中加了3个类(原先b包中是5个类)。

小张接完电话后再打开b包,看到了8个类,很自然得出结论:包中一共有15个类。

很遗憾,15个永远不是正确的答案。

在小李修改前,正确答案是12(7+5),修改后是17(9+8)。

这两个答案都是正确的,虽然有一个不是当前的。但15不对,因为小张读取的数据是不一致的。

小结:不一致读指你要读取两种数据,这两种数据都是正确的,但是在同一时刻两者并非都正确。

 

三、隔离和不可变:

在企业应用中,解决并发冲突的两种常用手段是隔离和不可变。

只有当多个活动(进程或者线程)同时访问同一数据时才会引发并发问题。一种很自然的思路就是同一时刻只允许一个活动访问数据。如果小张打开了文件,就不允许其他人打开,或者其他人只能通过只读的方式打开副本,就可以解决这个问题。

隔离能够有效减少发生错误的可能。我们经常见到程序员陷入到并发问题的泥潭里,每一段代码写完都要考虑并发问题,这样太累了。我们可以利用隔离技术创建出隔离区域,当程序进入隔离区域时不用关心并发问题。好的并发性设计就是创造这样的一些隔离区域,并保证代码尽可能的运行在其中。

另一种思路:只有当你需要修改共享的数据时才可能引发并发性问题,所以我们可以将要共享的数据制作为“不可变”的,以避免并发性问题。当然我们不可能将所有的数据都做成不可变的,但如果一些数据是不可变的,对它们进行并发操作时我们就可以放松自己的神经了。

 

四、乐观并发控制、悲观并发控制:

如果数据是可变的,并且无法隔离呢?这种情况下最常用的两种控制就是乐观并发控制和悲观并发控制。

 

假设小张和小李想要同时修改同一个文件。

 

如果使用乐观锁,俩人都能打开文件进行修改,如果小张先提交了内容,没有问题,他所做的改变会保存到服务器上。但小李提交时就会遇到麻烦,版本控制服务器会检测出两种修改的冲突,小李的提交会被拒绝,并由小李决定该如何处理这种情况(对于绝大部分版本控制软件来说,会读取并标识出小张做的改变,然后由小李决定是否合并)。

 

如果使用的是悲观锁,小张先检出(check out)文件,那么小李就无法再次检出同一文件,直到小张提交了他的改变。

 

建议你将乐观锁想成一种检测冲突的手段,而悲观锁是一种避免冲突的手段(严格来说,乐观锁其实不能称之为“锁”,但是这个名字已经流传开了,那就继续使用吧)。

 

两种锁各有优缺点。

乐观锁可以提高并发访问的效率,但是如果出现了冲突只能向上抛出,然后重来一遍;悲观锁可以避免冲突的发生,但是会降低效率。

 

选择使用哪一种锁取决于访问频率和一旦产生冲突的严重性。如果系统被并发访问的概率很低,或者冲突发生后的后果不太严重(所谓后果应该指被检测到冲突的提交会失败,必须重来一次),可以使用乐观锁,否则使用悲观锁。

 

我们可以使用两种形式的并发控制策略:乐观并发控制和悲观并发控制。

在实际应用的源代码控制系统中,这两种策略都可以被使用,但是现在大多数源代码开发者更倾向于使用乐观锁策略。

 

这两种策略各有优缺点。悲观锁的问题是减少了并发的程序。

在乐观锁和悲观锁之间进行选择的标准是:冲突的频率与严重性。

如果冲突很少,或者冲突的后果不会很严重,那么通常情况下应该选择乐观锁,因为它能得到更好的并发性,而且更容易实现。

但是,如果冲突的结果对于用户来说痛苦的,那么就需要使用悲观策略。

 

 

乐观锁的局限是:

只能在提交数据时才发现业务事务将要失败,而且在某些情况下,发现失败太迟的代价会很大。用户可能花了一个小时的时间输入一份租约的详细信息,错误太多会让用户对系统失去信心。另一个方法是使用悲观锁,它可以尽早地发现错误,但理难以编程实现,而且会降低系统的灵活性。

注:以上是对并发控制中的乐观锁策略和悲观锁策略概念及解决思路的文字描述,下面我将对项目中具体怎么实现乐观锁策略及悲观锁策略进行描述。

 

乐观锁应用
  1. 使用自增长的整数表示数据版本号。更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。
  2. 使用时间戳来实现.

 

悲观锁应用


需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

 

在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;

但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.
悲观锁会造成访问数据库时间较长,并发性不好,特别是长事务。
乐观锁在现实中使用得较多,厂商较多采用。
 

 

https://blog.csdn.net/zl1zl2zl3/article/details/89277672

https://blog.csdn.net/xlgen157387/article/details/47906553

标签:读取,小张,乐观,并发,冲突,悲观,使用
来源: https://blog.51cto.com/u_15077160/2915134

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

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

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

ICode9版权所有