返回顶部
首页 > 资讯 > 数据库 >MySQL 中怎么实现可重复读操作
  • 302
分享到

MySQL 中怎么实现可重复读操作

2024-04-02 19:04:59 302人浏览 泡泡鱼
摘要

这篇文章将为大家详细讲解有关Mysql 中怎么实现可重复读操作,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。账户表(省去其他字段)结构如下:CREATE&n

这篇文章将为大家详细讲解有关Mysql 中怎么实现可重复读操作,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

账户表(省去其他字段)结构如下:

CREATE TABLE `account` (     `id`      bigint(20) NOT NULL,     `balance` bigint(20) DEFAULT NULL,     PRIMARY KEY (`id`)  ) ENGINE = InnoDB   DEFAULT CHARSET = utf8mb4   COLLATE = utf8mb4_bin;

扣减余额时,sql 语序如下所示:

MySQL 中怎么实现可重复读操作

更新余额 sql 语序

ps:看到上面的语序,有没有个小问号?为什么相同查询了这么多次?

其实这些 SQL 语序并不在同个方法内,并且有些方法被抽出复用,所以导致一些相同查询结果没办法往下传递,所以只得再次从数据库中查询。

为了防止并发更新余额,在 t3 时刻,使用写锁住该行记录。若加锁成功,其他线程的若也执行到 t3,将会被阻塞,直到前一个线程事务提交。

t5 时刻,进入到下一个方法,再次获取账户余额,然后在 Java 方法内比较余额与扣减金额,若余额充足,在 t7 时刻执行更新操作。

上面的 SQL 语序看起来没有什么问题吧,实际也是这样的,账户系统已经在生产运行很久,没出现什么问题。但是这里需要说一个前提,系统数据库oracle  。

但是从上面表结构,可以得知此次数据库被切换成 mysql,系统其他任何代码以及配置都不修改(sql 存在小改动)。

就是这种情况下,并发执行发生余额多扣,即实际余额明明小于扣减金额,但是却做了余额更新操作,最后导致余额变成了负数。

下面我们来重现并发这种情况,假设有两个事务正在发执行该语序,执行顺序如图所示。

注意点:数据库使用的是 MySQL,默认事务隔离等级,即 RR。数据库记录为 id=1  balance=1000,假设只有当时只有这两个事务在执行。

MySQL 中怎么实现可重复读操作

各位读者可以先思考一下,t2,t3,t4,t5,t6,t11 时刻余额多少。

下面贴一下事务隔离等级RR 下的答案。

事务 1 的查询结果为:

  • t2 (1,1000)

  • t4 (1,1000)

  • t6 (1,1000)

事务 2 的查询结果为:

  • t3 (1,1000)

  • t5 (1,900)

  • t11 (1,1000)

有没有跟你想的结果的一样?

接着将事务隔离等级修改成 RC,同样再来思考一下 t2,t3,t4,t5,t6,t11 时刻余额。

再次贴下事务隔离等级RC 下的答案。

事务 1 的查询结果为:

  • t2 (1,1000)

  • t4 (1,1000)

  • t6 (1,1000)

事务 2 的查询结果为:

  • t3 (1,1000)

  • t5 (1,900)

  • t11 (1,900)

事务 1 的查询结果,大家应该会没有什么问题,主要疑问点应该在于事务 2,为什么换了事务隔离等级结果却不太一样?

下面我们先带着疑问,了解一下 MySQL 的相关原理 ,看完你就会明白这一切。

  • mvcC

  • 一致性视图

  • 快照读与当前读

MVCC

我们先来看下一个简单的例子,

事务隔离等级为 RR , id=1 balance=1000

MySQL 中怎么实现可重复读操作

更新时序

事务 1 将 id=1 记录 balance 更新为 900,接着事务 2 在 t5 时刻查询该行记录结果,很显然该行记录应该为 id=1  balance=1000。

如果 t5 查询最新结果 id=1 balance=900,这就读取到事务 1 未提交的数据,显然不符合当前事务隔离级别。

从上面例子可以看到 id=1 的记录存在两个版本,事务 1 版本记录为 balance=1000 ,事务 2 版本记录为 balance=900。

上述功能,MySQL 使用 MVCC 机制实现功能。

MVCC:Multiversion concurrency control,多版本并发控制。摘录一段淘宝数据库月报的解释:

多版本控制:  指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了  InnoDB 的并发度。在内部实现中,与 Postgres 在数据行上实现多版本不同,InnoDB 是在 undolog 中实现的,通过 undolog  可以找回数据的历史版本。找回的数据历史版本可以提供给用户读(按照隔离级别的定义,有些读请求只能看到比较老的数据版本),也可以在回滚的时候覆盖数据页上的数据。在  InnoDB 内部中,会记录一个全局的活跃读写事务数组,其主要用来判断事务的可见性。

可以看到 MVCC 主要用来提高并发,还可以用来读取老版本数据。

学习 MVCC 原理之前,首先我们需要了解 MySQL 记录结构。

MySQL 中怎么实现可重复读操作

行记录

如上图所示,account 表一行记录,除了真实数据之外,还会存在三个隐藏字段,用来记录额外信息。

  • DB_TRX_ID:事务 id。

  • DB_ROLL_PTR: 回滚指针,指向 undolog。

  • ROW_ID:行 id,与此次无关。

MySQL InnoDB 里面每个事务都会有一个唯一事务 ID,它在事务开始的时候会跟 InnoDB 的事务系统申请的,并且严格按照顺序递增的。

每次事务更新数据时,将会生成一个新的数据版本,然后会把当前的事务 id 赋值给当前记录的  DB_TRX_ID。并且数据更新记录(1,1000---->1,900)将会记录在 undo log(回滚日志)中,然后使用当前记录的  DB_ROLL_PTR 指向 und olog。

这样 MySQL 就可以通过 DB_ROLL_PTR 找到 undolog 推导出之前版本记录内容。

查找过程如下:

MySQL 中怎么实现可重复读操作

查找过程

若需要知道 V1 版本记录,首先根据当前版本 V3 的 DB_ROLL_PTR 找到 undolog,然后根据 undolog 内容,计算出上一个版本  V2。以此类推,最终找到 V1 这个版本记录。

V1,V2 并不是物理记录,没有真实存在,仅仅具有逻辑意义。

一行数据记录可能同时存在多个版本,但并不是所有记录都能对当前事务可见。不然上面 t5 就可能查询到最新的数据。所以查找数据版本时候 MySQL  必须判断数据版本是否对当前事务可见。

一致性视图

MySQL 会在事务开始后建立一个一致性视图(并不是立刻建立),在这个视图中,会保存所有活跃的事务(还未提交的事务)。

假设当前事务保存活跃事务数组为如下图。

MySQL 中怎么实现可重复读操作

视图数组

判断版本对于当前事务是否可见时,基于以下规则判断:

  • 若版本事务 id 小于当前活跃事务 id 数组最小值,比如版本 id 为 40,小于活跃数组最小值  45。这就代表当前版本的事务已提交,当前版本对于当前事务可见。

  • 若版本事务 id 大于当前活跃事务数组的最大值,如版本事务 id 为 100, 大于数组最大事务 id  90。说明了这个版本是当前事务创建之后生成,所以这个版本对于当前事务不可见。

  • 若版本事务 id 是当前活跃数组事务之一,比如版本事务 id 为 56。代表记录版本所属事务还未提交,所以该版本对于当前事务不可见。

  • 若版本事务 id 不是当前活跃数组事务之一,但是事务 id 位于活跃数组最小值与最大值之一,比如如事务 ID  57。代表当前记录事务已提交,所以该版本对于当前事务可见。

  • 若版本事务 id 为当前事务 id,代表该行数据是当前事务变更的,当然得可见。

4 这个规则可能比较绕,结合上面图片比较好理解。

以上判断规则可能比较抽象,看不懂,没事,我们再用大白话解释一下:

  • 未提交事务生成的记录版本,不可见。

  • 视图生成前,已提交事务生成记录版本可见。

  • 视图生成后,新事务生成记录版本不可见。

  • 自身事务更新永远可见。

一致性视图只会在 RR 与 RC 下才会生成,对于 RR 来说,一致性视图会在第一个查询语句的时候生成。而对于 RC  来说,每个查询语句都会重新生成视图。

当前读与快照读

MySQL 使用 MVCC 机制,可以读取之前版本数据。这些旧版本记录不会且也无法再去修改,就像快照一样。所以我们将这种查询称为快照读。

当然并不是所有查询都是快照读,select .... for update/ in share mode  这类加锁查询只会查询当前记录最新版本数据。我们将这种查询称为当前读。

问题分析

讲完原理之后,我们回过头分析一下上面查询结果的原因。

这里我们将上面答案再贴过来。

MySQL 中怎么实现可重复读操作

事务隔离级别为 RR,t2,t3 时刻两个事务由于查询语句,分别建立了一致性视图。

t4 时刻,由于事务 1 使用 select.. for update 为 id=1 这一行上了一把锁,然后获取到最新结果。而 t5  时刻,由于该行已被上锁,事务 2 必须等待事务 1 释放锁才能继续执行。

t6 时刻根据一致性视图,不能读取到其他事务提交的版本,所以数据没变。t8 时刻余额扣减 100,t9 时刻提交事务。

此时最新版本记录为 id=1 balance=900。

由于事务 1 事务已提交,行锁被释放,t5 成功获取到锁。由于 t5 是当前读,所以查询的结果为最新版本数据(1,900)。

重点来了,当前这条记录的最新版本数据为 (1,900),但是最新版本事务 id,却是事务 2  创建之后未提交的事务,位于活跃事务数组中。所以最新记录版本对于事务 2 是不可见的。

没办法只能根据 undolog 去读取上一版本记录 (1,1000) ,这个版本记录刚好对于事务 2 可见,所以 t11 的记录为  (1,1000)。

而当我们将事务隔离等级修改成 RC,每次都会重新生成一致性视图。所以 t11 时刻重新生成了一致性视图,这时候事务 1 已提交,当前最新版本的记录对于事务  2 可见,所以 t11 的结果将会变为 (1,900)。

关于MySQL 中怎么实现可重复读操作就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

您可能感兴趣的文档:

--结束END--

本文标题: MySQL 中怎么实现可重复读操作

本文链接: https://lsjlt.com/news/58819.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • MySQL 中怎么实现可重复读操作
    这篇文章将为大家详细讲解有关MySQL 中怎么实现可重复读操作,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。账户表(省去其他字段)结构如下:CREATE&n...
    99+
    2024-04-02
  • mysql怎么实现可重复读
    要实现可重复读,可以通过以下两种方式:1. 使用事务隔离级别为"可重复读":在MySQL中,可通过设置事务隔离级别来实现不同的并发控...
    99+
    2023-09-15
    mysql
  • php中怎么实现Mysql读写分离操作
    php中怎么实现Mysql读写分离操作,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。连接案例:<php require...
    99+
    2024-04-02
  • 解决HttpServletRequest 流数据不可重复读的操作
    目录前言ServletRequest 数据封装原理Spring MVC 对不同类型数据的封装读取参数时出现的问题具体的问题可以细分成多种情况:最佳解决方案tips:总结附录代码前言 ...
    99+
    2024-04-02
  • MySQL数据库中怎么实现复制操作
    MySQL数据库中怎么实现复制操作,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。 MySQL数据...
    99+
    2024-04-02
  • Mysql中的幻读和不可重复读的区别
    Mysql中的幻读和不可重复读的区别是什么?这个问题可能是我们日常学习或工作经常见到的。通过这个问题,希望你能了解幻读和不可重复读的区别。不可重复读:同样的条件下,读取过的数据,当我们再次读取时值发生了变化...
    99+
    2024-04-02
  • mysql中怎么实现alter操作
    今天就跟大家聊聊有关mysql中怎么实现alter操作,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 --给某一张表添加...
    99+
    2024-04-02
  • C#中怎么实现操作符重载
    C#中怎么实现操作符重载,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。C#操作符重载学习实践操作using System;  using&nb...
    99+
    2023-06-17
  • MySQL中怎么重复读间隙锁
    MySQL中怎么重复读间隙锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。间隙锁(Gap  Lock)是Innodb...
    99+
    2024-04-02
  • MySQL事务隔离级别中可重复读与幻读
      每次谈到数据库的事务隔离级别,大家一定会看到这张表. 其中,可重复读这个隔离级别,有效地防止了脏读和不可重复读,但仍然可能发生幻读,可能发生幻读就表示可重复读这个隔离级别防不住幻读吗 我不管从数据库方面的教科书还是一些网络...
    99+
    2019-11-23
    MySQL事务隔离级别中可重复读与幻读
  • Cookie的读写操作怎么实现
    本篇内容主要讲解“Cookie的读写操作怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Cookie的读写操作怎么实现”吧!  一、Cookie的出现  浏览器和服务器之间的通信少不了HT...
    99+
    2023-06-04
  • mysql中怎么实现视图操作
    mysql中怎么实现视图操作,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1.MySQL 视图的操作是在新版本才支持的,视图操...
    99+
    2024-04-02
  • mysql中怎么实现查询操作
    这篇文章将为大家详细讲解有关mysql中怎么实现查询操作,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。1、选择所有的记录   ...
    99+
    2024-04-02
  • Shell中怎么实现文本去重操作
    这期内容当中小编将会给大家带来有关Shell中怎么实现文本去重操作,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。代码如下:ccccaaaabbbbddddbbbbccccaaaa现在需要对它进行去重处理,...
    99+
    2023-06-09
  • Java中怎么实现文件的读写操作
    在Java中,文件的读写操作可以通过使用Java I/O库来实现。以下是一些常用的方法: 使用FileInputStream和Fi...
    99+
    2024-03-05
    java
  • MySQL的不可重复读和幻读是什么意思
    这篇文章主要介绍“MySQL的不可重复读和幻读是什么意思”,在日常操作中,相信很多人在MySQL的不可重复读和幻读是什么意思问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”My...
    99+
    2024-04-02
  • MySQL中怎么使用LOOP语句执行重复操作
    在MySQL中,可以使用WHILE循环语句来执行重复操作,示例如下: DELIMITER // CREATE PROCEDURE ...
    99+
    2024-04-30
    MySQL
  • 在Map中实现key唯一不重复操作
    目录Map中如何实现key唯一不重复问题:如何做到Map中key唯一不重复,每次都遍历来equals比较吗?结合HashMap.class中的put方法来说明:如何找到这个区域呢?得...
    99+
    2024-04-02
  • MySQL脏读幻读不可重复读及事务的隔离级别和MVCC、LBCC实现
    目录前言事务因并发出现的问题有哪些 脏读不可重复读幻读不可重复读与幻读的区别事务的四个隔离级别InnoDB默认的隔离级别是RRRead UnCommited 读未提交 RURead Commited 读已提交 RCRep...
    99+
    2022-07-11
    MySQL脏读幻读不可重复读 MySQL脏读幻读事务的隔离   MySQL MVCC MySQL LBCC
  • golang怎么实现mysql操作
    这篇文章主要介绍“golang怎么实现mysql操作”,在日常操作中,相信很多人在golang怎么实现mysql操作问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”golang怎么实现mysql操作”的疑惑有所...
    99+
    2023-07-06
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作