事务

什么是事务


一个事务是一个完整的业务逻辑单元,不可再分
比如:银行账户转账,从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)


可以看到第一条插入,然后在去另一个窗口查询,查询就卡住了,
必须要等第一个事务执行完毕才能去执行下一个事务
事务_mysql_02