Java-数据库事务学习分享-数据库事务的实现原理(以mysql为例)

事务的目的

在对数据库经过一系列的并发读写操作后,保持数据的一致性。

事务的特性(ACID)

原子性(Atomicity):
一个事务中的所有操作,要么全部执行完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性(Consistency):
在事物开始之前和事物结束之后,数据库的完整性没有被破坏。

隔离性(Isolation):
并发多个事物时,各个事物不干涉其他事务的内部数据,处理的都是另一个事物处理之前或之后的数据。

持久性(Durability):
事务处理结束以后,对数据的修改就是永久的,即便系统故障也不会丢失。

满足ACID的事务才是真事务,否则是假的事务。

一、原子性的实现

undo log

undo log叫做回滚日志,用于记录数据被修改前的信息,在发生错误的时候根据这些信息对数据进行回滚。

undo log的生成

假设有两张表bank和finance,表中原始数据如图所示,当进行插入,删除以及更新操作时生成undo log
在这里插入图片描述
从上图可以了解到数据的变更产生了undo log:

  1. 产生了变更前数据(bank -> zhangsan,1000)的undo log
  2. 产生了变更前数据(finance -> zhangsan,0)的undo log

根据undo log进行回滚

当系统发生错误或者执行rollback操作时需要根据undo log进行回滚。
在这里插入图片描述
根据undo log生成回滚语句,比如:

  • 如果在回滚日志里有新增数据记录,则生成删除该条的语句
  • 如果在回滚日志里有删除数据记录,则生成新增该条的语句
  • 如果在回滚日志里有修改数据记录,则生成修改到原先数据的语句

补充

undo log主要分为两种,一种是insert undo log(插入undo log),只有在事务回滚时需要,所以在事务提交后可以立即丢弃;另一种是update undo log(修改/删除undo log),不仅在事务回滚时需要,在MVCC中也需要,所以在事务提交后、MVCC中不需要时由后台purge线程删除。

二、隔离性的实现

前提概念理解

1、读写锁
  • 共享锁,又称读锁。读锁状态下,其他读请求可以共享读锁,写请求阻塞,直到读锁释放(写请求阻塞时,读请求也会阻塞,避免读请求长期占用锁,造成写请求长期阻塞)。
  • 排它锁,又称写锁。写锁会排斥其他所有获取锁的请求,一直阻塞,直到写入完成释放锁。
    在这里插入图片描述
2、快照读和当前读
  • 当前读:
    它读取的数据库记录,都是当前最新的版本,会对当前读取的数据进行加锁,防止其他事务修改数据。是悲观锁的一种操作。
  • 快照读:
    快照读的实现是基于多版本并发控制,即MVCC,既然是多版本,那么快照读读到的数据不一定是当前最新的数据,有可能是之前历史版本的数据。
3、MVCC

MVCC(MultiVersion Concurrency Control)——多版本并发控制。
InnoDB的MVCC,是通过在每行记录的后面保存两个隐藏的列来实现的:

  • DB_TRX_ID:最近插入/修改这条记录的事务ID(事务ID唯一自增。删除当作是更新,采用逻辑删除)。
  • DB_ROLL_PTR:回滚指针,指向undo log。

Read View(读视图):
事务进行快照读操作的时候生成读视图(Read View),Read View的属性:

  • trx_ids: 创建此read view 时,当前系统活跃(未提交)事务版本号集合。
  • low_limit_id: 创建此read view 时,当前系统活跃事务最大版本号+1。
  • up_limit_id: 创建此read view 时,当前系统活跃事务最小版本号。
  • creator_trx_id: 创建此read view的事务版本号。

Read View可见性判断:

if(DB_TRX_ID < up_limit_id || DB_TRX_ID == creator_trx_id){
	// 显示
	// 1、比最小的版本号小,说明该数据在当前事务之前就已存在
	// 2、和creator_trx_id相等,说明是自己修改的
}else if(DB_TRX_ID  >= low_limit_id){
	// 不显示
	// 比最大的版本号大,说明该数据在当前事务之后才存在
}else if(trx_ids.contains(DB_TRX_ID )){
	// 不显示
	// 事务还未提交
}else{
	// 显示
	// 事务已提交
}

隔离级别

  1. read uncommited(读未提交)
  2. read commited(读已提交)
  3. repeatable read(可重复读)
  4. serializable(序列化)

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

1、read uncommited(读未提交)

在read uncommited隔离级别下,读不会加任何锁,写加排他锁,并到事务结束之后释放。所以在读的过程中其他事务修改数据的话,就会导致脏读。
优点:读写并行,并发处理性能高
缺点:造成脏读

2、read commited(读已提交)

InnoDB在read commited隔离级别下,写数据时使用排它锁,读数据时不加锁而是使用了MVCC机制(读写分离机制),解决了脏读的问题。
由于每次快照读都会新生成一个Read View,所以会出现可重复读的问题。

3、repeatable read(可重复读)

InnoDB在repeatable read隔离级别下,也是写数据时使用排它锁,读数据时不加锁而是使用了MVCC机制(读写分离机制)。但是每次快照读都是使用同一个Read View,所以解决了脏读、可重复读的问题。

4、serializable(序列化)

在serializable隔离级别下,读加读锁 (S锁),写加写锁 (X锁),事务串行化,并发性能最低。

三、持久性的实现

redo log

redo log叫做重做日志,用于记录数据被修改后的信息,是用来实现事务的持久性。

mysql的数据存储机制

mysql的表数据是存放在磁盘上的,因此想要存取的时候都要经历磁盘IO,磁盘IO是非常消耗性能的。
为了提升性能,InnoDB提供了缓冲池(Buffer Pool),Buffer Pool中包含了磁盘数据页的映射,可以当作缓存来使用:

  • 读数据:会首先从缓冲池中读取,如果缓冲池中没有,则从磁盘读取再放入缓冲池;
  • 写数据:会首先写入缓冲池,缓冲池中的数据会定期同步到磁盘中。

redo log的作用

缓冲池的措施虽然在性能方面带来了质的飞跃,但是也引入了新的问题,当mysql系统宕机、断电的时候可能会丢失数据。因为我们的数据已经提交了,但此时数据还在缓冲池里,还没来得及在磁盘持久化,所以需要一种机制存一下已提交的数据,为恢复数据使用。即redo log。
redo log 的产生:
在这里插入图片描述

四、一致性的实现

原子性、持久性、隔离性的实现也是实现一致性的一部分。在涉及并发的情况下,往往在性能和一致性之间做平衡,做一定的取舍,所以隔离性也是对一致性的一种破坏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值