Mysql锁机制--乐观锁 & 悲观锁

前言

mysql的并发操作时而引起的数据的不一致性(数据冲突):

丢失更新:两个用户(或以上)对同一个数据对象操作引起的数据丢失。

    解决方案:1.悲观锁,假设丢失更新一定存在;sql后面加上for update;这是数据库的一种机制。

         2.乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。

第一部分 悲观锁

1 概念

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

2 命令行演示

2.1 准备数据

DROP DATABASE IF EXISTS cyhTest;
CREATE DATABASE cyhTest;

USE cyhTest;

DROP TABLE IF EXISTS employee;

CREATE TABLE IF NOT EXISTS employee (
  id      INTEGER NOT NULL,
  money   INTEGER,
  version INTEGER,
  PRIMARY KEY (id)
)
  ENGINE = INNODB;

INSERT INTO employee VALUE (1, 0, 1);

SELECT * FROM employee;

目前数据库中只有一条记录,且初始Money=0

2.2 测试

测试准备:

  • 还是两个会话(终端),左边会话是白色背景、右边会话是黑色背景
  • 关闭自动提交:set autocommit = 0;

现在开始测试:

第一步:两个终端均关闭自动提交

左边:

右边:

第二步:左边利用 select .... for update 的悲观锁语法锁住记录

select * from employee where id = 1 for update; 

第三步:右边也尝试利用 select .... for update 的悲观锁语法锁住记录

可以看到,Sql语句被挂起(被阻塞)!

提示:如果被阻塞的时间太长,会提示如下:

第四步:左边执行更新操作并提交事务

Sql语句:

update employee set money = 0 + 1 where id = 1;
commit; 

结果:

分析:

  • Money 的旧值为0,所以更新时 Money=0+1
  • 一执行 commit 后,注意查看右边Sql语句的变化

第五步:查看右边Sql语句的变化

分析:

  • 被左边悲观锁阻塞了 11.33 秒
  • Money=1,这是左边更新后的结果

2.3 结论

可以看到,当左边(事务A)使用了 select ... for update 的悲观锁后,右边(事务B)再想使用将被阻塞,同时,阻塞被解除后事务B能看到事务A对数据的修改,所以,这就可以很好地解决并发事务的更新丢失问题啦(诚然,这也是人家悲观锁的分内事)

第二部分 乐观锁

1 概念

1.1 理解方式一(来自网上其它小伙伴的博客)

乐观锁认为一般情况下数据不会造成冲突,所以在数据进行提交更新时才会对数据的冲突与否进行检测。如果没有冲突那就OK;如果出现冲突了,则返回错误信息并让用户决定如何去做。

1.2 理解方式二(来自网上其它小伙伴的博客)

乐观锁的特点是先进行业务操作,不到万不得已不会去拿锁。乐观地认为拿锁多半会是成功的,因此在完成业务操作需要实际更新数据的最后一步再去拿一下锁。

1.3 我的理解

理解一:就是 CAS 操作

理解二:类似于 SVN、GIt 这些版本管理系统,当修改了某个文件需要提交的时候,它会检查文件的当前版本是否与服务器上的一致,如果一致那就可以直接提交,如果不一致,那就必须先更新服务器上的最新代码然后再提交(也就是先将这个文件的版本更新成和服务器一样的版本)

乐观锁不是数据库自带的,需要我们自己去实现。

2 如何实现乐观锁呢

首先说明一点的是:乐观锁在数据库上的实现完全是逻辑的,数据库本身不提供支持,而是需要开发者自己来实现。

常见的做法有两种:版本号控制及时间戳控制。

版本号控制的原理:

  • 为表中加一个 version 字段;
  • 当读取数据时,连同这个 version 字段一起读出;
  • 数据每更新一次就将此值加一;
  • 当提交更新时,判断数据库表中对应记录的当前版本号是否与之前取出来的版本号一致,如果一致则可以直接更新,如果不一致则表示是过期数据需要重试或者做其它操作(PS:这完完全全就是 CAS 的实现逻辑呀~)

至于时间戳控制,其原理和版本号控制差不多,也是在表中添加一个 timestamp 的时间戳字段,然后提交更新时判断数据库中对应记录的当前时间戳是否与之前取出来的时间戳一致,一致就更新,不一致就重试。

特点

乐观并发控制相信事务之间的数据竞争概率是较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁

原文地址:https://www.cnblogs.com/nangec/p/12016321.html

时间: 2024-10-12 19:28:54

Mysql锁机制--乐观锁 & 悲观锁的相关文章

乐观锁(optimistic locking)与悲观锁(pessimistic locking)

首先,乐观锁(optimistic locking)与悲观锁(pessimistic locking)基本是针对数据处理来说,也就是跟数据库有关的术语,目的是为了解决并发处理时所遇到的相关性能问题,以避免数据丢失更新. 悲观锁(pessimistic locking):指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态,以保证操作最大程度的独占性,当然也就给数据库增加了大量开销降低了性能.一般依靠数据库提供的锁

Hibernate学习---第十二节:Hibernate之锁机制&乐观锁实现

1.悲观锁 它指的是对数据被外界修改保持保守态度,因些,在整个数据处理过程中,将数据牌锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层的锁机制才能保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据). 一个典型的悲观锁调用示例: select * from account where name = "12345" for update 通过for update子句,这条SQL锁定了account表中所有符合检索条件的记录.本次事务

数据库并发事务控制四:postgresql数据库的锁机制二:表锁

在博文<数据库并发事务控制四:postgresql数据库的锁机制 > http://blog.csdn.net/beiigang/article/details/43302947 中后面提到: 常规锁机制可以参考pg的官方手册,章节和内容见下面 13.3. Explicit Locking http://www.postgresql.org/docs/9.4/static/explicit-locking.html 这节分为:表锁.行锁.页锁.死锁.Advisory锁(这个名字怎么翻译好???

java 多线程9 : synchronized锁机制 之 代码块锁

synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用synchronized同步语句块来解决问题.看一下例子: 下面例子是优化后的例子 使用代码块锁,原先例子是方法锁,就是同步 必须要执行2个for  public class ThreadDomain18 { public void doLongTimeTask() throws Exception

synchronized锁机制 之 代码块锁(转)

synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用synchronized同步语句块来解决问题.看一下例子: 下面例子是优化后的例子使用代码块锁,原先例子是方法锁,就是同步必须要执行2个for. public class ThreadDomain18 { public void doLongTimeTask() throws Exception {

mysql基础--锁机制,表级锁,行级锁

一.表级锁 1.读锁,lock table t_student read;添加了读锁,使得其他sessionA和sessionB都不能修改数据,仅仅可以读数据. show processlist;查看进程,修改的时候状态是在等待表级锁,已经等待了8s 在解锁unlock tables;之后,修改数据的sql也执行成功,如下图所示 2.写锁,当某一个进程在对某一张表实施写锁后,在该进程如果完成了更新(写.insert.update.delete)之后,如果不释放写锁,其他的进程连查看这张表的权限都

Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

synchronized 是互斥锁: lock 更广泛,包含了读写锁 读写锁特点: 1)多个读者可以同时进行读2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者) 互斥锁特点: 一次只能一个线程拥有互斥锁,其他线程只有等待 所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronize

数据库乐观锁与悲观锁

前面说到了数据库的隔离级别,隔离性是数据库中数据有意义的条件之一,而不同的隔离级别,归根到底其实是在读和写的操作中对表.事务后者是表进行对应的锁定操作,所以下面简单总结下数据库的两种类型锁:乐观和悲观锁,很多是概念性的东西和个人理解,不足之处也请指正. 一.锁的概念 简单说说数据库锁的概念,和多线程中的锁类似,数据库中对数据的锁定其实也是保证数据同步的主要手段,它表现出来的是:并发下,线程之间对数据库数据的操作是单方的.而在数据库里面主要有两种锁手段保证这种并发下的线程单方面操作:悲观锁和乐观锁

数据库的乐观锁与悲观锁

概述 无论是悲观锁还是乐观锁,都是人们定义出来的概念,是一种读取和修改数据的并发访问策略,由应用和业务需求来确定的.其实不仅仅是数据库系统中有乐观锁和悲观锁的概念,像memcache.hibernate.tair等都有类似的概念.所以,不要把乐观锁和悲观锁狭义的理解为DBMS中的概念,更不要把他们和数据中提供的锁机制(行锁.表锁.排他锁.共享锁)混为一谈.在DBMS中,只是利用数据库本身提供的锁机制和数据的版本(version)来实现的这两种不同的并发访问策略. 悲观锁 正如其名,它指的是对数据