ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

数据库-事务及相关技术

2021-05-14 09:29:16  阅读:145  来源: 互联网

标签:事务 加锁 快照 隔离 版本号 数据库 相关 数据


事务

事务的提出主要是为了解决并发情况下保持数据一致性的问题

用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位

ACID

原子性、一致性、隔离性、永久性

原子性和隔离性都会影响一致性(原子性相当于是看事务自己是否正确做完或都不做,隔离性主要是并发的时候)

永久性是为了应对系统崩溃的情况,一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。


并发一致性问题

丢失修改、读脏数据(原先的rollback了,即读到另一个未提交事务的数据)、不可重复读、幻读

不可重复读和幻读的区别:

  • 总的结果看都表现为两次读取结果不一致

  • 从控制的角度看,不可重复读只要锁住满足条件的记录幻读需要锁住满足条件及相近的记录(理解成前者锁行,后者锁表)

  • 不可重复读的重点在于update和delete,幻读的重点在于insert

产生并发一致性问题的主要原因是破坏了事务的隔离性(不同事务互相影响了)

解决方法是并发控制,可以通过封锁来实现,但是封锁需要用户自己控制。

DBMS提供事务的隔离级别,以更轻松的方式处理并发一致性问题


事务隔离级别

作用是让事务之间互相隔离,互不影响,这样可以保证事务的一致性。

下面这四个隔离级别是越来越高的,对性能的影响也是越来越大的

MySQL默认的隔离级别是可重复读(为了平衡)

1.未提交读

一个事务可以读到另一个事务已经修改了但是还没提交的数据。(会导致脏读)

2.提交读

一个事务只能读取已提交的事务所做的修改。相当于只有commit了之后,才能被其他事务读到,不然其他事务读到的还是之前的。(所以就可以解决脏读)

3.可重复读(RR)

保证在同一个事务中多次读取同一数据的结果是一样的。

只能读在该事务之前提交的修改。(MVCC里根据版本号来判断)

不过还是可能出现幻读的问题(因为修改的数据是本身有的,新插入的话还是会被读出来

为什么上了写锁,别的事务还可以读?(没有提交的话别的事务读到的是原始的数据)

因为InnoDB有MVCC机制(多版本并发控制),可以使用快照读,不会被阻塞

4.可串行化

强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。

该隔离级别需要加锁实现,因为要使用加锁机制保证同一时间只有一个事务执行,也就是保证事务串行执行

读读操作不会被阻塞,读写、写读、写写都会被阻塞

脏读不可重复读幻读
未提交读×××
提交读××
可重复读×
可串行化

隔离级别实现原理

MVCC(多版本并发控制)

是MySQL的InnoDB存储引擎实现隔离级别的一种具体的方式,用于实现读已提交可重复读这两种隔离级别。读未提交总是读取最新的数据行,无需使用MVCC可串行化隔离级别需要对所有的行都加锁,仅仅依赖MVCC是无法实现的。

MVCC可以认为是行级锁的一个变种,但是他在很多情况下都避免了加锁操作,因此开销更低。与基于锁的并发控制相比,多版本并发控制好处是:读不加锁,读写不冲突。可以避免不必要的加锁操作。

利用多版本的思想,写操作更新最新的版本快照,而读操作去读旧版本快照,没有互斥关系

MVCC规定只能读取已经提交的快照,就可以避免脏读和不可重复读问题

系统版本号:每开始一个新的事务,系统版本号就会自动递增

事务版本号 TRX_ID:事务开始时的系统版本号

在MVCC中事务的修改操作(DELETE、INSERT、UPDATE)都会为数据行新增一个版本快照多版本指的就是多个版本的快照。快照存储在Undo日志中,通过回滚指针把一个数据行的所有快照连接起来。Undo日志里相当于存着多个快照版本。
在这里插入图片描述

在这里插入图片描述

MVCC维护了一个 ReadView 结构,主要包含当前系统未提交的事务列表TRX_IDs,还有该列表的最小值和最大值。

在这里插入图片描述


在进行SELECT操作的时候,根据数据行快照的TRX_ID和最小值最大值之间的关系,从而判断该数据行快照是否可以使用:小于最小值,说明该数据行快照在当前所有未提交事务之前更改的,可以使用,相当于就是可以读已提交的;大于最大值,说明该数据行快照是在事务启动之后被更改的,不能使用;在最小值和最大值之间,则需要根据隔离级别再进行判断。

提交读:如果该快照在列表中,说明该快照对应的事务还未提交,则该快照不可使用。若不在列表中,说明已经提交了,就可以使用。

可重复读都不可以使用

在当前数据行快照不可使用的情况下,需要沿着 Undo log的回滚指针找到下一个快照,再进行上面的判断。


简单来说,就是查询时,

  • 如果该行数据没有被加行锁中的X锁(也就是没有其他事务对这行数据进行修改),那么直接读取数据(前提是数据的版本号<=当前事务版本号的数据,不然不会放到查询结果集里面)。
  • 该行数据被加了行锁X锁(也就是现在有其他事务对这行数据进行修改),那么读数据的事务不会进行等待,而是回去undo log端里面读之前版本的数据(这里存储的数据本身是用于回滚的)。
  • 提交读的隔离级别下,从undo log中读取的总是最新的快照数据(有提交的就可以读出来了);在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务ID的数据)

在多版本并发控制中,读操作分成两类:

  1. 快照读(snapshot read):快照读,读取的是记录的可见版本,可能是历史版本,不用加锁
  2. **当前读(current read):**读取读,读取的是最新版本,当前读返回的记录,都会加锁,保证其他事务不会并发地修改这条记录

简单的select操作属于快照读,不用加锁;会对数据库进行修改的操作:插入、更新、删除操作,属于当前读,需要加锁(需要读最新版本的需要加锁)

在一个事务内,多次执行SELECT语句,查询到的数据都是事务开始时那个状态的数据(这样就不会受其他事务修改数据的影响)

  • 数据行要被查询出来必须满足两个条件,
    • 数据行删除版本号为空或者 > 当前事务版本号的数据(否则数据已经被标记删除了)
    • 创建版本号 <= 当前事务版本号的数据(否则数据是后面的事务创建出来的)

简单来说,就是查询时,

  • 如果该行数据没有被加行锁中的X锁(也就是没有其他事务对这行数据进行修改),那么直接读取数据前提是数据的版本号 <= 当前事务版本号的数据,不然不会放到查询结果集里面)。
  • 该行数据被加了行锁X锁(也就是现在有其他事务对这行数据进行修改),那么读数据的事务不会进行等待,而是回去undo log端里面读之前版本的数据(这里存储的数据本身是用于回滚的)。在提交读的隔离级别下,从undo log中读取的总是最新的快照数据。在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务ID的数据)。

事务在对数据修改后,进行保存时,如果数据行的当前版本号与事务开始取得数据的版本号一致就保存成功,否则保存失败。

delete操作实际上不会直接删除,而是将delete对象打上delete flag,标记为删除,最终的删除操作是purge线程完成的。但是会将数据行的删除版本号设置为当前的事务的ID,这样后面的事务B即便查到这行数据由于事务B的ID>删除版本号,也会忽略这条数据

更新时可以简单的认为是先将旧数据删除,然后插入一条新数据。执行更新操作时,其实是会将原数据行的删除版本号设置为当前事务的ID,生成一条INSERT语句,写入undo log,用于事务执行失败时回滚。插入一条新的数据,将事务的ID作为数据行的的创建版本号。


MVCC不能解决幻读问题,在可重复读的隔离级别下,使用MVCC + Next-Key Locks可以解决幻读的问题。

Record Locks 记录锁是锁定一个记录上的索引,而不是记录本身。如果表没有设置索引,InnoDB就会自动在逐渐上创建隐藏的聚簇索引。(记录锁是行锁

Gap Locks 间隙锁锁住的是索引区间(开区间)。在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。比如在 1、2、3中,间隙锁的可能值有 (∞, 1),(1, 2),(2, ∞),间隙锁可用于防止幻读,保证索引间不会被插入数据

Next-Key Locks临键锁 = record lock + gap lock, 左开右闭区间。但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。Next-KeyLocks当对数据进行条件范围检索的时候,对其范围内存在的值进行加锁防止其他事务的插入操作来达到防止幻影读的目的。

(InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁)


常见问题

说一下数据库事务的四大属性?知道事务有什么应用场景

数据库的ACID分别是什么?

隔离级别有哪些?

不同的隔离级别分别解决了哪些并发问题?

幻读和不可重复读有什么区别,为什么会有这种区别?

如何在可重复读级别解决幻读?

mvcc是怎么解决幻读问题?

标签:事务,加锁,快照,隔离,版本号,数据库,相关,数据
来源: https://blog.csdn.net/D23333A/article/details/116642068

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有