事务
保证数据状态一致性
在数据库管理中,确保数据状态的一致性是至关重要的,而这一目标通常通过ACID原则来实现。
ACID中的A(原子性)、I(一致性)和D(持久性)是手段,而C(隔离性)则是最终目的。
当一个服务仅使用一个数据源时,通过原子性、一致性和持久性来获得数据一致性是最传统且容易实现的做法。在这种情况下,多个并发事务读写的数据能够被单一数据源实时感知冲突的存在。并发事务在时间线上的最终顺序由数据源来控制,这种事务间的一致性被称为“内部一致性”。
然而,情况就复杂多了,当一个服务涉及多个不同的数据源,甚至同时有多个服务操作多个不同的数据源时,保证数据一致性就变得非常具有挑战性。在这个场景下,并发执行或先后执行的多个事务,其在时间线上的执行顺序并不再由任一单一数据源来决定。这种涉及多个数据源的事务间一致性被称为“外部一致性”,其实现需要更复杂的协调机制和策略,以保证系统的整体一致性。
本地事务
本地事务是指仅操作单一事务资源的、不需要全局事务管理器进行协调的事务
实现原子性与持久性
原子性确保事务中的多个操作要么全部成功执行,要么全部不执行,从而保证数据的一致性。
持久性则确保一旦事务成功生效,其修改的数据不会因为任何原因而丢失或被改变。
数据需要成功写入到磁盘、磁带等持久化存储设备上,只有这样才能实现持久性。如果数据只存储在内存中,一旦出现应用程序崩溃、数据库或操作系统故障,甚至是突然断电等意外情况,数据就会面临丢失的风险。
为了有效应对崩溃恢复,必须记录下所有修改数据的操作信息,包括:修改了哪些数据,数据在内存和磁盘上的具体位置,以及从什么值修改到什么值。这些信息需要以日志追加的形式写入到磁盘中。只有在所有日志安全落盘,并且数据库能够在日志中找到代表事务成功提交的“提交记录”(Commit Record)后,才会根据日志的信息真正修改数据。在数据修改完成后,再在日志中添加一条“结束记录”(End Record),以表明事务已成功持久化。
Commit Logging
Commit Logging 保障数据持久性和原子性的原理。首先,当日志成功写入 Commit Record 时,表明整个事务已成功完成。在这种情况下,即使在后续的数据修改过程中发生崩溃,系统重启后仍然可以根据已经写入磁盘的日志信息恢复现场并继续进行数据修改,从而确保了数据的持久性。
其次,如果在崩溃发生前日志尚未成功写入 Commit Record,那么整个事务将视为失败。系统重启后,部分未包含 Commit Record 的日志将被识别并标记为回滚状态,这样整个事务就如同从未发生过一样,从而实现了原子性。
通过这种机制,Commit Logging 有效地保护了数据的完整性和一致性,使得系统在面对意外情况时能够安全地恢复和维护事务的状态。
Write-Ahead Logging
在对数据库进行任何修改之前,先将这些修改的详细信息记录到日志中
- 先写日志:在对数据库的实际数据进行修改之前,所有相关的操作都必须先写入日志。这意味着,任何事务的修改都需要在日志中记录下相关的操作信息,包括修改的内容和事务状态。
- 持久性保障:一旦日志中的信息成功写入到持久化存储设备(如磁盘)中,系统就可以安全地应用这些修改到数据库。这保证了即使在写入数据库过程中发生崩溃,系统也能够通过日志恢复到一致的状态。
- 事务的原子性:如果在将修改应用到数据库之前发生了崩溃,那么在系统重启时,数据库可以检查日志,以确定哪些事务已成功提交(包含Commit Record),哪些事务未提交(未包含Commit Record)。未提交的事务将被回滚,从而确保数据库的状态如同这些事务从未发生过。
- 恢复过程:在系统重启后,通过扫描WAL日志,数据库可以重放已成功提交的事务,同时忽略未提交的事务。这一过程确保了数据的一致性与完整性。
按照事务提交时间点写入变动数据分为FORCE和STEAL
- FORCE:当事务提交后,要求变动数据必须同时完成写入则称为FORCE,如果不强制变动数据必须同时完成写入则称为NO-FORCE。当变动数据写入磁盘前,必须先记录Undo Log,注明修改了哪个位置的数据、从什么值改成什么值等,以便在事务回滚或者崩溃恢复时根据Undo Log对提前写入的数据变动进行擦除。
- STEAL:在事务提交前,允许变动数据提前写入称STEAL,不允许是NO-STEAL
Write-Ahead Logging在崩溃恢复时会经历以下三个阶段。
·分析阶段(Analysis):该阶段从最后一次检查点(Checkpoint,可理解为在这个点之前所有应该持久化的变动都已安全落盘)开始扫描日志,找出所有没有End Record的事务,组成待恢复的事务集合,这个集合至少会包括事务表(Transaction Table)和脏页表(Dirty Page Table)两个组成部分。
·重做阶段(Redo):该阶段依据分析阶段中产生的待恢复的事务集合来重演历史(Repeat History),具体操作是找出所有包含Commit Record的日志,将这些日志修改的数据写入磁盘,写入完成后在日志中增加一条End Record,然后移出待恢复事务集合。
·回滚阶段(Undo):该阶段处理经过分析、重做阶段后剩余的恢复事务集合,此时剩下的都是需要回滚的事务,它们被称为Loser,根据Undo Log中的信息,将已经提前写入磁盘的信息重新改写回去,以达到回滚这些Loser事务的目的。
实现隔离性
隔离性保证了每个事务各自读、写的数据互相独立,不会彼此影响
在数据库中,事务隔离级别定义了事务之间的可见性和影响程度,常见的隔离级别包括:
可重复读(Repeatable Read):
- 在可重复读隔离级别下,事务在第一次读取数据后,再次读取该数据时,看到的数据是相同的。这意味着在事务执行期间,其他事务不能修改或者删除该数据。
- 问题:可重复读无法防止“幻读”现象,即在同一事务中,如果再次执行范围查询,可能会看到新的数据被插入。
读已提交(Read Committed):
- 在该隔离级别下,事务只能读取已经提交的数据。这意味着一个事务在执行过程中,无法读取到其他未提交事务的数据。
- 问题:虽然防止了“脏读”,但仍然可能出现“不可重复读”现象,即同一事务的两次读取可能会返回不同的结果,因为其他事务在两次读取之间可能已经提交了修改。
读未提交(Read Uncommitted):
- 在最底层的隔离级别,事务可以读取到其他事务未提交的数据。这种方式允许最大程度的并发,但造成了数据一致性风险。
- 问题:这种级别容易出现“脏读”,即一个事务读取了未提交的事务数据,从而得到可不信赖的结果。
MVCC
MVCC(Multi-Version Concurrency Control)是一种数据库管理中的并发控制机制,其基本思路是通过为数据库中的数据记录多个版本,以支持高效的并发操作,从而实现读操作时无需加锁,避免了传统锁机制带来的性能瓶颈。
基本原理
在 MVCC 中,数据库的任何修改都不会直接覆盖原有的数据,而是创建新的数据版本与旧版本共存。这种设计使得在读取数据时,不会阻塞写操作,从而提高了并发性能。具体运作方式如下:
插入数据:
- 当插入新数据时,系统会创建一个新版本的记录,
CREATE_VERSION
字段记录插入数据的事务ID,而DELETE_VERSION
字段保持为空,表示数据尚未被删除。
- 当插入新数据时,系统会创建一个新版本的记录,
删除数据:
- 删除数据时,系统将标记删除,通过在
DELETE_VERSION
字段中记录删除操作的事务ID,同时将CREATE_VERSION
置为空,表示该版本数据已被删除。
- 删除数据时,系统将标记删除,通过在
修改数据:
- 对数据的修改被视为“删除旧数据,插入新数据”的组合过程。首先,旧数据的副本将被保留,旧记录的
DELETE_VERSION
字段被更新为修改操作的事务ID,CREATE_VERSION
则保持为空。接着,会创建一个新的数据版本,记录修改后的数据,其中CREATE_VERSION
字段记录此次修改的事务ID,DELETE_VERSION
仍为空,表明该新记录是有效的。
- 对数据的修改被视为“删除旧数据,插入新数据”的组合过程。首先,旧数据的副本将被保留,旧记录的
优点
- 无需加锁:由于读取操作总是访问数据的快照,因此读取过程无需加锁,从而避免了因等待锁而造成的性能损失。
- 提高并发性:MVCC 提供了更高的并发处理能力,多个事务可以同时读取和修改数据库而不相互阻塞。
- 减少脏读:通过版本控制,能够确保读取到的数据是已提交的状态,降低了数据不一致的风险。
缺点
- 版本管理开销:管理多个数据版本会增加系统的复杂性和存储开销,因为每个修改都涉及到数据的复制和版本存储。
- 清理旧版本:随着时间的推移,旧版本的数据可能会占用大量存储空间,因此需要定期进行垃圾回收,清理无用的版本。
分布式事务
强一致性的事务
两阶段提交(2PC)
两阶段提交协议分为两个阶段:
- 投票阶段:协调者(Coordinator)向所有参与者(Participants)发送一个准备请求。参与者执行事务操作,但不提交,并记录日志。然后,参与者向协调者发送投票结果,表示是否准备好提交。
- 提交阶段:如果所有参与者都投票准备提交,协调者向所有参与者发送提交请求。参与者提交事务,并释放资源。如果有任何一个参与者投票不准备提交,协调者向所有参与者发送回滚请求。参与者回滚事务,并释放资源。
两阶段提交的主要问题是它可能导致阻塞,因为在提交阶段之前,参与者必须等待协调者的指令。如果协调者发生故障,参与者可能会无限期地等待。
前提条件
假设网络在提交阶段的短时间内是可靠的
假设因为网络分区、机器崩溃或者其他原因而导致失联的节点最终能够恢复,不会永久性地处于失联状态
缺点
协调者等待参与者回复时可以有超时机制,允许参与者宕机,但参与者等待协调者指令时无法做超时处理。一旦宕机的不是其中某个参与者,而是协调者的话,所有参与者都会受到影响。如果协调者一直没有恢复,没有正常发送Commit或者Rollback的指令,那所有参与者都必须一直等待。
FLP
FLP 不可能原理,全称为 Fischer-Lynch-Paterson 不可能原理,是分布式计算领域中的一个重要理论。该原理指出,在一个异步分布式系统中,即使只有一个进程失败,也不存在一种确定性的共识算法能够保证所有进程对某个值达成一致。
FLP 不可能原理的核心观点是,在异步系统中,由于消息传递的延迟和不确定性,进程可能会在不同的时间点接收到不同的信息,这导致了共识的不确定性。即使系统中只有一个进程出现故障,也可能导致整个系统无法达成共识。
三阶段提交(3PC)
三阶段提交协议是两阶段提交的改进,旨在减少阻塞的可能性。它分为三个阶段:
- CanCommit 阶段:协调者向所有参与者发送一个准备请求。参与者检查是否可以提交事务,并向协调者发送响应。如果所有参与者都可以提交,进入第二阶段。
- PreCommit 阶段:协调者向所有参与者发送预提交请求。参与者执行事务操作,并记录日志,但不提交。然后,参与者向协调者发送准备就绪的响应。
- DoCommit 阶段:如果所有参与者都准备就绪,协调者向所有参与者发送提交请求。参与者提交事务,并释放资源。如果有任何一个参与者没有准备就绪,协调者向所有参与者发送回滚请求。参与者回滚事务,并释放资源。
三阶段提交通过引入一个额外的准备阶段来减少阻塞的可能性。如果协调者在提交阶段之前发生故障,参与者可以在超时后自动提交事务。然而,三阶段提交仍然可能在某些情况下导致阻塞,并且实现起来比两阶段提交更复杂。
CAP定理
它指出在一个分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个特性不能同时被满足。
- 一致性:在分布式系统中,所有节点在同一时刻看到的数据是相同的。
- 可用性:系统中的每个请求都能在有限的时间内返回结果。
- 分区容错性:系统能够在网络分区的情况下继续工作。
CAP定理的核心思想是,在分布式系统中,如果网络发生分区(即节点之间的通信出现问题),那么系统必须在一致性和可用性之间做出选择。如果选择一致性,那么系统在分区期间可能无法提供服务;如果选择可用性,那么系统在分区期间可能返回不一致的数据。
BASE 理论
BASE 理论是对 CAP 定理的延伸和补充,它指出在分布式系统中,即使无法做到强一致性(Strong Consistency),也可以通过牺牲一定的一致性来换取系统的高可用性(Availability)和分区容错性(Partition Tolerance)。BASE 理论的核心思想是:
- Basically Available(基本可用):系统在出现故障时,仍然能够提供基本的服务,允许损失部分功能或性能。
- Soft state(软状态):系统中的数据状态可以在一段时间内不同步,即存在中间状态,但最终会达到一致。
- Eventually consistent(最终一致性):系统中的所有数据副本在经过一段时间后,最终能够达到一致的状态。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。