多个事务并发写相同对象时,会出现脏写和更新丢失两种竞争条件。为避免数据不一致,可:

  • 借助DB内置机制
  • 或通过显式加锁、执行原子写操作

但这还不算并发写可能引发的全部问题。

为医院写一个值班管理程序。医院通常会同时要求几个医生待命,前提是至少有一位医生在待命。医生可以放弃他们的班次(例如,如果他们自己生病了),只要至少有一个同事在这一班中继续工作。

Alice、Bob两位值班医生都不适,所以他们都决定请假。但他们恰在同一时刻点击调班按钮

每笔事务总先检查是否至少有两名医生目前在值班。若是,则有一名医生可安全离开去休班。由于DB使用快照隔离,两次检查都返回2 ,所以两个事务都进入下一阶段。Alice更新自己的记录为休班,Bob也更新自己的记录。两个事务都成功提交,最后结果没有医生值班,显然违反至少有一名医生值班的业务要求。

定义写倾斜

这种异常称为写倾斜,不是脏写,也不是丢失更新,这俩事务更新的是两个不同对象(Alice 和 Bob 各自值班记录)。这里发生的冲突不是那么明显,但很显然确实是竞争状态:若两个事务串行,则第二个医生就不能歇班。异常行为只有在事务并发时才可能。

可将写倾斜视为广义的丢失更新。即若两事务读取相同一组对象,然后更新其中一部分:

  • 不同事务可能更新不同对象,则可能发生写倾斜
  • 而若更新同一对象,则可能脏写或丢失更新

我们有很多方法防止丢失更新。但对写倾斜,方案更受限制:

  • 由于涉及多对象,单对象的原子操作无效
  • 基于快照隔离来实现自动检测丢失更新也有问题:PostgreSQL的可重复读,MySQL/InnoDB 的可重复读,Oracle可串行化或SQL Server快照隔离级别中,都不支持自动检测写倾斜。自动防止写倾斜要求真正的可串行化隔离
  • 某些DB支持自定义约束,然后由DB强制执行(如唯一性,外键约束或特定值限制)。但为指定至少有一名医生必须在线,涉及多个对象的约束,大多DB都未内置这种约束,但你可使用触发器或物化视图来实现类似约束
  • 若无法使用可串行化,则次优方案可能是显式锁定事务依赖的行:
BEGIN TRANSACTION;SELECT * FROM doctorsWHERE on_call = TRUE# 告诉DB锁定返回的所有结果行,以用于更新AND shift_id = 1234 FOR UPDATE;UPDATE doctorsSET on_call = FALSEWHERE name = 'Alice'AND shift_id = 1234;COMMIT;

写倾斜案例

写倾斜乍看晦涩,但意识到本质后,很容易注意到更多case:

  • 会议室预订系统

    不能在同一时间对同一会议室进行多次预订。当有人想要预订时,首先检查是否存在相互冲突的预订(即预订时间范围重叠的同一房间),若无,则创建会议(参阅示例-2)

    例-2 会议室预订系统,避免重复预订(在快照级别隔离下不安全)

    BEGIN TRANSACTION;-- 检查所有现存的与 12:00~13:00 重叠的预定
    SELECT COUNT(*) FROM bookings
    WHERE room_id = 123 ANDend_time > '2015-01-01 12:00' AND start_time < '2015-01-01 13:00';-- 若之前的查询返回 0
    INSERT INTO bookings(room_id, start_time, end_time, user_id)VALUES (123, '2015-01-01 12:00', '2015-01-01 13:00', 666);COMMIT;

    快照级别隔离无法防止并发用户预订同一会议室。为避免预订冲突,需可串行化隔离级别。

  • 多人游戏

    例-1中,使用一个锁来防止丢失更新(即两个玩家不能同时移动同一数字)。但锁不妨碍玩家将两个不同数字移动到棋盘的相同位置或其他违反游戏规则的行为。可能需更多约束,否则很容易发生写倾斜。

  • 抢注用户名

    在每个用户拥有唯一用户名的网站上,两个用户可能会尝试同时创建具有相同用户名的帐户。可采用事务检查名称是否被抢占,若无,则使用该名称创建账户。但和之前案例类似,快照隔离下不安全。但唯一约束是简单方案(第二个事务在提交时会因为违反用户名唯一约束而被中止)。

  • 防止双重开支

    支付或积分服务一般需检查用户的支付数额不超过余额。可通过在用户帐户中插入一个临时支出项目,列出帐户中的所有项目,并检查总和是否为正值。由于写倾斜,可能发生两个支出项目同时插入,两个交易都不超额,但一起会导致余额变为负值。

导致写倾斜的幻读

所有这些案例都遵循类似模式:

  1. 首先输入一些匹配条件,即 SELECT 查询所有符合条件的行并检查是否符合一些要求。如至少有两名医生在值班;不存在对该会议室同一时段的预订;棋盘某位置没有出现棋子;用户名还没被抢注;账户里还有余额等

  2. 根据查询结果,应用代码决定是否继续

  3. 若应用决定继续执行,就发起DB写入(插入、更新或删除),并提交事务

    而该写操作会改变步骤2做出决定的前提条件。即若提交写入后,再重复执行步骤1的 SELECT查询,将得到不同结果。因为刚才的写改变了符合搜索条件的行集(现在少了一个医生值班,那时的会议室现已被预订,棋盘上的这个位置已被占,用户名已被抢注,账户余额不够)。

上述步骤可能有不同执行顺序。如可先写,然后SELECT查询,最后根据查询结果决定是放弃还是提交。

医生值班案例,步骤3所修改的行恰好是步骤1查询结果的一部分,所以若通过锁定步骤 1 中的行( SELECT FOR UPDATE )再查询可保证事务安全,避免写倾斜。但其他四个案例不同:它们检查是否 不存在 某些满足条件的行,写入会 添加 一个匹配相同条件的行。若步骤1中的查询没有返回任何行,则 SELECT FOR UPDATE 锁不了任何东西。

这种效应:一个事务中的写入改变另一个事务的搜索查询结果,即幻读。快照隔离避免了只读查询中的幻读,但是在像我们讨论的例子那样的读写事务中,幻读会导致特别棘手的写倾斜。

物化冲突

若幻读的问题是没有对象可以加锁,也许可以考虑人为在DB引入一个锁对象?

如会议室预订案例,想象创建一个关于时间槽和房间的表。此表中的每行对应于特定时间段(如 15min)的特定房间。可提前插入房间和时间的所有可能组合行(例如接下来的六个月)。

现在,要创建预订的事务可以锁定( SELECT FOR UPDATE )表中与所需房间和时间段对应的行。锁定后,它可检查重叠预订并像以前一样插入新预订。该表不是用来存储预订相关信息的,它完全就是一组锁,以防止同时修改同一房间和时间范围内的预订。

这被称为物化冲突(materializing conflicts)方案,因为它将幻读变为DB中一组具体行上的锁冲突。但弄清楚如何物化冲突很难,也很易出错,而让并发控制机制泄漏到应用数据模型是很丑陋的做法。出于这些原因,若无其他办法可以实现,物化冲突应被视为最后手段。大多数情况下, 可串行化(Serializable) 隔离级别更可取。

Java事务编程-弱隔离级别之写倾斜与幻读相关推荐

  1. mysql 并没有幻读_MySQL默认隔离级别REPEATABLE-READ并没有解决幻读问题

    刷脉脉,发现一个帖子讨论幻读问题: https://maimai.cn/web/gossip_detail?src=app&webid=eyJhbGciOiJIUzI1NiIsInR5cCI6 ...

  2. MySQL事务的四种隔离级别,mysql中的不可重复读和幻读的区别,Repeatable read可重复读隔离级别下怎么不存在幻读问题?

    1. 事务的隔离级别 1.1 read uncommited:读未提交.一个事务读到了另一个事务未提交的脏数据,称之为脏读. 1.2 read commited:读已提交.解决了脏读问题,但当前事务两 ...

  3. mysql 脏读 不可重复读 幻读_mysql事务隔离级别/脏读/不可重复读/幻读详解

    一.四种事务隔离级别 1.1read uncommitted 读未提交 即:事务A可以读取到事务B已修改但未提交的数据. 除非是文章阅读量,每次+1这种无关痛痒的场景,一般业务系统没有人会使用该事务隔 ...

  4. 精通Java事务编程(8)-可串行化隔离级别之可串行化的快照隔离

    本系列文章描述了DB并发控制的黯淡: 2PL虽保证了串行化,但性能和扩展不好 性能良好的弱隔离级别,但易出现各种竞争条件(丢失更新,写倾斜,幻读 串行化的隔离级别和高性能就是相互矛盾的吗?也许不是,一 ...

  5. 事务的四大隔离级别中的幻读问题

    1. 什么是幻读? 大部分学习过数据库的人应该都了解幻读这个概念,我在这里帮大家复习下.以下都是以InnoDB存储引擎为例进行说明.数据库并发访问中,针对delete和insert操作可能出现幻读这种 ...

  6. 《MySQL》入门基础知识点大全:数据库操作、增删改查、联表查询、常用函数、MD5加密、事务特性、隔离级别

    MySQL基础知识大全 1.操作数据库 1.1 创建表 1.2 修改表名 1.3 增加表的字段 1.4 修改表的字段 1.4.1 修改表的字段 1.4.2 修改表名 1.5 删除表的字段 1.6 删除 ...

  7. 脏读、不可重复读 共享锁、悲观锁 和 事务五种隔离级别

    http://www.cnblogs.com/adforce/archive/2011/04/20/2021929.html 一.脏读.不可重复读.幻读 1.脏读:脏读就是指当一个事务正在访问数据,并 ...

  8. 数据库 之 事务控制和隔离级别

    1  概述 事务是指一组原子性的SQL查询.或者是一个或多个SQL语句组成的独立工作单元:MyISAM不流行的原因很大是因为其不支持事务的处理功能. 2  事务日志 事务日志定义属性,有些参数可以运行 ...

  9. MySQL 之事务 及 其隔离级别

    MySQL 之事务 及 其隔离级别 /* 事务:表示一组操作(sql),要么同时成功,要么同时失败,那么这种操作就构成了一个事务. 例如: 张三 给 李四 转账 500元 (1)把张三的余额减少500 ...

最新文章

  1. c语言pi的乘法,c语言 根据公式求出π的近似值。
  2. 数据科学竞赛-自然语言处理赛流程
  3. 用 Mahout 和 Elasticsearch 实现推荐系统
  4. 阿里测试环境运维及研发效率提升之道
  5. matlab for循环太慢,Matlab中每个for循环迭代的速度降低
  6. 初识编码 gbk unicode utf-8
  7. 复制文件夹时同时拷贝其权限
  8. 克拉克拉(KilaKila):大规模实时计算平台架构实战 1
  9. 【报告分享】2020中国商业智能化发展研究报告.pdf(附下载链接)
  10. 张一鸣、王欣和罗永浩的社交梦
  11. JavaScript核心语法学习部分(七)
  12. quartz 定时任务 表达式
  13. python排版_python排版
  14. C/C++文件指针偏移
  15. Meson构建系统(一)
  16. 两平面平行但不重合的条件是_____怎样证明平行
  17. PCB工程师级别评定标准
  18. 移动设备管理(MDM)有哪些关键功能?
  19. [解决问题]注册表修改windows用户名后,vscode以前的扩展消失
  20. [翻译]Why Functional Programming Matters

热门文章

  1. intellij idea全局查找和全局替换
  2. java上传文件到项目路径
  3. 经纬度5位数和6位数差多少_请问经纬度 经度一秒大概相差多少,纬度一秒相差多少?...
  4. Vue中遇到无法加载文件的问题,在系统上禁止运行脚本
  5. 服务器2008怎么系统盘为d盘,硬盘安装Windows Server 2008(解决系统盘符变成D盘)
  6. 以太网网口信号测试软件,以太网网口流量测试,吞吐量测试
  7. 算法自我分析——汉诺塔算法
  8. 腾讯云上攻防战事(三)丨千里追凶,云上黑产虽远必诛
  9. 伊苏比的梦幻之旅(四)比赛题解
  10. 4月4日我儿子和我过生日,共享快乐。顺便贡献多年写的delphi数据库封装原代码。