说一下数据库基础(五) 事务/隔离性

举个例子:

你存100万到银行,数据库的基本操作应该是

  • 数据库读取金额
  • 你的余额加上100万
  • 更新数据库数据

但是如果其他一步出现错误,可能就会导致100万存了但是余额又没有。

而数据库的事务就是解决这类问题,他保证所有操作是一起完成然后再给数据库提交事务,但其他一步出现问题,则会初始化到操作之前的状态。

事务的特性

Mysql的事务是通过mysql的引擎来实现的。常用引擎innoDB就是支持事务的。

事务四大特点

  • 原子性
  • 一致性
  • 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
  • 持久性

InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?

  • 原子性和持久性是通过 redo log (重做日志)来保证的;
  • 一致性是通过 undo log(回滚日志) 来保证的;
  • 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;

事务的隔离性

并发事务的问题

说隔离性我们就要先说并发事务。

MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。

在同时处理多个事务的时候,就可能出现脏读、不可重复读、幻读的问题

脏读

不可重复读

幻读

事务的隔离级别

  • 脏读:读到未事务提交的数据/读到过期的数据
  • 不可重复读:前后读的数据不一样。
  • 幻读:前后读的数据数量不一样。

三种并发问题的严重程度:

SQL 标准提出了四种隔离级别来规避这些现象,隔离级别约高,性能效率就越低,这四个隔离级别如下:

  • 读未提交(read uncommitted,指一个事务还没提交时,它做的变更就能被其他事务看到;
  • 读提交(read committed,指一个事务提交之后,它做的变更才能被其他事务看到;
  • 可重复读(repeatable read,指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别
  • 串行化(serializable;会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

按隔离水平高低排序如下:

针对不同事务隔离级别,会出现的问题。

所以,要解决脏读现象,就要升级到「读提交」以上的隔离级别;要解决不可重复读现象,就要升级到「可重复读」的隔离级别

不过,要解决幻读现象不建议将隔离级别升级到「串行化」,因为这样会导致数据库在并发事务时性能很差。InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它通过next-key lock 锁(行锁和间隙锁的组合)来锁住记录之间的“间隙”和记录本身,防止其他事务在这个记录之间插入新的记录,这样就避免了幻读现象。

四种隔离级别的实现*

  • 对于「读未提交」隔离级别的事务来说,因为可以读到未提交事务修改的数据,所以直接读取最新的数据就好了;
  • 对于「串行化」隔离级别的事务来说,通过加读写锁的方式来避免并行访问;
  • 对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同,大家可以把 Read View 理解成一个数据快照,就像相机拍照那样,定格某一时刻的风景。「读提交」隔离级别是在每个读取数据前都生成一个 Read View,而「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View

可重复读隔离级别是如何实现的?

启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View。

那 Read View 到底是个什么东西?

下图是Read View结构。

Read View 有四个重要的字段:

  • m_ids :指的是创建 Read View 时当前数据库中活跃且未提交的事务的事务 id 列表,注意是一个列表。
  • min_trx_id :指的是创建 Read View 时当前数据库中活跃且未提交的事务中最小事务的事务 id,也就是 m_ids 的最小值。
  • max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值
  • creator_trx_id :指的是创建该 Read View 的事务的事务 id

害剩下不会了,有空再补充。

正文完