当前位置: 首页 > news >正文

EFCore_悲观锁与乐观锁(MySQL)

简述

悲观锁:对并发可能导致的资源抢占问题持悲观态度,总是认为会有其他的线程来抢占资源,所以会在实际上对资源上锁

乐观锁:对并发可能导致的资源抢占问题持乐观态度,并不对资源实际上锁,而是在对资源进行更新时,对表中的并发令牌(单字段并发控制)或行版本字段(多字段并发控制)进行一致性检测(是否与开始读取该资源时一致),若一致则认为此时不存在资源抢占问题,进而更新数据(更新数据时会一同更新行版本字段)

悲观锁

悲观锁实际上是通过MySQL的 for update 语句实现的
优点: 简单
缺点: 使用不当会严重影响性能甚至造成死锁
所以实际上更推荐使用乐观锁

示例:

internal class Program
{static void Main(string[] args){using ThisDbContext dbContext = new();using var tr = dbContext.Database.BeginTransaction(); // 开启事务// 加锁(for update语法会添加行锁)var result = dbContext.Books.FromSqlInterpolated($"select * from books where price > 0 for update");///Thread.Sleep(3000);if (result.Any()){result.ForEachAsync(e =>{e.Price += 1;Console.WriteLine(e.ToString());});}else{Console.WriteLine("books not found");}///// 解锁dbContext.SaveChanges();// 提交事务tr.Commit();Console.ReadLine();}
}

乐观锁:单字段并发控制

实体类对应的配置类中设置并发令牌:

builder.Property(e => e.Price).IsConcurrencyToken(); // 设置并发令牌

对字段执行更新前,会检测字段的当前值与读取数据时的值是否相等,若相等则认为不存在并发冲突,可以进行更新,否则认为存在并发冲突,抛出异常(DbUpdateConcurrencyException)

示例:

internal class Program
{static void Main(string[] args){using ThisDbContext dbContext = new();var result = dbContext.Books.Where(e => e.Price >= 1).SingleOrDefault();Thread.Sleep(3000);if (result != null){try{// 此处进行更新操作// ---------------dbContext.SaveChanges();Console.WriteLine("数据更新完成");}catch (DbUpdateConcurrencyException ex){Console.WriteLine("并发冲突");// 获取发生并发冲突时的数据实体var enrty = ex.Entries[0];var nowId = enrty.GetDatabaseValues().GetValue<int>("Id");//var nowId = enrty.GetDatabaseValuesAsync().Result.GetValue<int>("Id");Console.WriteLine($"并发冲突发生于Id: {nowId}的数据");}}else{Console.WriteLine("不存在符合条件的数据");}Console.ReadLine();}
}

注意点:

1. 捕获DbUpdateConcurrencyException异常并处理

2. 通过异常实例可以获取发生并发冲突时的数据实体

单字段并发控制无法识别ABA问题,且单字段的并发控制存在其局限性,所以通常推荐使用多字段并发控制

乐观锁:多字段并发控制

1.实体类添加行版本属性:

public byte[] RowVersion { get; set; }

版本控制属性的类型必需为byte[],在表中对应的行版本字段类型为timestamp(6)

2.实体类对应的配置类中设置行版本字段:

builder.Property(e => e.RowVersion).IsRowVersion();

当任意数据被更新时,行版本属性对应的表字段---行版本字段也会被更新(行版本字段的更新EFCore控制,无需人工更新)

并发状态下EFCore通过比对前后获得的行版本是否相同来判断是否存在并发冲突,以此实现并发控制

示例略

题外话

timestamp(6)在极高的并发情况下存在精度隐患,若想规避该隐患可将并发令牌设置为Guid,并在执行SaveChanges()之前人工更新并发令牌(注意,是并发令牌,在实体类对应的配置类中通过IsConcurrencyToken()设置)

该方式实质上是单并发控制,但只要记得在每次更新数据时都更新并发令牌,是可以实现多并发控制的效果的

相关文章:

  • Unity 粒子特效(下)
  • 服务器BMC基础知识总结
  • vue3弹窗usehook
  • C#面:举列 a=10,b=15,在不用第三方变量的前提下,把a,b的值互换
  • Linux驱动开发实战宝典:设备模型、模块编程、I2C/SPI/USB外设精讲
  • 图形化用户界面-java头歌实训
  • python本学期所有代码!
  • 容器内存
  • 13 Redis-- 数据一致性模型、MySQL 和 Redis 的数据一致性
  • 【JavaScript脚本宇宙】从Cypress到Nightwatch.js:全面解析前端自动化测试工具
  • ROS2自定义接口Python实现机器人移动
  • llama.cpp
  • CPP入门:日期类的构建
  • [AIGC] HashMap的扩容与缩容:动态调整容量以提高性能
  • 【JavaEE精炼宝库】多线程进阶(2)synchronized原理、JUC类——深度理解多线程编程
  • 2017 前端面试准备 - 收藏集 - 掘金
  • 30天自制操作系统-2
  • Angular 4.x 动态创建组件
  • gcc介绍及安装
  • Intervention/image 图片处理扩展包的安装和使用
  • isset在php5.6-和php7.0+的一些差异
  • java2019面试题北京
  • JAVA并发编程--1.基础概念
  • JS字符串转数字方法总结
  • magento 货币换算
  • react 代码优化(一) ——事件处理
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 记一次和乔布斯合作最难忘的经历
  • 今年的LC3大会没了?
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 区块链共识机制优缺点对比都是什么
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 在Mac OS X上安装 Ruby运行环境
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​比特币大跌的 2 个原因
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • #ifdef 的技巧用法
  • #Linux(权限管理)
  • #大学#套接字
  • (2)Java 简介
  • (52)只出现一次的数字III
  • (C#)获取字符编码的类
  • (ZT)薛涌:谈贫说富
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (五)c52学习之旅-静态数码管
  • (轉貼) UML中文FAQ (OO) (UML)
  • .chm格式文件如何阅读
  • .NET Core 中插件式开发实现
  • .NET 的静态构造函数是否线程安全?答案是肯定的!