MySQL事务

引言: 更新:将本篇改为纯介绍事务的一篇blog,将其他内容移出

Mysql事务

  1. 概念:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败

  2. 操作:

    1. 开启事务: start transaction;
    2. 回滚:ROLLBACK
    3. 提交:COMMIT

例如:王给张转500元钱

  1. 判断王有没有500
  2. 王-500
  3. 张+500
  4. 转账完成,中间出现差错会从头回滚
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    START TRANSACTION;# 开启事务
    # 王给张转500
    # 判断是否大于五百元
    # 王账户减去五百
    UPDATE deliver SET bank = bank -500 WHERE `NAME` = '王' && bank>500;
    # 张账户加五百
    #这里出错
    UPDATE deliver SET bank = bank +500 WHERE `NAME` = '张';
    #这里出错 ROLLBACK; 出错就添加ROllBACK 回到开启事务的地方
    COMMIT;# 成功就会完成这项事务
  5. MySQL数据库中事务默认自动提交(Oracle默认是手动提交的)
  6. 手动打开事务才是自动提交
  7. 修改事务的默认提交方式
    1
    2
    3
    4
    5
    # 查看默认提交方式
    SELECT @@autocommit
    # 1代表自动提交,0代表手动提交
    SET @@autocommit = 0;
    # 更改为手动提交,只有COMMIT才会

事务ACID原则

  1. 原子性A:同时成功,要么同时失败
  2. 一致性C:在事务开始之前和事务结束以后,数据库的完整性没有被破坏
  3. 隔离性I:多个事务之间,相互独立
  4. 持久性D:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失

隐式提交

mysql中事务提交后,才会改变数据的值,但是如果在事务执行时执行了如下的操作,mysql就会隐式的替我们提交:

  • 执行了DDL语句(create、drop、alter)
  • 开启了一个新的事务
  • 使用了锁
  • 使用了加载语句操作

事务引发的问题

多个事务操作同一批数据,存在问题:

脏读:一个事务读取到另一个事务中没有提交的数据

比如A、B、C三个人,A有500元,B有100、C有1000

1
2
3
4
5
6
7
8
9
A向B转100元——正常流程:
A = 500
B = 100
A = A - 100 = 400
B = B + 100 = 200
但C同时也向B转100元,导致这种情况发生
C = 1000
B = 100 (B现在已经是200元,但是事务还未提交,所以读到了100)
B = 100 + 100 = 200 (此时Commit后,B仍是200,少了100元)

不可重复读:在同一个事务中两次读取到的数据不一样(不一定是错误,但是某些场合不对

比如总支出计算,第一次读出开支100元,准备生成报表,结果读出为200元(在那一瞬间别的事务又花了100)

幻读:一个事务(同一个read view)在前后两次查询同一范围的时候,后一次查询看到了前一次查询没有看到的行(一般指数据行记录变多了或者少了

比如给员工都涨薪,update set salary= salary + 100,但是此时又来一个新人,最后发现,命名给所有人涨薪100的操作,缺疏忽了一个人

事务的隔离级别

多个事务之间是隔离的,相互独立的。

但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题

隔离级别:

  1. read uncommited读未提交:所有事务都可以看到没有提交事务的数据

    产生的问题:脏读、不可重复度、幻读

  2. read commited读已提交(Oracle默认):事务成功提交后才可以被查询到

    产生的问题:不可重复度、幻读

  3. repeatable read可重复读(MySQL默认):同一个事务内多次查询却返回了不同的数据值

    产生的问题:幻读

  4. serializable串行化:写加写锁,读加读锁
    可以解决所有的问题

注意:隔离级别从小到大安全性越来越大,但是效率越来越低

查询隔离级别

1
2
select @@tx_isolation # 老版MySQL
SELECT @@transaction_isolation # 新版MySQL

设置隔离级别

1
2
set global transaction isolation level 级别字符串;# 老版
set session transaction isolation level 级别字符串;

区别

脏读与不可重复读的区别:

  • 脏读:读了另一事务未提交的数据
  • 不可重复读:读了另一事务提交的数据