本文是《数据密集型应用系统设计》(DDIA)的读书笔记,一共十二章,我已经全部阅读并且整理完毕。
采用一问一答的形式,并且用列表形式整理了原文。 笔记的内容大概是原文的 1/5 ~
1/3,所以你如果没有很多时间看书的话,看我的笔记也就够了!
之前的章节在讨论现状;
本章讨论未来。
我们的目标是,发现如何设计出比现有应用更好的应用 —— 健壮,正确,可演化,且最终对人类有益。
随着数据不同表示形式的增加,集成问题变得越来越困难。
某人认为鸡肋而毫无意义的功能可能是别人的核心需求。当你拉高视角,并考虑跨越整个组织范围的数据流时,数据集成的需求往往就会变得明显起来。
当需要在多个存储系统中维护相同数据的副本以满足不同的访问模式时,你要对输入和输出了如指掌:
几种写入方式:
与分布式事务相比,使用衍生数据系统的方法如何?
对于足够小的系统,构建一个完全有序的事件日志是完全可行的(如单主数据库)。但是大系统会出现麻烦:
共识算法存在的问题?
数据集成的目标是,确保数据最终能在所有正确的地方表现出正确的形式。
这样做需要消费输入,转换,连接,过滤,聚合,训练模型,评估,以及最终写出适当的输出。
批处理和流处理是实现这一目标的工具。
如何迁移系统?
什么是 Lambda 架构?
缺点?
最近的工作使得 Lambda 架构摒弃缺点。
在一个系统中统一批处理和流处理需要以下功能,这些功能也正在越来越广泛地被提供:
Unix发展出的管道和文件只是字节序列,而数据库则发展出了SQL和事务。
这两种哲学的矛盾,希望能各取其美。
在本书的过程中,我们讨论了数据库提供的各种功能及其工作原理,其中包括:
各种数据库产品发展在未来将会把我们带到哪里?如果我们从没有适合所有访问模式的单一数据模型或存储格式的前提出发,我推测有两种途径可以将不同的存储和处理工具组合成一个有凝聚力的系统:
联合数据库:统一读取
分拆数据库:统一写入
怎么写入到几个存储系统中?
基于日志的集成的一大优势是各个组件之间的松散耦合(loose coupling),这体现在两个方面:
什么时候拆分系统,什么时候集成系统?
用于组成数据系统的工具正在变得越来越好,但我认为还缺少一个主要的东西:我们还没有与Unix shell类似的分拆数据库等价物(即,一种声明式的、简单的、用于组装存储和处理系统的高级语言)。
即使是电子表格也在数据流编程能力上甩开大多数主流编程语言几条街:某一个单元格可以使用公式对其他列求和。
当一个数据集衍生自另一个数据集时,它会经历某种转换函数。如:
但是,当创建衍生数据集的函数不是像创建二级索引那样的标准函数时,需要自定义代码来处理特定于应用的东西,数据库就做不好了!!
为什么要让计算和存储分离?
应用代码和状态怎么联系?
维护衍生数据不同于执行异步任务:
服务的优点?
数据流中组装流算子与微服务方法的区别:
数据流系统的优点:
什么是写路径?
更新搜索索引的例子:
什么是读路径?
写路径和读路径的区别于联系:
如何加速查询?
这些操作起到了什么作用?
为什么客户端有状态?
怎么做到客户端有状态?
怎么做到服务器推送给客户端?
什么含义?
编程语言支持了端到端的写路径:
为何不使用端到端?
为什么说读也是事件?
为什么要有多分区数据处理?
多分区数据处理举例?
为什么需要正确?
虽然数据库有较强的可靠安全属性,但是并不能保证数据没有损失或损坏:还要考虑应用代码。
举一个可能发生数据损坏的例子:
如何解决?
怎么抑制重复?
下面的例子,如果客户端没收到转账成功的响应,那么可能再次提交,导致转了 22$。
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance + 11.00 WHERE account_id = 1234;
UPDATE accounts SET balance = balance - 11.00 WHERE account_id = 4321;
COMMIT;
不能依赖网络实现幂等,需要考虑端到端(end-to-end) 的请求流。
在数据库上可以对此标识符进行唯一的标记,防止重复插入:
ALTER TABLE requests ADD UNIQUE (request_id);
BEGIN TRANSACTION;
INSERT INTO requests
(request_id, from_account, to_account, amount)
VALUES('0286FDB8-D7E1-423F-B40B-792B3608036C', 4321, 1234, 11.00);
UPDATE accounts SET balance = balance + 11.00 WHERE account_id = 1234;
UPDATE accounts SET balance = balance - 11.00 WHERE account_id = 4321;
COMMIT;
抑制重复事务的这种情况只是一个更普遍的原则的一个例子,这个原则被称为端到端原则(end-to-end argument):
只有端到端的安全,才能抑制重复事务。
我们只需要记住,低级别的可靠性功能本身并不足以确保端到端的正确性。
在端到端去重之外,我们来关注其他的约数。特别关注一下唯一性约束。
怎么实现?
怎么使用日志消息传递中实现唯一性?
当涉及多个分区时,确保操作以原子方式执行且同时满足约束就变得很有趣了。
如果一个操作需要三个分区:一个包含请求ID,一个包含收款人账户,另一个包含付款人账户,咋办?
有没有更好的方法?
但事实证明,使用分区日志可以达到等价的正确性而无需原子提交:
因为有确定的 ID 的存在,那么失败重启可以从断点恢复,如果重复提交可以用 ID 去重。
一致性要同步吗?
更一般地来讲,我认为术语一致性(consistency) 这个术语混淆了两个值得分别考虑的需求:
及时性(Timeliness)
完整性(Integrity)
完整性意味着没有损坏;即没有数据丢失,并且没有矛盾或错误的数据。衍生数据也必须都正确。
结论:
数据库、流系统的正确性?
怎么保证完整性?
为了达成这种正确性,我们组合使用了多种机制:
在执行唯一性约束时,许多真实世界的应用实际上可以摆脱这种形式,接受弱得多的唯一性:
两个现象:
总之:
所以:
另一种协调与约束的角度是:
我们所有关于正确性,完整性和容错的讨论都基于一些假设,假设某些事情可能会出错,但其他事情不会。我们将这些假设称为我们的系统模型(system model)。
这些假设相当合理,但是错误是个概率问题。
软件有存在Bug的风险,Mysql 也可能有 bug。
ACID 也可能有 bug。
错误使用数据库,也可能导致数据出问题。
数据有概率损坏,检查数据完整性称为审计(auditing)
定期审计数据和备份。
预测性分析系统只是基于过去进行推断;
推荐算法,使人进入信息茧房、回音室;
反馈算法,导致恶性循环,增大贫富差距。
并不是所有的数据收集都称得上监视,但检视这一点有助于理解我们与数据收集者之间的关系。
没有单种工具能高效服务所有可能的用例,因此应用必须组合使用几种不同的软件才能实现其目标。
通过使这些衍生和转换操作异步且松散耦合,能够防止一个区域中的问题扩散到系统中不相关部分,从而增加整个系统的稳健性与容错性。
将数据流表示为从一个数据集到另一个数据集的转换也有助于演化应用程序。
这些过程与数据库内部已经完成的过程非常类似,因此我们将数据流应用的概念重新改写为,分拆(unbundling) 数据库组件,并通过组合这些松散耦合的组件来构建应用程序。
衍生状态可以通过观察底层数据的变更来更新。
接下来,我们讨论了如何确保所有这些处理在出现故障时保持正确。
通过围绕数据流构建应用,并异步检查约束,我们可以避免绝大多数的协调工作,创建保证完整性且性能仍然表现良好的系统,
最后,我们退后一步,审视了构建数据密集型应用的一些道德问题。
由于软件和数据对世界产生了如此巨大的影响,我们工程师们必须牢记,我们有责任为我们想要的那种世界而努力:一个尊重人们,尊重人性的世界。我希望我们能够一起为实现这一目标而努力。