《数据密集型应用系统设计》整理11

第十二章 数据系统的未来

If a thing be ordained to another as to its end, its last end cannot consist in the preservation of its being. Hence a captain does not intend as a last end, the preservation of the ship entrusted to him, since a ship is ordained to something else as its end, viz. to navigation. (Often quoted as: If the highest aim of a captain was the preserve his ship, he would keep it in port forever.)
—St. Thomas Aquinas, Summa Theologica (1265–1274)

数据集成

对于给定的任何问题,都有多种解决方案,而这些解决方案都由各自优缺点和折中之处。
软件的实现必须选择一种特定的方法,通常一种实现很难同时兼具鲁棒与性能。
因此你不可避免的要将几个不同的软件组合在一起,以提供应用程序的功能性。

采用派生数据来组合工具

使用数据流:通过单个系统来决定所有输入的写入顺序,那么以相同的顺序处理写操作就可以更容易的派生出数据的其他表示形式。
根据事件日志来更新一个派生数据系统通常会比较好实现,并且可以实现确定性和幂等性。

全局的限制:系统小的时候,构建一个完全有的事件日志是可行的。系统大了之后,瓶颈就出现了:

  • 单主节点有性能瓶颈,需要主节点来决定完全有序的日志的顺序;分拆为多主后,则两个不同分区中的事件顺序变得不明确了。
  • 服务器分布在多个数据中心,数据中心之间同步效率很低,因此通常在每个数据中心有个主节点。导致两个数据中心的顺序不确定。
  • 微服务的每个服务与其持久化状态独立部署,来自两个不同服务的事件顺序不清楚。
  • 某些应用程序在客户端维护一些状态,客户端和服务端可能看到不同的事件顺序。

顺序事件以捕获因果关系:如果不支持全序排序,则有因果依赖的事件会导致一些微妙的情况。

  • 逻辑时间戳可以在无协调者的情况下提供全序关系,但是需要接收者处理乱序事件以及额外的元数据;
  • 记录一条事件来标记用户在做决定前看到的系统状态,并给该事件一个唯一的标识符,后续的事件可以通过引用该事件标识符来记录因果关系;
  • 冲突解决算法可以处理异常顺序的事件。

批处理和流处理集成

数据整合的目标是确保数据在所有正确的地方以正确的形式结束,而批处理和流处理则是实现这一目标的有效工具。

批处理和流处理有许多共同的原则,而根本区别在于流处理器运行在无界数据集上。

在需要维护派生数据时,批处理和流处理都可以用上。流处理可以将输入的变化数据迅速反映在派生视图中,而批处理则可以反复处理大量的累积数据,以便将新视图导出到现有数据集上。

Lambda架构
批处理重新处理历史数据,流处理来处理最近的更新,比较不错的解决方案是Lambda架构。

核心思想是进来的数据以不可变事件形式追加写到不断增长的数据集,类似于事件溯源。

lambda建议并行运行两个不同的系统,一个批处理比如hadoop,一个流处理比如strom。

在lambda中,流处理器处理事件并快速产生对视图的近似更新,批处理器处理同一组事件并产生派生视图的校正版本。
因为流处理相对不可靠但是快速,批处理更简单不易出错但是比较慢。

lambda推广了将派生视图用于不可变事件流以及必要时重新处理事件的原则。

不足:

  • 流处理和批处理器运行同样的处理逻辑,增加了调试和维护的复杂度;
  • 流处理和批处理各自产生单独的输出,需要合并才能响应客户请求;
  • 在大型数据集上重新处理整个历史数据集代价太高,因此批处理器通常设置为处理增量批处理。这会引发处理之后消息和处理跨批量边界的窗口等问题。

统一批处理和流处理
最近的发展使得Lambda可以充分发挥优点而规避缺点,那就是允许批处理和流处理在同一个系统实现。

在同一个系统中统一批处理和流处理需要以下功能,而且现在正在慢慢普及:

  • 支持相同的处理引擎来处理最新事件和历史回放事件。
  • 支持只处理一次语义。
  • 支持一句事件发生事件而不是处理时间来进行窗口化,因为重新处理的时候历史事件的处理时间毫无意义。

分拆数据库

本书套路哪里数据库提供的各种功能,包括:

  1. 二级索引:根据字段值搞笑的搜索所有记录;
  2. 实体化视图:预先计算查询结果并将其缓存;
  3. 复制日志:使多节点上数据副本保持最新;
  4. 全文搜索索引:在文本中进行关键字搜索,并且内置于某些关系型数据库。

创建一个索引CREATE INDEX 的时候,数据库扫描表的一致性快照,挑选出所有被索引的字段值,进行排序,然后得到索引。接下来,必须处理从一致性快照创建以来所累积的写入操作。

整个组织的数据流开始变得像一个巨大的数据库,每当批处理、流或者ETL将数据从一个位置传输到另一个位置,它就像数据库子系统一样需要保持索引或者实体化视图至最新状态。

批处理和流处理就像触发器,存储过程和实体化视图维护相关实现。

没有一个统一的数据模型或存储格式适用于所有的访问模式,未来可能会由两种途径把不同的工具组合成一个紧密结合的系统:

  1. 联合数据库:统一读端;
  2. 分离式数据库:统一写端。

观察派生状态

-----2019-07-28---2.29.15
写路径可以看做是预计算的一部分,即一旦数据进入,即刻完成,无论是否有人要求访问它。而过程中的读路径则只有当明确有人要求访问时才会发生。

全文搜索索引:写路径更新索引,读路径搜索关键字索引。

我们甚至可以将此数据流一直传送到显示数据的最终用户设备,从而构建动态更新的用户界面以反映数据更改并支持离线使用。React,Flux和Redux工具链,已经支持从服务器端订阅代表用户输入的事件流来管理内部的客户端状态。

端到端的正确性

底层(TCP)的可靠性功能本身不足以确保端到端的正确性。
事务的处理代价很高,特别是在涉及异构存储技术时。
因此,探索更好的容错抽象非常必要:

强制约束

使用唯一性约束,比如用户名必须唯一,两个人不能预订同一个航班等。

需要达成共识,常见方法是单节点做决定。采用分区的多节点可以提高唯一性检查的扩展性,但是无法支持异步的多主节点复制。

日志机制可以确保所有消费者以相同的顺序查看消息。不仅适用于唯一性约束,还适用于许多其他类型的约束。

多分区请求处理:划分为两个不同分区的处理阶段,并使用端到端的请求ID。比如先提交到日志,然后流处理读取日志,发出不同的消息。

时效性与完整性

一致性=时效性+完整性

时效性:确保用户观察到系统的最新状态。
完整性:避免数据损坏,即没有数据丢失,也没有互相矛盾或错误的数据。

可靠的流处理系统可以在不需要分布式事务和原子提交协议的情况下保持完整性:

  • 将写入操作的内容表示为单条消息,可以轻松采用原子方式编写;
  • 使用确定性派生函数从该条消息派生所有其他状态的更新操作;
  • 通过所有这些级别的处理来传递客户端生产的请求ID,实现端到端重复消除和幂等性;
  • 消息不可变,并支持多次重新处理派生数据,从而使错误恢复变得更容易。

信任,但要确认

使用审计来确认数据的完整性以及检验数据是否发生破坏。

做正确的事情

软件工程师如果只专注于技术而忽视其后果是不够的,道德责任也是我们要担起的责任。

预测性分析:在很多国家,司法制度会在证明有罪之前推定当事人无罪。然而,自动化的系统则有可能系统的、任意的排除某个人参与社会活动,而且是在这个人没有任何犯罪证据的情况下。

预测分析系统是基于过去而推断,带有偏见。如果我们希望未来比过去更好,那么就需要道德想象力。

数据隐私与追踪:当个人的数据被收集来使用的目的不是为了用户的利益,用户得到免费服务,但是对用户的追踪被服务于资助广告客户的需求,这种关系可以用监视来形容。

对于不同意被监视的用户,唯一真正的选择就是不使用服务。但是这个选择也不是免费的:如果一项服务非常受欢迎以至于“被大多数人认为是基本的社会参与所需要的”,那么指望人们选择退出这项服务是不合理的。

comments powered by Disqus