日志:分布式系统的核心

日志是什么?

提起日志,可能你最先想到的是日常开发中写入到某个文件的信息,例如:

log.Println("start ...")

除此之外,不难想到在数据复制、版本控制等概念中,也有日志两个字的身影,而本文讨论的正是这一种日志。

image.png

日志,可以理解为一种存储结构,日志项按时间有序。日志记录了不同事件发生的先后次序,这对于分布式系统有着重要意义。

数据库中的日志

为了保证事务的原子性和持久性,在更改数据前,会将要做的更改写入到日志中,日志记录了每一次更改的数据变化和时间。由于日志是立即持久化的,因此可以通过日志,恢复数据库到任意历史时间的状态。

日志除了用于实现事务 ACID 的细节外,还被用于数据库间的数据复制。备库通过执行和主库一样的更改序列即可做到与主库数据同步,而更改序列即主库的日志,主库要做的就是将日志传输到备库。

分布式系统中的日志

上一节中,日志解决了数据库中,更改行为的排序和数据的传输两个问题。而在分布式系统中,这两个问题更是重要。分布式系统通过日志,让多个副本达成一致的行为顺序,该方案称之为状态机复制原理:

如果两个相同的,确定性的进程从同一状态开始,且以相同顺序获得相同输入,则这两个进程会产生相同的输出,并结束在相同的状态。

分布式系统的日志复制方案通常有两种:

  1. Peer & Peer
  2. Primary & Secondary

image.png

这里讲 Primary & Secondary,Leader & Worker 或者 Master & Slave 等都是一样的概念。

对于分布式系统的讨论,理论往往多于实际应用,而在实际应用中往往不会过分关注实现日志复制的算法,就像我们经常会使用哈希表而不会纠结其实现细节,这里只将日志看作一种抽象概念,而不取讨论其具体的算法实现([我之前有写过 Raft 算法的实验课笔记,感兴趣的可以看一看](MIT 6.824 - 李素晴的专栏 - 掘金 (juejin.cn)))。

日志和数据的关系

如果把日志比作账单,那么数据就是余额。日志和数据的关系就可以这样理解:数据是静态的,日志是数据的变更记录,是每个历史时间点的数据备份。

数据的统一管理

举个例子,用户点击了某条广告,该事件需要记录到系统中,此时写一条事件数据到日志中,数据库、缓存等不同的系统都从日志读取日志项并应用。

image.png

这是一种基于日志订阅的系统模型,日志项的索引作为逻辑时钟,极大的简化了不同的订阅方的系统状态是否一致的问题。

举个例子,假设写了一条数据,对应日志项索引为 9,如上图所示,数据库已经更新自身状态到 11,而缓存则是更新自身的状态到 7,如果我们想要读取刚刚写入的数据,就只能在数据库中读,而不是通过缓存读取。

在该系统模型中,日志还起到了缓冲的作用,数据的生产和消费是异步的,即使某一时刻有大量的写请求,数据库也不会感受到压力。除此之外,无状态的订阅方被允许宕机或下线维护,重新上线后依然可以继续处理数据。

日志与流处理

通过一张图来了解什么是流处理:

image.png

  • 系统 1 订阅了 Log A 和 Log B,系统 2 订阅了 Log B 和 Log C
  • 系统 1 计算并发布数据到 Log D 和 Log E,系统 2 计算并发布数据到 Log E 和 Log F
  • 系统 3 订阅了 Log D 和 Log E
  • 系统 3 计算并发布数据到 Log F

在如图所示的流处理中,日志是其中的核心。

有状态的流处理

上文说过,无状态的订阅方被允许宕机或下线维护,重新上线后依然可以继续处理数据。那么对于有状态的订阅方,在可能宕机的情况下,如何维护正确的状态?

什么是无状态和有状态?例如同样是提供计算服务,一个是每次输入 2 个值,输出这两个值的和;另一个是每次输入 1 个值,输出所有历史输入的累加值和这一次输入值的和。前者是无状态服务,后者是有状态服务,需要记录到历史输入的累加值。

回顾日志和数据的关系的讨论,订阅系统可以将输入流(日志)转换为数据,并保存在本地的数据库中。

还是以上面提到的这个有状态计算服务为例,日志中记录可以这样表示:

+1, +2, +3, +4, +5

该日志的订阅方已经消费到第四个位置了,并输出 10,此时应该继续向后消费 +5,但是由于某些原因宕机了,如果恢复后本地没有记录 10 这个状态,再次消费 +5 就会输出 5,而记录了状态,就是输出 15。

本例中,订阅系统将输入流:“+1,+2,+3,+4”看作变更日志,对应的数据就是 10 记录在本地数据库,在消费了 +5 这一日志项后,数据库中的数据就变成了 15。

需要注意的是,本例中订阅方宕机恢复后消费的起始位置并不归类于状态中。此外,日志的增长不该是无限的,可以使用日志合并来收缩空间,例如本例中可以将日志:“+1,+2,+3,+4”合并为:”+10“。

参考资料

The Log: What every software engineer should know about real-time data's unifying abstraction | LinkedIn Engineering

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

猜你喜欢

转载自juejin.im/post/7126111208125497375
今日推荐