如今,越来越多的新产品旨在对全球产生影响,用户体验正迅速成为决定它们成败的关键因素。 应用程序的这些属性会极大地影响用户体验
- 性能和低延迟
- 应用程序按预期工作
- 安全
- 功能和 UI
让我们开始追求完美的用户体验吧!
1) 性能和低延迟
其他人之前也说过;性能就是用户体验(1,2)。 当您吸引了潜在访问者的注意力时,轻微的延迟增加会导致您再次失去这种注意力。
2) 应用程序按预期工作
“按预期工作”究竟意味着什么? 意味着,如果我在我的应用程序中将我的姓名更改为“Robert”,然后重新加载应用程序,我的姓名将是 Robert 而不是 Brecht。 似乎应用程序提供这些保证很重要,对吧?
应用程序是否能够提供这些保证取决于数据库。 当追求低延迟和性能时,我们最终会进入分布式数据库领域,其中只有少数较新的数据库提供这些保证。 在分布式数据库领域,可能会有龙,除非我们选择强一致(相对于最终一致)的数据库。 在本系列中,我们将详细介绍这意味着什么,哪些数据库提供了这种称为强一致性的功能,以及它如何帮助您轻松构建出色的快速应用程序。
3) 安全
安全似乎并不总是会立即影响用户体验。 但是,一旦用户注意到安全漏洞,关系可能会受到不可弥补的损害。
4) 功能和 UI
令人印象深刻的功能和出色的 UI 对有意识和无意识的思维都有很大影响。 通常,人们只有在体验了产品的外观和感觉之后才会想要它。
如果数据库在设置和配置方面节省了时间,那么我们就可以将剩余的工作重点放在提供令人印象深刻的功能和出色的 UI 上。 有一个好消息:如今,有一些数据库可以满足上述所有要求,无需配置或服务器配置,并提供易于使用的 API(例如开箱即用的 GraphQL)。
这种新型数据库有什么不同? 让我们退一步,看看对更低延迟和更好 UX 的持续追求,再加上数据库研究的进步,最终导致了这种新型数据库的出现,它们是现代应用程序的理想构建模块。
追求分布式
I. 内容交付网络
正如我们之前提到的,性能对 UX 影响很大。 有多种方法可以提高延迟,其中最明显的是优化应用程序代码。 一旦应用程序代码相当优化,网络延迟和数据库的写/读性能通常仍然是瓶颈。 为了实现我们的低延迟要求,我们需要确保我们的数据尽可能靠近客户端,方法是将数据全局分布。 我们可以通过让多台机器协同工作来实现第二个要求(写/读性能),或者换句话说,复制数据。
分布式导致更好的性能,进而带来良好的用户体验。 我们已经看到了广泛使用的一种分布式解决方案,可以加速静态数据的交付,它被称为内容交付网络 (CDN)。 CDN 被 Jamstack 社区高度重视,以减少其应用程序的延迟。 它们通常使用 Next.js/Now、Gatsby 和 Netlify 等框架和工具将前端 React/Angular/Vue 代码预先组装成静态网站,以便它们可以从 CDN 提供服务。

不幸的是,CDN 并不适用于所有用例,因为我们不能依赖于静态生成的 HTML 页面来处理所有应用程序。 还有许多类型的动态应用程序,您不能静态生成所有内容。 例如
- 需要实时更新以实现用户之间即时通信的应用程序(例如,聊天应用程序、协作绘图或写作、游戏)。
- 通过过滤、聚合、排序以及以多种方式操作数据来以多种形式呈现数据的应用程序,因此您无法提前生成所有内容。
II. 分布式数据库
一般来说,高度动态的应用程序将需要分布式数据库来提高性能。 就像 CDN 一样,分布式数据库也旨在成为全球网络,而不是单个节点。 本质上,我们希望从只有一个数据库节点的场景...

…到数据库成为网络的场景。 当用户从特定大洲连接时,他将自动重定向到最近的数据库。 这将导致更低的延迟和更满意的最终用户。

如果数据库是通过电话等待的员工,数据库员工会通知您附近有员工,并转发通话。 幸运的是,分布式数据库会自动将我们路由到最近的数据库员工,因此我们永远不必打扰其他大洲的数据库员工。

分布式数据库是多区域的,您始终会被重定向到最近的节点。
除了延迟之外,分布式数据库还提供第二和第三个优势。 第二个是冗余,这意味着如果网络中的某个数据库位置被哥斯拉袭击完全摧毁,您的数据不会丢失,因为其他节点仍然拥有您数据的副本。


最后但同样重要的是,使用分布式数据库的第三个优势是可扩展性。 在一台服务器上运行的数据库很快就会成为应用程序的瓶颈。 相反,分布式数据库在多台服务器上复制数据,并且可以根据应用程序的需求自动扩展和缩减。 在一些高级分布式数据库中,这方面完全由您负责。 这些数据库被称为“无服务器”,这意味着您甚至不必配置数据库何时应该扩展和缩减,您只需为应用程序的使用付费,仅此而已。
分布动态数据将我们带到了分布式数据库领域。 如前所述,可能会有龙。 与 CDN 相比,数据是高度动态的;数据可以快速变化,并且可以进行过滤和排序,这会带来额外的复杂性。 数据库界研究了不同的方法来实现这一点。 早期方法不得不做出牺牲才能实现所需的性能和可扩展性。 让我们看看分布式搜索是如何演变的。

传统数据库的分布式方法
一种合乎逻辑的选择是建立在传统数据库(MySQL、PostgreSQL、SQL Server)的基础上,因为我们在这些数据库上投入了大量精力。然而,传统数据库并非为分布式设计,因此在分布式方面采用了一种相当简单的方法。扩展读取的典型方法是使用读取副本。读取副本只是您的数据的副本,您可以从中读取但不能写入。这样的副本(或复制)将查询从包含原始数据的节点中卸载。该机制非常简单,因为数据随着传入被增量复制到副本中。
由于这种相对简单的方案,副本中的数据始终比原始数据旧。如果您在特定时间点从副本节点读取数据,您可能会获得比从主节点读取的数据更旧的值。这被称为“陈旧读取”。使用传统数据库的程序员必须意识到这种可能性,并考虑到这种限制进行编程。还记得我们在开头给出的示例吗?我们写入一个值并重新读取它?在使用传统数据库副本时,您不能指望读取您写入的内容。
您可以通过在所有副本意识到写入之前在前端乐观地应用写入结果来稍微改善用户体验。但是,如果更新尚未到达副本,则重新加载网页可能会将 UI 返回到之前状态。然后用户会认为他的更改从未保存。

第一代分布式数据库
在传统数据库的复制方法中,明显的瓶颈是所有写入都到同一个节点。机器可以向上扩展,但不可避免地会遇到天花板。随着您的应用程序越来越受欢迎,写入量不断增加,数据库将不再能够快速地接受新数据。为了在读取和写入方面水平扩展,分布式数据库应运而生。分布式数据库也保存数据的多个副本,但您可以写入这些副本中的每一个。由于您通过每个节点更新数据,所有节点都必须相互通信,并相互告知新数据。换句话说,它不再是像传统系统中那样单向的。

然而,这些类型的数据库仍然可能受到上述陈旧读取的困扰,并引入许多其他与写入相关的潜在问题。它们是否会受到这些问题的影响取决于他们在可用性和一致性方面的决定。
第一代分布式数据库通常被称为“NoSQL 运动”,这个名字受到诸如 MongoDB 和 Neo4j 等数据库的影响,这些数据库也为 SQL 提供了替代语言和不同的建模策略(文档或图形而不是表)。NoSQL 数据库通常没有典型的传统数据库功能,例如约束和联接。随着时间的推移,这个名字似乎是一个糟糕的名字,因为许多被认为是 NoSQL 的数据库确实提供了一种 SQL 形式。出现了多种解释,声称 NoSQL 数据库
- 不提供 SQL 作为查询语言。
- 不只提供 SQL(NoSQL = Not Only SQL)
- 不提供典型的传统功能,例如联接、约束、ACID 保证。
- 对数据进行不同的建模(图形、文档或时间模型)
一些较新的非关系型数据库,但提供了 SQL,后来被称为“NewSQL”以避免混淆。
对 CAP 定理的错误解释
第一代数据库受到了 CAP 定理的强烈启发,该定理规定在网络分区期间,您不能同时拥有数据一致性和可用性。网络分区本质上是当发生某些事件导致两个节点无法相互通信以了解新数据时,这可能是由于许多原因 (例如,据报道,鲨鱼有时会啃食谷歌的电缆)。一致性意味着您的数据库中的数据始终是正确的,但并不一定对您的应用程序可用。可用性意味着您的数据库始终在线,并且您的应用程序始终能够访问这些数据,但不保证数据是正确的或在多个节点中相同。我们通常谈论高可用性,因为 不存在 100% 的可用性。可用性以 9 的数字表示(例如 99.9999% 的可用性),因为始终有可能出现一系列事件导致停机。

但是,如果不存在网络分区会发生什么?数据库供应商对 CAP 定理的理解过于笼统,要么选择接受潜在的数据丢失,要么选择保持可用性,无论是否存在网络分区。虽然 CAP 定理是一个良好的开端,但它并没有强调在不存在网络分区的情况下,可以实现高可用性和一致性。在大多数情况下,不存在网络分区,因此通过将 CAP 定理扩展为 PACELC 定理来描述这种情况是有意义的。主要区别在于最后三个字母(ELC),代表 Else Latency Consistency(其他延迟一致性)。该定理规定,如果不存在网络分区,数据库必须在延迟和一致性之间取得平衡。

简单来说:当不存在网络分区时,随着一致性保证的提高,延迟也会增加。但是,我们会看到现实比这还要微妙。
这与用户体验有什么关系?
让我们来看一个放弃一致性如何影响用户体验的示例。假设有一个应用程序为您提供一个友好的界面来组成人员团队;您可以将人员拖放到不同的团队中。

将人员拖放到团队后,会触发更新以更新该团队。如果数据库不保证您的应用程序可以立即读取此更新的结果,那么 UI 必须乐观地应用这些更改。在这种情况下,可能会发生一些不好的事情:
- 用户刷新页面,不再看到他的更新,并认为他的更新消失了。当他再次刷新时,它突然又回来了。
- 由于与另一个更新冲突,数据库未成功存储更新。在这种情况下,更新可能会被取消,用户永远不会知道。他可能只会在下次重新加载时注意到他的更改消失了。
一致性和延迟之间的这种权衡引发了前端和后端开发人员之间许多激烈的讨论。第一组想要一个很棒的 UX,用户在执行操作时会收到反馈,并可以 100% 确定,一旦他们收到反馈并做出响应,他们的操作结果就会始终如一地保存。第二组想要构建一个可扩展且高效的后端,他们认为除了牺牲上述 UX 要求之外,别无他法才能实现这一点。
两组都有自己的理由,但没有灵丹妙药可以同时满足两者的要求。当事务增加,数据库成为瓶颈时,他们唯一的选择是选择传统数据库复制或分布式数据库,它牺牲强一致性以换取所谓的“最终一致性”。在最终一致性中,数据库的更新最终会在所有机器上应用,但不能保证下一个事务能够读取更新的值。换句话说,如果我将我的姓名更新为“Robert”,我不能保证在更新后立即查询我的姓名时,我将真正收到“Robert”。
一致性税
为了处理最终一致性,开发人员需要了解这种数据库的局限性,并做大量额外的工作。程序员通常会诉诸用户体验技巧来隐藏数据库的局限性,而后端必须编写大量额外的代码层来适应各种故障场景。围绕这些限制找到和构建创造性的解决方案深刻地影响了前端和后端开发人员的工作方式,显著增加了技术复杂性,同时仍然无法提供理想的用户体验。
我们可以将这种确保数据正确性所需的额外工作视为应用程序开发人员必须支付的“税款”,以提供良好的用户体验。这就是使用不提供一致性保证的软件系统的税款,这些保证在当今的网络规模并发环境中无法成立。我们称之为一致性税。
值得庆幸的是,新一代数据库已经出现,它们不需要您支付一致性税,并且可以在不牺牲一致性的情况下进行扩展!
第二代分布式数据库
新一代的分布式数据库已经出现,旨在提供强一致性(而非最终一致性)。这些数据库可扩展性强,不会丢失数据,也不会返回过时数据。换句话说,它们的行为符合预期,不再需要了解其局限性或支付一致性成本。如果您更新了一个值,那么下次读取该值时,它始终反映更新后的值,并且不同的更新将按照写入时的相同时间顺序应用。FaunaDB、Spanner和FoundationDB是在本文撰写时唯一提供无限制强一致性(也称为严格串行化)的数据库。
PACELC 定理再探

新一代分布式数据库实现了一直被认为不可能的事情:它们偏向于一致性,同时仍然提供低延迟。这得益于智能同步机制,例如Calvin、Spanner和Percolator,我们将在本系列的第 4 篇文章中详细讨论这些机制。虽然旧的数据库在低延迟情况下仍然难以提供高一致性保证,但基于这些新型智能算法构建的数据库则不存在这种局限性。
数据库设计极大地影响了在高一致性情况下可达到的延迟。
由于这些新算法允许数据库同时提供强一致性和低延迟,因此通常没有理由放弃一致性(至少在没有网络分区的情况下)。您唯一需要这样做的情况是,如果极低的写入延迟是唯一真正重要的事情,并且您愿意为实现它而丢失数据。

这些数据库还是 NoSQL 吗?
对这一代新的分布式数据库进行分类不再是件容易的事。虽然人们仍在努力(1、2)解释 NoSQL 的含义,但这些解释仍然不尽完美,因为 NoSQL 和 SQL 数据库正在互相靠拢。新的分布式数据库借鉴了不同的数据模型(文档、图、关系型、时序型),其中一些数据库提供了ACID 保证,甚至支持 SQL。它们与 NoSQL 数据库仍然有一个共同点:它们旨在解决传统数据库的局限性。一个词无法完全描述数据库的行为。在未来,用以下问题来描述分布式数据库更有意义。
- 它是否强一致性?
- 分布式是否依赖于只读副本,还是真正分布式的?
- 它借鉴了哪些数据模型?
- 查询语言的表达能力如何,它的局限性是什么?
结论
我们解释了应用程序如何从新一代全球分布式数据库中受益,这些数据库可以以类似 CDN 的方式从最接近的位置提供动态数据。我们简要回顾了分布式数据库的历史,发现它并非一帆风顺。许多第一代数据库被开发出来,它们的一致性选择(主要受 CAP 定理驱动)要求我们编写更多代码,同时仍然降低用户体验。直到最近,数据库社区才开发出允许分布式数据库将低延迟与强一致性相结合的算法。一个新的时代已经到来,一个我们不再需要在数据访问和一致性之间做出权衡的时代!
此时,您可能想看看最终一致性数据库的潜在陷阱的具体示例。在本系列的下一篇文章中,我们将专门讨论这一点。敬请期待这些即将发布的文章。
非常有见地!期待阅读剩下的文章!
嗨 Gust,感谢你的反馈!我们很高兴你喜欢它。
这些文章将每周发布,因此下一篇文章应该在 3 月 12 日发布。
如果您有任何反馈或想法,请在 Twitter 上告诉我们(标签 @fauna)/ Slack(http://community.fauna.com/)