并发控制——悲观锁和乐观锁详解

2024-06-03 08:08

本文主要是介绍并发控制——悲观锁和乐观锁详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

并发控制——悲观锁和乐观锁详解

背景

考虑下面两个并发带来的问题:

1、丢失更新:一个事务的更新结果覆盖了其它事务的更新结果,即所谓的更新丢失。

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

例如:

两个用户同时修改商品库存表,A、B同时进入,看到的库存都是100,A购买一件把库存修改为99(100-1)。此时B购买两件把库存修改为98(100-2),因为A、B同时读到的库存都是100,B并不能看到A做的库存更新,所以造成B脏读,造成A丢失更新。

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

锁分类

悲观锁

悲观锁就是用户修改数据时看起来很悲观,保守态度,担心别的用户会同时修改这条数据,所以每次修改时会提前把这条数据锁定起来,只有自己可修改(但别的用户可以读),等自己修改完了再释放锁。

乐观锁

乐观锁就是用户修改数据时心态很乐观,不管别人修改不修改数据,我都不上锁,我修改的时候判断下数据有没有发生变化,没发生变化我就会更新成功,发生变化了就不会更新成功我再去重试之前的动作直到更新成功。

锁应用

悲观锁

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

关闭命令为:set autocommit=0;

悲观锁一般使用select…for update实现,在执行的时候会锁定数据,虽然会锁定数据,但是不影响其他事务的普通查询使用。

在我们使用悲观锁的时候事务中的语句例如:

//开始事务

begin;/begin work;/start transaction; (三选一)

//查询信息

select * from order where id=1 for update;

//修改信息

update order set name='names';

//提交事务

commit;/commit work;(二选一)

此处的查询语句for update关键字,在事务中只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一条数据时会等待其它事务结束后才执行,一般的SELECT查询则不受影响。

注意事项

执行事务时关键字select…for update会锁定数据,防止其他事务更改数据。但是锁定数据也是有规则的。

查询条件与锁定范围:

1、具体的主键值为查询条件

比如查询条件为主键ID=1等等,如果此条数据存在,则锁定当前行数据,如果不存在,则不锁定。

2、不具体的主键值为查询条件

比如查询条件为主键ID>1等等,此时会锁定整张数据表。

3、查询条件中无主键

会锁定整张数据表。

4、如果查询条件中使用了索引为查询条件

明确指定索引并且查到,则锁定整条数据。如果找不到指定索引数据,则不加锁。

乐观锁

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

例如表

student(id,name,version)

1 a 1

当事务一进行更新操作:update student set name='txt' where id = #{id} and version = #{version};

此时操作完后数据会变为id = 1,name = txt,version = 2,当另外一个事务二同样执行更新操作的时候,却发现version != 1,此时事务二就会操作失败,从而保证了数据的正确性。

2、使用时间戳来实现,原理同上。

3、使用其他数据库字段,如:金额,更新时添加条件判断金额是否变化,原理同上。

乐观锁图示

并发控制——悲观锁和乐观锁详解

结论

两种锁各有优缺点,不能单纯的定义哪个好于哪个。乐观锁比较适合数据修改比较少,读取比较频繁的场景,即使出现了少量的冲突,这样也省去了大量的锁的开销,故而提高了系统的吞吐量。但是如果经常发生冲突(写数据比较多的情况下),上层应用不不断的retry,这样反而降低了性能,对于这种情况使用悲观锁就更合适。

关注我的头条号,每天一篇干货,进步不是一点点!

这篇关于并发控制——悲观锁和乐观锁详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1026421

相关文章

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input