04-事务的四大特征,四种隔离级别,三大读,乐观锁和悲观锁

2023-12-17 11:12

本文主要是介绍04-事务的四大特征,四种隔离级别,三大读,乐观锁和悲观锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

事务(transaction)

四个特征ACID

一个业务通常需要多条DML(增删改)语句共同联合起来(同时成功或失败)才能完成,那么事务其实就是这个完整的业务逻辑,是一个最小的工作单元不可再分

  • 只有执行DML语句时才会考虑事务问题, 因为一旦涉及到数据的增、删、改就要考虑安全问题

MySQL默认情况下是支持自动提交事务的,即每执行一条DML语句会自动提交一次,自动提交事务并不符合我们的开发需求

  • 一个业务通常需要多条DML语句同时成功后才能提交事务,为了保证数据的安全我们需要关闭MySQL事务自动提交机制start transaction

事务在执行过程中每一条DML(增删改)的操作都会被记录到InnoDB存储引擎提供的事务性活动的日志文件中,最后根据需求决定是提交还是回滚事务

  • commit,提交事务(标志事务成功结束):,执行之前批量的DML操作然后将数据全部持久化到数据库表中,然后清空事务性活动的日志文件
  • rollback,回滚事务(标志事务失败结束): 将之前所有的DML操作全部撤销(回滚永远都是只能回滚到上一次的提交点),然后清空事务性活动的日志文件
  • 事务只能回滚insert,delete和update语句,不能回滚select,create,drop,alter这些操作因为没有任何意义

事务的四个处理过程

  • 第一步: 开启事务start transaction

  • 第二步: 执行核心业务代码包含批量的DML语句

  • 第三步: 如果核心业务代码处理过程中没有出现异常则提交事务commit

  • 第四步: 如果核心业务代码处理过程中出现异常回滚事务rollback

事务的四个特性原子性,一致性,隔离性,持久性

特性描述
Atomicity(原子性)说明事务是最小的工作单元,整个事务中的所有操作必须作为一个单元即全部完成或全部取消
Consistency(一致性)在同一个事务当中所有操作必须同时成功或失败,保证数据库从一个一致性状态转换到另一个一致性状态
Isolation(隔离性)A事务和B事务操作同一张表的时候,事务之间需要具有一定的隔离性
事务并发就是一个线程一个事务
Durability(持久性)事务一旦提交后该事务对数据库所作的更改将持久地保存在数据库之中,即使数据库发生故障也无法回滚

事务的提交与回滚

-- 使用bjpowernode数据库
mysql> use bjpowernode;
-- 查询dept_bak表中的数据        
mysql> select * from dept_bak;+--------+-------+------+| DEPTNO | DNAME | LOC  |+--------+-------+------+|     10 | abc   | bj   |+--------+-------+------+1 row in set (0.00 sec)
-- 开启事务,关闭事务的自动提交机制
mysql> start transaction;
-- 向dept_bak表中插入数据
mysql> insert into dept_bak values(20,'abc','bj');
-- 提交事务
mysql> commit;
-- 再次查看表中的数据
mysql> select * from dept_bak;+--------+-------+------+| DEPTNO | DNAME | LOC  |+--------+-------+------+|     10 | abc   | bj   ||     20 | abc   | tj   |+--------+-------+------+
-- 回滚事务只能回滚到上一次的提交点
mysql> rollback;
-- 再次查看表中的数据
mysql> select * from dept_bak;+--------+-------+------+| DEPTNO | DNAME | LOC  |+--------+-------+------+|     10 | abc   | bj   ||     20 | abc   | tj   |+--------+-------+------+

事务的隔离级别(三大读)

InnoDB存储引擎实现了四个隔离级别用以控制每个事务所做的修改.并将修改通告至其它并发的事务

名称描述
脏读在事务A中读取到了事务B未提交到数据库的数据,由于事务B可能回滚,所以事务A可能会读取到数据库中不存在的数据
不可重复读(读-读)事务B在事务A多次读取同一数据的过程中对事务A读取的数据做了更新操作并提交,导致事务A每次读取的数据都不一致
幻读(读-写)某一次的读操作得到的结果无法支撑后续的业务操作,多事务并发的情况下一定会存在幻读现象
在可重复读的情况下,事务A想插入一条id=5的记录,此时事务B把这条记录插入了并提交了事务,但是事务A看不到事务B插入的记录,结果就是事务A插入时报错了
隔离级别描述脏读不可重复读幻读加锁读
读未提交(理论级别)(READ_UNCOMMITTED)在一个事务中可以看到其他事务未提交的修改数据,大多数的数据库隔离级别都是二档起步不加锁
读已提交 (Oracle默认级别)
(READ_COMMITTED)
在一个事务中只能看到其他事务已经提交的修改数据,这种隔离级别每次读到的都是真实数据不加锁
可重复读(MySQL默认级别)
(REPEATABLE_READ)
一个事务中多次执行相同的SELECT语句得到的是相同的结果,永远读取的都是自己刚开启事务时的数据,不管其他事务是否提交了修改数据不加锁
序列化(最高隔离级别)
SERIALIZABLE
一个事务与其他事务完全地隔离,每一次读取到的数据都是最真实的,但是所有事务只能排队执行,不支持并发所以效率最低加锁

设置服务的隔离级别

设置MySQL服务默认的事务隔离级别: 在my.ini配置文件中配置[mysqld]选项的transaction-isolation属性

#该选项值可以是READ-UNCOMMITTED , READ-COMMITTED , REPEATABLE-READ , SERIALIZABLE
[mysqld]
#设置隔离级别,如果没有设置默认是REPEATABLE-READ
transaction-isolation00 = READ-COMMITTED

动态设置MySQL服务的隔离级别:SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL <isolation-level>,设置完后需退出MySQL服务重新进入

  • isolation-level(隔离级别): 隔离级别可以是READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ,SERIALIZABLE

  • 全局级(GLOBAL): 设置的隔离级别对所有的会话有效

  • 会话级(SESSION): 默认设置的隔离级别只对当前的会话有效

-- 设置隔离级别为READ COMMITTED ,默认只对当前的会话有效
mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED-- 设置会话级隔离级别为READ COMMITTED 
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED-- 设置全局级隔离级别为READ COMMITTED :
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED

查看MySQL服务当前的隔离级别: 服务器变量tx_isolation默认保存着当前的会话隔离级别

-- 查看当前隔离级别,默认是会话级别
mysql> SELECT @@tx_isolation;
-- 查看当前会话的隔离级别
mysql> SELECT @@session.tx_isolation;
-- 查看当前全局的隔离级别
mysql> SELECT @@global.tx_isolation;

验证四种隔离界别

验证read uncommited读未提交

事务A事务B
s1>use bjpowernode;s2>set global transaction isolation level read uncommitted;
s1>create table tx ( id int(11),num int (10));s2>use bjpowernode;
s1>start transaction;s2>start transaction;
s2>select * from tx;(空表)
s1>insert into tx values (1,10);事务A还没有提交
s2>select * from tx;事务B读取到了事务A未提交的数据
s1>rollback;
s2>select * from tx;

验证read commited读已提交

事务A事务B
s1>use bjpowernodes2> set global transaction isolation level read committed;
s1>start transaction;s2>use bjpowernode;
s2>start transaction;
s2>select * from tx;(空表)
s1>insert into tx values (1,10);
s2>select * from tx;由于事务A没有提交数据,事务B的读取的还是空表
s1>commit;事务A提交了数据
s2>select * from tx;事务B读取到了事务A已提交的数据

验证repeatable read可重复读

事务A事务B
s1>use bjpowernodes2>set global transaction isolation level repeatable read;
s1>start transaction;s2>use bjpowernode;
s2>start transaction;
s2>select * from tx;事务B开启时的数据
s1>insert into tx values (1,10);
s1>commit;事务A提交了数据
s2>select * from tx;即使事务A提交了数据,事务B读取的还是自己开启事务时的数据

验证serializable序列化

事务A事务B
s1>use bjpowernode;s2>set global transaction isolation level serializable;
s1>start transaction;s2>use bjpowernode;
select * from tx;s2>start transaction;
s1>insert into tx values (1,10);事务A在操作tx表时,只要当前事务不结束,其他事务就不能访问tx表
s2>select * from tx;事务B不能访问tx表

乐观锁和悲观锁

乐观锁: 支持事务并发,只不过需要设置给数据库中的表设置一个版本号字段

悲观锁(行级锁): 事务必须排队执行

  • 事务A在执行select语句时加上for update,表示将查询到的所有记录整行锁住,此时只要事务A不提交事务,其他事务就无法对这些记录进行修改操作

第一步: 开启一个事务A专门进行查询,并且使用行级锁锁住查询到的记录

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 使用工具类获取连接对象
conn = DBUtil.getConnection();
// 将数据库的自动提交功能改为手动提交,即开启数据库事务的功能
conn.setAutoCommit(false);
// 获取预编译的数据库操作对象
String sql = "select ename from emp where job = ? for update";
ps = conn.prepareStatement(sql);
ps.setString(1, "MANAGER");
rs = ps.executeQuery();
while(rs.next()){System.out.println(rs.getString("ename"));
}
// 提交事务
conn.commit();
// 关闭资源
DBUtil.close(conn,ps,rs)

第二步: 开启一个事务B修改锁定的记录,此时发现只要事务A不结束,事务B的修改操作就无法执行

Connection conn = null;
PreparedStatement ps = null;
// 使用工具类获取连接
conn = DBUtil.getConnection();
// 关闭数据库的自动提交功能改为手动提交, 即开启数据库事务的功能
conn.setAutoCommit(false);
// 获取预编译的数据库操作对象
String sql = "update emp set sal = sal * 1.1 where job = ? ";
ps = conn.prepareStatement(sql);
ps.setString(1, "MANAGER");
int count = ps.executeUpdate();
System.out.println(rs.getString(count));
// 提交事务
conn.commit();
// 关闭资源
DBUtil.close(conn,ps,null)

这篇关于04-事务的四大特征,四种隔离级别,三大读,乐观锁和悲观锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/504171

相关文章

MySQL快速复制一张表的四种核心方法(包括表结构和数据)

《MySQL快速复制一张表的四种核心方法(包括表结构和数据)》本文详细介绍了四种复制MySQL表(结构+数据)的方法,并对每种方法进行了对比分析,适用于不同场景和数据量的复制需求,特别是针对超大表(1... 目录一、mysql 复制表(结构+数据)的 4 种核心方法(面试结构化回答)方法 1:CREATE

C# List.Sort四种重载总结

《C#List.Sort四种重载总结》本文详细分析了C#中List.Sort()方法的四种重载形式及其实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录1. Sort方法的四种重载2. 具体使用- List.Sort();- IComparable

MyBatisPlus乐观锁和悲观锁的实现示例

《MyBatisPlus乐观锁和悲观锁的实现示例》本文主要介绍了MyBatisPlus乐观锁和悲观锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小... 目录1.场景2.乐观锁和悲观锁3.乐观锁实现4.悲观锁1.场景一件商品,成本价是80元,售价是10

JAVA Log 日志级别和使用配置示例

《JAVALog日志级别和使用配置示例》本文介绍了Java中主流的日志框架,包括Logback和Log4j2,并详细解释了日志级别及其使用场景,同时,还提供了配置示例和使用技巧,如正确的日志记录方... 目录一、主流日志框架1. Logback (推荐)2. Log4j23. SLF4J + Logback

java中ssh2执行多条命令的四种方法

《java中ssh2执行多条命令的四种方法》本文主要介绍了java中ssh2执行多条命令的四种方法,包括分号分隔、管道分隔、EOF块、脚本调用,可确保环境配置生效,提升操作效率,具有一定的参考价值,感... 目录1 使用分号隔开2 使用管道符号隔开3 使用写EOF的方式4 使用脚本的方式大家平时有没有遇到自

Python打包成exe常用的四种方法小结

《Python打包成exe常用的四种方法小结》本文主要介绍了Python打包成exe常用的四种方法,包括PyInstaller、cx_Freeze、Py2exe、Nuitka,文中通过示例代码介绍的非... 目录一.PyInstaller11.安装:2. PyInstaller常用参数下面是pyinstal

Spring的基础事务注解@Transactional作用解读

《Spring的基础事务注解@Transactional作用解读》文章介绍了Spring框架中的事务管理,核心注解@Transactional用于声明事务,支持传播机制、隔离级别等配置,结合@Tran... 目录一、事务管理基础1.1 Spring事务的核心注解1.2 注解属性详解1.3 实现原理二、事务事

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

详解Spring中REQUIRED事务的回滚机制详解

《详解Spring中REQUIRED事务的回滚机制详解》在Spring的事务管理中,REQUIRED是最常用也是默认的事务传播属性,本文就来详细的介绍一下Spring中REQUIRED事务的回滚机制,... 目录1. REQUIRED 的定义2. REQUIRED 下的回滚机制2.1 异常触发回滚2.2 回

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志