事务管理、事务特性、数据库并发访问问题、事务应用【转账】

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 事务管理、事务特性、数据库并发访问问题、事务应用【转账】

事务管理


1.什么是事务呢?


事务指的是:在一组业务逻辑中,要么全部成功,要么全部失败。


2. 事务的特性又有那些呢?【ACID】


原子性【Atomicity】:在一组事务操作中,要么全都成功,要么全都失败,互相不可分割


一致性【Consistency】: 在事务发生的前后数据的完整性必须保存一致。


隔离性【Isolation】:事务被多个用户并发访问时,多个用户之间的数据要互相隔离


持久性【Durability】:事务一旦提交,对数据的修改将是持久的。


3. 并发访问会出现那些问题?【三种隔离问题】


------ 如果不考虑事务的隔离性,事务会出现并发访问问题


3.1 脏读


---- 含义: 一个事务读到了另一个事务没有提交是事务


详解:举例转账功能 --- 事务A、B各有10元


事务A说 : 给事务B转账2元 ----- 8 元


事务B说:好的我已经收到了 ----- 12元


-------------【可在A、B事务并发访问时,出现了脏读问题,事务B读到了事务A没有提交的事务】


结果:事务A和事务B的金额还是没有进行转账前的各自10元


-------------【脏读:读到了没有提交的事务,事务进行了回滚操作,恢复了原来的数据信息】


图集详解


d71a804c5eb9477eba14c5ae51daa42b.png


3.2 不可重复读

----含义:一个事务读到了另一个事务已经提交的事务【update】,造成在一个事务中多次查询结果不一致


详解:


事务A进行查账100元,事务B从账号取出20元


事务A: 第一次查询到100快,并没有提交事务commit


事务B:从账户上取出20元,并对账户余额进行update修改


----------- 事务A第二次查询,查到了事务B已经提交的数据80元


----------- 两次查询结果不一样,发生并发访问问题,不可重复读


图集详解


37430d16e9a740169e12a8a069455787.png


3.3 虚读/幻读


--- 含义:一个事务读到了另一个事务已经提交的事务【insert】,在事务中多次查询结果不一致。(数据量不同)


详解


事务A进行数据查询功能,事务B进行insert添加功能


事务A: 第一次查询到3条,并没有提交事务commit


事务B:添加了一条数据


---------- 事务A第二次查询查询到了4条数据,查到了事务B已经提交的数据


---------- 两次查询结果不一样,发生并发访问问题,不可重复读


图集详解


b618a0a605e64ba183867f678e8f0993.png


4 那不可重复读【update】和幻读【insert】又有什么区别呢?


不可重复读是基于update修改发生的并发访问问题


幻读是基于insert添加,发生的并发访问问题


5 隔离级别有那些?


------ 为了解决并发访问问题,数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况


1)读未提交【read uncommitted】:一个事务读到了另一个事务没有提交的数据


存放:3个问题【脏读、不可重复读、虚读】


解决:0个问题


------- 效率最高,引发所有读问题,基本不设置


2)读已提交【read committed】: 一个事务读到了另一个事务已经提交的数据


存放:2个问题【不可重复读、虚读】


解决:1个问题【脏读】


------- 如果要效率,那么选择这个 read committed 读已提交


3)可重复读【repeatable read】: 在一个事务中读到的数据信息始终保持一致,无论另一个事务是否提交


存放:1个问题【虚读】


解决:2个问题【脏读、不可重复读】


------- 如果要安全,那么选择这个 repeatable read 可重复读


------ 还剩一个问题未解决,但是虚读的问题可以通过程序来规避


事务刚开启时,可以count(*)


事务要关闭时,可以count(*)


对比:如果两次数据一致,说明没有虚读


4)串行化【serializable】: 同时只能执行一个事务,相当事务中的单线程


存放:0个问题


解决:3个问题【脏读、不可重复读、虚读】


------- 没有效率,但安全性最高,基本不设置


5.1 事务性能和安全性的比较

性能:读未提交 > 读已提交 > 可重复读 > 串行化


安全:串行化 > 可重复读 > 读已提交 > 读未提交


5.2 常见数据库的默认隔离级别

MySql : 可重复读:安全,本身做的优化比较好


Oracle : 读已提交:效率高


6.事务注解关联Spring


相关注解


@EnableTransactionManagement  //书写在SpringConfiguration配置类中

@Transactional  //开启事务 书写在service接口实现类中


7.转账案例,事务应用


7.1图集导航


2cf6921aacdf4003b2ccaf89521ddb4f.png


7.2 代码数据【可参考】


7.2.1 config 配置类


MyBatisConfiguration

package com.czxy.demo17_accountSW.config;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
public class MyBatisConfiguration {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        //1 创建工厂
        // 1.通过工厂bean创建对象,最后需要调用 getObject()获得具体的对象
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        //2 设置数据-- SqlMapConfig.xml 配置信息
        // 1.1 设置数据源
        factoryBean.setDataSource(dataSource);
        // 1.2 设置别名包扫描
        factoryBean.setTypeAliasesPackage("com.czxy.demo17_accountSW.doamin");
        // 1.3 全局配置:驼峰映射
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        config.setMapUnderscoreToCamelCase(true);
        factoryBean.setConfiguration(config);
        // 2 插件配置
        // 2.1 分页插件
        PageHelper pageHelper = new PageHelper();
        Properties pageProps = new Properties();
        pageProps.setProperty("dialect", "mysql");
        pageProps.setProperty("rowBoundsWithCount", "true");
        pageHelper.setProperties(pageProps);
        factoryBean.setPlugins(new Interceptor[] { pageHelper });
        // 返回SqlSessionFactory
        return factoryBean.getObject();
    }
    /**
     * 扫描Dao的包,查找各种XxxMapper接口,创建好UserMapper等对象存入到IOC的容器中
     * @return
     */
    @Bean
    public MapperScannerConfigurer mapperScanner() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.czxy.demo17_accountSW.mapper");
        return configurer;
    }
}

SpringConfiguration

package com.czxy.demo17_accountSW.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:db2.properties")
@ComponentScan(basePackages = "com.czxy.demo17_accountSW")
@EnableTransactionManagement
public class SpringConfiguration {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url ;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
    //事务管理器【管理事务,事务出现异常会进行回滚事务】
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

7.2.2 daomain

  • Account
@Table(name = "account")
public class Account {
    @Id
    private Integer id;
    private String name;
    private Float money;
 //注:构造和set/get方法省略
}

7.2.3 mapper

  • AccountMapper
public interface AccountMapper extends Mapper<Account> {
}

7.2.4  service

  • AccountService接口
public interface AccountService {
    /**
     * 转账
     * @param outId  汇款
     * @param inId   收款
     * @param money  金额
     */
    public void change(Integer outId,Integer inId,Float money);
}
  • AccountServiceImpl接口实现类
package com.czxy.demo17_accountSW.service.impl;
import com.czxy.demo17_accountSW.doamin.Account;
import com.czxy.demo17_accountSW.mapper.AccountMapper;
import com.czxy.demo17_accountSW.service.AccountService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
@Transactional  //开启事务
public class AccountServiceImpl implements AccountService {
    @Resource
    private AccountMapper accountMapper;
    //转账 ---》  汇款、收款、金额
    @Override
    /**
     * 转账
     * @param outId  汇款
     * @param inId   收款
     * @param money  金额
     */
    public void change(Integer outId, Integer inId, Float money) {
        //(id查询、修改金额、更新)
        //根据汇款人id进行汇款,返回一个汇款人的对象信息
        Account outAccount = accountMapper.selectByPrimaryKey(outId);
        //进行设置汇款人的剩余金额 = 汇款钱的金额 - 汇款的金额
        outAccount.setMoney(outAccount.getMoney() - money);
        //进行更新设置剩余金额,根据id进行修改保存
        accountMapper.updateByPrimaryKey(outAccount);
        //模拟错误异常,出错了,
        // 但是汇款已经完成,数据已经修改,但收款人这边由于报错没有收到汇款,那怎么办呢?
        // 不用担心事务注解@Transactional 就是为了防止该类事情发生而存在的。
        // 开启事务,当报异常了,停止了业务的进行,那么事务就会执行回滚事务,恢复数据
        int i = 1/0 ;
        //收款人 + 钱
        //根据收款人id,获取收款人对象
        Account inAccount = accountMapper.selectByPrimaryKey(inId);
        //对其收款人金额进行累加 = 原本金额+汇款金额
        inAccount.setMoney(inAccount.getMoney() +money);
        //根据收款人id进行更新保存金额
        accountMapper.updateByPrimaryKey(inAccount);
    }
}

7.2.5 properties 文本

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/day12_02_affair
jdbc.username=root
jdbc.password=1234

7.2.6测试类

package com.czxy.demo17_accountSW;
import com.czxy.demo17_accountSW.config.MyBatisConfiguration;
import com.czxy.demo17_accountSW.config.SpringConfiguration;
import com.czxy.demo17_accountSW.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class, MyBatisConfiguration.class})
public class TestTx {
    @Resource
    private AccountService accountService;
    @Test
    public void testDemo(){
        accountService.change(1,2,300f);
    }
}


  • 测试结果
  • 测试前


f7f2a0e7ae3a47cd9c6c4b0c59b9e34d.png


  • 测试后

0a6542175d1045bb83a0c4d68342e26f.png


8 .当测试时发生异常报错了怎么呢?在我已经汇款之后就出现系统异常,导致收款方没有收到款,怎么办呢?


//模拟错误异常,出错了,
// 但是汇款已经完成,数据已经修改,但收款人这边由于报错没有收到汇款,那怎么办呢?
// 不用担心事务注解@Transactional 就是为了防止该类事情发生而存在的。
// 开启事务,当报异常了,停止了业务的进行,那么事务就会执行回滚事务,恢复数据
//------ 这里已除0异常为例
 int i = 1/0 ;


9.图集总结


fa7c87b2739145ea865e161a6bbd2f53.png


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
缓存 NoSQL 数据库
运用云数据库 Tair 构建缓存为应用提速,完成任务得苹果音响、充电套装等好礼!
本活动将带大家了解云数据库 Tair(兼容 Redis),通过体验构建缓存以提速应用,完成任务,即可领取罗马仕安卓充电套装,限量1000个,先到先得。邀请好友共同参与活动,还可赢取苹果 HomePod mini、小米蓝牙耳机等精美好礼!
|
9天前
|
消息中间件 数据库 云计算
微服务架构下的数据库事务管理策略####
在微服务架构中,传统的单体应用被拆分为多个独立的服务单元,每个服务维护自己的数据库实例。这种设计提高了系统的可扩展性和灵活性,但同时也带来了分布式环境下事务管理的复杂性。本文探讨了微服务架构下数据库事务的挑战,并深入分析了几种主流的事务管理策略,包括Saga模式、两阶段提交(2PC)以及基于消息的最终一致性方案,旨在为开发者提供一套适应不同业务场景的事务处理框架。 ####
|
16天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
30 3
|
16天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
33 2
|
16天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
36 2
|
18天前
|
SQL Java 数据库连接
打破瓶颈:利用Java连接池技术提升数据库访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,避免了频繁的连接建立和断开,显著提升了数据库访问效率。常见的连接池库包括HikariCP、C3P0和DBCP,它们提供了丰富的配置选项和强大的功能,帮助优化应用性能。
37 2
|
21天前
|
数据库
什么是数据库的事务隔离级别,有什么作用
【10月更文挑战第21】什么是数据库的事务隔离级别,有什么作用
12 3
|
21天前
|
存储 关系型数据库 数据挖掘
什么是数据库的事务隔离级别
【10月更文挑战第21】什么是数据库的事务隔离级别
15 1
|
25天前
|
XML 存储 数据库
XML在数据库中有哪些应用?
【10月更文挑战第17天】XML在数据库中有哪些应用?
26 2
|
27天前
|
存储 数据库 数据库管理
数据库事务安全性控制如何实现呢
【10月更文挑战第15天】数据库事务安全性控制如何实现呢