数据库相关

Explosion! / 2023-08-15 / 原文

事务:

概念

事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

MySQL默认就是自动事务管理(自动开启事务,自动提交事务),一条sql语句就是一个事务

事务执行的过程中,若无异常,则会commit完成curd,若出现异常,则会rollback回滚到事务回滚点

四大特性(ACID)

  • 原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency):事务前后数据的完整性必须保持一致。
  • 隔离性(Durability):事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
  • 持久性(Isolation):多个用户并发操作数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。即事务之间互不干扰。

事务隔离级别:

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

脏读:事务A读到了事务B还未commit的内容,事务B若rollback,则事务A读到了不存在的内容即为脏读。

不可重复读:事务A在两次读取同一数据的过程中,若该数据在此期间被事务B修改,则事务A读取的同一数据内容不同,则为不可重复读。

幻读:事务A在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行(如事务B在此期间插入了一条数据)。

个人理解:幻读与不可重复读的区别是,幻读注重范围查和并发增,不可重复读注重重复读和并发改

级别名字隔离级别脏读不可重复读幻读数据库默认隔离级别
1 读未提交 read uncommitted  
2 读已提交 read committed Oracle
3 可重复读 repeatable read MySQL
4 串行化 serializable
  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生(当前读)。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

需注意:

  • InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**
  • InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。

 

MVCC:

当前读和快照读:

MVCC属于快照读,即普通的select语句

当前读即每次读取最新数据,需要利用锁机制来实现,常见sql语句如下:

  • select lock in share mode(共享锁)
  • select for update(排他锁)
  • update(排他锁)
  • insert(排他锁)
  • delete(排他锁)

实现原理:

 MVCC 没有正式的标准,所以在不同的 数据库管理系统(DBMS) 中,MVCC 的实现方式可能是不同的。

mvcc的实现,基于undolog、版本链、readview。

在mysql存储的数据中,除了我们显式定义的字段,mysql会隐含的帮我们定义几个字段。

  • trx_id:事务id,每进行一次事务操作,就会自增1。

  • roll_pointer:回滚指针,用于找到上一个版本的数据,结合undolog进行回滚。

mvcc所提到的读是快照读,也就是普通的select语句。快照读在读写时不用加锁,不过可能会读到历史数据。

readview:

readview即快照,事务中每个select读前会生成一个readview。快照是用来获取当前事务能够读取版本的字段集,包含以下字段:

  • m_ids:当前活跃的事务id列表。活跃的事务就是指还没有commit的事务。

  • max_trx_id:下一个活跃事务将被分配的id值。例如m_ids中的事务id为(1,2,3),那么下一个应该分配的事务id就是4,max_trx_id就是4。

  • min_trx_id:活跃事务中的最小事务id,即min(m_ids)。
  • creator_trx_id:当前readview的事务id。

由上可知,若trx ∈ m_ids,则min_trx_id <= trx <= max_trx_id  

     若trx == creator_trx_id 或 trx < min_trx_id 或 trx!=m_ids,则trx版本可以被当前事务访问

MVCC对RC和RR事务隔离级别的实现:

RC:每个快照读都会生成并获取最新的readview,即选择可选取的最大的trx

RR:有在同一个事务的第一个快照读才会创建readview,之后的每次快照读都使用的同一个readview,所以每次的查询结果都是一样的。

需注意:

  • 仅作MVCC的快照读时不会产生幻读
  • innodb的RR级别,当前读时,通过使用where给定区间(select...from...where... for update)则不止会加行锁(record lock),还有间隙锁(gap),即next-key锁(行锁+gap锁),这种锁会解决绝大部分幻读问题。若仅仅只有select for update,则还是会产生幻读。

 

 

 

参考文章:https://blog.csdn.net/m0_49790240/article/details/123775697?spm=1001.2014.3001.5506

     https://blog.csdn.net/lans_g/article/details/124232192