为什么数据库扩容很难?

超级欧派课程 2024-03-08 00:55:30

关于数据库扩容,有一个苛刻的事实常常被忽视。

事实:扩大您的数据库读取是容易的。麻烦在于您想要扩大写入!

为什么扩大写入复杂呢?

要扩大读取,您只需将数据复制到多个节点。

Leader节点处理写入请求,Followers节点处理读取。

确实,领导者和跟随者之间存在一些复制延迟,但你可以处理这个问题。

然而,扩大写入操作则是完全不同的比赛。

让我解释一下原因。

为了扩大写入,你允许多个节点处理写入请求。

这被称为 Active-Active 设置。

这是它的样子:

当一个节点接收到写请求时,它将会将变化传播到其他节点。

但是...这种安排可能会导致冲突。

例如:

✅设想我们有两个节点A和B运行在 Active-Active 设置中。

✅节点A接收到一个针对键'foo'的写请求,值为'123'。

✅它接受这个请求,但在确认客户端之后失败了。

✅现在,B接收到一个针对键'foo'的读取请求。因为找不到该键,所以返回 NULL。

✅接着,B接收到一个针对键'foo'的写请求,值为'456',并且接受这个请求。

✅现在A从死亡中恢复过来,试图与B相接。

✅但他们都看到键'foo'有冲突。系统中存在不一致性。

看看下面的插图,以便更清楚地了解:

在处理数据库冲突时,通常会使用以下几种冲突解决算法:

1. 最后写入胜出(Last Write Wins):这是最简单也最常见的一种冲突解决策略。在存在冲突的情况下,直接选择最后写入的数据作为最终的数据。这种策略很简单,但是可能会导致数据丢失,尤其当两个并发的写操作对同一个数据项进行修改时。

2. 冲突可解决数据类型(Conflict-free Replicated Data Type, CRDTs):CRDTs 是一种特殊的数据类型,通过一些数学属性来保证在分布式系统中的最终一致性。维护一种无冲突的数据类型,即使在没有协调的情况下,各地的副本也能达到相同的状态。常见的有计数器 CRDT、Set CRDT 等。

3. 版本向量(Vector Clocks):版本向量实际上是对每个操作进行时间戳打标,通过这种方式来解决冲突。当数据库遇到冲突时,它可以通过比较版本向量来决定哪个版本更新,因此应该胜出。然而,这种方法在面临“并发更新”问题时可能仍然无法避免冲突。

4. 合并(Merging):对于某些种类的数据,比如文档或者其他复杂数据类型,通常使用的冲突解决策略是将冲突的数据合并起来。这种情况下,需要开发者定义自己的合并规则。

5. 操作转换(Operational Transformation):操作转换是一种用于实现实时协作系统的算法,它通过确保每个副本看到相同的操作执行的顺序来解决冲突。这在多人实时协作编辑同一份文档时非常有效。

选择哪种冲突解决策略依赖于您的具体情况和需求,包括您的数据模型、负载特性、一致性需求以及可接受的延迟等。在实际应用中,往往会结合使用多种策略。

这绝对不是一件简单的事情

但是,关于这个问题的更多内容,我会在以后的帖子中介绍。

那么 - 在扩展您的数据库时,你更偏向于哪一种策略呢?欢迎大家留言沟通交流。

对我的文章感兴趣的朋友可以点击查看主页我的专栏,更多高质量内容都会收录专栏。

0 阅读:0

超级欧派课程

简介:感谢大家的关注