事务
什么是事务
一个事务是一个完整的业务逻辑单元,不可再分
比如:银行账户转账,从A账户向B账户转账1000,需要执行两条update语句
update t_a set balance - 1000 where actno='act-001';
update t_a set balance + 1000 where actno='act-002';
以上两条DML语句必须同时成功,或者同时失败,不允许出现一条成功,一条失败。
和事务相关的语句只有:DML语句(insert delete update)
为什么
因为这三个语句都是和数据库表当中的 数据 相关的。
事务的存在是为了保证数据的完整性,安全性
事务的特性?
事务包括四大特性:ACID
A:原子性:事务是最小的工作单元,不可再分。
C:一致性:事务必须保证多条DML语句同时成功或者同时失败
I:隔离性:事务A与事务B之间具有隔离。
D:持久性:持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束
关于事务之间的隔离性
事务隔离性存在隔离级别,理论上隔离级别包括4个
- 第一级别:读未提交(read uncommitted)
- 对方事务还没有提交,我们的当前事务可以读取到对方未提交的数据。
- 读未提交存在脏读(Dirty Read)现象:表示读到了脏的数据
- 第二级别:读已提交(read committed)
- 对方事务提交之后的数据我方可以读取到。
- 读已提交存在的问题是:不可重复读
例子:现在A表有5条数据 ABCDE,我读取到5条, 此时对方已经提交了删除 A数据,我现在再去读就变成了 BCDE,如果此时对方又提交了删除B数据,我再去读取就变成了CDE
- 第三级别:可重复读(repeatable read)
- 这种隔离级别解决了:不可重复读问题
- 这种隔离级别存在的问题是:读取到的数据是幻想。
- 第四级别:序列化读/串行化读(serializable)
- 事务A与事务B不能并发,只能排队。这种隔离级别最高,效率最低,数据最安全 ,解决了所有问题
- 问题:效率低,需要事务排队
使用两个事务演示以上的隔离级别
第一演示readuncommitted
设置事务的隔离级别
set global transaction isolation level read uncommitted;
查看事务的全局隔离级别:
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
## 第二演示read committed读已提交
第一步
-- 设置事务的隔离级别
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
-- 查看事务的隔离级别
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-COMMITTED | -- 读已提交
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> exit -- 退出 重进
Bye
mysql> use rzk;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
-- 打开两个窗口进行操作
mysql> start transaction; -- 打开MySQL在第一个窗口执行
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction; -- 打开MySQL在第二个窗口执行
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | lisan |
| 4 | lin |
| 5 | linkimn |
| 6 | waghjn |
| 7 | uyjjn |
+----+----------+
7 rows in set (0.00 sec)
第二步在第二个窗口进行插入
mysql> insert into t_user(username) values("rzk");
Query OK, 1 row affected (0.00 sec)
第三步插入完成后在第一窗口查看有没有插入成功
mysql> select * from t_user; -- 可以看到未成功
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | lisan |
| 4 | lin |
| 5 | linkimn |
| 6 | waghjn |
| 7 | uyjjn |
+----+----------+
7 rows in set (0.00 sec)
第四步在插入的窗口里面进行commit提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
第五步 返回第一个窗口查询
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | lisan |
| 4 | lin |
| 5 | linkimn |
| 6 | waghjn |
| 7 | uyjjn |
| 10 | rzk | -- 此时就可以看到数据已经插入成功
+----+----------+
8 rows in set (0.00 sec)
第三演示 repeateble read 可重复读
设置事务隔离级别
mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
退出,重登
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
在窗口二删除数据,窗口一查看数据
窗口二 commit; 窗口一的数据还是不会变
可为什么读到数据了呢,其实读到的是备份数据,幻想,这种就是可重复读
第四演示serializable
第一步开启事务
mysql> set global transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| SERIALIZABLE |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> exit -- 重进
Bye
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
可以看到第一条插入,然后在去另一个窗口查询,查询就卡住了,
必须要等第一个事务执行完毕才能去执行下一个事务