前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >关于大数据和数据库的一篇学习笔记

关于大数据和数据库的一篇学习笔记

作者头像
哒呵呵
发布于 2020-07-27 15:20:45
发布于 2020-07-27 15:20:45
7940
举报
文章被收录于专栏:鸿的学习笔记鸿的学习笔记

这篇文章来自于我非常崇敬的一个学者 Martin Kleppmann(下文用马丁指代) 的一篇访谈,包含了很多有趣的观点,比如为什么要写Designing Data-Intensive Applications(缩写为DDIA)这一本书,关于计算机行业专有名词乱用的点评,对分布式系统里广为流传的 CAP 定理的批评以及讨论了事件溯源(Event Sourcing)这种架构的适用场景和缺点,最后还附带了对计算机行业里去中心化趋势的看法。

Martin Kleppmann 是英国剑桥大学的分布式系统研究员,也是Designing Data-Intensive Applications一书的作者,Designing Data-Intensive Applications是目前数据领域的技术书籍里写的最好的一本书(我认为不带之一),这本书行文流畅,见解深刻,感兴趣的可以找到英文原版或者是中文翻译版读一读。

本文在翻译过程中们,删除了无意义的谈话,聚焦于核心观点。原文参见:https://martin.kleppmann.com/2019/06/27/hydra-interview.html

为什么要写 Designing Data-Intensive Applications 这一本书

得益于计算机行业的蓬勃发展,各种各样的软件、产品,无论是开源或者是闭源,总归是多了起来。相比于二十世纪的程序员,现在的程序员不再为了某个问题而需要“造轮子”(发明新的工具),GitHub 等网站上充斥着各种各样的工具,甚至于同一种场景会有五六种工具可以选择,以数据系统而言,数据库有非关系型数据库和关系型数据库,非关系型数据库中还有 Hbase、Cassandra等等,关系型数据库有 Oracle、MySQL等等,除了数据库外,数据系统里还有各式各样的流处理工具(Flink、Spark Steaming等等)、批处理工具(Spark、 MapReduce),让人眼花缭乱。程序员幸福于不需要花时间开发工具而苦恼于工具众多,不知道选什么才是最适合自己面临的业务场景。

然而现在的很多计算机书籍都没有回答好这个问题,比如,当读者在阅读有关 Cassandra 的书籍时,这本书会告诉你 Cassandra 是如何优秀,但是不会说 Cassandra 有哪些不适合的场景。DDIA 这本书就是告诉大家在构建一个大型的系统应该考虑哪些方面,然后如何确定哪种类型的技术可以帮你解决特定的问题,哪种技术不适合你需要解决的问题。

这就是 DDIA 这本书的意义。不过话说回来,计算机行业里没有万能的银弹,也就是说没有任何一种技术可以完美的解决所有的问题,选型就特别重要了。

反对计算机行业里的虚假宣传

这是我们行业存在的一个问题:当人们在谈论某种工具时,就会大肆宣传这种工具,这是可以理解的,因为这些工具是由公司发明的,显然这些公司希望能够推广这些产品,然后就会派人参加各种会议,说它的产品是多么的有些。有时候这种宣传会伪装成技术分享,但实际上依然是一种销售活动。作为行业内的人,我们需要正确地了解某些产品的优缺点,部分内容需要一种通用专有名词去描述,没有通用的专有名词的话,就无法放在同一维度下进行比较。当然,除了通用的专有名词外,还需要一些方法去推理并获知某些技术的优缺点。

所以对于程序员而言,了解某一种软件就必须认真仔细地去阅读官方文档,最好能够去部署并使用这个软件,因为对于官方文档而言,这个软件的开发者往往不会告诉你这个软件的真正缺点以及不适用场景,这个时候就需要个人分辨了。

CAP定理的问题

我认为在很多情况下,在计算机行业里,一项技术只能做某一件事而不能做另一件事,不是所谓的错误,而是某一种的权衡。但是 CAP 就是一个错误,而不是某种权衡。

CAP 定理从根本上讲是不够好的,而且也没有用。每当人们使用 CAP 定理证明它的架构设计或者是某种决策合理时,我认为他们理解错了 CAP 定理。CAP 作为一个定理的问题在于它只是说明了一件显而易见的事实,而且只讨论了一个被定义的非常狭隘的一致性模型,学术名叫线性化,以及一个被定义的非常狭隘的可用性模型:你希望每一个副本对于读取和写入而言都是完全可用的,即使这些副本无法与其它副本通信。这些定义是非常合理的,但是非常狭隘,许多应用程序根本就不能简单的划分到非常严格的一致性和可用性情况。这个时候,CAP 定理并不会告诉你更多的有用的信息,仅仅只是空洞的描述而已。

在 CAP 定理的严格定义下,有许多技术既不一致又不可用,实际上只是P!不是CP,不是CA,不是AP,只是P而已。从来就没有人这么说过,因为这看起来非常糟糕,但老实说,这有可能是一个非常合理的设计决策。实际上,我认为 CAP 定理没有任何帮助的原因在于:CAP 定理有很大的一部分只是很空洞的讨论。

事件溯源的适用场景

在读这个章节前可以先读下马丁写的Online Event Processing

首先事件溯源(Event Sourcing)是一种用于数据建模的机制:如果开发者的数据库有很多不同的表,并且开始无法管理好它,这些表又被很多事务更新。那么事件溯源这种方式就可以让开发者更加容易数据建模,因为事件可以非常直接的表示业务级别,用户正在操作什么,并且这种操作带来的后果是什么,也许是更新各种表。实际上开发者可以借由事件溯源将事件(动作)本身与其对下游的影响分隔开来。

这就是为什么可以使用 Kafka 之类的系统去构建一个高可用系统。从某种意义上来说,如果你在使用 Kafka 不一定在使用事件溯源方式,也有可能在使用事件这个概念。相反,你无需为了使用事件溯源而专门的使用 Kafka,你也可以使用特定的数据库。

当你想使用 Kafka 可能只是想方便地使用他的可扩展性:比如当你收到了很多数据,在单节点数据库上是很难处理的,就必须采用分区,并使用像 Kafka 这种事件日志方式去把工作分担到各个机器上。当你想集成多个不同类型的数据库时,例如不仅要更新关系型数据,也要更新 Elasticsearch 这样的全文搜索索引或者是像 Redis 这样的缓存系统,并且你希望一个事件在各个不同的系统上产生的效果是一样的,那么 Kafka 这种工具就很有用。

至于何时使用事件溯源,有一个很简单的原则:那就是使用最简单的方式去实现业务。例如当你的业务仅仅只是对数据库执行增删改查的操作,那么仅仅使用关系型数据库就好了。但是当你发现自己单纯地使用数据库这种方式时已经无法解决业务问题时,比如数据模型过于复杂,那么使用事件溯源的方式就很好了。

至于可扩展性的考虑,如果数据量足够少,那么就可以使用 PostgreSQL。但是如果数据量太大,那么就可以考虑 Kafka 这样的方式了。一切以实用作为标准。

事件溯源遇到的问题

当有很多消费者同时消费 Kafka 里的事件,比如当一个新的文档出现时,基于 Elasticsearch 的搜索系统需要让这文档可搜索,还需要将这文档放入到基于Memcached的键值缓存系统缓存数据,并且还需要更新关系型数据库里的数据。这个文档可以是任何事情,而且这些消费者系统都需要同时工作。这个时候就很需要事件溯源这种方式了。

这确实是一个挑战,实际上这需要跨不同类型的存储系统的“因果一致性”。当一个系统包含某个数据时,那么另一个系统也需要能看到这些数据,可惜的是,将不同存储系统之间的因果关系整合在一起是非常困难的,这不是事件溯源这种方式的错,而是无论使用哪一种方式或方法,都会遇到这个问题。

因为两种不同系统即使是同时写入,因为网络延迟的原因,导致到达系统的时间会略有不同,并且实际写入系统的时间也会略有不同。从而影响两个系统的使用者的观察状态。目前已经有一些人在研究解决这些问题。

一个很好的解决办法是,建立搜索引擎、缓存系统和数据库统一的一致性快照。如果仅仅只使用数据库的话,这种方式叫做快照隔离。快照隔离很重要的点在于任何人读取数据库,都只会看到独属于它的一份数据库数据库的副本,任何时候查看数据,都只是看到数据库某个时刻的快照状态。即使这个时候,数据被某一个事务更改了,实际上你依然会看到较旧的数据,因为这个较旧的数据也构成了一致性快照的一部分。

此时你想为 Elasticsearch 和 Memcached 这两个系统去建立一致性的快照是不可能的,因为像 Memcached,Redis 和 Elasticsearch 这种系统时没有有效的机制去建立可以和不同存储系统向匹配的快照,每个系统都只会考虑自己的情况,只能看到最新的数据,而不能看到较旧的数据。这就导致了不一致的情况。

对于这种情况,比较束手无力,因为要建立一致性快照这种事情需要对存储系统的底层代码进行改造,并且这种改造代价不能太大,因为有可能几秒钟就需要这样的一次快照。现在还没有一个存储系统满足这种条件,因此这是一个非常有趣的主题。

由事件溯源谈全局的快照隔离

全局的快照隔离实际上就是共享的多版本并发控制(https://en.wikipedia.org/wiki/Multiversion_concurrency_control)。

就像分布式事务系统一样。XA 分布式事务可以为解决这种问题提供一些帮助,但是就目前的情况而言,XA 并不适合,因为 XA 是一种基于锁的并发控制方式,这意味着当你在读取数据时,就必须对其进行锁定,让其他人无法在锁定的时候对数据进行修改。这导致了这类基于锁的并发控制方式的性能很差,所以在实践中还没有任何系统实际的使用它。换句话说,如果没有这种锁定的话,那么 XA 分布式事务也不会提供必要的隔离行为。所以我们需要的是一种新的可以提供跨不同系统的快照隔离机制,但是现在还没有看到实现这一目标的东西。

例如在微服务的宣传中,构建微服务的方式应该是每个微服务都有着自己的存储系统,自己的数据,而且另一个微服务不能直接访问另一服务的数据,因为这会破坏服务的封装。所以,每个服务仅仅只会管理自己的数据。

例如,您有一个用于管理用户的微服务,并且有一个保存着用户信息的数据库,其他所有想了解有关用户的信息的人都必须通过这个用户的微服务。从封装的角度来看,这很不错:因为你对其他服务隐藏数据库架构的详细信息。

但是,从不同服务之间的一致性的角度来看,现在遇到了一个大问题:我们可能在两个相互依赖的不同服务中拥有相同数据,并且在时间上,可能会轻易地以一项服务稍稍领先于另一项服务而告终,然后可能会导致有人读取不同的服务,从而导致结果不一致。而且我认为目前没有任何人构建微服务可以解决这个问题。

去中心化、边缘计算、存储

目前我们过于依赖服务器和集中化了。想一下,最初的因特网本来就是一个非常灵活的网络,可以通过数种不同的路径发送数据包,然后依然可以到达目的地。而且当一枚核弹袭击了某一个特定的美国城市时,其它的网络依然可以绕过系统的故障部分正常运行。这就是所谓的冷战设计。

现在我们将所有的东西都存储在云上了,比如基本上所有的东西都要经过 AWS 的一个数据中心。我们放弃了去中心化的想法,而是将所有的内容都存储在服务器上,也就是集中化了。所以我现在希望回到去中心化的路上,从某种意义上来说,将控制数据的权力从服务器交还给用户。

我想补充的一件事是很多人都在谈论去中心化,实际上谈论的是加密货币,因为他们还试图通过一种去中心化的方式将控制权从中央机构(如银行)转移到网络中。但这并不是我真正感兴趣的去中心化:因为我发现这些加密货币实际上仍然非常集中,在某种意义上,如果您要进行比特币交易,则必须使用比特币网络。因此一切东西都集中在这个特定网络上。虽然它的构建方式是分散的,没有单个控制节点,但是整个网络极其集中,因为必须通过该网络进行任何交易,无法通过其他方式。所以我觉得它仍然是一种集中化形式。

在使用加密货币的情况下,这种集中化可能是不可避免的,因为必须要用一个网络就达成的交易和未达成的交易达成共识。而这正是比特币网络所做的。但是有许多应用程序不需要像区块链这样的东西,因为更为灵活的数据模型。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鸿的笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
力扣739.每日温度
本题很容易想到的一种解法是,使用两层for循环,计算每个位置后面第一个比自己大的元素位置。代码如下:
ccf19881030
2023/04/06
2020
力扣739.每日温度
LeetCode 739. 每日温度(单调栈)
根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
Michael阿明
2020/07/13
3090
LeetCode 739. 每日温度(单调栈)
【算法/训练】:单调队列&&单调栈
当01100符合时,窗口外面的肯定也符合要求 注意:循环截止条件为 right < n - 1.
IsLand1314
2024/10/15
1390
【算法/训练】:单调队列&&单调栈
【代码随想录】二刷-单调栈
单调栈 739. 每日温度 // 时间复杂度:O(n) // 空间复杂度:O(n) class Solution { public: vector<int> dailyTemperatures(vector<int>& t) { stack<int>st;// 存下标 int n = t.size(); vector<int>ret(n,0); st.push(0); for(int i = 1; i < n;i++){
半生瓜的blog
2023/05/13
1630
单调栈解题模板秒杀三道算法题
单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。
labuladong
2021/09/23
4990
LeetCode 1793. 好子数组的最大分数(单调栈)
一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1) 。一个 好 子数组的两个端点下标需要满足 i <= k <= j 。
Michael阿明
2021/09/06
4160
【leetcode刷题】T27-每日温度
Given a list of daily temperatures T, return a list such that, for each day in the input, tells you how many days you would have to wait until a warmer temperature. If there is no future day for which this is possible, put 0 instead.
木又AI帮
2019/07/17
6280
C++ 线性数据结构系列之低调而强大的单调栈
单调栈是在栈基础上进行变化后的数据结构。除了遵循栈的先进后出的存储理念,在存储过程中还需保持栈中数据的有序性。
一枚大果壳
2023/08/18
3070
C++ 线性数据结构系列之低调而强大的单调栈
【LeetCode热题100】【栈】每日温度
用单调栈记录下标,先将头个温度下标压入栈,判断栈顶温度是否比当天温度低,低则更新低温的天数弹栈,高则继续压栈,这样栈里面的温度必定是递减的,一遇到温度高的便可同步更新低温的天数
叶茂林
2024/04/08
1170
PAT 1051 Pop Sequence (25分) 模拟入栈
Given a stack which can keep M numbers at most. Push N numbers in the order of 1, 2, 3, ..., N and pop randomly. You are supposed to tell if a given sequence of numbers is a possible pop sequence of the stack. For example, if M is 5 and N is 7, we can obtain 1, 2, 3, 4, 5, 6, 7 from the stack, but not 3, 2, 1, 7, 5, 6, 4.
vivi
2020/07/14
5380
Leetcode 856. Score of Parentheses 括号得分(栈)
简而言之,遇到右括号就一直出栈并累加到一个值直到遇到左括号,这个累加值就表示这对括号的得分。如此周而复始到字符串结尾即可。
racaljk
2018/12/12
7210
LeetCode-739-每日温度
请根据每日气温列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
benym
2022/07/14
1570
LeetCode 456. 132模式(逆序遍历+单调栈)
给定一个整数序列:a1, a2, …, an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。 设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。
Michael阿明
2020/07/13
7450
LeetCode的第230场周赛题解
【GiantPandaCV导语】这是LeetCode的第230场周赛的题解,本期考察的知识点有暴力,搜索,贪心,单调栈等等。
BBuf
2021/03/09
3000
【题解】糟糕的一天
农夫约翰有N(N≤80000)头奶牛正在过乱头发节。每一头牛都站在同一排面朝东方,而且每一头牛的身高为
fishhh
2022/08/31
4260
【题解】糟糕的一天
C++13-STL模板-栈stack
在线练习: http://noi.openjudge.cn/ https://www.luogu.com.cn/
IT从业者张某某
2023/10/16
1880
C++13-STL模板-栈stack
单调栈总结_进栈和出栈的算法思想
单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性。 假设下图是一个栈内元素的排列情况(单调递增的栈):
全栈程序员站长
2022/11/09
3420
单调栈总结_进栈和出栈的算法思想
C++STL模板库适配器之stack容器
Stl中的适配器,有栈 (stack) 队列 queue 根priority_queue 适配器都是包装了 vector list deque等顺序容器. 也可以看做是由这些容器实现的一个新的容器. 适配器没有提供迭代器.也不能同事插入或者删除多个元素.
IBinary
2019/05/25
4780
剑指offer--栈的压入、弹出序列
题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
AI那点小事
2020/04/20
3310
利用栈转换中缀表达式到后缀表达式
本篇是栈篇的最后一篇,记录一下如何用栈实现中缀表达式转后缀表达式。 先举例一个后缀表达式9 3 1 - 2 * + 5 2 / + 他的中缀表达式是9+(3-1)*2+5/2 首先我们要找到这个表达式的优先级优先级最高的是括号 其次是乘法和除法再然后是加法 那么如何用栈来演示呢。 之前那个表达式很长难以理解,我们用A+(B*C)很明显B*\C的优先级高,所以把*置后 然后 A的操作数就变成了BC*
用户7272142
2023/10/11
2480
利用栈转换中缀表达式到后缀表达式
相关推荐
力扣739.每日温度
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档