数据库 -- 悲观锁与乐观锁

锁( locking )

业务逻辑的实现过程中,往往需要保证数据访问的排他性。如在金融系统的日终结算

处理中,我们希望针对某个 cut-off 时间点的数据进行处理,而不希望在结算进行过程中

(可能是几秒种,也可能是几个小时),数据再发生变化。此时,我们就需要通过一些机

制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓

的 “ 锁 ” ,即给我们选定的目标数据上锁,使其无法被其他程序修改。

hibernate 支持两种锁机制:即通常所说的 “ 悲观锁( Pessimistic Locking ) ”

和 “ 乐观锁( Optimistic Locking ) ” 。

悲观锁( Pessimistic Locking )

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自

外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定

状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能

真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系

统不会修改数据)。

一个典型的倚赖数据库的悲观锁调用:

select * from account where name=”Erica” for update

这条 sql 语句锁定了 account 表中所有符合检索条件( name=”Erica” )的记录。

本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。

Hibernate 的悲观锁,也是基于数据库的锁机制实现。

注意,只有在查询开始之前(也就是 Hiberate 生成 SQL 之前)设定加锁,才会

真正通过数据库的锁机制进行加锁处理,否则,数据已经通过不包含 for update

子句的 Select SQL 加载进来,所谓数据库加锁也就无从谈起。

乐观锁( Optimistic Locking )

相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依

靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库

性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进

行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过

程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作

员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几

百上千个并发,这样的情况将导致怎样的后果。

乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本

( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于

数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来

实现。

读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提

交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据

版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个

version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。

1 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50

( $100-$50 )。

2 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并

从其帐户余额中扣除 $20 ( $100-$20 )。

3 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣

除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大

于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。

4 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数

据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的

数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记

录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。

这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作

员 A 的操作结果的可能。

从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A

和操作员 B 操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系

统整体性能表现。

需要注意的是,乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局

限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户

余额更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。在

系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进行相应调整(如

将乐观锁策略在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途

径,而不是将数据库表直接对外公开)。

时间: 2024-09-30 21:29:46

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

Oracle数据库悲观锁与乐观锁详解

数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做. 先从悲观锁开始说.在SqlServer等其余很多数据库中,数据的锁定通常采用页级锁的方式,也就是说对一张表内的数据是一种串行化的更新插

数据库中的悲观锁和乐观锁详解

数据中的锁分为两类:悲观锁和乐观锁,锁还有表级锁.行级锁 表级锁例如: SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除 SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 行级锁例如: select * from table_name where id = 1 for update; 悲观锁(Pressimistic Locking) 对数据被外界(包括本系统当前的其他事务,以及来自

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

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

数据库事务的悲观锁和乐观锁

转载出处:http://www.hollischuang.com/archives/934 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段. 无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想.其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像memcache.hibernate.tair

聊聊数据库乐观锁和悲观锁,乐观锁失败后重试

在写入数据库的时候需要有锁,比如同时写入数据库的时候会出现丢数据,那么就需要锁机制. 数据锁分为乐观锁和悲观锁,那么它们使用的场景如下: 1. 乐观锁适用于写少读多的情景,因为这种乐观锁相当于JAVA的CAS,所以多条数据同时过来的时候,不用等待,可以立即进行返回. 2. 悲观锁适用于写多读少的情景,这种情况也相当于JAVA的synchronized,reentrantLock等,大量数据过来的时候,只有一条数据可以被写入,其他的数据需要等待.执行完成后下一条数据可以继续. 他们实现的方式上有所

悲观锁、乐观锁和数据库的锁之间的关系

首先悲观锁和乐观锁是基于业务逻辑来讲的,他们和数据库的锁是不同的概念,数据库的锁是实现数据库事务的机制. 乐观锁就在并发情况下,避免覆盖更新的一种机制,是程序员自己通过添加一个version字段实现,每次load数据都读出这个字段,在更新的时候比较下这个version字段是否一致而避免覆盖了不同的版本.至于他为什么叫乐观锁就是因为认为他的并发情况不高,即使发生并发导致更新失败也可以忍受.(适度控制并发,占用资源少,性能高,当然完全放任不管性能更高,根本不占用资源) 而悲观锁是基于数据库的锁机制来

数据库的锁:行级锁、表锁、乐观锁、悲观锁的实现原理

一.相关名词 表级锁(锁定整个表) 页级锁(锁定一页) 行级锁(锁定一行) 共享锁(S锁,MyISAM 叫做读锁) 排他锁(X锁,MyISAM 叫做写锁) 悲观锁(抽象性,不真实存在这个锁) 乐观锁(抽象性,不真实存在这个锁) 二.InnoDB与MyISAM Mysql 在5.5之前默认使用 MyISAM 存储引擎,之后使用 InnoDB .查看当前存储引擎: show variables like '%storage_engine%'; MyISAM 操作数据都是使用的表锁,你更新一条记录就要

数据库的悲观锁、乐观锁

并发控制 并发情况下,需要做一些控制(一般是加锁),保证共享数据的一致性. 并发操作数据库时,需要给数据库中的数据加锁,确保数据库中数据的一致性. 数据库锁的常见分类 按使用方式来分:悲观锁.乐观锁 按锁级别来分:共享锁.排它锁(主要是这2种,当然还有其他的) 按锁粒度来分:行级锁.表级锁.页级锁 悲观锁  Pessimistic Lock 悲观的,假设是最坏的情况,认为其它线程一定会修改当前线程使用的数据库数据,当前线程一定要给使用的数据库数据加锁. 悲观锁只是个统称,并不是指某一种具体的锁.

数据库锁之乐观锁

一.乐观锁的介绍 乐观锁是相对悲观锁而言,也是为了避免数据库幻读.业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性. 乐观锁的机制:对每条数据库加上版本号或时间撮,在每次对数据进行操作(尤其是修改操作)时,总会带上版本号获取数据同时更改后修改版本号. 二.乐观锁的代码示例 2.1 创建一张表 create table em_oplock ( id VARCHAR(100) not null, value VARCHAR(

025 hibernate悲观锁、乐观锁

Hibernate谈到悲观锁.乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差 并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了.称为并发性不好 数据库隔离级别:见前面章级 025-1悲观锁: 悲观锁:具有排他性(我锁住当前数据后,别人看到不此数据) 悲观锁一般由数据机制来做到的. 悲观锁的实现 通常依赖于数据库机制,在整修过程中将数据锁定,其它任何用户都不能读取或修改(如:必需我修改完之后,别人才可以修改) 悲观锁的适用场景: 悲观锁一般适合短事务比较