根据实际需求,通过设置数据库的事务隔离级别可以解决多个事务并发情况下出现的脏读、不可重复读和幻读问题,数据库事务隔离级别由低到高依次为Read uncommitted、Read committed、Repeatable read和Serializable等四种。我们这篇博客主要来分析 Read uncommitted 这种事务隔离级别。

一、修改MySQL数据库事务隔离级别。

  • 在my.ini配置文件最后加上如下配置:
#可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
[mysqld]
transaction-isolation = READ-UNCOMMITTED
  • 重启MySQL服务。
  • 查看MySQL数据库事务隔离级别。

二、在 Read uncommitted隔离级别下使用Java代码来演示上篇博客MySQL数据库——事务的隔离级别 中提到的三种情形。

1、脏读

  • 场景

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

  • 创建数据库表并添加数据。
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);
  • 上述场景对应的Java代码如下:

①Boss类

import java.sql.*;public class Boss {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 balance=balance+5000 where card_id='6226090219290000'";statement.executeUpdate(sql);Thread.sleep(30000);//30秒后发现工资发错了connection.rollback();sql = "update account set balance=balance+2000 where card_id='6226090219290000'";statement.executeUpdate(sql);connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

②Employee类

import java.sql.*;
public class Employye {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 {//释放资源}}
}
  • 按照“脏读”场景依次执行上述Java代码并观察输出结果

①执行Boss类代码后,立即执行Employee类代码并查看Employee类输出结果。输出结果如下:

②在Boss类代码执行结束后,再次执行Employee类代码并查看Employee类输出结果。输出结果如下:

  • 分析:员工读取了老板更新但并未提交的数据,员工第一次读到的“6000”即为脏数据。

2、不可重复读

  • 场景

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

  • 创建数据库表并添加数据。
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',3000);
insert into account (id,card_id,name,balance) values (2,'6226090219299999','LilY',0);
  • 上述场景对应的Java代码如下:

①Machine类

import java.sql.*;
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 balance from account where card_id='6226090219290000'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println("余额:"+resultSet.getDouble("balance"));}System.out.println("请输入支付密码:");Thread.sleep(10000);//30秒后密码输入成功resultSet = statement.executeQuery(sql);if(resultSet.next()) {double balance = resultSet.getDouble("balance");System.out.println("余额:"+balance);if(balance<sum) {System.out.println("余额不足,扣款失败!");return;}}sql = "update account set balance=balance-"+sum+" where card_id='6226090219290000'";statement.executeUpdate(sql);connection.commit();System.out.println("扣款成功!");} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

②Wife类

import java.sql.*;
public class Wife {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 balance=balance-"+money+" where card_id='6226090219290000'";statement.executeUpdate(sql);sql = "update account set balance=balance+"+money+" where card_id='6226090219299999'";statement.executeUpdate(sql);connection.commit();System.out.println("转账成功");} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}
  • 按照“不可重复读”场景依次执行上述Java代码并观察输出结果

①执行Machine类代码之后,立即执行Wife类代码并查看对应的输出结果。

Machine类输出结果

Wife类输出结果

②Machine类最终执行完之后的输出结果

  • 分析:“事务A:POS机扣款”、“事务B:Tom的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新数据并提交了事务,而事务A再次读取该数据扣款时,数据已经发生了改变。

3、幻读

  • 场景

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生活在了水深火热之中,只感到膝盖针扎的痛......

  • 创建数据库表并添加数据。
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',3000);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');
  • 上述场景对应的Java代码如下:

①Bank类

import java.sql.*;
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(10000);//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 {//释放资源}}
}

②Husband类

import java.sql.*;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 balance=balance-"+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 {//释放资源}}
}
  • 按照“幻读”场景依次执行上述Java代码并观察输出结果

①执行Bank类代码之后立即执行Husband类代码,观察Bank类代码输出结果。

②待Bank类代码执行完之后,观察Bank类代码输出结果。

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

三、结论

当数据库事务隔离级别设置为Read uncommit时,可能会出现脏读、不可重复读和幻读。

事务隔离级别——Read uncommitted相关推荐

  1. MySQL数据库——事务隔离级别Read uncommitted(一)

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

  2. ORACLE数据库事务隔离级别

    为什么80%的码农都做不了架构师?>>>    事务隔离级别:一个事务对数据库的修改与并行的另一个事务的隔离程度. 两个并发事务同时访问数据库表相同的行时,可能存在以下三个问题: 1 ...

  3. 4. 事务隔离级别之Read Uncommitted

    前面我们说过,要获得最高的事务隔离性,可以采取序列化/串行的方式,代价是严重影响系统处理事务的吞吐量.就好像数据库是个多核CPU,事务串行后,那么意味着我们总是在使用单核,没办法发挥多核CPU的并行威 ...

  4. MySQL(InnoDB剖析):43---事务之(事务隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE)

    ISO和ANIS SQL标准指定了4种事务隔离级别的标准,但是很少有数据库厂商循环这些标准.例如Oracle数据库就不支持READ UNCOMMITTED和REPEATABLE READ SQL标准定 ...

  5. mysql 事务隔离规范_MySQL事务隔离级别以及脏读、幻读、不可重复读示例

    事务的隔离性 MySQL是一个客户端/服务器架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接上之后,就可以称之为一个会话(Session).每个客户端都可以在自己的 ...

  6. SQL SERVER的锁机制(三)——概述(锁与事务隔离级别)

    五.锁与事务隔离级别 事务隔离级别简单的说,就是当激活事务时,控制事务内因SQL语句产生的锁定需要保留多入,影响范围多大,以防止多人访问时,在事务内发生数据查询的错误.设置事务隔离级别将影响整条连接. ...

  7. 关于MySQL的四种事务隔离级别!

    本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不 ...

  8. pb设置Oracle事务的隔离级别,Oracle的事务隔离级别

    ANSI/ISO SQL规定了四种事务隔离级别,分别是:read uncommitted,read committed,repeatable read,serializable ORACE提供了SQ9 ...

  9. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因...

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

最新文章

  1. C++拾趣——C++11的语法糖auto
  2. Directx11教程(11) 增加一个debug宏
  3. 极客新闻——15、软件测试自动化的最新趋势
  4. java redis多主多备_java 集成Redis 一主多从
  5. 【HDOJ】1890 Robotic Sort
  6. html表单php连接mysql数据库,PHP 连接MySQL数据库
  7. 使用 dpu 检视 dump 中的字符串.
  8. 关于数据库中的锁,你不知道的是...
  9. 配置nfs环境的一些命令
  10. python3字典详解_python3 字典的常用 方法
  11. 音频专用linux系统,适用于Linux系统的6款音频编辑软件,
  12. 【哈佛公开课】积极心理学笔记-05环境的力量
  13. 如何在输入特殊符号,例如角度“∠”
  14. CSS-Grid(网格)布局
  15. Js简单实现音乐播放器
  16. 数据分析入门学习指南|零基础小白必看
  17. 少壮不努力,长大干IT。
  18. 三种登录形式的实现—永久登录、每次进入页面登录、设置登录有效期
  19. Cocos2D引擎学习-动作类的学习(一)
  20. 联想Miix 5 Pro二合一笔记本如何装win7系统?

热门文章

  1. 从远古文明和科学角度来回答你:水晶真的有灵性吗?
  2. 爬虫状态码412状态
  3. Window10下出现的WiFi功能消失,适配器显示错误56的解决办法
  4. MySQL索引-B+树(看完你就明白了)
  5. paddle 中的backward()函数
  6. opencv基于ycrcb的皮肤检测(改进版)
  7. 性格决定命运,习惯决定未来
  8. 现阶段大数据算法的困境是什么?
  9. 看这一篇就可以入门了——游戏测试
  10. 数据结构——双向链表