ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

mysql 乐观锁和悲观锁

2021-05-29 13:02:20  阅读:138  来源: 互联网

标签:版本号 更新 乐观 CAS version 线程 悲观 mysql 数据


 前言

悲观锁与乐观锁本质上不是数据库中具体的锁,而是人们定义出来的概念,可以理解为一种思想,是处理并发问题的常用手段(方法)。可以将数据库中的行锁,表锁,排他锁,共享锁根据这种锁思想进行分类。

 

乐观锁的实现方式

一般来说,有两种:版本号和时间戳。

  1. 使用数据版本(Version)记录机制,这是乐观锁最常用的一种实现方式。

通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,就对此version值加一。

当提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,就进行更新操作,否则认为是过期数据。

2. .乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

 

问题抛出

当多人同时对同一条数据做修改,那么数据的最终结果是怎样的呢?

这也就是我们常说的并发问题,会导致你修改的数据被别人覆盖了,然后脏读,你读到的数据不是预期的,而是一些莫名其妙的数据,即别人修改后的数据。

 

这样的问题如何解决呢?锁,可分为乐观锁和悲观锁。

 

  • 乐观锁

简述:很乐观,总是认为不会产生并发问题。每次读取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新的时候会判断其他线程在这之前有没有对数据进行修改。这个判断一般会使用版本号机制或CAS操作实现。

 

常见的实现方式:版本号version

具体实现:在数据表中增加版本号字段,每次对一条数据做更新之前,先查出该条数据的版本号,每次更新数据都会对版本号进行更新(加1)。

先把数据的版本号查出来,在更新时,跟库中数据此时的版本号进行比对,如果相同,则说明该数据没有被修改过,执行更新且成功。如果比对的结果是不一致的,则说明该条数据已经被其他人修改过了,则更新失败(即不更新),客户端可以进行相应的操作提醒。

具体示例:

//1.查询出商品信息,包括版本号



select status,version from t_goods where id=#{id} 



//2.根据商品信息生成订单 

…



//3.修改商品status为2 (比对版本号在这里)



update t_goods 



set status=2,version=version+1 



where id=#{id} and version=#{version};

 

悲观锁

简述:很悲观,总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时(读取或修改),都需要阻塞挂起。悲观锁可以依靠数据库实现,如行锁(共享锁、排它锁)、读锁和写锁等,这些都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁的实现。

 

MySQL InnoDB中使用悲观锁

要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个SQL操作后,MySQL会立刻将结果进行提交。set autocommit=0;

 

CAS

乐观锁的一种典型实现机制就是CAS。

乐观锁主要就是两个步骤:冲突检测和数据更新。当多个线程尝试使用CAS同时更新同一个变量时,只有一个线程可以更新变量的值,其他的线程都会失败,失败的线程并不会挂起,而是告知这次竞争中失败了,并可以再次尝试。

 

CAS操作包括三个操作数:需要读写的内存位置(V)、预期原值(A)、新值(B)。

原理:如果内存位置与预期原值相等,那么将内存位置的值更新为新值B。如果内存位置与预期原值不相等,则不更新。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值)

int compare_and_swap (int* reg, int oldval, int newval)
{
  int old_reg_val = *reg;
  if (old_reg_val == oldval) {
     *reg = newval;
  }
  return old_reg_val;
}

乐观锁是一种思想,CAS只是这种思想的一种实现方式。

 

ABA 问题
比如说一个线程 T1 从内存位置V中取出 A,这时候另一个线程 T2 也从内存中取出 A,并且 T2 进行了一些操作变成了 B,然后 T2 又将 V 位置的数据变回了 A,此时,线程 T1 进行 CAS 操作发现内存中仍然是A,然后 T1 操作是成功的。(假设没有变回A,线程T1操作则是失败的)

 

Ps:本文主要讨论数据库的悲观锁。在java中,也是一样的原理,按照是否对资源加锁分为乐观锁和悲观锁,前者不会加锁,后者会加锁。 

标签:版本号,更新,乐观,CAS,version,线程,悲观,mysql,数据
来源: https://blog.csdn.net/wxd772113786/article/details/117383069

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

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

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

ICode9版权所有