第二课 事务的使用

1 事务的分类

  • 隐式事务:事务没有明显的开启和结束的标记,比如INSERTUPDATEDELETE语句
    因为系统中存在autocommit常量,默认值为on,表示自动提交默认开启,SHOW VARIABLES LIKE 'autocommit';
  • 显式事务:具有明显的开启和结束标志,前提需要关闭自动提交功能,SET autocommit = 0;,只针对当前会话有效

2 事务步骤

  • 第0步:关闭自动提交
    SET autocommit = 0;
  • 第1步:开启事务
    START TRANSACTION; # 可选
  • 第2步:编写事务中的SQL语句(增删改查)
  • 第3步:结束事务(需要配合JDBC实现)
    • 如果事务中没有异常或错误,COMMIT; # 提交事务
    • 如果事务中存在异常或错误,ROLLBACK; # 回滚事务

结束条件:

  1. COMMIT;ROLLBACK;语句
  2. DDL或DCL语句(自动提交)
  3. 用户会话正常结束
  4. 系统异常终了

注:一个事务中的SQL语句只是对表中数据进行修改的语句(UPDATE,INSERT,DELETE),不包括(CREATE,DROP,ALTER,不会报错,但是也没有效果)

CREATE TABLE account (
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(20),
	balance DOUBLE
);

INSERT INTO account (username,balance) VALUES ('甲方',500);
INSERT INTO account (username,balance) VALUES ('乙方',500);

# 事务模拟:转账
# 关闭自动提交
SET autocommit = 0;

# 开启事务
START TRANSACTION;
UPDATE account SET balance = 300 WHERE username = '甲方';
UPDATE account SET balance = 700 WHERE username = '乙方';

# 结束事务
COMMIT;

3 数据库的隔离级别(事务并发问题)

3.1 脏读、幻读及不可重复读

  • 对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
    • 脏读:对于两个事务T1和T2,T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容旧是临时且无效的
    • 不可重复读:对于两个事务T1和T2,T1读取了一个字段,然后T2更新了该字段。之后,T1再次读取同一个字段,值不同了
    • 幻读:对于两个事务T1和T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,之后,如果T1再次读取同一个表就会多出几行

3.2 数据库事务的隔离性

  • 数据库系统必须具有隔离并发运行各个事务的能力,使他们不会互相影响,避免各种并发问题

3.3 隔离级别

  • 一个事务与其他事务隔离的程度称为隔离级别,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱
隔离级别描述解决问题
READ UNCOMMITTED(读未提交数据)允许事务读取未被其他事务提交的变更脏读、幻读及不可重复读的问题都会出现
READ COMMITTED(读已提交数据)只允许事务读取已经被其他事务提交的变更解决了脏读,但是幻读和不可重复读依旧存在
REPEATABLE READ(可重复读)确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新解决了脏读和不可重复读,但是幻读的问题仍然存在
SERIALIZABLE(串行化)确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作解决所有并发问题,但是性能十分低下
  • Oracle支持两种事务隔离级别:READ COMMITED(默认事务隔离级别)和SERIALIZABLE
  • MySQL支持四种事务隔离级别:REPEATABLE READ为默认事务隔离级别

3.4 隔离级别的设置

# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 设置当前会话事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

# 设置数据库系统的全局的隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

3.5 实际操作

3.5.1 脏读
-- T1 会话 START -- 
-- 脏读:执行顺序_1 START -- 
# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 脏读测试
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
# 脏读解决
# SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
-- 脏读:执行顺序_1 END -- 

-- 脏读:执行顺序_4 START -- 
# 读取T2修改未提交的
SELECT * FROM account;
-- 脏读:执行顺序_4 END -- 

/*
执行结果:T2没有提交,但是T1读取到了T2没有提交的修改后数据
*/

COMMIT;
-- T1 会话 END --
-- T2 会话 START -- 
-- 脏读:执行顺序_2 START -- 
# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 脏读测试
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
# 脏读解决
# SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
-- 脏读:执行顺序_2 END -- 

-- 脏读:执行顺序_3 START -- 
# 修改T2的数据,T1会进行读取
UPDATE account SET balance = '200' WHERE id = 1;
-- 脏读:执行顺序_3 END -- 
 
COMMIT;
-- T2 会话 END -- 
3.5.2 不可重复读
-- T1 会话 START -- 
-- 不可重复读:执行顺序_1 START -- 
# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 不可重复读测试
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
# 不可重复读解决
# SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
-- 不可重复读:执行顺序_1 END -- 

-- 不可重复读:执行顺序_3 START -- 
SELECT * FROM account -- balance 的值没有改变
-- 不可重复读:执行顺序_3 END -- 

-- 通常有其他操作

-- 不可重复读:执行顺序_5 START -- 
SELECT * FROM account -- balance 的值发生变化
-- 不可重复读:执行顺序_5 END -- 
COMMIT;
-- T1 会话 END --  
-- T2 会话 START -- 
-- 不可重复读:执行顺序_2 START -- 
# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 不可重复读测试
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
# 不可重复读解决
# SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
-- 不可重复读:执行顺序_2 END -- 

-- 不可重复读:执行顺序_4 START -- 
UPDATE account SET balance = 99;
COMMIT;
-- 不可重复读:执行顺序_4 END -- 

-- T2 会话 END -- 
3.5.3 幻读
-- T1 会话 START -- 
-- 幻读:执行顺序_1 START -- 
# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 幻读测试
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
# 幻读解决
# SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
-- 幻读:执行顺序_1 END -- 

-- 幻读:执行顺序_3 START -- 
SELECT * FROM account; -- 没有记录
-- 幻读:执行顺序_3 END -- 

-- 通常有其他操作

-- 幻读:执行顺序_5 START -- 
UPDATE account SET balance = 10; -- 两条记录被更改
COMMIT;
-- 幻读:执行顺序_5 END -- 
-- T1 会话 END --  
-- T2 会话 START -- 
-- 幻读:执行顺序_2 START -- 
# 查询当前会话事务隔离级别
SELECT @@tx_isolation;

# 幻读测试
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
# 幻读解决
# SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

# 开启事务
SET autocommit = 0;
START TRANSACTION;
-- 幻读:执行顺序_2 END -- 

-- 幻读:执行顺序_4 START -- 
# 此时发生死锁等待T1完成COMMIT指令
INSERT INTO account VALUES(NULL,'A',123); 
INSERT INTO account VALUES(NULL,'B',456);
-- 幻读:执行顺序_4 END -- 
COMMIT;
-- T2 会话 END -- 

4 回滚点设置

  • 设置回滚点
    SAVEPOINT 节点名
    
  • 回滚到节点处
    ROLLBACK TO 节点名
    
# 开启事务
SET autocommit = 0;
START TRANSACTION;

# 删除2号数据
DELETE FROM my_employees WHERE id = 2;

# 设置回滚点,从此处开始回滚
SAVEPOINT demo;
DELETE FROM my_employees WHERE id = 3;

# 跳转到回滚点
ROLLBACK TO demo;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值