事务
什么是事务
事务是一个不可分割的、独立的执行单元,一个事务内的语句要么全部执行,要么全部不执行。
事务的特性
- 原子性:事务是不可分割的最小的工作单元,只有完全执行或完全不执行两种执行逻辑。
- 一致性:数据库总是从一个执行状态转换到另一个一致性状态。
- 隔离性:通常来说,一个事务在提交修改前,对其他事务是不可见的。
- 持久性:一旦事务已提交,其所做的更改就会永久保存到数据库中。
事务的隔离级别
数据库事务的隔离性
是指数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔 离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就 越好, 但并发性越弱.
隔离级别种类
隔离级别 | 描述 | 备注 |
---|---|---|
READ UNCOMMITTED | 允许事务读取未被其他事务提交的变更,脏读、不可重复度和幻读的问题都会出现 | 读未提交 |
READ COMMITTED | 只允许事务读取已经被其它事务提交的变更.可以避免脏读,但不可重复读和幻读问 题仍然可能出现 | Oracle 默认:读已提交 |
REPEATABLE READ | 确保事务可以多次从一个字段中读取相同的值.在这个事务持续期间,禁止其他事物 对这个字段进行更新.可以避免脏读和不可重复读,但幻读的间题仍然存在 | mysql 默认:可重复读 |
SERIALIZABLE | 确保事务可以从一个表中读取相同的行.在这个事务持续期间,禁止其他事务对该表 执行插入,更新和删除操作.所有并发问题都可以避免,但性能十分低下 | 相当于锁表, 串行 |
-
比较
- 安全性来说:
Serializable
>Repeatable read
>Read committed
>Read uncommitted
- 效率来说:
Serializable
<Repeatable read
<Read committed
<Read uncommitted
- 安全性来说:
通常来说,一般的应用都会选择Repeatable read
或Read committed
作为数据库隔离级别来使用。
查看事务的隔离级别
- mysql 8.0
SELECT @@transaction_isolation;
- mysql 5.0
SELECT @@tx_isolation;
- 通用
show variables like '%isolation%';
设置隔离级别
- serializable
set session transaction isolation level serializable;
- read committed
set session transaction isolation level read committed;
- read uncommitted
set session transaction isolation level read uncommitted;
- repeatable read
set session transaction isolation level repeatable read;
session
:表示当前会话级别,如果是全局,则换成 global
即可。
事务的使用
- 事务的分类:隐式事务和显示事务。
隐式事务
没有明显的开启和结束事务的标志,比如 insert、update、delete语句本身就是一个事务
显式事务
具有明显的开启和结束事务的标志
- 步骤1、开启事务:取消自动提交事务的功能
set autocommit=0;
start transaction;#可选的
- 步骤2、编写事务的一组逻辑操作单元(多条sql语句)
insert、update、delete
- 步骤 3、提交事务或回滚事务,此次事务操作结束。
commit;
或
rollback;
演示表
创建表
create table if not exists students
(
id int,
name varchar(20),
balance decimal(10, 2)
);
插入测试数据
insert into students values (1, '张三', 1000);
并发对事物的影响
并发会造成事务间出现 脏读、不可重复度、覆盖、幻读现象。
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没 有采取必要的隔离机制, 就会导致各种并发问题。
脏读
是一个事务在处理过程中读取了另外一个事务未提交的数据。
对于两个事务 T1、T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的,T1 读到的这个数据就是脏数据。
不可重复读
指在一个事务内多次读取同一数据,在这个事务还没结束时,另外一个事务也访问了这个数据并对这个数据进行了修改,那么就可能造成第一个事务两次读取的数据不一致,这种情况就被称为不可重复读
对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
幻读
是指同一个事务内多次查询返回的结果集总数不一样(比如增加了或者减少了行记录)。
对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插 入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行。
脏读演示
执行顺序 | 转账事务1 | 转账事务2 |
---|---|---|
T1 | 开始事务 | |
T2 | 读取账户余额1000元 | 开始事务 |
T3 | 存款100 元,修改账户余额 1100 元(1000+100) | 读取账户余额:1100 元(脏读) |
T4 | 事务结束:撤销事务,数据回滚到 1000 元 | 存款400 元,修改账户余额 1400 元(1000+400) |
T5 | 事务结束:提交事务 |
开启两个终端,分别登录数据库
设置事务的隔离级别为读未提交
set session transaction isolation level read uncommitted;
查看数据库
show databases;
选择测试数据库
use my_demo;
select * from students;
表中只有一条测试数据
+------+--------+---------+
| id | name | balance |
+------+--------+---------+
| 1 | 张三 | 1000.00 |
+------+--------+---------+
1 row in set (0.01 sec)
开启事务
两个终端都开启事务
start transaction;
查询余额
select balance from students where id=1;
+---------+
| balance |
+---------+
| 1000.00 |
+---------+
1 row in set (0.01 sec)
事务 1修改余额
update students set balance=balance+100 where id=1;
事务 2 查询余额
select balance from students where id=1;
- 此时如何事务 2 执行更新操作也会阻塞。
事务1 执行回滚,之后,在事务 2窗口 执行更新操作
- 在事务 1 回滚后,事务 2 不要再次查询余额,否则演示就没有意义了。
rollback
事务 1 回滚之后,事务 2 执行更新操作
update students set balance=balance+400 where id=1;
不可重复读演示
基于脏读案例,事务 1 继续查询,发现数据与回滚前不一致了,数据被其他进程修改了,这就是不可重复读现象。
事务 1查询余额,发现与上次不一致了
此时发现余额为1400,这就是事务 2 在食事务 1 为执行完读取了事务1 修改而未提交的数据导致的。也就是事务隔离级别为 可读未提交
导致的。
+---------+
| balance |
+---------+
| 1400.00 |
+---------+
1 row in set (0.01 sec)
错误演示说明
- 下图为事务 1 回滚后,事务 2 再次查询余额,结果两个事务数据是一致的
- 这里为了演示,事务 2 只在事务 1 更新以后,回滚之前,做一次查询操作。