基础
事务
- 原子性(Atomicity):不可分割,要嘛全部成功,要么全部失败,通过该思想实现了
事务回滚机制
; - 一致性(Consistency):说白了就是Mysql指定的一些规则与约束,保持一致性有如下几种机制:
- 约束(Constraints):如主键约束、外键约束、唯一约束等
- 事务隔离级别:MySQL提供不同的事务隔离级别,包括读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。通过合理选择事务隔离级别,可以在并发环境下有效地维护数据的一致性
- 锁定机制(Locking Mechanisms):MySQL使用锁定机制来控制对数据的并发访问,包括行级锁(Row-Level Locks)、表级锁(Table Locks)等,以确保在并发事务中数据的一致性和完整性。
- 原子性操作(Atomic Operations):MySQL通过原子性操作确保事务中的所有操作要么全部成功,要么全部失败,从而保持数据的一致性。
- 持久性(Durability):持久性(Durability)指的是一旦事务被提交,它对数据库的修改应该是永久性的,即使发生系统崩溃或其他故障。
- 隔离性(Isolation):多个用户并发(同时)访问数据库时, 数据库为每一个用户都开启事物, 为了防止多个事物的操作数据相互干扰, 多个并发事物之间要相互隔离.隔离性通过事务的隔离级别来定义, 并用锁机制来保证写操作的隔离性, MVCC来保证读操作的隔离性.
- 读未提交(Read Uncommitted):最低的隔离级别,允许事务读取未被其他事务提交的更改。这可能导致
“脏读”
,即读取到其他事务未提交的数据。 - 读提交(Read Committed):允许事务读取并仅读取已经被其他事务提交的更改。这个级别可以避免脏读,但仍然可能遇到
“不可重复读”
的问题,即在同一事务中,多次读取同一数据集合可能会得到不同的结果。 - 可重复读(Repeatable Read):确保在同一事务中多次读取同一数据集合时,结果是一致的,避免了不可重复读的问题。但是,它可能遇到
“幻读”
,即当其他事务插入或删除行时,当前事务的后续查询会“看到”新的行。(幻读:老的数据修改不可变,但是新数据造成幻读)(结合MVCC可避免幻读)(Mysql默认隔离级别) - 串行化(Serializable):最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读的问题。这提供了最强的一致性保证,但性能开销也最大。
- 读未提交(Read Uncommitted):最低的隔离级别,允许事务读取未被其他事务提交的更改。这可能导致
进阶
1、原子性和一致性区别?
原子性: 讲的是操作不可分,把事务看为一个原子,所有的步骤是一个整体,通过该概论,实现了事务回滚机制
。
一致性: 事务有很多个步骤,如果其中某一步操作违法了规则和约定(比如说唯一性),因为原子性的事务回滚机制
存在,N个步骤是一个整体,所以N个步骤的操作都需要回滚。所以一致性更像是出发原子性的一种开关。
总结: 原子性是整体的一种不可分割的概念,通过该概论实现了事务回滚机制
;一致性是事务内部各种规则约定的判断,一致性发现错误之后会通知原子性来实现事务回滚。
2、隔离性是怎么实现的?
回答这个问题之前,我们需要知道一下知识点:锁机制、MVCC、间隙锁\快照读与当前读
2.1 隔离性是怎么实现的——锁机制
加锁以防止其他事务同时对该数据对象进行冲突操作。
- 行级锁、表级锁
- 行级锁:细粒度锁,允许更高的并发性,不同的事务可以同时操作不同的行。开销大。当并发事务操作的数据分布在不同的行时,行级锁可以显著提升性能。
- 表级锁:锁定整个数据表,开销小,并发性差,一个事务锁定整个表后,其他事务必须等待该事务完成才能继续操作。
- 共享锁(S锁)和排他锁(X锁)
- 共享锁 S:读共享:SELECT查询操作通常使用共享锁
- 排他锁 X:独占资源::INSERT、UPDATE、DELETE操作通常使用排他锁
- 意向锁(Intention Locks)
- 意向锁:是管理行级锁和表级锁之间关系的一种机制!意向锁并不会加锁,而是给表打上一个标记,所以他算“表锁”,意图给表中某些行进行加锁 (意向锁就意味着对同表不同行加锁)。
- 意向共享锁:表示事务有意在表的某些行上加共享锁(S锁)。
- 意向排他锁:表示事务有意在表的某些行上加排他锁(X锁)。
- 其实就是对表的不同部位加锁,有点像
ConcurrentHashMap分段加锁
- 意向锁:是管理行级锁和表级锁之间关系的一种机制!意向锁并不会加锁,而是给表打上一个标记,所以他算“表锁”,意图给表中某些行进行加锁 (意向锁就意味着对同表不同行加锁)。
对访问统一资源是否兼容?
S锁 | IS锁 | IX锁 | |
---|---|---|---|
S锁 | 兼容 | 兼容 | × |
IS锁 | 兼容 | 兼容 | 兼容 |
IX锁 | × | 兼容 | 兼容 |
为什么IX锁和IX锁兼容?
因为意向锁只是给表打上标记,说明表内某些行加了X锁,如果IX锁是针对同一行,那就会出发X锁判断机制。
为什么需要意向锁?
因为意向锁代表了表中某行加锁了,这样就可以避免表锁冲突!
2.2 隔离性是怎么实现的——MVCC
-
数据的版本控制
- 创建版本号:生成该版本的事务ID
- 删除版本号:删除该版本的事务ID
-
读取操作(快照读)
- A事务开始时,Mysql会为这个事务分配一个ID号,即事务ID(TXID)
- A事务开始时,事务会获取一个快照,存储的是开始时间点前所有
已提交的数据版本
(就是所有已经提交的事务ID号) - A事务有了自己的ID,A事务又有了快照,就可以通过比对ID号来判断那个版本对自己可以见
-
写入操作(当前读)
- 当事务需要更新某一行数据时,它会创建一个新的版本,并更新创建版本号字段为自己的TXID。同时,将旧版本的删除版本号字段设为当前事务的TXID,表示旧版本对新事务不可见。
-
提交和回滚
- 事务提交后,它生成的新版本会对其他事务可见。
- 如果事务回滚,则丢弃它生成的新版本,数据的版本链条保持不变。
-
版本链
在MVCC机制下,每次对数据库中的一行数据进行更新操作时,数据库并不会立即覆盖掉这行数据的旧版本,而是创建一个新的版本,将旧版本保留在数据库中。这些版本按照时间顺序链接在一起,形成一个版本链。- 版本存储的位置:MySQL的InnoDB存储引擎:会使用UNDO日志来存储旧版本的数据。UNDO日志是一个回滚日志,它记录了事务在执行时对数据所做的更改。
- 版本的清除:
- 垃圾回收(Garbage Collection):当不再有任何
活动事务
需要访问某个版本时,数据库会将这些旧版本标记为垃圾,并进行回收。垃圾回收的时机通常是在所有事务都完成后,特别是当所有事务都已经提交或回滚,且不再需要访问这些旧版本的数据。 - Purge 过程
- 垃圾回收(Garbage Collection):当不再有任何
- 何时版本被清除:
- 事务提交后
- Purge 机制
版本链中提到的UNDO是什么?
UNDO日志是数据库管理系统中用于支持事务回滚和多版本并发控制(MVCC)的重要机制之一。它记录了事务在执行过程中对数据所做的更改,特别是原始数据的备份,以便在需要时恢复数据到事务执行之前的状态。
UNDO日志的功能与作用
- 事务回滚:
- 当一个事务正在执行时,可能会遇到某些问题导致事务需要回滚,例如违反了约束条件、出现死锁,或者用户主动中止事务。在这种情况下,数据库需要撤销事务所做的所有更改,将数据恢复到事务开始前的状态。
- 支持MVCC
- UNDO日志在MVCC机制下也起着至关重要的作用。它记录了数据的历史版本,使得数据库能够在并发事务中为不同事务提供一致的历史视图。
- 崩溃恢复
- 在数据库系统发生崩溃时,UNDO日志也用于崩溃恢复。数据库系统会利用UNDO日志将所有未提交的事务进行回滚,确保系统恢复到一个一致的状态。
UNDO日志的结构与存储
- 日志内容:
- 事务ID:指示哪个事务进行了该操作
- 操作类型:例如INSERT、UPDATE、DELETE等操作
- 旧数据值:在执行写操作之前,记录下数据的原始值。
- 行指针:指向被修改的具体数据行。
问题1: MVCC的快照存储的是什么数据?
答: 快照存储的是事务开始时所有的数据版本
(创建版本号、删除版本号)
问题2: 两个事务开始时间相差无几,拿得到的快照大同小异,如果操作同一条数据,都产生了新版本,这两个事务提交时,又是怎么处理的?
答: 并发场景主要看事务隔离级别
以及并发冲突处理机制
:
① 读操作:如果是两个时候都是读操作,那么各自读取独立的数据版本,互补干扰。
② 写操作:那就要看不同事务级别的处理机制,如下:
乐观并发控制
:快照只是可以看到什么数据,最终事务要提交时,才会判断是否并发冲突
乐观并发控制
:在事务执行过程中加锁,防止其他事务同时修改同一数据。如果一个事务正在修改某行数据,其他事务将被阻塞,直到第一个事务提交或回滚。
- 读已提交(Read Committed):
在这个隔离级别下,每次读取数据时,事务都会看到其他已提交事务的最新数据版本
。如果两个事务几乎同时开始,并且都对同一条数据进行了修改,通常第一个提交的事务会成功,而第二个提交的事务会在提交时检测到冲突。第二个事务在提交时会检查自己基于的快照版本是否仍然是最新的。如果不是(因为第一个事务已经提交了修改),第二个事务的提交会失败,并引发回滚或需要重新执行。- 可重复读(Repeatable Read):
在这个隔离级别下,事务会一直看到自己开始时的快照数据,无论其他事务是否已经提交。两个事务如果几乎同时开始,并且都修改了同一条数据,第一个提交的事务会成功,第二个事务在提交时同样会检测到冲突(即它基于的快照版本已经过时),导致提交失败,必须回滚。- 可串行化(Serializable):
这是最高的隔离级别,保证事务仿佛是串行执行的。在这种情况下,数据库系统会强制所有并发事务看起来是一个接一个地执行的。如果两个事务试图同时修改同一行数据,系统可能会使其中一个事务等待,直到另一个事务完成为止。这样可以确保不会产生冲突,虽然这会降低系统的并发性。
问题3: MVCC与加锁之间的关系?怎办么配合工作的?
答: MVCC快照是为了让事务可以安心的读取了数据,而不用担心被别的版本扰乱,但如果事务AB同时修改一行数据,即便他们各自操作快照中的数据,在提交时人任然需要加锁。 快照先于加锁、加锁发生在写操作时。
2.3 隔离性是怎么实现的——间隙锁
2.3.1 间隙锁的定义
间隙锁是加在索引记录之间的“间隙”上的一种锁。这种锁不会锁定具体的数据行,而是锁定两个索引记录之间的区域(即所谓的“间隙”)。如果你对表中索引列的某个范围进行查询(例如使用BETWEEN或范围扫描查询),间隙锁会锁住范围内的所有“间隙”,防止其他事务在这个范围内插入新的记录。
2.3.2 间隙锁的目的
主要目的是防止“幻读”现象,即一个事务在两次读取之间发现了“新”插入的记录。如果不加间隙锁,另一个事务可能会在同一个间隙内插入新记录,这样当前事务再读取时就会看到这些新插入的记录,导致幻读。在高隔离级别下,通过锁定间隙,InnoDB确保没有其他事务能够在这个间隙内插入新的记录,从而避免幻读,保证一致性。
查询时索引覆盖也是要加锁避免幻读的啊