07、事务(TCL)
1、什么是事务(Transaction)
一个事务是一个完整的业务逻辑单元,不可拆分。
比如:银行转账,A账户给B账户转账10000,需要执行2条update的语句。
A余额减少10000
B余额增加10000
以上两条数据,要么同时成功,要么同时失败。
要想保证以上两条数据,要么同时成功,要么同时失败,就需要使用数据库的事务机制。
2、事务什么时候使用
和事务相关的语句只有DML语句。(insert,delete,update)
因为DML语句都是和数据库表当中的数据有关的,事务的存在就是为了保证数据的完整性、安全性。
如果所有的业务都可以一条DML语搞定,就不需要事务了。
3、事务的四大特性:ACID
1 、原子性
事务是数据库的逻辑工作单位, 不可再分。
2 、一致性
事务必须保证多条DML语句要么同时成功,要么同时失败。
3 、隔离性
一个事务的执行不能其它事务干扰。
4 、持续性
持久性就是最终数据必须持久化到硬盘文件中,事务才算完成。
4、事务之间的隔离性
隔离级别包括4个:
- 第一级别:读未提交(Read Uncommitted)
- 可以读取对方未提交的数据
- 存在脏读(Dirty Read)现象,读到了脏数据,读到的数据可能已经改变了。
- 第二级别:读已提交(Read Committed)
- 大多数数据库系统默认的初始状态,Oracle数据库默认级别,mysql是第三级别初始。
- 可以读取对方提交后的数据
- 解决了脏读现象
- 存在不可重复读,不可重读读意味着我们同一事务执行完全相同的select语句时可能看到不一样的结果。
- 第三级别:可重复读(Repeatable Read)
- 这是MySQL的默认事务隔离级别
- 它确保同一事务的多个实例在并发读取数据时,看到同样的数据行
- 解决了不可重复读
- 存在幻读现象(Phantom Read),当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
- 第四级别:可串行化/序列化(serializable)
- 最高的隔离级别
- 它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它在每个读的数据行上加上共享锁。
- 缺点:
- 效率低
- 可能导致大量的超时现象和锁竞争
5、演示事务
mysql默认情况是自动提交的。
关闭自动提交:
start transaction;
准备表:
drop table if exists t_user;
create table t_user(
id int primary key auto_increment,
username varchar(255)
);
演示:
(1)mysql中事务是自动提交的:
insert t_user (username) values('张三');
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
+----+----------+
rollback;
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
+----+----------+
(2)关闭自动提交事务:start transaction;
rollback: 回滚到开始事务的地方
start transaction;
insert t_user (username) values('李四');
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
+----+----------+
rollback;
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
+----+----------+
(3)提交 commit,把这次事务提交。
rollback 只能回滚到这次事务的开始位置,如果已经提交了,就不能在回滚了。
start transaction;
insert t_user (username) values('李四');
insert t_user (username) values('王五');
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+----+----------+
commit;
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+----+----------+
rollback;
select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+----+----------+
(4)演示隔离级别
设置事务的全局隔离级别:
set global transaction isolation level read uncommitted;
查看事务的全局隔离界别:
SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-UNCOMMITTED |
+--------------------------------+
1)演示 读未提交(Read Uncommitted)
先设置隔离界别:设置完成之后需要关闭
mysql -uroot -p123456
use bookshop
set global transaction isolation level read uncommitted;
SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-UNCOMMITTED |
+--------------------------------+
需要开启两个窗口演示
窗口1:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user;
insert t_user(username) values ('麻六');
窗口2:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user; //起始的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
+----+----------+
select * from t_user; //未提交之前的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
| 5 | 麻六 |
+----+----------+
2)演示读已提交(Read Committed)
先设置隔离界别:设置完成之后需要关闭
mysql -uroot -p123456
use bookshop
set global transaction isolation level read committed;
SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-COMMITTED |
+--------------------------------+
需要开启两个窗口演示
窗口1:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user;
insert t_user(username) values ('唐三');
commit;
窗口2:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user; //起始的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
+----+----------+
select * from t_user; //未提交之前的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
+----+----------+
select * from t_user; //提交之后的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
| 6 | 唐三 |
+----+----------+
3)演示可重复读(Repeatable Read)
先设置隔离界别:设置完成之后需要关闭
mysql -uroot -p123456
use bookshop
set global transaction isolation level Repeatable Read;
SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
需要开启两个窗口演示
窗口1:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user;
delete from t_user;
commit;
窗口2:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user; //起始的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
| 6 | 唐三 |
+----+----------+
select * from t_user; //提交之后的查询
+----+----------+
| id | username |
+----+----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 王五 |
| 6 | 唐三 |
+----+----------+
4)演示可串行化/序列化(serializable)
先设置隔离界别:设置完成之后需要关闭
mysql -uroot -p123456
use bookshop
set global transaction isolation level serializable;
SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| SERIALIZABLE |
+--------------------------------+
需要开启两个窗口演示
窗口1:
mysql -uroot -p123456
use bookshop
start transaction;
insert t_user(username) values ('唐三');
commit;
窗口2:
mysql -uroot -p123456
use bookshop
start transaction;
select * from t_user; //起始的查询
+----+----------+
| id | username |
+----+----------+
| 7 | 张三 |
+----+----------+
select * from t_user; //提交之前的查询
//会一直卡在这块,当窗口一提交的时候,才能接着运行
//提交之后
+----+----------+
| id | username |
+----+----------+
| 7 | 张三 |
+----+----------+