概述
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
默认MySQL的事务是自动提交的,也就是说任意执行一次DML,MySQL会立即隐式的提交事务,当然这是MySQL默认的事务操作,下面我们将介绍另外两种事务操作
事务操作
建立表结构,初始化表结构
1
2
3
4
5
6
7
create table account(
id int auto_increment primary key comment '主键id',
name varchar(10) comment '姓名',
money int comment '余额'
) comment '账户表';
insert into account(name, money) values('张三', 2000),('李四', 2000);
当前表结构:
事务操作-转账演示1-@@autocommit=1
1
2
3
4
5
6
7
8
9
10
11
12
13
set @@autocommit = 1;
start transaction;
-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';
-- 此语句出错后张三钱减少但是李四钱没有增加
模拟sql语句错误
-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';
总结:
1.像这样逻辑相关的语句连续执行,一旦中间有执行错误,后面的语句都不会执行,这样就会导致非常严重的后果
2.由于MySQL的默认行为,每条语句就是一个事务,实际上对于具有逻辑相关的语句应该放在同一个事务中执行
事务操作-转账演示2-@@autocommit=0
1.关闭事务自动提交
1
2
select @@autocommit;
set @@autocommit = 0;
2.执行转账操作但不提交
3.执行转账操作并提交
4.执行转账操作失败并回滚
总结:在实际编程业务中,当事务执行成功,应该commit,事务执行失败,应该rollback
事务操作-转账演示3-start transaction
1
2
set @@autocommit = 1;
start transaction;
总结:start transaction开启事务不需要关闭自动提交
事务四大特性
以MySQL的InnoDB存储引擎为例
- A:原子性(atom),即以事务为最小操作单位,事务内的所有操作要么都成功要么都失败;依靠回滚日志(undo log)实现
- I:隔离性(Isolation),依赖数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行;依靠锁机制和MVCC实现
- D:持久性(durable),事务一旦提交或回滚,它对数据库中数据的改变就是永久的,即使这之后数据库发生故障,影响依然存在(可以还原);依靠重做日志(redo log)实现
- C:一致性(consistency),即事务执行前后,数据依然保持逻辑一致性,这个逻辑一致性一般符合现实逻辑,或由DBA指定的规则;上述三者实现后,一致性才能实现
并发事务问题
问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务还没提交的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在 |
提醒:
1.不可重复读出现的原因可能是其他并发事务对同一条记录进行了修改
2.在可重复读的隔离级别下,事务内两次读到的记录一定是相同的,但如果数据库确实被修改了,就会出现明明读到的某条记录不存在,却插入不了的情况,因为在其他并发事务已经插入并提交了该条记录
事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read(默认) | × | × | √ |
Serializable | × | × | × |
- √表示在当前隔离级别下该问题会出现
- Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差
查看事务隔离级别:
1
2
3
select @@tx_isolation;
-- 或
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别:
1
2
3
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
-- 如:
set session transaction isolation level read uncommitted;
SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效
遗留问题
1.为什么在当前会话,虽然事务未提交,查询的结果却已经被影响?
2.读已提交如何触发幻读问题(本人认为幻读问题是因为可重复读隔离级别造成的后果)
3.在不可重复读当中,两个事务不可能同时写数据,如果A事务进行写操作,B事务的写操作就会被阻塞,A事务提交,B事务才会继续执行