mysql数据库事务的实现原理

1、为什么要知道数据库事务的实现原理

数据库事务在日常的作业中可或缺,我们了解事务只是停留在上课时候的事物的几种特性:原子性、隔离性、一致性、持久性,也知道数据库会有一个默认的隔离级别,那么为什么要有四种特性,以及默认的隔离级别的作用(可能会清楚是避免幻读等等),我相信有一部分同学是不清楚的,所以如果我们了解了这些特性背后的东西,那么日后对于数据库的应用会更加的得心应手,所以借此机会谈一谈我自己的理解,整理成文章,如有不严谨的地方,还望批评改正。

2、事务使用场景

数据库事务的场景有很多,使用事务一般出现在对于数据库的更新问题上,新增、删除、修改,一般都用在对于数据库包含多个更新操作的时候使用事务,以保证多个更新操作的能完成或者失败之后不会影响数据库原来的状态。当然这里的数据库不是局限的数据库,有可能是分库分表的多个物理库,还有可能是分布式的数据库,大家需要认真体会,结合实际情况。下面用具体的例子:

  • 转账场景最为常见,表现出来就是A账户数据库数据减少,B账户数据库数据增加,这两个更新操作要么完成、要么AB的账户保持原状,所以事务会用到这里

3、事务的特性

相信大家都知道事务的四种特性,在此列出事务的特性的解释,具体网上有很多的文章都有相关的说明。

  • 原子性:原子性是指单个事务本身涉及到的数据库操作,要么全部成功,要么全部失败,不存在完成事务中一部分操作的可能,以上文说的转账为例,就是要么操作全部成功,A的钱少了,B的钱多了;要么就是全部失败,AB保持和原来一直的数目;
  • 隔离性:隔离性侧重于多个事务之间的特性,也就是说多个事务之间是没有相互影响的,比如A给B转账;和B给C转账这两个事务是没有影响的(这里B给C转账如果和A给B转账的事务同时进行的时候,B的金额正确性问题保证就要看隔离级别了,后面会讲)
  • 一致性:一致性指事务执行前后的状态要一直,简单的例子还是转账,原来AB账户的钱加一起是100,相互转账完成时候彼此还是100(抛出别人给其转账的事务发生),所以一致性,理解起来就是事务执行前后的数据状态是稳定的,对于转账就是金额稳定不变,对于其他的事务操作就是事务执行完成之后,数据库的状态是正确的,没有脏数据。
  • 持久性:持久性是数据库一旦完成事务的提交之后,那么这个事务的状态就会持久在数据库中,而不用担心数据库 自身是否存在问题,只要事务完成提交,那么这个事务的操作都会影响数据库最终的状态。

4、事务实现原理

以 InnoDB引擎为例对事务的实现进行说明。我们知道事务的四个特点,那么这四个特点背后的意义是什么呢?首先我们得知道 实际运行事务的环境是什么样子的,很显然,当前的数据库支持的都是并发的事务,也就多个事务同时在操作数据库,因此就会出现多个事务 对数据库的同一内容进行操作,所以实现事务的的四个特性,也是基于事务的特点来设计的。下面基于事务的四个特性背后的原理以及机制进行说明:

4.1、原子性

原子性是事务的基本特性,保证了事务中的操作是不可拆分的整体,那么原子性是如何实现的呢?事务的原子性表现的两个方面:

  • 事务提交成功时,那么事务中的操作总会完成
    事务提交成功保证事务中的操作都会完成。1、是正确执行完事务,没有出现任何问题;2、是事务提交成功但是出异常,数据库恢复之后,提交完成的事务会保证数据库完成该事物的操作。对于第一种正常情况不予讨论,因为不存在 异常情况,那么第2种实际上是和上文说的持久性是相关联的,而这个是基于重做日志(redo log)来保证提交完成的事务在异常情况下保证数据操作能够进行:
  • 事务提交失败,那么事务中的操作都失败
    事务提交失败,那么事务中的操作都失败,这个是通过数据库的撤销操作日志来保证的,也称之为undo log,那么这两种日志如何保证事务的正确执行呢,后面将进行分析:

4.2、隔离性与一致性

隔离性与一致性是事务的特性,之所以要将其放在一起,是因为这两种特性相互制约,所以如何平衡他们之间的GAP需要我们自身的经验去判断。那为什么这么说呢,上文说过,对于单个事务来说甚至隔离性都不需要,但是对于多个事务并行执行的大场景来说,如果不用的事务对数据库同一数据进行读写,那么一致性就容易造成破坏,所以事务的隔离级别,意味着事务的并发处理是不一样,而不同的并发处理方式对于数据库的一致性是有影响的。那么就来说一说,不同的隔离级别是如何影响事务之间的并发程度的:

  • RAED UNCOMMITED:读未提交,任何操作都不加锁,所以能读到其他事务修改但未提交的数据行,也称之为脏读(Dirty Read);
  • READ COMMITED:读操作不加锁,写操作加锁。读被加锁的数据时,读事务每次都读undo log中的最近版本,因此可能对同一数据读到不同的版本(不可重复读),但能保证每次都读到最新的数据(事务提交之后的,不可重复读,两次读不一致),但是不会在记录之间加间隙锁,所以允许新的记录插入到被锁定记录的附近,所以再多次使用查询语句时,可能得到不同的结果(Non-Repeatable Read);
  • REPEATABLE READ:第一次读数据的时候就将数据加行锁(共享锁),使其他事务不能修改当前数据,即可实现可重复读。但是不能锁住insert进来的新的数据,当前事务读取或者修改的同时,另一个事务还是可以insert提交,造成幻读
    (注:mysql的可重复读的隔离级别解决了 “不可重复读” 和 “幻读” 2个问题,因为使用了间隙锁,下文讲解)
  • SERIALIZABLE:InnoDB 锁表,读锁和写锁阻塞,强制事务串行执行,解决了幻读的问题;
    可见不同的隔离级别,对于读写事务是否加锁进行了限定,而不同的锁,对于数据库的事务的并发性能的影响,是不一样的,这里对于性能、一致性进行了对比,由此看出他们之间的关系:
    性能:读未提交>读提交>可重复读>串行化
    一致性:串行化>可重复读>读提交>读未提交

4.3、事务的锁

通过上文,我们知道不同的隔离级别对于读写有不同的加锁方式,那么下面我们看看这些锁到底有什么作用,之间有什么不同,并结合上文的隔离级别,理解其对于一致性和性能如何造成影响?

  • 根据读写,分为共享锁S和排它锁X
    共享锁:即读加锁,不能写并且可并行读
    排它锁:写加锁,其他读写都阻塞

  • 根据加锁范围分为表锁、行锁、间隙锁
    表锁:锁 整个表,性能开销最大,其他的读写都要挂起
    行锁:锁整个行,以默认隔离级别为例:如果是读,那么会上共享锁,不允许写,如果是写,那么改行其他事务无论读写都得阻塞
    间隙锁:间隙锁分为两种,一种是不包含记录间隙锁(GAP),一种是包含记录间隙锁(Next-Key Lock: Gap Lock+Record Lock),比如对于默认隔离级别的innoDB下, 比如表A 上的id字段有索引, 并且id有 3,8,12,20这几个值,那么该索引可能被上的包含记录间隙锁区间为:(负无穷,3)、[3,8)、[8,12)、[12,20)、[20,正无穷)

    1、当事务T1锁定了 [8,12),[12,20)这2个区间时,当插入15时,上面的区间变成:
    [8,12)、[12,15)、[15,20).
    2、但查询索引含有唯一索引时,Next-Key Lock 降级为 Record Lock,仅锁住索引本身的数据行.
    3、好,现在表A的id值变成了: 3,8,12,15,20
    4、如果执行下列语句:
    select * from A where id>16 for update.
    InnoDB会对(16,正无穷) 加锁,
    5、但在 read committed的事务隔离级别下,因为采用Record Lock,只会锁定20这个值.
    6、如果在此时另外一个事务T2,插入了22这个值,此时, read committed 隔离级别下就会产生"幻读"的问题.
    7、但在InnoDB默认存储引擎下的Next-key Lock 模式下,22是插入是会被阻塞的,直到事务T1提交后,释放X锁,才能提交22这值.这样,InnoDB就这样解决了幻读的问题.

4.4、innoDB默认隔离级别下的mvcc机制

搞清楚了各个隔离级别 下的锁的使用情况,那么在默认的隔离级别下,innoDB如何实现事务的隔离性并保证其高效的并发性呢,那么就应该谈一谈MVCC多版本控制,基于此有必要了解一下多版本控制的概念。

  • MVCC:多版本并发控制(MVCC,Multiversion Currency Control)。一般情况下,事务性储存引擎不是只使用表锁,行加锁的处理数据,而是结合了MVCC机制,以处理更多的并发问题。Mvcc处理高并发能力最强,这种方式系统开销 比最大(较表锁、行级锁),这是最求高并发付出的代价。

innodb存储的最基本row中包含一些额外的存储信息 DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE BIT
在这里插入图片描述
6字节的DATA_TRX_ID 标记了最新更新这条行记录的transaction id,每处理一个事务,其值自动+1
7字节的DATA_ROLL_PTR 指向当前记录项的rollback segment的undo log记录,找之前版本的数据就是通过这个指针
6字节的DB_ROW_ID,当由innodb自动产生聚集索引时,聚集索引包括这个DB_ROW_ID的值,否则聚集索引中不包括这个值.,这个用于索引当中
DELETE BIT位用于标识该记录是否被删除,这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候

持续更新中,请关注

  • 10
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值