spring事务隔离

news2024/11/30 2:43:31

在数据库中读取数据时,可能会遇到以下三个常见的问题:脏读(Dirty Read)、不可重复读(Non-repeatable Read)和幻读(Phantom Read)。

这些问题主要涉及并发事务的隔离性和一致性。

  1. 脏读(Dirty Read): 脏读指的是一个事务读取了另一个事务未提交的数据。假设事务A读取了一行数据,但此时事务B修改了该行数据并未提交。如果事务A读取了未提交的数据,即读取到了不正确或无效的数据,就称为脏读。

示例: 事务A读取某个账户的余额,得到100元。 事务B在事务A未提交的情况下,将该账户的余额修改为200元。 事务A继续执行并提交,此时读取到的余额是200元,但实际上事务B后来又将余额改回了150元。

        2.不可重复读(Non-repeatable Read): 不可重复读指的是在一个事务内,对同一行数据进行多次读取,但得到的结果不一致。这是因为在事务读取期间,其他事务修改了该行数据并提交。

示例: 事务A读取某个订单的金额,得到100元。 事务B在事务A读取的过程中修改了该订单的金额为200元,并提交。 事务A再次读取同一行数据,得到200元。

        3.幻读(Phantom Read): 幻读指的是在一个事务内,多次查询某个范围的数据时,得到的结果集不一致。这是因为在事务读取期间,其他事务插入或删除了符合该范围条件的数据,并提交。

示例: 事务A查询某个范围内的商品数量,得到10个商品。 事务B在事务A查询的过程中插入了2个新的商品,并提交。 事务A再次查询同一范围内的商品数量,得到12个商品。

事务隔离级别:

1.读未提交(Read Uncommitted):

         在该隔离级别下,事务可以读取其他事务尚未提交的数据。这可能导致脏读、不可重复读和幻读的问题。

2.读已提交(Read Committed):

        在该隔离级别下,事务只能读取已经提交的数据。这可以解决脏读的问题,但仍可能遇到不可重复读和幻读的问题。

3.可重复读(Repeatable Read):

        在该隔离级别下,事务保证在同一个事务内多次读取同一行数据时,得到的结果一致。这可以解决脏读和不可重复读的问题,但仍可能遇到幻读的问题。

可重复读的实现原理主要依赖于数据库的锁机制和多版本并发控制(MVCC)。

  1. 锁机制:可重复读可以通过共享锁(Shared Lock)和排他锁(Exclusive Lock)来实现。当一个事务需要读取某行数据时,它会获取该数据的共享锁。在共享锁被持有的期间,其他事务可以读取该行数据,但不能对其进行修改。因此,可确保在事务进行过程中,其他事务不会修改该行数据,从而实现可重复读。

    当一个事务需要修改某行数据时,它会获取该数据的排他锁。在排他锁被持有期间,其他事务既不能读取也不能修改该行数据。这样,可以在事务提交之前保持数据的独占访问。

  2. 多版本并发控制(MVCC):MVCC通过为每个事务创建一个快照来确保可重复读。每个快照对应数据库在某一时刻的状态,而每个事务只能访问它开始时的快照。这样,即使其他事务在此期间修改数据,本事务也不会看到这些改变,因此可以实现可重复读。

    在MVCC中,每个数据行都有两个额外的属性:创建时间戳和过期时间戳(或事务ID)。当事务读取数据时,它只会选择满足以下条件的数据行:创建时间戳早于事务开始时间,并且过期时间戳晚于事务开始时间。这样,事务可以确保读取到的数据行与它开始时的数据库状态一致,从而实现可重复读。

总之,可重复读隔离级别通过锁机制和多版本并发控制来确保在一个事务的生命周期内,同一行数据的多次读取结果是一致的,即使该数据在事务进行过程中被其他事务修改。

4.串行化(Serializable):

         在该隔离级别下,事务完全隔离,每个事务按顺序依次执行。它可以解决脏读、不可重复读和幻读的问题,但也降低了并发性能。

示例: 事务A开始并查询某个范围内的商品数量,得到10个商品。 事务B在事务A查询的过程中试图插入新的商品,但被阻塞等待事务A完成。 事务A完成查询后,事务B插入商品。

在spring中可以使用 @Transactional 的 isolation 属性设置 事务的隔离级别:

@Transactional(isolation = Isolation.READ_COMMITTED)

table:

CREATE TABLE `account` (
  `actno` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `balance` int(11) DEFAULT NULL,
  PRIMARY KEY (`actno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

dao interface

package com.dao;

import com.pojo.Account;

public interface AccountDao {

    /**
     * 根据账号查询余额
     * @param actno
     * @return
     */
    Account selectByActno(String actno);

    /**
     * 更新账户
     * @param act
     * @return
     */
    int update(Account act);

}

 dao implement

package com.dao.impl;

import com.dao.AccountDao;
import com.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    //JdbcTemplate 由spring提供用以简化jdbc
    @Resource(name = "jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public Account selectByActno(String actno) {
        String sql = "select actno, balance from account where actno = ?";
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno);
        return account;
    }

    @Override
    public int update(Account act) {
        String sql = "update account set balance = ? where actno = ?";
        int count = jdbcTemplate.update(sql, act.getBalance(), act.getActno());
        return count;
    }
}

pojo 

package com.pojo;

public class Account {
    private String actno;
    private Double balance;

    @Override
    public String toString() {
        return "Account{" +
                "actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }

    public Account() {
    }

    public Account(String actno, Double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }
}

service

package com.service;

import com.dao.AccountDao;
import com.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @program: spring_learn
 * @description:
 * @author: Mr.Wang
 * @create: 2023-06-09 07:09
 **/
@Service
public class Service01 {

    @Resource(name="accountDao")
    public AccountDao accountDao;

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void getByActno(String actno) {
        Account account = accountDao.selectByActno(actno);
        System.out.println("查询到的账户信息:" + account);
    }
}
package com.service;

import com.dao.AccountDao;
import com.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @program: spring_learn
 * @description:
 * @author: Mr.Wang
 * @create: 2023-06-09 07:09
 **/
@Service
public class Service02 {

    @Resource(name="accountDao")
    public AccountDao accountDao;

    @Transactional
    public void update(Account act) {
        accountDao.update(act);
        // 睡眠一会
        try {
            Thread.sleep(1000 * 20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

test:

package com;

import com.pojo.Account;
import com.service.Service01;
import com.service.Service02;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @program: spring_learn
 * @description:
 * @author: Mr.Wang
 * @create: 2023-06-09 07:47
 **/
public class IsolationTest {

    @Test
    public void testIsolation1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Service01 i1 = applicationContext.getBean("service01", Service01.class);
        i1.getByActno("004");
    }

    @Test
    public void testIsolation2(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Service02 i2 = applicationContext.getBean("service02", Service02.class);
        Account act = new Account("004", 1000.0);
        i2.update(act);
    }
}

先执行testIsolation2 更新数据,再执行testIsolation1查询数据,更新的数据还未提交时读取数据,读到的是未提交的数据 

因为使用了  @Transactional(isolation = Isolation.READ_UNCOMMITTED)

 

更新的数据还未提交 

 

 

 使用 @Transactional(isolation = Isolation.READ_COMMITTED) 读已提交

package com.service;

import com.dao.AccountDao;
import com.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @program: spring_learn
 * @description:
 * @author: Mr.Wang
 * @create: 2023-06-09 07:09
 **/
@Service
public class Service01 {

    @Resource(name="accountDao")
    public AccountDao accountDao;

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void getByActno(String actno) {
        Account account = accountDao.selectByActno(actno);
        System.out.println("查询到的账户信息:" + account);
    }
}

retest:

 

睡眠事件过,更新数据被提交后

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/626038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT 4 的 6 个最佳使用场景

作者&#xff1a;SYDNEY BUTLER 译者&#xff1a;明明如月 无论是在 ChatGPT 中还是通过 API&#xff0c;对 OpenAI 的 GPT-4 模型的访问比 GPT-3.5 限制更多。这意味着你需要慎重考虑在何种情况下使用 GPT-4&#xff0c;并选择性地将最适合的任务交给它&#xff0c;以便让其发…

来薅羊毛!阿里云 AI 神器公测了

阿里云 AI 神器「通义听悟」上线了&#xff0c;宣称是身边的 AI 学习助手。这名字听着挺玄乎的&#xff0c;老逛也去试了一下&#xff0c;确实解决了之前遇到的很多问题。 01 视频转文字 老逛是小破站的资深用户&#xff0c;喜欢几个不错的 UP 主&#xff0c;比如老蒋&#xff…

sklearn中的roc_auc_score(二分类或多分类)

官方API地址&#xff1a; sklearn.metrics.roc_auc_score — scikit-learn 1.2.2 documentationExamples using sklearn.metrics.roc_auc_score: Release Highlights for scikit-learn 0.22 Release Highlights for scikit-learn 0.22 Probability Calibration curves Probabi…

AI创作与大语言模型:2023亚马逊云科技中国峰会引领企业应用新潮流

川川出品&#xff0c;必属精品。 文章目录 CodeWhispere免费的代码生成器安装教程使用自动编码 2023亚马逊云科技中国峰会最后总结 CodeWhispere免费的代码生成器 这里我介绍亚马逊云科技的一个产品&#xff0c;那就是Amazon codewhisperer。大家肯定对AI各种产品的火爆已经有…

F/S系统分分钟系统秒变BS/CS,但共享文件夹上的DBF访问掉了个坑

接VFP MIX ALL社群狐友求助&#xff0c;说IIS访问共享文件夹的DBF出错了&#xff1a; 猫猫复现了一下错误: 错误号1705 不能访问DBF表 这个问题估计还是会有很多狐友会遇到这个问题&#xff0c;那么我们就来解决一下吧. 在服务器上面建好共享文件夹 \\newserver\dbf 里面放一个…

开源项目|EasyOCR一款实用的图片OCR文字识别项目

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; “ 花开堪折直须折&#xff0c;莫待无花空折枝。 ” 作者主页&#xff1a;[ https://www.weiyigeek.top ] 博客&…

一级建造师执业资格考试--工程经济--速学36记--联想法

第一记&#xff1a;利息的计算 第二记&#xff1a;等值计算 第三记&#xff1a;名义利率与有效利率 第四记&#xff1a;经济效果评价指标体系 第五记&#xff1a;静态投资回收期分析 第六记&#xff1a;静态投资回收期分析 第七记&#xff1a;预付款 第八记&#xff1a;施工索赔…

2023年深圳某互联网公司前端开发初级岗笔试真题(含解析和源码)

&#x1f4da;关于该专栏: 该专栏的发布内容是前端面试中笔试部分真题、答卷类、机试等等的题目&#xff0c;题目类型包括逻辑题、算法题、选择题、问答题等等&#xff0c;除了内容的分享&#xff0c;还有解析和答案。真实来自某些互联网公司&#xff0c;坐标广东广州、深圳。 …

如何使用Python Flask和MySQL创建管理用户的REST API

部分数据来源&#xff1a;ChatGPT 引言 在现代化的应用开发中&#xff0c;数据库是一个非常重要的组成部分。关系型数据库&#xff08;例如&#xff1a;MySQL、PostgreSQL&#xff09;在这方面尤其是很流行。Flask是一个Python的web框架&#xff0c;非常适合实现REST API。在…

NLP学习笔记六-lstm模型

NLP学习笔记六-lstm模型 上一篇我们讲的是simple RNN模型&#xff0c;那么其实lstm模型更像是simple RNN模型的改进或者变种。 对于lstm模型&#xff0c;我们先看下面一张图&#xff1a; 其实lstm模型的思想是建立在simple RNN模型上的&#xff0c;但是要更加贴近于显示&…

内网安全:内网渗透.(拿到内网主机最高权限 vulntarget 靶场 A)

内网安全&#xff1a;内网渗透.&#xff08;拿到内网主机最高权限&#xff09; 内网穿透又被称为NAT穿透&#xff0c;内网端口映射外网&#xff0c;在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题。通过映射端口&#xff0c;让外网的电脑找到处于内网的电脑。…

数据分析第19课pyecharts布局(基础图形绘制)

官网:https://pyecharts.org/#/zh-cn/global_options?id=legendopts%ef%bc%9a%e5%9b%be%e4%be%8b%e9%85%8d%e7%bd%ae%e9%a1%b9 不想每个属性方法的看,可以直接看gallery 官网的数据都是静态的,如果要做数据实时更新的,即做前后端结合时,会用到Vue框架,与后端连接,实现动…

Nacos架构与原理 - CAP一致性协议 ( Raft Distro)

文章目录 为什么 Nacos 需要⼀致性协议为什么 Nacos 选择了 Raft 以及 Distro从服务注册发现来看从配置管理来看为什么是 Raft 和 Distro &#xff1f;Raft (CP模式)Distro &#xff08;AP模式&#xff09; Nacos ⼀致性协议的演进早期的 Nacos ⼀致性协议当前 Nacos 的⼀致性协…

[Python图像处理] 基于离散余弦变换的安全扩频数字水印

基于离散余弦变换的安全扩频数字水印 数字水印基于离散余弦变换的安全扩频数字水印实现安全扩频数字水印相关链接 数字水印 数字水印是可见的或不可见的标识码&#xff0c;这种标识码被永久嵌入图像中&#xff0c;并且即使在解码过后后仍存在于图像中。为了保证有效性&#xf…

Jetpack Compose 中在屏幕间共享数据的 5 种方案

1. 路由传参 Jetpack Compose 中路由传参的方式有很多种&#xff0c;具体可以参考 Jetpack Compose 中的导航路由 以下是最简单的路由传参测试代码&#xff1a; import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.…

Java开发框架:Spring介绍

Spring 概述特点Spring 程序遇到的问题与解决 概述 Spring 是 Java EE 编程领域中的一个轻量级开源框架&#xff0c;由 Rod Johnson 在 2002 年最早提出并随后创建&#xff0c;目的是解决企业级编程开发中的复杂性&#xff0c;实现敏捷开发的应用型框架 。其中&#xff0c;轻量…

代理模式的运用

文章目录 一、代理模式的运用1.1 介绍1.2 结构1.3 静态代理1.3.1 火车站买票案例类图1.3.2 代码 1.4 JDK动态代理1.4.1 代码1.4.2 JDK动态代理的执行流程 1.5 CGLIB动态代理1.5.1 导包1.5.2 代码 1.6 三种代理的对比1.7 优缺点1.8 使用场景 一、代理模式的运用 1.1 介绍 由于…

从0开始搭建Hyperledger Fabric2.x环境(fabric2.5版本)

Hyperledger Fabric 2.5环境搭建 一.Linux环境准备 # root登录 yum -y install git curl docker docker-compose tree yum -y install autoconf autotools-dev automake m4 perl yum -y install libtool autoreconf -ivf # 安装jq相关包 cd /opt git clone --recursive https…

软考A计划-系统架构师-官方考试指定教程-(8/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例

文章目录 0.前言1. 基本概念1.1. Stream的结构1.2. 持久化1.3. Stream的消费者组 2.实现原理2.1. Stream的数据结构2.2. Stream的消息追加2.3. Stream的消费2.4. Stream的消费者组 3.Redis Stream底层原理3.1. 基数树&#xff08;Radix Tree&#xff09;3.2. listpacks小结 4.命…