IT技术博客大学习 共学习 共进步

第七章 事务

Home on codedump notes 2026-06-03 09:03:24 累计浏览 3 次
本机暂存

到目前为止,我们已经介绍了复制和分区技术,复制技术(包介后面绍的共识算法)提升了系统的容错性,而分区技术提升了系统的扩展性,这两项技术解决的是数据的*“物理问题”。除此以外,分布式系统中的数据访问还经常面临着“逻辑问题”,此时就需要本章将要介绍的事务*技术来解决:

  • 复制 (Replication):主要目标是高可用性和数据冗余。通过在不同的节点上存储相同的数据副本,当某个节点发生故障时,系统可以从其他副本继续提供服务。它回答的是:“我的数据会不会因为一台机器挂掉而丢失或无法访问?"。
  • 分区 (Partitioning / Sharding):主要目标是可扩展性。当单台机器的存储或计算能力无法承载全部数据和请求时,我们将数据水平切分到多个节点上。它回答的是:“我的系统如何处理不断增长的数据量和访问压力?”
  • 事务 (Transaction):主要目标是数据操作的正确性。它将一系列操作打包成一个不可分割的逻辑单元,保证这些操作要么全部成功,要么全部失败,并且在并发执行时互不干扰。它回答的是:“我如何确保一系列相关的操作在任何情况下(并发、故障)都能保持数据的正确状态?”

为什么只有复制和分区是不够的?我们来看以下几个典型场景:

场景一:转账操作(原子性缺失的灾难)

用户A要向用户B转账100元。这个操作至少包含两个步骤:

  1. 从用户A的账户中扣除100元。
  2. 给用户B的账户增加100元。

在一个缺少事务技术的分布式系统中,可能会出现以下的问题:

  • 发生故障:系统在执行完第1步后突然崩溃(比如数据库节点宕机)。此时,A的钱被扣了,但B没收到钱。钱"凭空消失"了。即使数据有多个副本(复制技术),但所有副本记录的都是这个"中间状态"的错误数据。
  • 跨分区操作:假设用户A的数据在分区1,用户B的数据在分区2。这个转账操作需要一个协调者来分别通知两个分区执行操作。如果分区1成功扣款,但分区2因为网络问题或自身故障未能成功收款,同样会导致数据不一致。

但是如果使用事务技术,事务的原子性 (Atomicity) 保证了这一系列操作是一个"全有或全无"的原子单元。系统会确保"扣款"和"收款"这两个步骤要么都完成,要么如果中间有任何差错,所有已经完成的步骤都会被回滚(Rollback),系统状态恢复到操作开始之前。这样就绝不会出现钱平白消失或多出来的情况。

场景二:并发抢购(隔离性缺失的混乱)

一个电商网站上,某件商品只剩下最后1件库存。此时,两个用户(用户C和用户D)同时点击了"购买"按钮。

在一个缺少事务技术的分布式系统中,可能会出现以下的问题:

  1. 用户C的请求到达,系统读取库存为1。
  2. 在用户C的请求完成"减库存"操作之前,用户D的请求也到达了,系统读取库存仍然为1。
  3. 用户C的请求执行"减库存”,库存变为0。
  4. 用户D的请求也执行"减库存",库存变为-1(超卖)。
  5. 最终结果是:两个用户都以为自己买到了商品,而系统库存出现了负数。这造成了严重的业务逻辑错误。

但是如果使用事务技术,事务的隔离性 (Isolation) 保证了并发执行的多个事务之间互不干扰,就像它们是串行执行的一样。当用户C的事务开始处理库存时,它会锁定该数据。用户D的事务在用户C的事务完成(提交或回滚)之前,无法修改库存数据,它要么等待,要么读取到一个旧的值然后操作失败。这样就保证了最终只有一个人能成功买到商品。

场景三:系统崩溃后的数据完整性(持久性缺失的风险)

一个订单系统刚刚完成了一笔重要订单的创建,所有数据已经写入。在数据从内存刷到磁盘的瞬间,服务器断电了。

在一个缺少事务技术的分布式系统中,如果系统依赖于操作系统的缓存写入,那么这笔订单数据可能就永久丢失了。尽管你可能收到了"操作成功"的响应,但数据并未真正持久化。

事务的持久性 (Durability) 保证了一旦事务被提交,其结果就是永久性的。即使系统崩溃,数据也能够被恢复。这通常通过预写日志(Write-Ahead Logging, WAL)等机制来实现:在修改数据本身之前,先将操作记录到持久化的日志中。

我们可以把分布式系统想象成一个大型的、跨部门的公司项目:

  • 复制技术保证了每个部门都有核心成员的备份,或者有完整的项目文档副本。如果一个核心成员请假或离职,备份人员可以顶上,保证部门工作不中断(高可用性)。
  • 分区技术把项目拆分给不同的部门(前端部、后端部、数据库部),这提高了整个公司的处理能力(可扩展性)。
  • 事务技术是项目管理中的一个"工作流"或"流程规定"。比如,“产品上线"这个工作流必须包含:1. 代码部署成功, 2. 数据库迁移成功, 3. CDN缓存刷新成功。这个流程规定了:这三件事必须全部搞定,这个"产品上线"才算真正成功。 如果任何一步失败,整个上线流程就要回滚到初始状态(比如代码回滚,数据库恢复),绝不能停在一个"部署了一半"的尴尬状态。

除此以外,事务还为应用开发者提供简化的编程模型。试想一下,如果没有事务的各种保证,应用开发者需要:

  • 手动处理各种失败场景下的回滚逻辑。
  • 手动加锁来避免并发冲突。
  • 编写复杂的补偿代码来修复部分失败导致的数据不一致。

这会使业务代码变得异常复杂、容易出错且难以维护。事务将所有这些复杂性封装起来,向开发者提供了一个简洁的抽象:“你可以把一系列操作当作一个不可分割的单元来执行,系统保证其ACID属性。” 这极大地提升了开发效率和应用可靠性。

在本章中,我们先从单机数据库事务开始展开对事务ACID特性的讨论,进而延展到涉及多个服务的分布式事务。在分布式系统中,由于可能跨多个服务(例如一次电商的购买行为中中涉及订单服务、库存服务、支付服务),面临着更大的挑战:

  • 网络不可靠,可能出现延迟、分区、丢包等情况。
  • 节点会失败,服务器可能会宕机、进程可能会崩溃。
  • 数据不一致的风险,一部分操作成功,一部分失败(例如,订单创建成功,但扣库存失败)。

7.1 深入理解ACID #

在单机数据库中,可能出现各种故障:

  • 数据库正在写入数据时系统崩溃,在重启数据库之后,如何从故障数据中恢复。
  • 多个客户端同时写入多个数据,例如本章开头的电商购买例子中,

为了保障应用开发者不被这些故障困扰,事务技术一直据库系统的首选机制。事务技术向应用开发者提供了ACID的安全保证,我们首先来了解这些特性。ACID最早在论文中被提出,做为精确描述数据库的容错机制而定义,是以下四个单词的缩写:

  • A(Atomicity):原子性,事务保证了对多个数据的修改,要么同时成功,要么同时失败。
  • C(Consistency):一致性:一个事务在执行前后,数据库都必须处于正确的状态,满足完整性约束。
  • I(Isolation):隔离性,多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • D(Durability):持久性,事务处理完成后,对数据的修改就是永久的,即便系统故障也不会丢失。

总体而言,ACID属性提供了一种机制,使每个事务都作为一个单元,完成一组操作,产生一致结果,事务之间彼此隔离,更新永久生效”,从而来确保数据库的正确性和一致性。

建议继续学习

  1. 深入浅出INNODB MVCC机制与原理 (累计阅读 9,620)
  2. MTU值的调整导致MySQL复制异常 (累计阅读 4,740)
  3. MySQL5.5复制/同步的新特性及改进 (累计阅读 4,600)
  4. SQLIte这么娇小可爱,不多了解点都不行啊 (累计阅读 4,501)
  5. CentOS分区规律大总结 (累计阅读 3,701)
  6. Google Megastore系统事务机制 (累计阅读 3,380)
  7. Innodb 多版本实现 (累计阅读 3,161)
  8. MySQL锁问题最佳实践 (累计阅读 2,820)
  9. Hive-如何基于分区优化 (累计阅读 2,560)
  10. 深入剖析 redis 事务机制 (累计阅读 2,561)