文章目录
- 一、什么是事务?
- 二、事务四大特性ACID
- 2.1、原子性(Atomicity)
- 2.2、一致性(Consistency)
- 2.3、隔离性(Isolation)
- 2.4、持久性(Durability)
- 三、事务操作/事务的用法
- 3.1、事务操作系列语法
- 3.2、模拟转账失败(修改提交方式为手动)
- 四、并发事务问题(脏读-幻读-不可重复读)
- 4.1、并发事务概念
- 4.2、并发事务问题
- 4.2.1、脏写/更新丢失
- 4.2.2、脏读
- 4.2.3、不可重复读
- 4.2.4、幻读
- 五、事务隔离级别
- 5.1、查看、修改事务隔离级别
- 六、总结
- 6.1、事务特性:
- 6.2、隔离级别:
- 6.3、如何手动开启事务?【默认是自动commit的】
本文详细介绍了数据库事务的概念及操作,包括事务的定义、开启、提交与回滚。
一、什么是事务?
思考:我去银行给朋友汇款,我卡上有100元,朋友卡上50元,我给朋友转账50元,如果我的钱刚扣,而朋友的钱又没加时,网线断了, 怎么办?
事务:(Transaction)是数据库管理系统(DBMS)中的一个核心概念,它确保了一系列数据库操作要么全部成功,要么全部失败
,从而维护数据库的完整性和一致性。
MySQL在5.5版本开始,就将InnoDB引擎作为默认存储引擎。
由于Mysql中的事务是存储引擎实现,而且只有InnoDB支持事务 ,其他常见的如MyISAM和Memory都是不支持事务的,因此我们讲解InnoDB的事务。
二、事务四大特性ACID
- 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务内的操作要么都发生,要么都不发生
- 一致性(Consistency):事务前后数据的完整性必须保持一致
- 隔离性(Isolation):多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。隔离性由隔离级别保障!
- 持久性(Durability):一个事务一旦提交,他对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
其中:原子性和一致性由undolog实现;隔离性由mvcc实现;持久性由redeolog实现
举个例子:
A向B转账500,转账成功,A扣除500元,B增加500元,原子操作体现在要么都成功,要么都失败
在转账的过程中,数据要一致,A扣除了500,B必须增加500
在转账的过程中,隔离性体现在A向B转账,不能受其他事务干扰
在转账的过程中,持久性体现在事务提交后,要把数据持久化(可以说是落盘操作)
2.1、原子性(Atomicity)
原子性:事务是一个不可分割的工作单位,事务中的操作要么全部执行,要么全部不执行。这确保了事务的完整性,防止了部分操作成功而部分操作失败的情况。
- 事务是一个完整的操作,事务的各元素是不可分的。
- 事务中的所有元素必须作为一个整体提交或回滚。
- 如果事务中的任何元素失败,则整个事务将失败。
2.2、一致性(Consistency)
一致性:指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
- 当事务完成时,数据必须处于一致状态 。
- 在事务开始前,数据库中存储的数据处于一致状态。
- 在正在进行的事务中,数据可能处于不一致的状态。
- 当事务成功完成时,数据必须再次回到E知的一致状态。
2.3、隔离性(Isolation)
隔离性:指在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。事务之间的操作不会互相干扰。
-
对数据进行修改的所有并发事务是彼此隔离的,表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
-
修改数据的事务可在另一个使用相同数据的事务开始之前访问这些数据,或者在另一一个使用相同数据的事务结束之后访问这些数据。
-
也就是说并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。
-
隔离级别:
- 未提交读(Read Uncommitted):允许脏读,即允许一个事务看到其他事务未提交的修改。这种隔离级别最低,性能最高,但一致性最差。
- 提交读(Read Committed):只允许一个事务看到其他事务已经提交的修改。这种隔离级别可以防止脏读,但不能防止不可重复读和幻读。
- 可重复读(Repeatable Read):确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果。这种隔离级别可以防止脏读和不可重复读,但不能完全防止幻读(在某些数据库系统中,如MySQL的InnoDB引擎,通过间隙锁等技术可以进一步防止幻读),MySQL默认事务级别。
- 串行化(Serializable):将事务完全隔离,使得它们按顺序执行。这种隔离级别最高,一致性最好,但性能最低。
2.4、持久性(Durability)
持久性:指事务一旦提交,它对数据库所做的更改就会永久地保存在数据库中,即使系统发生故障也不会丢失。
- 指不管系统是否发生故障,事务处理的结果都是永久的。
- 一旦事务被提交,事务的效果会被永久地保留在数据库中。
总结:在事务管理中,原子性是基础,隔离性是手段,一致性是目的,持久性是结果。
三、事务操作/事务的用法
3.1、事务操作系列语法
- 查看/设置事务提交方式
-- 查看会话级自动提交设置 ON:开启自动提交 OFF:禁用自动提交
show session variables like 'autocommit';-- 注意:原命令中的 'lile' 应为 'like',查看全局级自动提交设置 ON:开启自动提交 OFF:禁用自动提交
show global variables like 'autocommit';-- 查看/设置事务提交方式 1: 自动提交 0:手动提交
select @@autocommit; -- 会显示 @@autocommit=1;默认为自动--设置事务提交方式
set @@autocommit=0; -- 手动
- 开启事务
--开启事务
START TRANSACTION 或 BEGIN;
- 提交事务
--提交事务
COMMIT ;
- 回滚事务
--回滚事务
ROLLBACK;
- 保存点
-- 设置保存点
SAVEPOINT;
-- 设置保存点并命名为S1
SAVEPOINT S1;
测试事务的使用及回滚
begin;
update account set money= money + 100 where name='A';
SAVEPOINT S1;
update account set money= money + 100 where name='B';
SAVEPOINT S2;
insert into account values(3,'C',1000);select * from account; -- 查看当前状态
ROLLBACK TO S1; -- 回滚到S1点
select * from account; -- 查看回滚后的状态
在这个案例中,创建了多个回滚点,并进行了多次更新和插入操作。然后,我们将事务回滚到S1
点,此时只有A的money字段被增加了100,而B的money字段和C的插入操作都被撤销了。这意味着从S1点到事务结束之间的所有操作(更新B的money和插入C的记录)都被撤销了,而S1点之前的操作(更新A的money)则保留了下来。
3.2、模拟转账失败(修改提交方式为手动)
数据准备
-- 数据准备
create table account(id int auto_increment primary key comment '主键ID',name varchar(10) comment '姓名',money int comment '余额'
) comment '账户表';
insert into account(id, name, money) VALUES (null,'张三',2000),(null,'李四',2000);-- 恢复数据操作
update account set money = 2000 where name = '张三' or name = '李四';
代码示例:
- 如下方代码所示,我们用
程序执行报错 ...
模拟抛异常 - 此时由于我们 设置为手动提交
set @@autocommit = 0;
, 所以事务并未提交; - 后续
rollback ;
回滚事务即可;
-- 方式一
select @@autocommit;set @@autocommit = 0; -- 设置为手动提交-- 转账操作 (张三给李四转账1000)
-- 1. 查询张三账户余额
select * from account where name = '张三';-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';程序执行报错 ...//模拟抛异常-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';-- 提交事务
commit;-- 回滚事务
rollback ;
四、并发事务问题(脏读-幻读-不可重复读)
4.1、并发事务概念
并发事务是指在数据库系统中,多个事务同时对数据进行读写和修改的过程。
对于同时运行的多个事务(多线程并发), 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题: (问题的本质就是线程安全问题,共享数据的问题)
应对措施:
- 事务隔离机制
- 锁机制
- MVCC多版本并发控制隔离机制
4.2、并发事务问题
多个事务并发会导致什么问题呢?
问题 | 描述 |
---|---|
脏写 | 一个事务修改了另一个事务已经修改但尚未提交的数据。 |
脏读 | 一个事务读到了另一个事务还未提交的数据 |
不可重复读 | 一个事务读到了另一个事务已经提交(update) 的数据。引起事务中的多次查询结果不一致。 |
虚读/幻读 | 一个事务读到了另一个事务已经插入(insert) 的数据。导致事务中多次的查询结果不一致。 |
注意:√表示可能存在的问题 ,×表示解决该问题。
4.2.1、脏写/更新丢失
脏写:一个事务修改了另一个事务已经修改但尚未提交的数据。这种情况可能会导致数据不一致性和丢失更新的问题。
具体场景:具体来说,假设有两个事务A和B,事务A修改了某条数据,但还没有提交,此时事务B也修改了同一条数据并提交了。这样就会导致事务A的修改被覆盖或丢失,从而造成数据的不一致性。
示例:
4.2.2、脏读
脏读:一个事务读到了另一个事务还未提交的数据。 脏读 (违反了事务的隔离性)
示例:
例:事务A先查询id为1的数据,再修改id为1的数据。修改完以后事务A还没提交,这时候事务B也来查询id为1的数据,但这时候事务B已经可以读到A修改完的数据了。这就是脏读。(如果A事务回滚,但B已经读到了A修改的数据,造成了数据不一致)
4.2.3、不可重复读
不可重复读: 一个事务读到了另一个事务已经提交(update) 的数据。引起事务中的多次查询结果不一致。不可重复读 (违反了事务的一致性)
例:事务A先查询id为1的数据,再执行某个逻辑。这时事务B修改id为1的数据。事务A再去查询id为1的数据,却和之前不一样了!简单来说就是:在同一个事务内,查询两次同一条数据,却出现不同结果!
4.2.4、幻读
虚读/幻读: 一个事务读到了另一个事务已经插入(insert) 的数据。导致事务中多次的查询结果不一致。幻读 (违反了事务的隔离性)(在已经解决了不可重复读的基础上)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
例:事务A查询id为1的数据,发现数据库中没有。这个时候事务B来了,正好往数据库中插入了一条id为1的数据并且提交了事务。这个时候,事务A进行插入操作,就直接报错了:里面已经有了id为1的数据(假设我们已经解决了不可重复读的问题)。但A再次进行查询,查询结果和第一次一样,发现还是没有id为1的数据,但插入就是报错,这就是幻读的问题。
注意:幻读是在解决了不可重复读的基础上,不可重复读是读取了其他事务更改的数据,针对update操作;幻读是读取了其他事务新增的数据,针对insert和delete操作。
五、事务隔离级别
数据库事务的隔离级别有4个,由低到高依次为:
- 未提交读(Read Uncommitted):允许脏读,即允许一个事务看到其他事务未提交的修改。这种隔离级别最低,性能最高,但一致性最差。
- 提交读(Read Committed):只允许一个事务看到其他事务已经提交的修改。这种隔离级别可以防止脏读,但不能防止不可重复读和幻读。
- 可重复读(Repeatable Read)(mysql默认):确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果。这种隔离级别可以防止脏读和不可重复读,但不能完全防止幻读(在某些数据库系统中,如MySQL的InnoDB引擎,通过间隙锁等技术可以进一步防止幻读)。
- 串行化(Serializable):将事务完全隔离,使得它们按顺序执行。这种隔离级别最高,一致性最好,但性能最低。
注意:事务隔离级别越高,数据越安全,但是性能越低。
这四个级别可以逐个解决脏写、脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED(读未提交) | 存在 | 存在 | 存在 |
READ COMMITTED(读已提交) | 不存在 | 存在 | 存在 |
REPEATABLE READ(可重复读) | 不存在 | 不存在 | 存在 |
SERIALIZABLE(串行化) | 不存在 | 不存在 | 不存在 |
- 以表格列举的顺序,从上到下,隔离级别越来越高,最高级别SERIALIZABLE强制事务按顺序执行,不允许并发。
- 隔离级别越高,安全性越高,但性能越差。 选择合适的隔离级别尤为重要,既要考虑安全性又要考虑性能。
REPEATABLE READ
(可重复读)尽管没有完全解决幻读问题,但通过多版本并发控制(MVCC)和一种称为“Next-Key Lock”的技术解决了部分幻读问题。
我们在上面并发事务中提到了脏写,为什么在隔离级别中不存在脏写的问题了?
答:文上提到的4种隔离级别下,都不存在脏写情况。因为在这些隔离级别下,当两个事务A和B尝试去更新同一条数据时,假定A先更新数据,会对更新的数据行记录加上排他锁(也叫写锁,悲观锁),除非事务A提交或终止从而释放排他锁,否则事务B都是无法更新数据的。(设计数据密集型应用只是说读提交隔离级别一定可以杜绝脏写问题,并未提到读未提交隔离级别,经过实践,读未提交下事务B的更新操作也是需要等待事务A的排他锁释放,才得以执行)
5.1、查看、修改事务隔离级别
--查看事务隔离级别语法 【8.0+版本】【下面2条语句都可以查】
SELECT @@TRANSACTION_ISOLATION;
show variables like 'transaction_isolation';--修改事务隔离级别语法
SET [SESSION|GLOBLE] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}-- 示例: 设置read committed级别:
set session transaction isolation level read committed;
设置 session 和 global 的区别:
- session(会话级):session参数仅在当前会话(或连接)中有效
- global(全局级):global参数是全局的,意味着改变某个系统变量的值将会对所有会话都产生影响。 需要注意的是,一旦数据库服务重启,除非在配置文件(如my.cnf或my.ini)中进行了设置,否则这些全局变量的改变就会失效。
六、总结
6.1、事务特性:
事务特性 | 描述 |
---|---|
原子性 | 把事务中的所有操作看作为一个不可分割的工作单元,要么都执行,要么都不执行 |
一致性 | 保证事务开始前和事务结束后数据的完整和一致 |
隔离性 | 使多个事务并发操作同一个数据时,每个事务都有自己各自独立的数据空间,事务的执行不会受到其它事务干扰。可以通过设置隔离级别来解决不同的一致性问题 |
持久性 | 当事务被提交以后,事务中的命令操作修改的结果会被持久化保存,且不会被回滚 |
6.2、隔离级别:
隔离级别 | 允许的操作类型 | 备注 |
---|---|---|
未提交读 (Read Uncommitted) | 脏读、不可重复读、幻读 | 最低级别的隔离,可能会读取到其他事务未提交的更改 |
提交读 (Read Committed) | 不允许脏读,允许不可重复读、幻读 | 只能读取到其他事务已经提交的更改,但同一事务内多次读取可能结果不同 |
可重复读 (Repeatable Read) | 不允许脏读、不可重复读,有条件的允许幻读(使用InnoDB存储引擎可以解决) | 保证同一事务内多次读取结果一致,但某些情况下可能产生幻读,InnoDB通过间隙锁等方式解决 |
串行读 (Serializable) | 都不允许(相当于锁表) | 最高级别的隔离,通过锁表保证事务的完全隔离,但会严重影响数据库的并发性能 |
注意:√表示可能存在的问题 ,×表示解决该问题。
6.3、如何手动开启事务?【默认是自动commit的】
-- 1、显式声明开启事务
begin;-- 2、输入需要执行的sql语句
update user set name ='李四' where name='lisi';-- 3、提交事务
commit;