细品事务机制(一)

2021-12-07 17:26:56 浏览数 (1)

细品事务机制(一)

什么是事务?

事务在每个系统中都会涉及,它存在的意义就时符合预期的期望。且相互关联的数据之间不会产生矛盾,也就是数据状态的一致性。数据库的经典理论需要达成一致性这个目标,需要三方面共同来努力保证:

  • 原子性:在同一项业务处理过程中事务保证了各业务正在读,写的数据同时成功,要么同时被撤销。通俗就时 要么完全完成某一件事,要么就要回到这件事情最初状态,就算你做了一般你也要进行“回滚”到最初的状态
  • 隔离性:在不同的业务中,事务保证了各业务正在读,写的数据相互独立,不被彼此影响。也许你在此想到了mysql的事务隔离机制,那就对了
  • 持久性: 事务应当保证所有成功被提交的数据修改都能够正确地被持久化,不丢失数据。

上面事务有4个特性,也就是ACID,原子性,一致性,隔离性,持久化。那这四个特性之间又有什么关系呢? 那就是我们可以看到A,I,D是手段,而C是最终达到的目的。至于为什么要ACID也许是因为好记忆。

事务的类型

  1. 在看事务的分类之前,我们先看一下我们是如何从根本上区分事物的,那就是看他”单个服务,单个数据源“,”单个服务多个数据源“,“多个服务单个数据源”。“多个服务多个数据源”来进行区分。不同的物理架构其事务实现的复杂度和解决方案还是有很大的差别的。
本地事务
  1. 本地事务也可以翻译为局部事务。适用于单个单个服务单个数据源场景。
  2. 实现方式:
    • 基于底层数据源的支持事务的支持,在应用层的代码中只能对事务接口进行封装和调 ,比如我们使用的JDBC::roolback() 方法。
    • AREIS理论,areis理论实是现代数据库的基础理论(基于语义的恢复与隔离算法),现在大部分主流的关系型数据库都受到了该理论的影响。
实现原子性和持久性
  • 上文也描述了,原子性要么全部成功,要么全部失败,全部成功必须保证持久化到磁盘中这才算成功,也就是持久化了。
  • 达到持久化和保持一致性,有可能出现的问题,未提交事务,在写入后崩溃,比如说有三个变动点,其中两个变动点已经修改改完成,但是在第三个的时候直接系统崩溃了,一旦重启后,数据库必须得知在崩溃前发生过一次不完整的操作,,将已修改的数据恢复为原来的样子。
  • 还有一种情况是三个数据已经修改完成,但是还没有进行写入磁盘。这个时候程序崩溃,导致数据μ进行持久化。在服务被重启的时候可以得知之前有一批数据没又被持久化进而进行持久化。
  • 由于服务的崩溃是不可避免的,所以我们必须采用某种方式进行数据的恢复和持久化,所以这种的数据恢复的方式叫做:”崩溃恢复“(Failure Recovery, or Transaction Recovery)
  • 如何实现奔溃恢复,也就是在进行日志写入的时候我们将每一步的操作所需的全部信息持久化到物理的数据块中,即已顺序追加文件写入的形式先记录到磁盘中。只有日志记录全部安全落盘,数据库在日志中看到“commit Record”后,才会根据日志上的信息对真正的数据进行修改,修改完成后再在日志后追加 End record 表示事务已完成持久化。这种方式也就是“commit logging”。可以联想一下mysqL 中的实现方式,readveiw 和 redo log MVCC 这些。
实现隔离性
  • 隔离性是什么,也就是事务之间读写数据时互不干扰的,这也就我们常说的“并发”。如果没有并发那么整个事务都是穿行的,那么事务之间就时天然隔离的。也就是本就不不干扰的。 对于并发我们自然会想到通过 =加锁机制来达到事务的安全。
  • 现代数据库提供的几种锁,写锁,读锁,范围锁。
    • 写锁:(write-lock or X-lock,互斥锁):如果数据有加写锁,就只有持有写锁的事务才能对数据进行写入操作,数据加持写锁,其他事务不能进行不能写入数据,也不能施加读锁。
    • 读锁(read-lock or S-lock,共享锁):如果数据多个事务可以多同一个数据添加读锁,但是不能添加写锁,如果一条数据只被一个事物加了读锁,这个时候可以将读锁升级为写锁。进行写入数据。
    • 范围锁(range lock):对于某个范围直接加排他锁,在这个范围内不能被写入。这里需要进行区别一个“范围不能被写入”和“一批数据不能被写入” 即不能把范围锁理解为一组排他锁的集合,加了范围锁后,不仅能在此范围写入数据,也不能在此范围进行删除和新增数据。这就是一组排他所和范围锁的区别,也是一组排他锁做不到的。
  • 由于在进行加锁后最后实现的目标是达到某种程度的串行化,但是串行化会导致我们系统性能的降低。所以在性能和安全之间架构师们给了4种隔离级别,读提交,读未提交,可重复读,串行化。
  • 为了不同达到不同隔离级别的空置和实现,就出现了并发空置(MVCC)
  • 在这里我们得注意下隔离界别中可重复读,当我们在进行两次读取的时候,其中间进行插入多条数据,这个时候两次读取的结果就会出现不一样,因为两次读取过程中只是加了共享锁,而没有加范围锁,以至于他不能阻止另一个事务的写入。但是对于mysql innodb来说,在可重复读的隔离级别 下他是有一个叫做间隙锁的东西,他是可以阻止幻读的出现。
  • 在次我们细致的去看一下mysql不同隔离级别下对应都加什么锁。
    • 读未提交:只加写锁
    • 读已提交:加写锁,直接到事务结束,读锁在查询操作完成后立马释放
    • 可重复读:加读写锁和间隙锁
    • 串行化:
  • MVCC(多版本并发控制):无锁优化方案,读取优化策略,读取的时候不需要加锁,其基本思路是对数据库的任何修改都不会进行数据覆盖之前的数据,而是产生一个新版本和老版本共存的目的,已达到读取的时候完全不用加锁的目的;
  • MVCC的细致理解,可以理解为数据库中每一行记录都存在两个看不见的字段:CREAT_VERSION 和DELETE_VERSION 两个字段,每一个记录都有一个 事务ID,事务ID是一个全局严格递增的数值。记录规则:
    • 插入数据:CREAT_VERSION transctionId
    • 删除数据:和DELETE_VERSION transctionId
    • 更新数据: DELETE_VERSION transctionId, CREAT_VERSION transctionId。
  • 此时如果有另外的一个事务需要需要读取这些发生变化的数据,将根据隔离级别来决定应该读取哪些数据。
    • 隔离级别可重复读: 总是读取CREATE_VERSION 小于或者等于当前事务ID的记录,在这个前提下如果还有多个版本,则取最新事务(事务ID最大的)
    • 隔离界别是读已提交:总取最新的版本,即最近被提交的那个版本的数据记录。
    • 另外两个版本,没有用到MVCC 读未提交直接修改的是原始的数据,其他事物即可直接看到。而串行化已经是阻塞其他事务的写和读取了,更和MVCC没有关系了。

下篇预告

全局事务和分布式事务的理解和实现

0 人点赞