一、事务的定义

​ 一组操作要么全部成功,要么全部失败,目的是为了保证数据的一致性

二、事务的ACID特性

  • 原子性(Atomicity):当前事务的操作要么同时成功,要么同时失败。原子性有undo log日志来保证
  • 一致性(Consistency):使用事务的最终目的,有业务代码正确逻辑保证
  • 隔离性(Isolation):在事务并发执行是,他们内部的操作不能互相干扰
  • 持久性(Durability):一旦提交了事务,他对数据库的改变应该是永久性的。持久性由redo log日志来保证

三、事务隔离性

​ InnoDB引擎中,定义了四种隔离级别供我们使用,级别越高事务隔离性越好,但性能就越低,二隔离性是由MYSQL的各种以及MVCC机制来实现的

  • read uncommitted(读未提交):脏读
  • read committed(读已提交):不可重复读
  • repeatable read(可重复读):脏写
  • serializable(串行)

image-20231124150540073.png

  1. read uncommitted

    读取所有未提交的数据,会出现脏读的情况

    set transaction isolation level read uncommitted;
    begin;
    -- balance = 100
    update acid set balance = balance + 500 where id = 1; 
    -- 暂时不执行commit
    commit;
    set transaction isolation level read uncommitted;
    begin;
    -- balance = 600
    select * from acid where id = 1;
    commit;
  2. read committed

    读取所有已提交的数据

    set transaction isolation level read committed;
    begin;
    -- balance = 100
    update acid set balance = balance + 500 where id = 1;
    -- balance = 600
    select * from  acid where id = 1;
    commit;
    
    
    -- 暂不执行下面的第二部分
    begin;
    update acid set balance = balance + 500 where id = 1;
    -- balance = 1100
    select * from  acid where id = 1;
    commit;
    set transaction isolation level read committed;
    begin;
    -- balance = 600
    select * from acid where id = 1;
    -- 执行第二部分 balance = 1100
    select * from acid where id = 1;
    -- 暂时不执行commit
    commit;
  3. repeatable read

    ​ 读取事务开始时的数据,会出现脏写的情况

    set transaction isolation level repeatable read;
    begin;
    -- balance = 100
    update acid set balance = balance + 500 where id = 1;
    -- balance = 600
    select * from  acid where id = 1;
    commit;
    
    
    -- 暂不执行下面的第二部分
    begin;
    update acid set balance = balance + 500 where id = 1;
    -- balance = 1100
    select * from  acid where id = 1;
    commit;
    set transaction isolation level repeatable read;
    begin;
    -- balance = 600
    select * from acid where id = 1;
    
    -- 执行第二部分balance = 600
    select * from acid where id = 1;
    -- 暂时不执行commit
    commit;
  4. serializable

    串行进行读写操作,读写不能一起执行,速度很慢

    set transaction isolation level serializable;
    begin;
    -- balance = 100
    update acid set balance = balance + 500 where id = 1; 
    -- 暂时不执行commit 后面的select不会执行
    commit;
    -- 等待事务提交才能开始执行
    set transaction isolation level repeatable read;
    -- balance = 600
    begin;
    select * from acid where id = 1;
    commit;

  • 读锁(共享锁、S锁):select ... lock in share mode;

    读锁是共享的,多个事务可以同时读取同一个资源,但不允许其他事务修改

  • 写锁(排它锁、X锁):select ... for update;

    写锁是排它的,会阻塞其他的写锁和读锁,update、delete、insert都会加写锁

MVCC机制

​ MVCC(Multi-Version Concurrency Control)多版本并发控制,就可以做到读写不阻塞,且避免了类似脏读这样的问题,主要通过undo日志链来实现

read committed(读已提交),语句级快照

repeatable read(可重复度),事务级快照

5b1c5ed946caadd028deca365fb591ee

解决脏写

1.添加VERSION字段
update acid set balance = 300 version = 2  where id = 1;
-- 未更新成功重复提交、乐观锁
update acid set balance = $(300) + 100 and version = 1 where version = 2 and id = 1
2.使用SQL语句直接对字段进行加减操作

在事务里update会自动加锁阻塞、不会出现脏写问题

update acid set balance = balance + 100 where id = 1;

事务优化

大事务

大事务问题

  • 并发情况下,数据库连接池容易被撑爆
  • 锁定太多数据,造成大量的阻塞和锁超时
  • 执行时间长,容易造成主从延迟
  • 回滚所需要的时间变长
  • undo log膨胀
  • 容易导致死锁

长事务优化

  • 将查询等数据操作放到事务外
  • 事务中避免远程调用,远程调用要设置超时时间,防止事务等待时间太久
  • 事务中避免一次性处理太多数据,可以拆分为多个事务分次处理
  • 更新等涉及加锁的操作尽可能放在事务靠后的位置
  • 能异步处理的尽量异步处理
  • 应用侧(业务代码)保证数据一致性,非事务执行

....

Last modification:May 28, 2024
如果觉得我的文章对你有用,请收藏本站