【愚公系列】2023年05月 Java教学课程 106-MySQL数据库(事务)
一、MySQL事务
1.事务的概念
事务是指一组操作,这些操作被视为单个逻辑单元,在数据库系统中被视为原子操作。如果其中任何一个操作失败,整个事务都会被回滚到之前的状态。在数据库系统中,事务是确保数据的一致性、可靠性和完整性的重要机制。事务通常具有四个基本属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
• 原子性表示一个事务中的所有操作要么全部成功,要么全部失败;
• 一致性表示事务执行前后,数据库的状态必须保持一致;
• 隔离性表示并发执行的事务之间是相互隔离的,互不干扰;
• 持久性表示一旦事务提交,其修改的数据将永久保存在数据库中,即使系统故障也不会丢失。
2.事务的数据准备
-- 创建db10数据库
CREATE DATABASE db10;
-- 使用db10数据库
USE db10;
-- 创建账户表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT, -- 账户id
NAME VARCHAR(20), -- 账户名称
money DOUBLE -- 账户余额
);
-- 添加数据
INSERT INTO account VALUES (NULL,'张三',1000),(NULL,'李四',1000);
3.未管理事务演示
-- 张三给李四转账500元
-- 1.张三账户-500
UPDATE account SET money=money-500 WHERE NAME='张三';
-- 2.李四账户+500
出错了...
UPDATE account SET money=money+500 WHERE NAME='李四';
-- 该场景下,这两条sql语句要么同时成功,要么同时失败。就需要被事务所管理!
4.管理事务演示
• 操作事务的三个步骤
%2. 开启事务:记录回滚点,并通知服务器,将要执行一组操作,要么同时成功、要么同时失败
%2. 执行sql语句:执行具体的一条或多条sql语句
%2. 结束事务(提交|回滚)
• 提交:没出现问题,数据进行更新
• 回滚:出现问题,数据恢复到开启事务时的状态
• 开启事务
-- 标准语法
START TRANSACTION;
• 回滚事务
-- 标准语法
ROLLBACK;
• 提交事务
-- 标准语法
COMMIT;
• 管理事务演示
-- 开启事务
START TRANSACTION;
-- 张三给李四转账500元
-- 1.张三账户-500
UPDATE account SET money=money-500 WHERE NAME='张三';
-- 2.李四账户+500
-- 出错了...
UPDATE account SET money=money+500 WHERE NAME='李四';
-- 回滚事务(出现问题)
ROLLBACK;
-- 提交事务(没出现问题)
COMMIT;
5.事务的提交方式
• 提交方式
– 自动提交(MySQL默认为自动提交)
– 手动提交
• 修改提交方式
– 查看提交方式
-- 标准语法
SELECT @@AUTOCOMMIT; -- 1代表自动提交 0代表手动提交
– 修改提交方式
-- 标准语法
SET @@AUTOCOMMIT=数字;
-- 修改为手动提交
SET @@AUTOCOMMIT=0;
-- 查看提交方式
SELECT @@AUTOCOMMIT;
6.事务的四大特征(ACID)
• 原子性(atomicity)
– 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响
• 一致性(consistency)
– 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态
– 拿转账来说,假设张三和李四两者的钱加起来一共是2000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是2000,这就是事务的一致性
• 隔离性(isolcation)
– 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离
• 持久性(durability)
– 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
7.事务的隔离级别
• 隔离级别的概念
– 多个客户端操作时 ,各个客户端的事务之间应该是隔离的,相互独立的 , 不受影响的。
– 而如果多个事务操作同一批数据时,则需要设置不同的隔离级别 , 否则就会产生问题 。
– 我们先来了解一下四种隔离级别的名称 , 再来看看可能出现的问题
• 四种隔离级别
1 |
读未提交 |
read uncommitted |
|
2 |
读已提交 |
read committed |
|
3 |
可重复读 |
repeatable read |
|
4 |
串行化 |
serializable |
• 可能引发的问题
问题 |
现象 |
|
脏读 |
是指在一个事务处理过程中读取了另一个未提交的事务中的数据 , 导致两次查询结果不一致 |
|
不可重复读 |
是指在一个事务处理过程中读取了另一个事务中修改并已提交的数据, 导致两次查询结果不一致 |
|
幻读 |
select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入。或不存在执行delete删除,却发现删除成功 |
• 查询数据库隔离级别
-- 标准语法
SELECT @@TX_ISOLATION;
• 修改数据库隔离级别
-- 标准语法
SET GLOBAL TRANSACTION ISOLATION LEVEL 级别字符串;
-- 修改数据库隔离级别为read uncommitted
SET GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted;
-- 查看隔离级别
SELECT @@TX_ISOLATION; -- 修改后需要断开连接重新开
8.事务隔离级别演示
• 脏读的问题
– 窗口1
-- 查询账户表
select * from account;
-- 设置隔离级别为read uncommitted
set global transaction isolation level read uncommitted;
-- 开启事务
start transaction;
-- 转账
update account set money = money - 500 where id = 1;
update account set money = money + 500 where id = 2;
-- 窗口2查询转账结果 ,出现脏读(查询到其他事务未提交的数据)
-- 窗口2查看转账结果后,执行回滚
rollback;
– 窗口2
-- 查询隔离级别
select @@tx_isolation;
-- 开启事务
start transaction;
-- 查询账户表
select * from account;
• 解决脏读的问题和演示不可重复读的问题
– 窗口1
-- 设置隔离级别为read committed
set global transaction isolation level read committed;
-- 开启事务
start transaction;
-- 转账
update account set money = money - 500 where id = 1;
update account set money = money + 500 where id = 2;
-- 窗口2查看转账结果,并没有发生变化(脏读问题被解决了)
-- 执行提交事务。
commit;
-- 窗口2查看转账结果,数据发生了变化(出现了不可重复读的问题,读取到其他事务已提交的数据)
– 窗口2
-- 查询隔离级别
select @@tx_isolation;
-- 开启事务
start transaction;
-- 查询账户表
select * from account;
• 解决不可重复读的问题
– 窗口1
-- 设置隔离级别为repeatable read
set global transaction isolation level repeatable read;
-- 开启事务
start transaction;
-- 转账
update account set money = money - 500 where id = 1;
update account set money = money + 500 where id = 2;
-- 窗口2查看转账结果,并没有发生变化
-- 执行提交事务
commit;
-- 这个时候窗口2只要还在上次事务中,看到的结果都是相同的。只有窗口2结束事务,才能看到变化(不可重复读的问题被解决)
– 窗口2
-- 查询隔离级别
select @@tx_isolation;
-- 开启事务
start transaction;
-- 查询账户表
select * from account;
-- 提交事务
commit;
-- 查询账户表
select * from account;
• 幻读的问题和解决
– 窗口1
-- 设置隔离级别为repeatable read
set global transaction isolation level repeatable read;
-- 开启事务
start transaction;
-- 添加一条记录
INSERT INTO account VALUES (3,'王五',1500);
-- 查询账户表,本窗口可以查看到id为3的结果
SELECT * FROM account;
-- 提交事务
COMMIT;
– 窗口2
-- 查询隔离级别
select @@tx_isolation;
-- 开启事务
start transaction;
-- 查询账户表,查询不到新添加的id为3的记录
select * from account;
-- 添加id为3的一条数据,发现添加失败。出现了幻读
INSERT INTO account VALUES (3,'测试',200);
-- 提交事务
COMMIT;
-- 查询账户表,查询到了新添加的id为3的记录
select * from account;
– 解决幻读的问题
/*
窗口1
*/
-- 设置隔离级别为serializable
set global transaction isolation level serializable;
-- 开启事务
start transaction;
-- 添加一条记录
INSERT INTO account VALUES (4,'赵六',1600);
-- 查询账户表,本窗口可以查看到id为4的结果
SELECT * FROM account;
-- 提交事务
COMMIT;
/*
窗口2
*/
-- 查询隔离级别
select @@tx_isolation;
-- 开启事务
start transaction;
-- 查询账户表,发现查询语句无法执行,数据表被锁住!只有窗口1提交事务后,才可以继续操作
select * from account;
-- 添加id为4的一条数据,发现已经存在了,就不会再添加了!幻读的问题被解决
INSERT INTO account VALUES (4,'测试',200);
-- 提交事务
COMMIT;
9.隔离级别总结
隔离级别 |
名称 |
出现脏读 |
出现不可重复读 |
出现幻读 |
数据库默认隔离级别 |
||
1 |
read uncommitted |
读未提交 |
是 |
是 |
是 |
||
2 |
read committed |
读已提交 |
否 |
是 |
是 |
Oracle / SQL Server |
|
3 |
repeatable read |
可重复读 |
否 |
否 |
是 |
MySQL |
|
4 |
serializable |
串行化 |
否 |
否 |
否 |
注意:隔离级别从小到大安全性越来越高,但是效率越来越低 , 所以不建议使用READ UNCOMMITTED 和 SERIALIZABLE 隔离级别.
- 点赞
- 收藏
- 关注作者
评论(0)