文章目录
前言
由现象引入:
-
从程序员角度来看 这就是事务中所谓的 “脏读 "
手机客户端进程里 使用套餐兑换券下单,进入待支付状态后,产生了一个记录,同时微信客户端进程看到了这个记录,微信客户端进程看到的这个记录就是脏数据。微信客户端进程读取了这个脏数据,产生了一条新记录提交了事务。手机客户端再进行了退款操作,回滚了数据。就导致了出现了这个新的兑换券
事务的概念:
数据库的事务(Transaction)是一种机制、一个操作序列,包含了一组数据库操作命令,它由一组相关的dml语句组成。
事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么同时成功,要么同时失败。如:转账就要用事务来处理,用以保证数据的一致性。
事务是一个不可分割的工作逻辑单元。
一、事务是什么?
1.举例说明
如下图有一张表
张三和李四账户中各有100块钱,现李四需要转换500块钱给张三,具体的转账操作为
- 第一步:查询李四账户余额
- 第二步:从李四账户金额 -500
- 第三步:给张三账户金额 +500
现在假设在转账过程中第二步完成后出现了异常第三步没有执行,就会造成李四账户金额少了500,而张三金额并没有多500;这样的系统是有问题的。如果解决呢?使用事务可以解决上述问题
从上图可以看到在转账前开启事务,如果出现了异常回滚事务,三步正常执行就提交事务,这样就可以完美解决问题。
2.演示事务
-- 事务的一个重要的概念和具体操作
-- 概念:
--一、回退事务
-- 在介绍回退事务之前,先介绍一下保存点(savepoint).保存点是事务中的点,用于取消部分事务,当结束事务(commit),会自动的删除该事务所定义的所有保存点。当执行回退事务时,通过指定保存点可以回退到指定的点。
--二、提交事务
-- 使用commit语句可以提交事务,当执行了commit语句子后,会确认事务的变化、结束事务、删除保存点、释放锁,数据生效。当使用commit语句结束事务子后,其它会话[其它连接]将可以查看到事务变化后的新数据【所有数据就正式生效】
-- 1. 创建一张测试表
CREATE TABLE t27
( id INT,
`name` VARCHAR(32));
-- 2. 开始事务
START TRANSACTION
-- 3. 设置保存点
SAVEPOINT a
-- 执行dml 操作
INSERT INTO t27 VALUES(100, 'tom');
SELECT * FROM t27;
SAVEPOINT b
-- 执行dml操作
INSERT INTO t27 VALUES(200, 'jack');
-- 回退到 b
ROLLBACK TO b
-- 继续回退 a
ROLLBACK TO a
-- 如果这样, 表示直接回退到事务开始的状态.
ROLLBACK
COMMIT
3.事务的细节
-- 讨论 事务细节
-- 1. 如果不开始事务,默认情况下,dml操作是自动提交的,不能回滚
INSERT INTO t27 VALUES(300, 'milan'); -- 自动提交 commit
SELECT * FROM t27
-- 2. 如果开始一个事务,你没有创建保存点. 你可以执行 rollback,
-- 默认就是回退到你事务开始的状态
START TRANSACTION
INSERT INTO t27 VALUES(400, 'king');
INSERT INTO t27 VALUES(500, 'scott');
ROLLBACK -- 表示直接回退到事务开始的的状态
COMMIT;
-- 3. 你也可以在这个事务中(还没有提交时), 创建多个保存点.比如: savepoint aaa;
-- 执行 dml , savepoint bbb
-- 4. 你可以在事务没有提交前,选择回退到哪个保存点
-- 5. InnoDB 存储引擎支持事务 , MyISAM 不支持
-- 6. 开始一个事务 start transaction, set autocommit=off;
二、MYSQL事务的隔离级别
1.事务的隔离级别介绍
2.设置事务的隔离级别
只有关了mysql服务 配置文件才会生效
3.mysql事务隔离级别-案例
开两个窗口(暂用a,b来代替)来演示事务隔离级别
涉及到的sql语句
-- 登录数据库
mysql -uroot -p
-- 查看所有数据库
show databates;
-- 查看所有表
show tables;
-- 查看表结构
desc account;
-- 创建表
CREATE TABLE `account`(
id INT,
`name` VARCHAR(32),
money INT);
-- 开启事务
start transaction;
-- 查看当前会话隔离级别
SELECT @@tx_isolation
-- 查看系统当前隔离级别
SELECT @@global.tx_isolation
-- 设置当前会话隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
-- 设置系统当前隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL [你设置的级别]
3.1、读未提交(Read uncommitted)
--窗口a、b
--1、窗口a、b登录MySQL
mysql -uroot -p
--2、查看事务级别
SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
--3、窗口a 设置隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
--4、窗口a、b 开启事务
start transaction;
--5、窗口b创建account表
-- 5.1使用数据库
use db01;
-- 5.2创建表
CREATE TABLE `account`(
id INT,
`name` VARCHAR(32),
money INT);
-- 6、窗口b插入数据(演示脏读)
insert into account values(100,'tom',1000);--脏读
--此时并没有提交事务,但是窗口a、b都可以查到这个表多了一个数据 在 select * from account;
select * from account;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 100 | tom | 1000 |
+------+------+-------+
--这个就是脏读
-- 7、窗口b修改、添加数据(演示不可重复读和幻读)
update account set money=800 where id=100;--不可重复读
insert into account values(200,'aom',1200);--幻读
--窗口a、b都提交事务
commit;
--一旦提交了事务,在窗口a看到了。这个就是不可重复读和幻读
select * from account;
mysql -uroot -p
3.2、读已提交(Read committed)
-- 1、窗口b开启事务
start transaction;
-- 2、窗口a修改隔离级别为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 3、窗口a开启事务
start transaction;
-- 4、窗口b添加数据
insert into account values(300,'mom',1000);
-- 5、查看数据
--在窗口b
select * from account;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 100 | tom | 800 |
| 200 | aom | 1200 |
| 300 | mom | 1000 |
+------+------+-------+
--在窗口a
select * from account;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 100 | tom | 800 |
| 200 | aom | 1200 |
+------+------+-------+
--窗口a的隔离级别为读已提交 没有显示id为300的数据 说明该隔离级别已经不会出现脏读现象了
-- 6、窗口b修改数据(演示不可重复读和幻读)
update account set money=1800 where id=200;--不可重复读
--窗口a查看
select * from account;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 100 | tom | 800 |
| 200 | aom | 1200 |
+------+------+-------+
-- 窗口b提交事务
commit;
--窗口a再次查看
select * from account;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 100 | tom | 800 |
| 200 | aom | 1800 |
| 300 | mom | 1000 |
+------+------+-------+
--说明在当前读已提交的隔离级别下 已经出现了不可重复读和幻读
3.3、可重复读(Repeatable read)
-- 1、登录a、窗口 进入数据库
mysql -uroot -p
use db01;
-- 2、a窗口设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL Repeatable read;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
-- 3、a、b窗口开启事务
start transaction;
-- 4、b窗插入数据
insert into account values('400','scott',8000);
-- 5、b窗更新数据
update account set money=2800 where id=300;
-- 6、a、b窗查询数据
select * from account;
--a
+------+------+-------+
| id | name | money |
+------+------+-------+
| 100 | tom | 800 |
| 200 | aom | 1800 |
| 300 | mom | 1000 |
+------+------+-------+
--b
+------+-------+-------+
| id | name | money |
+------+-------+-------+
| 100 | tom | 800 |
| 200 | aom | 1800 |
| 300 | mom | 2800 |
| 400 | scott | 8000 |
+------+-------+-------+
-- 7、b窗提交事务后再次查询a、b窗
commit;
select * from account;-- 查询数据未发生改变 即不可重复读、不可幻读
3.4、可串行化(Serializable)[演示重开客户端]
-- 1、重启客户端a 登录mysql
mysql -uroot -p;
-- 2、窗口a开启可串行化
SET SESSION TRANSACTION ISOLATION LEVEL Serializable;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
-- 3、窗口a、b开启事务
start transaction;
-- 4、窗口b插入数据
insert into account values('500','aatt',80000);
-- 5、窗口b更新数据
update account set money=6800 where id=300;
-- 6、重点! 此时a窗口去查询数据 会卡住
mysql> select * from account;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
-- 错误1205 (HY000):锁定等待超时超过;试着重新启动事务
-- 7、只有当b窗口提交事务的时候 a窗口才可以正常查询
commit;--b窗口
select * from account;--a窗口
+------+-------+-------+
| id | name | money |
+------+-------+-------+
| 100 | tom | 800 |
| 200 | aom | 1800 |
| 300 | mom | 6800 |
| 400 | scott | 8000 |
| 500 | aatt | 80000 |
+------+-------+-------+
5 rows in set (0.00 sec)
--
三、事务的四大特征
事务的 acid 特性
- 原子性(Atomicity): 事务是不可分割的最小操作单位,要么同时成功,要么同时失败
- 一致性(Consistency) :事务完成时,必须使所有的数据都保持一致状态
- 隔离性(Isolation) :多个事务之间,操作的可见性
- 持久性(Durability) :事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
总结
1、了解什么是MYSQL事务
事务用于保证书的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部成功,要么全部失败。
2、掌握事务的隔离级别
1、读未提交(Read uncommitted)
2、读已提交(Read committed)
3、可重复读(Repeatable read)
4、可串行化(Serializable)[演示重开客户端]
3、理解事务的四大特性
- 原子性(Atomicity): 事务是不可分割的最小操作单位,要么同时成功,要么同时失败
- 一致性(Consistency) :事务完成时,必须使所有的数据都保持一致状态
- 隔离性(Isolation) :多个事务之间,操作的可见性
- 持久性(Durability) :事务一旦提交或回滚,它对数据库中的数据的改变就是永久的