jdbc-3-事务以及隔离性验证
Posted 懒佯佯大哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdbc-3-事务以及隔离性验证相关的知识,希望对你有一定的参考价值。
事务介绍
-
什么是事务?
- 事务指的是由一系列操作,将系统从一个状态变化为另一个状态
-
事务的一致性?
- 事务的一系列操作,要么全部成功,要么全部失败,不存在中间状态,称为事务性
- 如果出现失败,则需要通过“回滚”rollback实现事务的一致性
-
数据一旦提交,则不可回滚
-
数据库事务注意点:
- DDL操作一但执行,不可回滚
- 即无法通过auto commit控制DDL操作
- DML操作默认为自动提交,一旦commit,不可回滚
- 可通过set auto_commit=false关闭自动提交
- 连接关闭后,DML操作会被执行,不论是否有设置过auto commit
- 故:如果需要实现事务,需要由同一个链接完成
- DDL操作一但执行,不可回滚
-
注意事项:
- 如果使用了数据库连接池,那么在连接使用完成后,需要将auto commit恢复原样。
ACID
-
事务的ACID特性:
- 事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
- 原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
- 一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相*关的。
- 隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
-
事务并发问题:
- 脏读dirty read:无效数据的读出,一般是指由于t1事务回滚,导致t2事务读取的值停留在t1回滚之前的无效数据
- 不可重复读non-repeatable read:t2事务开启期间,同一个查询命令却反回不同的查询结果
- 幻读phantom read:幻读是指当事务不是独立执行时发生的一种现象。
- 典型场景:有t1和t2两个事务(一般将autocommit置为false,便于手动控制、复现事务问题,否则一次提交事务就结束了,观察不到现象),t1和t2同时修改某一个结果集,导致结果不可控:比如t1将某一列的值+1,但是t2又新插入了一行值,那么t1和t2在提交后发现,t1会查到有一行没有+1。
- 更新丢失lost update:
- 第一类更新丢失,回滚覆盖:一个事务的回滚,覆盖了其它事务的提交
- 第二类更新丢失,提交覆盖:一个事务的提交,覆盖了另一个事务的提交
-
数据库的事务能力:
- 数据库需要有隔离运行各个事务的能力,避免出现并发问题
- 事务之间的隔离程度,称为隔离级别,数据库里存在多种隔离级别。
- 但隔离级别和并发型成反比:隔离性越高(一致性越好),但隔离性越弱
-
隔离级别:
- 一般,选择第2、3隔离级别:READ COMMITED、REPEATABLE READ
- 一般,选择第2、3隔离级别:READ COMMITED、REPEATABLE READ
mysql设置隔离级别的操作:
查看隔离级别
- SHOW VARIABLES WHERE Variable_name LIKE ‘%iso%’;
- select @@tx_isolation; 查看隔离级别:当前正在生效的隔离级别
- select @@global.tx_isolation; 查看全局隔离级别
- select @@session.tx_isolation;查看会话隔离级别
- global和session区别:
- global:
- 范围:全局生效(包括所有session),正在运行中的session不受到影响。
- 权限:只有超级用户才能修改
- 查看:需要登录退出,才能看到变化
- session:
- 范围:当前session(一次链接、一次cmd窗口)的所有事务,重新登录/连接后失效
- 权限:都可修改
- 查看:立马可查到:通过@@tx_isolation或者@@
- 省略不写时
- 范围:表示当前session的下一次(当前事务不受影响)事务生效
- 权限:都可修改
- 查看:查不到
- global:
- 注意:数据库重启后,所有的隔离设置都会失效
- 官方文档:https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_tx_isolation
- 注意:5.x版本时,transaction_isolation是tx_isolation的别名
- transaction_isolation was added in MySQL 5.7.20 as an alias for tx_isolation, which is now deprecated and is removed in MySQL 8.0. Applications should be adjusted to use transaction_isolation in preference to tx_isolation. See the description of transaction_isolation for details.
mysql> SHOW VARIABLES WHERE Variable_name LIKE '%iso%';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
| tx_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
2 rows in set (0.01 sec)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.01 sec)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| REPEATABLE-READ |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> mysql> select @@xx.tx_isolation;
ERROR 1272 (HY000): Variable 'tx_isolation' is not a variable component (can't be used as XXXX.variable_name)
修改隔离级别,查看生效范围—打开两个窗口
- 语法:MySQL 提供了 SET TRANSACTION 语句,该语句可以改变单个会话或全局的事务隔离级别。语法格式如下:
- SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE
- 示例1:set global transaction isolation level READ UNCOMMITTED;:全局
- 示例2:set transaction isolation level READ UNCOMMITTED;:下一次事务
- 默认为:REPEATABLE-READ
测试修改隔离级别:— 需要开启两个事务观察
-
测试global修改:
-
测试session级别修改:
验证不同级别的隔离性问题:— 需要开启两个事务观察
-
READ UNCOMMITTED:
-
READ COMMITTED:
-
REPEATABLE READ:没有复现出来,有点奇怪:从官网的描述来看,在该模式下,只要读了一次,那就会形成一个副本,但是没有提及幻读:
- 官网:https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_consistent_read
- 幻读的场景比较复杂,涉及到数据库锁相关的知识,目前未复现出来:https://blog.csdn.net/zl1zl2zl3/article/details/90755790
- 待后续对sql的锁进行分析后再看,目前已知的是:mysql的innodb引擎一定程度上解决了幻读的问题
java代码测试隔离性:
- 场景:这里只列出“读未提交+脏读”场景
- 代码:测试场景参见下面代码里的注释
package PreparedStatementTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import utils.ConnGetUtil;
import java.sql.*;
/**
* 测试隔离级别
*/
public class TestIsolation
private Connection connection;
private PreparedStatement preparedStatement;
private ResultSet resultSet;
@Before
public void setConnection()
try
connection = ConnGetUtil.getConnection();
catch (Exception e)
System.out.println("获取连接失败");
e.printStackTrace();
@After
public void close()
ConnGetUtil.closeConnection(connection, preparedStatement, resultSet);
/**
* t1事务进程
* 验证场景:
* t1用例和t2用例同时执行,重现脏读的场景:
* t1事务:开启事务下,先更新balance,然后回滚rollback掉
* t2事务:开启事务下,每隔3s查询一次balance
* 验证结果:
* 可以观察到t2事务的balance在变化,出现了脏读场景
*
* 备注:幻读、不可重复读暂时不管
*/
@Test
public void t1() throws Exception
// 4种隔离级别:1为读未提交、2位读已提交、4为可重复读、8为串行化读
connection.setTransactionIsolation(1);
connection.setAutoCommit(false);
String sql = "update user_table set balance=9999 where user='lisi'";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.execute();
Thread.sleep(1000 * 5); // 15s
connection.rollback(); // 回滚
/**
* t2事务
*/
@Test
public void t2() throws Exception
// 4种隔离级别:1为读未提交、2位读已提交、4为可重复读、8为串行化读
connection.setTransactionIsolation(1);
connection.setAutoCommit(false);
preparedStatement = connection.prepareStatement("select balance from user_table where user='lisi'");
for (int i = 0; i < 10; i++)
Thread.sleep(1000 * 3);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next())
int anInt = resultSet.getInt(1);
System.out.println("查询到balance为:" + anInt);
- t2事务的输出:可以看到,在事务开启之间,查到的balance值是在不断变化,出现了脏读现象
查询到balance为:3333
查询到balance为:3333
查询到balance为:9999
查询到balance为:9999
查询到balance为:9999
查询到balance为:9999
查询到balance为:9999
查询到balance为:3333
查询到balance为:3333
查询到balance为:3333
- 参考1:mysql 5.x版本的发布文档:https://dev.mysql.com/doc/relnotes/mysql/5.7/en/
- 参考2:https://www.jb51.net/article/182017.htm
以上是关于jdbc-3-事务以及隔离性验证的主要内容,如果未能解决你的问题,请参考以下文章