`

乐观锁与悲观锁的区别

 
阅读更多

为什么需要锁(并发控制)?

在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。

典型的冲突有:

丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。

脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。

为了解决这些并发带来的问题。 我们需要引入并发控制机制。

并发控制机制

    最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发访问最好,页锁粒度最大,表锁介于2者之间。

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

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。    乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。

 

     从数据库厂商的角度看,使用乐观的页锁是比较好的,尤其在影响很多行的批量操作中可以放比较少的锁,从而降低对资源的需求提高数据库的性能。再考虑聚集索引。在数据库中记录是按照聚集索引的物理顺序存放的。如果使用页锁,当两个用户同时访问更改位于同一数据页上的相邻两行时,其中一个用户必须等待另一个用户释放锁,这会明显地降低系统的性能。interbase和大多数关系数据库一样,采用的是乐观锁,而且读锁是共享的,写锁是排他的。可以在一个读锁上再放置读锁,但不能再放置写锁;你不能在写锁上再放置任何锁。锁是目前解决多用户并发访问的有效手段。  

乐观锁应用

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

2.      使用时间戳来实现.

注:对于以上两种方式,Hibernate自带实现方式:在使用乐观锁的字段前加annotation: @Version, Hibernate在更新时自动校验该字段。

悲观锁应用

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

SqlServer中使用

Begin Tran
select top 1 @TrainNo=T_NO
         from Train_ticket   with (UPDLOCK)   where S_Flag=0

      update Train_ticket
         set T_Name=user,
             T_Time=getdate(),
             S_Flag=1
         where 
T_NO=@TrainNo
commit

我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.

结论

在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.

分享到:
评论

相关推荐

    thinkPHP框架乐观锁和悲观锁实例分析

    除了上述的乐观锁和悲观锁实现技巧之外,ThinkPHP框架还提供了其他很多与数据操作和事务管理相关的功能和技巧。例如,在ThinkPHP中,可以使用事务控制相关的类和方法,如mysqli的transaction方法开始事务,commit...

    乐观锁与悲观锁

    介绍数据库事务的定义和事务带来的问题,详细讲解乐观锁与悲观锁的区别

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

    #### 一、悲观锁与乐观锁的概念 悲观锁和乐观锁是计算机科学中用于处理并发控制的两种不同策略,它们主要应用于多线程环境下数据的一致性和完整性保护。两种锁的设计哲学反映了对数据并发访问时的不同预期。 **...

    Hibernate的乐观锁与悲观锁

    ### Hibernate的乐观锁与悲观锁 #### 一、引言 在并发环境下,尤其是在金融、电商等业务场景中,确保数据的一致性和完整性至关重要。**Hibernate**作为一种流行的Java持久层框架,提供了多种机制来处理并发控制...

    Hibernate乐观锁和悲观锁分析

    【Hibernate乐观锁与悲观锁详解】 在开发过程中,尤其是在并发环境下,确保数据的一致性和完整性至关重要。Hibernate,作为Java领域广泛使用的ORM框架,提供了一种处理并发数据访问冲突的手段,那就是锁机制。主要...

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

    乐观锁和悲观锁是数据库事务控制...了解和掌握乐观锁与悲观锁的原理和应用场景,对于提升面试者的数据库管理技能和解决并发问题的能力至关重要。在面试中,深入探讨这些话题将有助于展示自己的专业水平和问题解决能力。

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

    【描述】:“面试必备之乐观锁与悲观锁.pdf”涉及的是并发控制中的两种重要锁机制——悲观锁和乐观锁,它们是多线程环境下确保数据一致性的重要手段。 【标签】:“求职面试 多线程” 【正文】: 悲观锁和乐观锁...

    数据库乐观锁与悲观锁1

    乐观锁与悲观锁各有优缺点。悲观锁适合于高并发写操作的场景,可以防止丢失更新,但可能导致死锁问题。乐观锁则适用于写操作较少,且可以接受偶尔的冲突重试的情况,它减少了锁的开销,提升了系统的吞吐量。然而,...

    面试资源乐观锁与悲观锁

    面试资源乐观锁与悲观锁

    hibernate乐观锁和悲观锁学习

    与悲观锁不同,乐观锁假设并发环境下数据不会被同时修改,因此在读取数据时不会立即加锁。只有在更新数据时,才会检查数据是否自上次读取后发生了变化。在Hibernate中,通常通过在实体类的映射文件中设置`optimistic...

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

    "面试必备之乐观锁与悲观锁" 在多线程编程中,锁机制是必不可少的一部分,锁机制可以防止多个线程同时访问共享资源,避免数据不一致和混乱的情况。锁机制可以分为悲观锁和乐观锁两种,下面我们将详细介绍这两种锁...

    mysql的乐观锁、悲观锁.md

    java,乐观锁,悲观锁详解释

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

    1. **事务隔离级别**:乐观锁和悲观锁与数据库的事务隔离级别密切相关。例如,读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)这四种隔离级别都会...

Global site tag (gtag.js) - Google Analytics