事务
一、操作演示(张三给李四转1000块)
数据准备
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);
正常情况
#张三2000 李四2000 #第一步,李四余额加上1000 UPDATE account SET money=money+1000 WHERE name='李四' #第二步,张三余额减去1000 UPDATE account SET money=money-1000 WHERE name='张三' AND money>=1000 #张三1000 李四3000
异常情况事务处理第一种方式:
创建一个转账的存储过程:
DELIMITER $$ CREATE PROCEDURE `proc_zhuan_zhang`( IN amount INT) BEGIN #转帐前查询 SELECT * FROM account; #第一步,李四余额加上1000 UPDATE account SET money=money+amount WHERE `name`='李四'; #第二步,张三余额减去1000 UPDATE account SET money=money-amount WHERE `name`='张三' AND money>=amount; IF ROW_COUNT() >= 1 THEN COMMIT; SELECT '转账成功'; ELSE ROLLBACK; SELECT '转账失败'; END IF; #转账结果查询 SELECT * FROM account; END$$
给张三余额改为800元,模拟余额不足1000转账失败事务回滚:
UPDATE account SET money=800 WHERE `name`='张三'
执行存储过程:
#查看事务提交方式 SELECT @@AUTOCOMMIT; #设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效 SET @@AUTOCOMMIT = 0; call proc_zhuan_zhang(1000)
结果输出:
转帐前:张三800,李四1000
转账失败
转帐后:张三800,李四1000
我们也可以演示一下转账100元的成功情况:
#查看事务提交方式 SELECT @@AUTOCOMMIT; #设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效 SET @@AUTOCOMMIT = 0; call proc_zhuan_zhang(100)
输出结果: 转帐前:张三800,李四1000 转账失败 转帐后:张三700,李四1100
二、开启/提交/回滚事务
上面演示操作异常的时候,SET @@AUTOCOMMIT = 0这样开启事务总是感觉有些扯淡,我们可以用如下的方式开启事务:
START TRANSACTION 或 BEGIN TRANSACTION;
例:
START TRANSACTION; call proc_zhuan_zhang(1000)
提交事务:
COMMIT;
回滚事务:
ROLLBACK;
三、事务的四大特性
- 原子性(Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败
- 一致性(Consistency):事务完成时,必须使所有数据都保持一致状态
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
四、并发事务问题
问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务还没提交的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在 |
五、并发事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read(默认) | × | × | √ |
Serializable | × | × | × |
- √表示在当前隔离级别下该问题会出现
- Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差
#查看事务隔离级别: SELECT @@TRANSACTION_ISOLATION; #设置事务隔离级别: SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }; SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效