文章目录

  • 一、设置事务隔离级别
  • 二、事务问题
    • 1、脏读
      • (1) 场景
      • (2) 分析
      • (3) 模拟演示
    • 2、不可重复读
      • (1)场景
      • (2)分析
      • (对比)(1*) 场景
      • (2*)分析
      • (3)模拟演示
    • 3、幻读
      • (1)场景
      • (2)分析
      • (3)模拟演示

一、设置事务隔离级别

首先,我们先设置MySQL事务隔离级别为READ-UNCOMMITTED

  1. 在my.ini配置文件最后加上如下配置
#可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
[mysqld]
transaction-isolation = READ-UNCOMMITTED
  1. 当前session修改,登录MySQL数据库后执行如下命令:
set session transaction isolation level read uncommitted;

二、事务问题

同一个应用程序中的多个事务或不同应用程序中的多个事务在同一个数据集上并发执行时, 可能会出现许多意外的问题,这些问题可分为如下三种类型:脏读(Drity Read)、不可重复读(Non-repeatable read)、幻读(Phantom Read)

1、脏读

又称无效数据的读取,是指在数据库访问时,事务A将某值修改,在事务A commit前,事务B读取了该值,而此后因某些原因事务A rollback了对该值的修改,这就导致了事务B读取到的数据是无效的。
e.g.已知有两个事务A和B, A读取了已经被B更新但还没有被提交的数据,之后,B回滚事务,A读取的数据就是脏数据。

(1) 场景

公司发工资了,领导把5000元打到Tom的账号上,但是该事务并未提交,而Tom正好去查看账户,发现工资已经到账,账户多了5000元,非常高兴,可是不幸的是,领导发现发给Tom的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,Tom再次查看账户时发现账户只多了2000元,因此Tom空欢喜一场……

(2) 分析

上述情况即为脏读,两个并发的事务:“事务B:领导给Tom发工资”、“事务A:Tom查询工资账户”,事务A读取了事务B尚未提交的数据。

(3) 模拟演示

创建表

create table account(id int(36) primary key comment '主键',card_id varchar(16) unique comment '卡号',name varchar(8) not null comment '姓名',balance float(10,2) default 0 comment '余额'
)engine=innodb;

插入数据

insert into account (id,card_id,name,balance) values (1,'6226090219290000','Tom',1000);

结果

公司发工资了,领导把5000元打到Tom的账号上,但是该事务并未提交。30秒后将工资数额改为2000元。

public class Boss {//公司给Tom发工资public static void main(String[] args) {Connection connection = null;Statement statement = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "update account set money=money+5000 where card_id='6226090219290000'";statement.executeUpdate(sql);Thread.sleep(30000);//30秒后发现工资发错了connection.rollback();sql = "update account set money=money+2000 where card_id='6226090219290000'";statement.executeUpdate(sql);connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Boss中main方法后立即执行Employee中的main方法得:

public class Employee {//Tom查询余额public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");statement = connection.createStatement();String sql = "select balance from account where card_id='6226090219290000'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println(resultSet.getDouble("balance"));}} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Boss中main方法后立即执行Employee中的main方法得:

在执行Boss中main方法后等待30秒,执行Employee中的main方法得:

因此,事务隔离级别为Read uncommitted(读未提交)时会出现“脏读”。

2、不可重复读

在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
e.g.已知有两个事务A和B,A 多次读取同一数据,B 在A多次读取的过程中对数据作了修改并提交,导致A多次读取同一数据时,结果不一致。

(1)场景

Tom拿着工资卡去消费,酒足饭饱后在收银台买单,服务员告诉他本次消费1000元,Tom将银行卡给服务员,服务员将银行卡插入POS机,POS机读到卡里余额为3000元,就在Tom磨磨蹭蹭输入密码时,他老婆以迅雷不及掩耳盗铃之势把Tom工资卡的3000元转到自己账户并提交了事务,当Tom输完密码并点击“确认”按钮后,POS机检查到Tom的工资卡已经没有钱,扣款失败,Tom十分纳闷,明明卡里有钱,于是怀疑POS有鬼,和收银小姐姐大打出手,300回合之后终因伤势过重而与世长辞,Tom老婆痛不欲生,郁郁寡欢,从此走上了不归路…

(2)分析

上述情况即为不可重复读,两个并发的事务,“事务A:POS机扣款”、“事务B:Tom的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新数据并提交了事务,而事务A再次读取该数据扣款时,数据已经发生了改变。

(对比)(1*) 场景

Tom拿着工资卡去消费时,一旦POS机读取工资卡信息(即事务开始),Tom老婆即便进行了转账,待Tom输入密码并点击“确认”按钮后,POS机检查到Tom工资卡上余额没有变化,最终扣款成功。

(2*)分析

上述情况即为重复读

(3)模拟演示

POS机扣款

public class Machine {public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {double sum=1000;//消费金额Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "select money from account where card_id='6226090219290000'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println("余额:"+resultSet.getDouble("money"));}System.out.println("请输入支付密码:");Thread.sleep(30000);//10秒后密码输入成功resultSet = statement.executeQuery(sql);if(resultSet.next()) {double money = resultSet.getDouble("money");System.out.println("余额:"+money);if(money<sum) {System.out.println("余额不足,扣款失败!");return;}}sql = "update account set money=money-"+sum+" where card_id='6226090219290000'";statement.executeUpdate(sql);connection.commit();System.out.println("扣款成功!");} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

Tom的老婆网上转账

public class Wife {//Tom的老婆网上转账public static void main(String[] args) {Connection connection = null;Statement statement = null;try {double money=3000;//转账金额Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "update account set money=money-"+money+" where card_id='6226090219290000'";statement.executeUpdate(sql);sql = "update account set money=money+"+money+" where card_id='6226090219299999'";statement.executeUpdate(sql);connection.commit();System.out.println("转账成功");} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Machine中main方法后立即执行Wife中的main方法得:

等待30秒后Machine中main方法执行结果:

因此,事务隔离级别为Read uncommitted(读未提交)时会出现“不可重复读”。

3、幻读

幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样.一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。
e.g.已知有两个事务A和B,A从一个表中读取了数据,然后B在该表中插入了一些新数据,导致A再次读取同一个表, 就会多出几行。

(1)场景

Tom的老婆工作在银行部门,她时常通过银行内部系统查看Tom的工资卡消费记录。2019年5月的某一天,她查询到Tom当月工资卡的总消费额(select sum(amount) from record where card_id=‘6226090219290000’ and date_format(create_time,’%Y-%m’)=‘2019-05’)为80元,Tom的老婆非常吃惊,心想“老公真是太节俭了,嫁给他真好!”,而Tom此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录并提交了事务,沉浸在幸福中的老婆查询了Tom当月工资卡消费明细(select amount from record where card_id=‘6226090219290000’ and date_format(create_time,’%Y-%m’)=‘2019-05’)一探究竟,可查出的结果竟然发现有一笔1000元的消费,Tom的老婆瞬间怒气冲天,外卖订购了一个大号的榴莲,傍晚降临,Tom生活在了水深火热之中,只感到膝盖针扎的痛…

(2)分析

上述情况即为幻读,两个并发的事务,“事务A:获取事务B消费记录”、“事务B:添加了新的消费记录”,事务A获取事务B消费记录时数据多出了一条。

(3)模拟演示

创建表

create table record(id int(36) primary key comment '主键',card_id varchar(16) comment '卡号',amount float(10,2) comment '金额',create_time date comment '消费时间'
)engine=innodb;

插入数据

insert into record (id,card_id,amount,create_time) values (1,'6226090219290000',37,'2019-05-01');
insert into record (id,card_id,amount,create_time) values (2,'6226090219290000',43,'2019-05-07');

老婆查看消费记录

public class Bank {public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "select sum(amount) total from record where card_id='6226090219290000' and date_format(create_time,'%Y-%m')='2019-05'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println("总额:"+resultSet.getDouble("total"));}Thread.sleep(30000);//30秒后查询2019年5月消费明细sql="select amount from record where card_id='6226090219290000' and date_format(create_time,'%Y-%m')='2019-05'";resultSet = statement.executeQuery(sql);System.out.println("消费明细:");while(resultSet.next()) {double amount = resultSet.getDouble("amount");System.out.println(amount);}connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

Tom消费1000元

public class Husband {public static void main(String[] args) {Connection connection = null;Statement statement = null;try {double sum=1000;//消费金额Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "update account set money=money-"+sum+" where card_id='6226090219290000'";statement.executeUpdate(sql);sql = "insert into record (id,card_id,amount,create_time) values (3,'6226090219290000',"+sum+",'2019-05-19');";statement.executeUpdate(sql);connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Bank中main方法后立即执行Wife中的Husband方法得:

等待30秒后控制台输出:

因此,事务隔离级别为Read uncommitted(读未提交)时会出现“幻读”。

MySQL数据库——事务隔离级别Read uncommitted(一)相关推荐

  1. 概述MySQL数据库---事务隔离级别

    同一个应用程序中的多个事务或不同应用程序中的多个事务在同一个数据集上并发执行时, 可能会出现许多意外的问题,事务并发处理可能引起的问题可分为如下三种类型: 脏读(Drity Read): 已知有两个事 ...

  2. mysql数据库事务隔离级别演示

    mysql数据库事务隔离级别演示 关键词: 一.基本概念 二.事务的四个特性(ACID) 三.事务的用法 3.1 相关命令 3.2 使用步骤 四.数据库的隔离级别 五.示例演示(每组事务结束手动com ...

  3. 如何更改MySQL数据库事务隔离级别

    第一步.关闭操作数据库软件和数据库服务 关闭数据库软件就不说了,下面说关闭数据库服务. 以win10为例 "我的电脑"右键-->点击"管理"-->找 ...

  4. mysql数据库事务隔离级别

    1修改事务隔离级别 全局修改 修改mysql.ini配置文件 [mysqlId] transaction-isolation=REPEATBLE-READ 对当前session修改 登录mysql客户 ...

  5. MySql数据库事务隔离级别底层实现原理总结

    一:隔离级别 众所周知,事务的隔离级别有四个等级,分别是读未提交(RU),读已提交(RC),可重复读(RR)与串行化(Serial),通过设置隔离级别,可以解决事务并发过程中导致的脏读,不可重复读与幻 ...

  6. 事务隔离级别——Read uncommitted

    根据实际需求,通过设置数据库的事务隔离级别可以解决多个事务并发情况下出现的脏读.不可重复读和幻读问题,数据库事务隔离级别由低到高依次为Read uncommitted.Read committed.R ...

  7. java 一个大事务下的新增、修改、查询_重新学习Mysql数据库8:MySQL的事务隔离级别实战...

    本文转自:https://blog.csdn.net/sinat_27143551/article/details/80876127 本系列文章将整理到我在GitHub上的<Java面试指南&g ...

  8. mysql数据库事务隔离级别是_数据库事务隔离级别-MySQL为例 · Sean

    在银行系统的存取款过程中,当遇到对一个账户并发存取的时候,系统该如何处理比较好.可能不少人会想到线程同步,然而在应用层使用同步会导致对象锁定,大大影响并发效率.此时,充分利用数据库的事务隔离机制可以很 ...

  9. mysql 默认事务隔离级别_MySQL 事务隔离级别详解

    个人公众号『码农札记』,欢迎关注,查看更多精彩文章. 简介: MySQL的事务隔离级别一共有四个,分别是读未提交.读已提交.可重复读以及可串行化. 四个特性ACID 原子性 (Atomicity) 事 ...

最新文章

  1. 打独立运行包遇到无法trim咋解决
  2. C++ 空指针和野指针
  3. storm日志bebug问题
  4. 多重异常处理 java
  5. 基于JAVA+SpringMVC+Mybatis+MYSQL的医院管理系统
  6. poj 3279 poj 1753
  7. SSM框架面试题及答案整理
  8. 西门子plc语句表是c语言吗,分享精心整理的西门子PLC指令表
  9. 重庆要做的“边缘计算”,是什么?
  10. sla的三个服务等级_联络中心的服务水平标准是什么?
  11. 从幼苗长成大树 中美两国GIS软件技术已并驾齐驱
  12. 照片如何转换成pdf?手机电脑都可以轻松转换
  13. PAT B1033旧键盘打字
  14. [USACO08DEC]在农场万圣节Trick or Treat on the Farm【Tarja缩点+dfs】
  15. python dataframe去掉索引_python中pandas.DataFrame(创建、索引、增添与删除)的简单操作方法介绍...
  16. 原生js-购物车案例(三)已选商品添加和删除
  17. VMware 虚拟机图文安装和配置 Rocky Linux 8.5 教程
  18. Kinect2.0远程控制鼠标操作
  19. 直播翻车的罗永浩,和多次哽咽的罗玉龙是什么关系?
  20. 全国计算机等级考试一级试题2,全国计算机等级考试一级考试练习(新大纲)试题及答案(二)...

热门文章

  1. Netty 实现聊天功能
  2. 海康威视技术面试总结
  3. Windows10登录背景虚化问题
  4. python实现农历和阳历日期转换
  5. java moco_moco入门
  6. Python学习之路 (五)爬虫(四)正则表示式爬去名言网
  7. Github博客备份
  8. 解读红帽的云计算战略 云端之争优势何在(1)
  9. RT_Thread_进程间通讯——消息队列
  10. java微信红包案例_Java--案例--微信发红包