一、事务(transaction)

1、什么是事务?

一个事务是一个完整的业务逻辑单元
只有DML语句才支持事务(才和事务有关系,insert update delete)

2、事务的ACID特性:

  • 原子性:要么都发生,要么都不发生。
  • 一致性:从一个一致性状态,变换到另一个一致性状态。
  • 隔离性:各个事务不会干扰。
  • 持久性:事务一旦提交,永久性。

    3、提交 commit 回滚 rollback

  • Rollback:回滚到上一次的提交点

  • Commit:提交事务

    4、事务的并发问题

    发生条件:多个事务同时操作同一个数据库的相同数据时

  • 脏读

一个事务读取了其他事务还没有提交的数据,只是读到的是其他事务”插入“的数据

  • 不可重复读

一个事务多次读取,结果不一样

  • 幻读

读到的数据有可能是别的事务已经删除的数据
设置隔离级别来解决并发问题

5、隔离级别

理论上隔离级别包括四个(所有的都是二档起步 MySQL3档):

  1. 读未提交(read uncommitted)【对方事务没有提交的数据,我们能读到】
    • 脏读:读到了脏数据
  2. 读已提交(read committed)【对方事务提交后的数据我们能读到】
    • 不可重复读:一个事务多次读取,结果不一样 解决了脏读
  3. 可重复读(repeatable read)【MySQL默认】
    • 幻读:别的事务把数据删了,但是我这可以读到。【解决了不可重复读问题 ,两次读的都是一致的】
  4. 序列化读/串行化读(serializable)
    • 解决了所有问题,但是效率低,需要事务排队执行

1) 演示事务隔离性

:::info mysql的事务是自动提交的,(也就是说只要执行一条DML语句,则自动提交一次)
关闭事务自动提交:start transaction[标志着开启一次事务]
如果一次事务完成后,开启第二次事务需要再次写一遍 start transaction ::: **
演示开启事务,提交回滚:

  1. 首先先创建一张表
  2. Drop table if exists t_user;
  3. Create table t_user(
  4. Id int primary key auto_increment,
  5. Username varchar(255)
  6. );

**Start transaction**【开启事务标志** **没开启一次事务都要来一遍Start transaction】

  1. Insert into t_user(username) values('zhangsan');
  2. Select * from t_user;
  3. +----+----------+
  4. | Id | Username |
  5. +----+----------+
  6. | 1 | zhangsan |
  7. +----+----------+
  8. Rollback; #回滚
  9. Select * from t_user;
  10. Empty set (0.00 sec)
  11. Insert into t_user(username) values('lisi');
  12. Commit; # 提交

2) 演示读未提交(别人没提交的事务,我能读到)

  1. 首先在开启一个cmd窗口(使用同一个root账户即可)
  2. 设置全局事务隔离级别
  3. Set global transaction isolation level read uncommitted;
  4. 查看事务隔离级别
  5. select @@global.tx_isolation;
  6. 把两个窗口的事务隔离级别都设置完成后,exit退出一下,在重新登录
  7. 然后都同时开启事务 start transaction
  8. 窗口一执行 insert into t_user(username) values('zhangsan'); 【没有commit;】
  9. 【脏读】窗口二执行 select * from t_user; (能查到窗口一没有commit的数据‘zhangsan’)

3) 演示读已提交(别人提交的我能读到,导致了两次读的结果一样)

  1. 设置全局事务隔离级别
  2. Set global transaction isolation level read uncommitted;
  3. 退出 exit
  4. Use test
  5. 第一个cmd
  6. 第一次开启事务
  7. Start transaction;
  8. Insert into t_user(username) values('smith');
  9. Commit;
  10. 第二次开启事务
  11. Start transaction;
  12. Insert into t_user(username) values('------');
  13. Commit;
  14. 第二个cmd
  15. 开启事务
  16. Start transaction;
  17. Select * from t_user;查到了提交的SMITH
  18. 等第二个事务提交
  19. Select * from t_user; 查到了提交的'--------'
  20. 两次查询的结果不一样【不可重复度】

4) 演示可重复读

  1. 设置隔离级别
  2. Set global transaction isolation level repeatable read;
  3. exit退出
  4. Use test
  5. 第一个cmd
  6. Start transaction
  7. Truncate t_user; #把表里面的内容全删掉了,此时里面是空的
  8. 第二个cmd
  9. Start transaction; #和第一个cmd同时开启
  10. Select * from t_user; #读到了数据,并不是空的【幻读,读到的数据是虚幻数据】

5) 演示序列化读

  1. 设置隔离级别
  2. Set global transaction isolation level serializable;
  3. 退出 exit
  4. 登录 use test
  5. 第一个cmd
  6. Start transaction
  7. Insert into t_user values('SMITH'); # 此时没有commit 第二个窗口的查询不会执行,等到commit之后才会执行
  8. Commit #此时 第二个cmd才会执行select语句
  9. 第二个cmd
  10. Start transaction
  11. Select * from t_user; 【排队依次等待执行】