事务简介
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或或撤销操作请求,即这些操作要么同时成功,要么同时失败。
典型事例:银行转账操作
假设张三向李四进行转账操作首先第一步我们应该查询张三用户的余额,如果余额充足,则张三用户余额减少,最后李四用户余额增加
事务的步骤分为:1、开启事务2、回滚事务(把临时修改的数据恢复回去,能够保证如果出现异常,全部操作都会回滚,保证数据的完整性和一致性)3、提交事务
默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务。
事务操作
· 查看/设置事务提交方式
SELECT @@autocommit;
SET @@autocommit=0;
· 提交事务
COMMIT;
· 回滚事务
ROLLBACK;
#事务操作
#数据准备
create table account(
id int primary key auto_increment comment '主键ID',
name varchar(10) comment '姓名',
money int comment '余额'
)comment '账户表';
insert into account values(null,'张三',2000),
(null,'李四',2000);
#转账操作
select @@autocommit;
set @@autocommit=0;
select * from account where name='张三';
#2、将张三账户余额减1000
update account set money =money-1000 where name='张三';
程序执行错误
#3、将李四账户余额增加1000
update account set money =money+1000 where name='李四';
#提交事务
commit;
#回滚事务
rollback;
#恢复数据
update account set money=2000 where name='张三'||name='李四';
方式二:不设置事务的提交方式
· 开启事务
START TRANSACTION 或 BEGIN;
· 提交事务
COMMIT;
· 回滚事务
rollback;
#方式二:
start transaction ;
set @@autocommit=1;
select * from account where name='张三';
#2、将张三账户余额减1000
update account set money =money-1000 where name='张三';
程序执行错误...
#3、将李四账户余额增加1000
update account set money =money+1000 where name='李四';
#提交事务
commit;
#回滚事务
rollback;
事务四大特性:ACID
· 原子性(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}
#查看事物的隔离级别
select @@transaction_isolation;
#设置事务隔离级别
set session transaction isolation level read uncommitted;
#改回默认值
set session transaction isolation level repeatable read ;
脏读演示:(两个客户端演示)
#第一个客户端
mysql> use test
Database changed
mysql> set session transaction isolation level read uncommitted
-> ;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 2000 |
| 2 | 李四 | 2000 |
+----+------+-------+
2 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 2000 |
| 2 | 李四 | 2000 |
+----+------+-------+
2 rows in set (0.00 sec)
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 1000 |
| 2 | 李四 | 2000 |
+----+------+-------+
#第二个客户端
mysql> use test
Database changed
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update account set money=money-1000 where name='张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
不可重复读:(此时隔离级别为Read committed)
#第一个客户端
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 1000 |
| 2 | 李四 | 2000 |
+----+------+-------+
2 rows in set (0.00 sec)
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 2000 |
| 2 | 李四 | 2000 |
+----+------+-------+
2 rows in set (0.00 sec)
#第二个客户端
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update account set money=money+1000 where name='张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
幻读:(此时隔离级别为Repeatable read)
#第一个客户端
start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 2000 |
| 2 | 李四 | 2000 |
+----+------+-------+
2 rows in set (0.00 sec)
mysql> select * from account where id=3;
Empty set (0.00 sec)
insert into account(id,name,money) values(3,'王小五',2000);
ERROR 1062 (23000): Duplicate entry '3' for key 'account.PRIMARY'
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | 张三 | 2000 |
| 2 | 李四 | 2000 |
| 3 | 王五 | 2000 |
+----+------+-------+
3 rows in set (0.00 sec)
#第二个客户端
start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account(id,name,money) values(3,'王五',2000);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)