1. 备注

本文译自https://www.confluent.io/blog/using-logs-to-build-a-solid-data-infrastructure-or-why-dual-writes-are-a-bad-idea/,作者Martin Kleppmann,May 29, 2015。

为了更好的组织语言和理解,符合我们的阅读习惯,原文的部分段落被合并或者分割,并标注大的章节。原文配图并未完整引用,但不影响示意、阅读和内容完整性。为体现完整性,不删减文字,保持原文文字内容。翻译纯属喜爱、分享和收藏。

2. 引言

本文是我在Craft Conference 2015会议上演讲的修订记录。视频和 PPT 请点击链接查看。

数据库怎么可靠地存储数据在磁盘?用日志。

    数据库备份怎么和另一个备份同步?用日志。

    分布式算法比如 Raft 是怎么达成一致性的?用日志。

    在一个系统比如 Apache Kafka中数据是怎么记录的?用日志。

    程序的数据基础设施是怎么在大规模下保持强健的?猜猜看……

日志无所不在。我谈的不是纯文本日志文件比如 syslog 或者 log4j,而是仅附加的、完全序列化的记录。它结构非常简单,但是你如果熟悉常规数据库的话会感觉到有点奇怪。不过,一旦你用日志的方式来理解,让大规模数据系统变得稳定、可扩展、可维护的问题就会突然变得更容易处理。

从在 LinkedIn 和其他初创项目中构建可扩展系统的经验展开,这个演讲将探索为什么日志是个好想法 —— 让维护检索索引和缓存更容易,让程序更具扩展性、更健壮,开放数据用以更加丰富的分析,同时避免资源竞争、不一致性和其他令人讨厌的问题。

3. 开场

大家好,我是Martin Kleppmann,致力于超大规模数据系统,尤其是你在互联网公司里发现的那种系统;曾任职在LinkedIn,贡献于一个叫着 Samza 的开源流处理系统。在 LinkedIn 工作期间,我和同事们对怎样构建操作上强健、稳定和高性能的应用程序有些心得。具体而言,得以和比如Jay Kreps、Chris Riccomini和Neha Narkhede一起共事。他们发现一个特别的基于日志的、运行相当给力的程序架构类型。在这个演讲中我将描述该方式,演示在多种不同计算领域中类似的模式。今天我要讲的并不是新的事物,有人早已听闻这些方法,但并非应该的那么广泛。你如果致力于不仅仅运用一个数据库的非普通的程序,可能会发现这些方法非常实用。

此刻,我正在休假来给 O’Reilly 写一本书《设计数据集中的程序》。这本书试图收集最近十年中我们习得的关于数据系统的重要基础性经验,覆盖数据库架构、缓存、索引、批处理和流处理。该书并不关于某个特定的数据库或者工具,而是运用在实践中的整个不同工具和算法、之间权衡和优劣。本演讲的一部分基于我为写这本书所做的研究。你如果感觉有意思,可在本书中找到更多的细节和背景。前面七个章节可见于当前的早期版本。

假设你正在搞一个网页应用。最简单情况下,它可能是典型的三层架构:客户端(浏览器、移动 app 或者两者),它们发送请求到网页程序。网页程序也就是程序代码或者业务逻辑承载者。程序要保存什么都需要保存在数据库中,查询之前存储的东西都需要查询数据库。该方式简单、容易理解并且运行相当好。

但是事情常常不会长久的这么简单下去。或许你有了更多的用户,发送更多请求,数据库变得缓慢,然后你增加缓存以求提高速度,比如 Memcached 或者 Redis;或许你需要为程序增加全文检索功能,并且数据库内置基础搜索工具不够好时,最终你会设立一个单独的索引服务比如 Elasticsearch 或者 Solr;或许你需要些图形操作,而这些在关系型和文档数据库中并不高效,比如社交功能或者推荐,所以你给系统增加一个单独的图形索引;或许你要把巨量操作移出网页请求流而成为一个异步后台处理,所以你添加一个消息队列以发送工作任务到后台程序。如此情形后续将变得更加复杂。

截至目前,系统的其他部分又变慢了,所以你添加另一个缓存。更多的缓存总是让速度更快,对吧?但是现在你有一大堆的程序和服务,所以你需要增加一个监控系统来查看他们到底是否在正在运行,而这个监控系统又是另一个自治的系统。然后,你需要发送通知,比如邮件或者推送通知到用户,所以你得在后台程序之外串接一个通知系统,并且可能需要一些独立的数据库来保存事务。现在你有很多数据需要分析,并且不能在主数据库上进行大量的业务分析,所以增加 Hadoop 或者数据仓库,并从数据库中导出数据到其中。现在业务分析搞定了,那么假设搜索系统挂了,你知道所有数据都在HDFS,你应该在 Hadoop 中创建检索索引并推送到搜索服务器,然后整个系统将会越来越复杂错乱。

我们是怎么变得如此的?怎么会如此复杂,什么都在彼此调用,没人知道是怎么回事?

一路上我们所做的某个决定并非不好。没有一个数据库或者工具能够做完程序所需要的所有事情。我们为一个任务用最好的工具,为具备多个功能的程序用多个工具。同样,随着系统体量的增加,为了程序的可管理性我们需要一种把程序分解为更小单元的方法,这就是微服务。不过如果你的系统变成一堆彼此调用而混乱的组件,那这本身也就不可管理了。

简单地有多个不同的存储系统,这本身不是问题,如果他们都彼此独立,那么也没啥大不了。真正的问题是他们有相同或者相关的数据,但不同的格式。比如在全文检索中的文档一般也存在数据库中,因为检索索引并不作为记录系统;缓存中的数据和某些数据库中的数据重复(或者夹杂了其他数据或者呈现为 HTML 格式或者其他)—— 缓存定义如此。同样,非规格化又是另一种格式的重复数据,类似于缓存。如果某些值重新计算太费力,你可存储在某些地方,但是你还得在底层数据发生变化时保持更新。聚合,比如计数、求和或者一堆记录的平均值(常从监控系统或者分析系统中获取)又是一种冗余数据。我并不是说这些重复数据不好,根本不是。缓存、索引和其他冗余数据格式都是获取良好读性能所必须的。但是,让这些不同呈现方式和存储系统之间的数据保持同步又是一个大的挑战。

找不到一个更好的词,我称这个问题为“数据集成”,表示“确保数据保存在正确的地方”。一点数据不论何时在一个地方发生变化时,它都需要在其他地方(备份或者派生的数据)对应地变化。

4. 双写

那么我们怎么同步这些不同的数据系统呢?这里有些不同的技术。一个流行的办法为双写。

双写很简单。你的程序来负责更新不同地方的数据。比如,一个用户提交数据到网页程序,网页程序的某些代码首先写入数据到数据库,再废弃或者刷新缓存中对应的实体,然后重新索引全文检索的文档,如此等等(或者并行处理这些事情,但并不影响我们的示意)。

双写方式很流行,因为它很容易实现,并且起初或多或少总能发挥作用。但是我想争议的是它并非是一个好主意,因为有些重要的问题。首先一个就是资源竞争。下图表示两个客户端双写到两个数据库。时间先后从左到右,如黑色剪头所指。

这里第一个客户端(蓝绿色)设置键值 X=A。首先发送一个请求到第一个数据仓库 —— 可能是个数据库,数据库回应说写入成功;然后发动请求到第二个数据仓库 —— 也许是个检索索引,也设置 X=A。同时,另一个客户端(红色)也想设置同一个键X为另一个不同的值 B。第二个客户端用同样的方式来处理,首先发送请求 X=B 到第一个数据仓库,然后 X=B 到第二个数据仓库。

这些写操作都成功了。但是,看看每个数据仓库中都存储了什么值。

第一个数据仓库中,值首先由蓝绿色客户端设置为 A,然后由红色客户端设置为 B,所以最终值为 B。第二个数据仓库中,两个请求不同次序的到达:值首先设置为 B,然后设置为 A,所以最终值为 A。现在两个数据仓库彼此不一致,并且永久保持不一致直到以后再复写键X 为止。最糟糕的是,可能甚至你都不会发现数据库和检索索引已经不同步了,因为没有错误发生。可能六个月后当你做完全不同的事情时才发现你的数据库和索引不匹配,并且不知道是怎么发生的。仅此已足以让大家摒弃双写。等等,还有更多……

我们看看非规格化数据。比如你有个用户可以彼此发送消息或邮件的程序,每个用户有一个收件箱。当一个新消息发送时,要做两件事:在用户收件箱中添加该消息到消息列表中;增加用户未读消息数。因总是在用户界面显示消息数所以设立个单独的计数,虽然每次需要显示该数值时扫描消息列表也不会太慢,但是该计算为非规格化信息,它从收件箱实际消息衍生而来,每次消息变化时你都需要对应的更新该计数。简单而言,一个客户端,一个数据库。想想随着时间的推移会发生什么:首先客户端在收件人收件箱中插入新消息,然后发送请求增加未读数。

就在此时,出现一个问题 —— 或许数据库挂了,或者一个进程崩溃,或者网络中断,或者网线拔错了。不管什么原因,更新未读数失败了。现在数据库不一致了:消息已添加到收件箱,但是未读数并未更新,除非你间歇性从零开始重新计算计数值或者收回插入的新消息,否则它将永远持续不一致。

当然你会争辩说这个问题早在几十年前就通过事务解决了。原子性,也就是“ACID”中的Atomicity,表示在一个事务中做几个更改,他们要么都发生要么一个都不发生。原子性的目的在于精确地解决这个问题,在写入过程中如果发生错误,你不必担心半途而废的修改导致数据的不一致性。包装两个写入到一个事务中的传统做法在支持它的数据库中可行,但是许多新生代的数据库中却不然,所以需要你自行解决。同时,如果这些非规格化的信息存储在另一个数据库中,比如邮件存储在一个数据库中而未读数在 Redis 中时,你将无法绑定这些写入操作在单个事务中。如果一个写入成功而另一个失败,你就很难清除这个不一致问题。

一些系统支持分布式事务,基于比如两阶段提交。但是当代很多数据仓库都不支持它,即便支持,分布式事务是好是坏还未有定论。所以我们说双写程序需要自行处理部分失败,而这个比较困难。

5. 日志

回到最初的问题,怎么确保数据保存在正确的地方?怎么存储一份同样的数据备份在几个不同的存储系统中,并且让他们在数据变化时持续同步?我们看到,双写并非想要的解决方案,因为它会因资源竞争和部分失败引入非一致性问题。我们怎么做才更好呢?

我是个极简主义者。简单方案的伟大之处在于你有机会理解他们并且说服自己那是正确的。这样的话,我看到的最简单的方案就是把写入操作置于固定次序,并且依照固定次序保持下来。如果序列化地写入,没有并发,那么就已经排除了资源竞争的可能性。此外,如果你记录了写入操作的次序,从部分失败中回恢复将会变得容易得多,我后面会演示给大家。我提议的极简方案就是任何时候写入数据时,我们就附加这个写入操作到记录序列的尾端。这个序列是完全有序的,仅可附加(从不更改已有记录,只是在尾端增加新记录),并且是永久的(持续存储在磁盘中)。

前面的图片呈现了这么一个数据结构示例:从左到右,记录了首先写入 X=5,然后 Y=8,然后 X=6,以此类推。这个数据结构我们称之为日志。日志有趣的地方在于它出现在多个不同的计算领域。虽然看起来非常简单感觉不大可能行得通,但事实证明非常强大。

当我们说“日志”时,你可能首先想到的是从 log4j 或者 syslog 中看到的纯文本的应用日志。比如,上面的是一行 Nginx 访问日志,表示某个 IP 地址在某个时刻访问了某个文件,还包括引用、用户浏览器、返回码和一些其他的东西。当然这是其中一种日志。我这里说的日志是更综合的东西,指完全次序化的、仅附加的、持久化的任何一种数据结构。任何仅附加的文件。

剩下的时间我将说说一些日志在实践中怎么运用的例子。其实日志已经在今天的多个数据库和系统中展现。一旦了解了日志在多个不同系统中如何使用,我们将会处于更好的场地来理解他们怎么帮助我们解决数据集成的问题。

5.1 存储引擎B-Trees

有四个运用日志的地方。首先就是数据库存储引擎的内部机制。

还记得算法课上的 B-Trees 么?他们是存储引擎中广泛应用的数据结构,几乎所有的关系型数据库和许多非关系型数据库都在使用。简而言之,一个 B-Tree由 pages 构成,page 为磁盘中固定大小的块,通常是4KB 或者8KB大小。当查找某个键时,从根部的 page 开始。page 包含指向其他 pages 的指针,每个指针标记有一定范围的键。比如一个键在0和100之间,顺着第一个指针;如果在100和300之间,就顺着第二个指针;以此类推。指针指引到另一个 page,这个page进一步分解键的范围到子范围,最终定位到包含所寻找的键的 page。

如果想插入一个新的键值对到B-Tree 会发生什么呢?你得把它插入到键范围包含这个键的 page 中。如果这个 page 有足够的空闲空间,那没问题;如果 page 满了,那这个 page 就需要分裂为两个独立的 pages。

当分裂一个 page 时,需要写入至少3个 pages 到磁盘。两个 pages 由分裂而生,还有一个父page(来更新指针到分裂后的 pages )。不过,这些 pages 可以存储在磁盘中多个不同的位置。

问题就来了,如果数据库在部分 pages 写入磁盘后,操作半途中崩溃了(或者断电,或者其他问题发生)会发生什么?这种情况下,在一些 pages 中有老数据(分裂之前),其他 pages 中有新数据(分裂后)。这样很可能就会有不指向任何东西的挂起的指针或者 pages。换言之,一个破损的索引。

5.1.1 WAL

存储引擎已经处理这个问题几十年了,那么他们怎么让 B-Tree 可靠的呢?答案是用写入前日志(write-ahead log,WAL)。写入前日志是种特殊的日志,比如磁盘中的仅附加性文件。存储引擎对 B-Tree 做任何修改之前,它首先把这个修改写入 WAL。只有在写入 WAL 之后,并且持久存入磁盘后,它才允许去更改实际的 B-Tree。这就使得 B-Tree 可靠了。如果数据库在数据正在附加到 WAL 时崩溃了,没关系,因为 B-Tree 都还尚未被触及;如果 B-Tree 正在被更改时,也没关系,因为 WAL 包含有将要修改的信息。数据库崩溃后运行时,它就用 WAL 来恢复 B-Tree 并进入一致状态。这就是第一个演示日志是个好办法的实例。


    当今存储引擎并不止步于 B-Trees。如果我们把所有东西都写入日志,可能就会把日志当着主要的存储媒介,这就是所谓的日志结构存储,使用于 HBase 和 Cassandra,以及出现在Riak中的一个变种。在日志结构存储中我们并非总是附加到同一个文件,因为文件将会变得太大,也很困难去查找需要的键。代替之,日志被分解为片,有时存储引擎会合并分片和丢弃重复的键。分片也可按照键来内部排序,使得查找键更容易,合并分片更简单。然后,这些分片还是日志,他们只能次序写入,写入后不可更改。正如你所见,日志在存储引擎中发挥着重要的功能。

5.2 数据库复制

第二个日志运用实例,数据库复制(备份)。

复制是许多数据库的一个功能,允许保留一份相同数据的备份在几个不同的节点上。利于分散负载,也意味着一个节点挂掉,还可故障转移到另一个节点。

有几个不同的方式来实现复制,但有个共同的选择就是指定某个节点为领导者(leader)(also known as primary or master),其他复制为跟随者(follower)(also known as standby or slave)。我不喜欢 master/slave 这个术语,所以下面只用领导者/跟随者(leader/follower)。

当客户端要向数据库写入时,它需要和领导者会话。只读客户端连接领导者和跟随者都行(虽然跟随者往往是异步的,所以如果最近的写入并未执行的话可能会有些许延迟数据)。

那么客户端写入数据到领导者时,数据又是如何跑到跟随者那去的呢?惊奇的是,他们用日志。他们用复制日志,这个实际上和写入前日志完全一致(Postgre 就是如此)或者是个单独的复制日志(MySQL 如此)。

复制日志是这么运行的,在数据写入领导的同时,也会附加到复制日志。跟随者依照写入的次序读取日志,并应用到自身的那份数据备份中。最后,每个跟随者都依照领导者的处理次序去处理同样的写入操作,这样就会保有同样的一份数据。即便领导者进行并行写入,日志中的写入照样严格排序。如此,日志其实就排解了写入的并发 —— “消除所有写入流中的非定论性”,跟随者也不用质疑写入的次序。

那么之前讨论到的双写资源竞呢?资源竞争在基于领导者的复制中不会发生,因为客户端不会直接写入跟随者。跟随者唯一处理的写入操作来源于接收到的复制日志。并且由于日志已解决好写入的次序问题,也就不存在写入先后的歧义。至于双写资源竞争的第二个问题呢?这个照样会发生,一个跟随者从事务中成功处理第一个写入操作,但是第二个失败了(也许磁盘满了,或者网络中断)。

如果领导者和跟随者之间的网络中断了,复制日志将无法从领导者流传到跟随者。就像之前讨论到的,这将导致不一致的备份。那么数据库复制如何从这样的错误中恢复并避免非一致性呢?请注意,日志有个很好的特征。因为领导者只是附加日志,我们可以给每一个记录添加一个只增的序列号(日志位置或者偏移量)。而且,跟随者只按照序列顺序来处理(比如从左到右,依照不断增加的日志位置次序),如此我们就可以用一个数字来描述一个跟随者当前的状态,也就是最后一条被处理记录的位置。只要你知道了跟随者当前日志中的位置,你就可以肯定之前的记录已处理完毕,之后的记录都还尚未处理,这也使得错误恢复非常简单。跟随者如果和领导者失联,或者崩溃了,它也就只需要存储复制日志中已处理的日志位置;在连接恢复时,跟随者就向领导者索要从之前处理的最后日志位置之后的复制日志。这样,跟随者就可以追上之前失联时缺失的写入操作,并且不会丢失任何数据或者收到重复数据。完全次序化的日志比起单独跟踪每一个写入来说恢复就容易得多了。

5.3 分布式一致性

第三个实践实例在另一个领域,分布式一致性。

达成一致性在分布式系统中是个众所周知也常常被讨论的问题。现实生活中的一个例子就是让一群朋友关于在哪里吃午饭达成共识。这是高度文明中的一个特征,一个很困难的问题,尤其当部分朋友还不在意(所以他们不回应你的提问)或者挑食。

在计算领域,分布式数据库系统中你想达成一致的一个实例就是,你想让所有数据库都一致同意哪个节点来作为数据库某个分区(分片)的领导者。让他们同意谁是领导者非常重要,如果两个节点都认为他们是领导者,他们都接受客户端的写入操作。然后其中一个发现它不是领导者,那么所接受的写入都将会丢失。这就是所谓的脑裂,会导致烦躁的数据丢失。

有几种实现一致性的算法。Paxos 也许是最知名的,也有 Zab(Zookeeper 使用),Raft 和其他的。这些算法很敏锐,有些并不明显的微妙之处。本演讲中我只是简单的走一遍 Raft 算法的一部分。

在一致系统中有多个节点(该算法为三个)来负责决策某个变量应该是什么值。一个客户端发送一个值到一个 Raft 节点,比如 X=8(也可表示节点 X 是分区8的领导者),这个节点收集其他节点的选票。如果大多数节点同意该值为X=8,那么该节点将提交这个值。提交之后呢?Raft 中,这个值将会附加在日志的末端。Raft 不仅让多个节点针对某个值达成共识,而且实质上还持续构建这些值的日志。所有 Raft 节点都确保他们的日志中拥有完全一样的提交数据顺序,而客户端消费这些日志。

一旦新值被提交、附加到日志并复制到其他节点,最初发送提议这个值的客户端将收到一个回应说系统已达成共识并且提交的值现在已成为 Raft 日志的一部分。

理论上讲,一致性问题和原子广播 —— 创建一次性交付日志 —— 是可彼此减少的。也即是 Raft 的日志应用不仅是一个便捷的实现细节,也还反射出一致性问题的一个重要属性。

5.4 Kafka

好了,我们已看到日志在许多计算领域中是个不断复现的主题,包括存储引擎、数据库复制和一致性。作为第四个也是最后一个例子,我来讲讲又一个围绕日志概念的系统,Apache Kafka。Kafka 有趣的地方是它对你并不隐藏日志,不把日志作为实现细节,而是暴露给你,所以你也可以据此创建程序。之前你可能听说过 Kafka,它是个开源项目,最初由 LinkedIn 开发,现在作为一个Apache 项目,有很多贡献者和用户。

Kafka的典型应用就是作为消息队列,所以可以与 AMQP、JMS 和其他的消息系统相提并论。 Kafka 有两种客户端,生产者(发送消息到 Kafka)和消费者(订阅 Kafka 的消息流)。举例而言,生产者可以是你的网页服务器或者移动 App,发送到 Kafka 的数据可以是日志信息,比如表示用户在某个时间点击某个链接的事件。客户端就是各式各样的处理程序,比如生成分析报告、监控异常行为、为用户生成个人推荐等等。

Kafka 与其他消息队列相比的不同之处在于它的数据用日志来构成。实际上,它有很多日志。Kafka 的数据流分割为分区,每个分区就是一个日志(完全序列化的消息)。不同分区彼此独立,分区间也就不需要次序保障,使得不同分区可被不同服务器处理,这对 Kafka 的扩展性非常重要。每个分区都存储在磁盘并在几个服务器间复制,持久且能承载机器故障而不会造成数据丢失。生产和消费日志非常类似于前面在数据库复制中看到的内容。

  • 每个发送到 Kafka 的消息都附加到分区的尾部,Kafka 只支持附加到日志尾部这个写入操作,不可能更改已有消息。
  • 每个分区中,消息有单向递增的偏移量(日志位置),客户端消费Kafka的消息就从某个偏移量开始顺序读取消息,这个偏移量由消费者管理。

6. 日志最佳实践

回到演讲开始所说的数据集成问题。假设你有一堆的不同数据仓库、缓存和索引需要保持彼此同步。既然我们已经看到实践中的几个日志程序示例,那么我们知道怎么去更好的构建这些系统了吗?

6.1 停用双写

首先,停止使用双写。正如讨论所得,双写可能造成数据不一致,除非你仔细考虑过程序中潜在的资源竞争和部分失败问题。请注意这个不一致性并非异步系统中常被提及的一种“最终一致”,我这里说的是永久不一致。如果双写到两个不同的数据仓库,由于资源竞争或者部分失败,造成的不一致是不会简单的自行解决。你必须采取明确的行动去搜索不匹配的数据再解决掉(这个很难,因为数据在不断地变化)。

6.2 使用日志

我们需要比双写更好的方法来让不同数据仓库中的数据保持同步。

我要提议的是,与其让程序直接写入多个数据仓库,不如让程序只附加数据到日志(比如Kafka)。所有数据的不同呈现方式,数据库、缓存、索引,都由顺序消费日志来构成。每个需要保持同步的数据仓库都是日志的独立消费者,每个消费者在日志中提取数据,每次一条记录,然后写入到自己的数据仓库。日志确保消费者看到的记录都在相同的次序;通过让写入操作在相同次序,资源竞争的问题将得以消除。这个就很像之前讲到的数据库复制。

部分失败的问题?如果其中一个仓储有问题一时半会儿不能接受写入操作将会怎么样呢?这个问题日志也解决了。每个消费者跟踪已处理的日志位置,当这个消费者的错误解决后,它可从日志中上次处理的位置开始重新处理记录。通过这样的方式,数据仓库并不会丢失任何更新,即便离线一段时间后。这对解耦系统组件很有帮助,即便一个数据仓库有问题,系统的其他部分也并不受影响。

6.2.1 日志问题 —— 延迟

日志 —— 把写入操作放入完全序列中的简单粗暴想法 —— 还有问题。只有一个遗留问题。日志的消费者都是异步更新他们的数据仓库,所以他们都是最终一致的。读取这些数据仓库就像读取数据库的跟随者,他们可能会些许延后于最新的写入,所以你不能保证所写即所读(当然并非线性问题)。

我认为,这个问题可以通过在日志顶层分层事务协议来克服,但这是个研究性领域还远未能在生产系统中广泛运用。当下,一个更好的选择是在数据库中提取日志。

更改数据捕捉,最近我就在写这方面的东西(并运用于PostGreSQL)。只要你只写入到单个数据库(不搞双写),并且从数据库中拿取写入日志(按照提交到数据库的顺序),那么它就将会和直接写入日志的方式一样发挥功效。

由于位于日志之前的这个数据库同步执行写入操作,你就可以通过它来执行要求“即时一致性”的读取操作(线性化),并强制约束(比如要求余额永不负数)。途经数据库也意味着你不必把日志当着记录系统(日志可能因为使用新技术让场景可怕)。你如果有比较了解并喜欢的已有数据库,那就可以从这个数据库中提取变化日志,也能获取面向未来架构的所有优点。在即将来临的会议演讲中我将谈及这个话题更多的细节。

7. 结尾

在演讲结束时,我给大家出一个思考题。

我们用的大多数 API 都有读写端。在RESTful 中,GET 表示读(比如无副作用的操作),POST、PUT 和 DELETE 表示写。如果只写入一个系统,这些写操作都没问题,但是有多个系统时,你一旦上线双写就将面临前面提及的问题。想象下API没有这些写入端的系统,保留所有 GET 请求,但是禁止所有 POST、PUT 和 DELETE。唯一写入系统的途径就是附加他们到日志中,并让系统去消费日志(日志必须在系统之外,然后同一个日志能有多个消费者)。

举例,想象一个 Elasticsearch 变种,你不能通过 REST API 去写文档,只能通过发送到 Kafka 来写。Elasticsearch 可以内置一个 Kafka 消费者来获取文档并添加到索引。这就可简化 Elasticsearch 的内部机制,因为它不用再担心并发控制,复制实现也会更简单,并且和其他消费同一个日志的系统也没啥关系。

我最喜欢面向日志架构的功能是,如果构建一个衍生数据仓库,你就开启一个新的从头消费的消费者,通过日志历史把所有写入应用于数据仓库。当消费到最后时,你就能有个全新的数据视图,而且简单地继续消费日志就能让它保持更新。这让已有数据试验新的呈现方式变得非常容易,比如换种索引方式。你可构建已有数据的实验性新的索引或者视图而不妨碍任何已有数据。如果测验行得通,你可转移用户读取新的视图;如果不,丢弃他们即可。这就给你了极大的自由来试验和调整你的程序。

用日志构建坚固的数据基础设施/为什么双写不好相关推荐

  1. 华为发布智能数据解决方案FusionData,重定义数据基础设施,释放数据价值

    [中国,北京,2019年6月5日]华为在北京发布智能数据解决方案FusionData,支持智能的数据全生命周期管理:从数据接入.数据处理和数据使能三个层面,重定义数据基础设施,帮助客户打造领先的智能数 ...

  2. 攀登数据基础设施高峰,这是一场勇敢者的游戏

    关注我们牛年牛气冲天 这世上还有比珠穆朗玛更难攀登的山峰吗?或许它更高.更难逾越,那就是数据高峰. 3月23日,华为宣布,其创新数据基础设施体验中心及行业创新体验中心正式落户成都.该体验中心就好像是在 ...

  3. 创新数据基础设施高峰论坛,构建安全高效的城市智慧底座 ​

    数字经济时代,新兴技术层出不穷,云.5G.AI等不断的推动着全球化进程,将人类推向万物互联的智能世界.在数字化转型的道路上,海量数据如何高效处理,让数字价值最大化?万物互联的时代下,智能协作在各场景下 ...

  4. 开启认知智能战略,加速爱数成为大数据基础设施领航者

    从2006年成立推出数据备份的传统业务,到2021年最新发布认知智能战略.现在,爱数已经转型到了大数据基础设施提供商的全新定位,并立志成为该行业的领航者. 历经十五年的风风雨雨,对于任何一个有志于此的 ...

  5. 基于Kubernetes构建现代大数据管道

    \ 看新闻很累?看技术新闻更累?试试下载InfoQ手机客户端,每天上下班路上听新闻,有趣还有料! \ \\ 容器技术(如Kubernetes)为现代数据管道的实现带来了可能性.来自lguazio的大数 ...

  6. 定义下一代存储,打造全新一代数据基础设施

    简介: 智能时代,阿里云正重新定义下一代存储,打造全新一代数据基础设施.在未来,数据势必呈爆发式地增长,那么对于存储的性能,必然会提出更高.更严苛的要求.此次直播阿里云将为大家带来7款存储产品新功能的 ...

  7. 波卡生态数据基础设施组件Kylin Network与借贷平台DefiCliq达成合作

    波卡生态数据基础设施组件Kylin Network宣布与基于波卡Substrate构建的DeFi借贷平台DefiCliq建立合作关系,将为DefiCliq金融服务提供进一步的安全性和准确性,为其用户提 ...

  8. 服务端设置忽略更新_深入理解Kafka服务端之日志对象的读写数据流程

    一.场景分析 在前面几篇分析过,Log类用于管理服务端日志相关的各种操作,如: 日志段管理:滚动生成新日志段.组织并管理分区下的所有日志段等 关键偏移量管理:如LogStartOffset.LEO等 ...

  9. 数据基础设施,证券行业跑赢时代的新引擎

    人们往往高估一件事情的短期影响,而忽视了它所带来的长期影响.去年疫情爆发,短期内对经济产生了巨大的冲击,影响到包括证券等多个行业的日常业务:但另一方面,也直接驱动着业务数字化.线上化的步伐,证券等行业 ...

最新文章

  1. vivado烧录flash过程
  2. 疑难杂症——bash: /dev/null: Permission denied
  3. CSS3初体验之奇技淫巧
  4. 03-spring bean
  5. Editplus查找替换的正则表达式应用说明
  6. python项目NoReverseMatch: Reverse for ‘topic‘ with arguments ‘(‘‘,)‘ not found解决方法
  7. Android鬼点子 100行代码,搞定柱状图!
  8. kafka 削峰_从面试角度一文学完 Kafka
  9. 事业单位计算机技术岗工资,事业单位新入职的人员在管理岗位和技术岗位工资待遇是否有区别?...
  10. 从逻辑回归到最大熵模型
  11. 不同类别游戏音效的特点
  12. 美股相关交易规则与业务知识
  13. 硬盘变成Raw格式 与 移动硬盘报I/O错误问题
  14. QT实现OPC_UA客户端程序以及与OPC_UA服务器通信
  15. Qt+大恒相机+OpenCV+MinGW界面开发
  16. 六级答案已出,计算机er要考多少分才能保个好学校?
  17. JackKnife开发专题-性能强大的ORM框架- JackKnife-ORM
  18. 如何一键关闭所有视窗?
  19. boss直聘一句话介绍优势_05.29 | 早读英语角 | 自我介绍,如何一句话拉近关系?...
  20. GSM/GPRS+GPS模块SIM808

热门文章

  1. php处理大数据量数据的思路
  2. Ubuntu Fcitx 繁简输入切换
  3. 利用Sympy计算sin1°的最小多项式
  4. 如何帮助传统批发零售业转型线上发展
  5. 加一度推荐超实用的7大拓词工具
  6. java安装以及配置环境变量详细步骤
  7. Linux 时间、时区设置
  8. App地推效果差?你可能缺少渠道效果统计
  9. 【计算机专业毕设之基于python猫咪网爬虫大数据可视化分析系统-哔哩哔哩】 https://b23.tv/jRN6MVh
  10. Blender图解教程:给苏珊一头美腻的秀发 之 用粒子实现写实风头发