Tech
谎言与数据一致性
保持数据一致性如同说真话,无需记住所有细节。而弱隔离则使开发变慢,增加错误风险。
"说谎的真正困难在于你必须记住你所说的所有谎言,以及对谁说的。" 这是我曾读过的一句无法准确找到出处的话。这句话深深吸引了我,因为它解释了为什么在弱隔离模型的世界里,作为程序员很难高效工作。
(异常)谎言
如果你对某人撒谎,你必须记住你对其他所有人说过的话,并推测谁可能会与谁交谈。然后,你还必须推理自己可能会如何被揭穿。这会减慢你的思考和精神敏捷度。同样,如果你使用不提供强隔离的数据平台,你必须仔细考虑这可能导致的错误状态或最终用户可见的不一致性。你可能在“撒谎”,需要记住这些谎言。
这会减慢你的开发速度。大部分时间都花在推理架构图上。你可能会给一个毫无防备的客户提供一个不一致的读取结果。你必须记住哪些服务没有通过数据库进行通信。我承认,“谎言”这个比喻可能有些挑衅,但它是理解数据库一致性概念的良好框架。
一些具有弱隔离的数据库被正确记录下来,因为它们几乎不做任何承诺,并且履行了这种最小的承诺。这不算撒谎(“我没说我要检查,你自己想的……”)。但实际上,这对开发者来说是误导的。至少,它会减慢他们的速度。正如我稍后所示,即使是最复杂的数据库程序员也常常被弱隔离模型的细微差别所困扰。
根本上,编程在弱隔离之上需要开发者付出大量工作。构建在强隔离之上的理由是:它允许局部推理。另一句疑似出自Mark Twain的引言是:“如果你说真话,你什么都不用记。” 强隔离的数据库几乎就像神谕。他们总是说实话。
在分布式系统中,“隔离”可以被字面解释:它允许单个查询的程序员仅对该查询进行推理。另一方面,弱隔离需要全局推理,这意味着每个编写查询的程序员必须始终保持一致。
当你进行不一致读取时,是否能逃脱惩罚取决于哪些读取可能在下游的其他系统之间产生冲突。在一个由多个客户端访问的数据库世界中,你总是需要推理它们在下游如何交互。任何错误都会向外传播。这意味着对于任何代码更改,用户必须考虑可能影响数据库的所有其他查询的上下文。
一个(异常)谎言的优先图
让我们正式建模这一点。一个强隔离级别是可串行化性,可以通过两种方式实现。首先,你可以使用保证可串行化的数据库。其次,你可以获取所有可能在数据库上运行的查询,构建一个有向优先图,然后检查该图是否有循环。
这是一个很好的定义。即使在提供较弱保证的系统上,你也可以有一组冲突可串行化的查询。这是因为它们被巧妙地设计为不干扰!一个提供可串行隔离的数据库确保任何事务都不会导致循环。
但如果你有一个仅提供快照隔离的数据库,它将无法捕捉一种特定的循环形状,称为写偏。你仍然可以通过手动检查运行的事务集来确保最终结果没有异常。但这个检查过程很难。
实际上,很少有人能成功做到这一点(更不用说使用正式算法了)。但考虑到Oracle仅提供快照隔离(由于历史原因被错误地称为“可串行”),所以有很多关于查看事务集时需要小心的传闻。在这个话题上,请咨询你的本地Oracle数据库管理员以获取更多信息。
随着数据库保证的减弱,潜在的异常数量增多。这导致了在优先图中更难以捕捉的形状。这需要更广泛的检查,考虑所有可能对数据库运行的事务集。如果你的数据库以提交读取模式运行(Postgres的默认模式),你必须确保它不允许幻读、丢失更新或不可重复读,这很难做到。
诚实通常是最好的政策
这与您在分布式基础设施中运行的所有检查一致吗?实际上,没有人以正式标准的严格性进行检查。他们也没有在每次更改每个数据库查询时都进行检查。但你可能会推理很多关于常见事务路径的问题。你会画出完整的架构图并使用分布式跟踪调查任何错误。你会寻找不一致性并用一些查询围栏来修补它们。
我的观点是:这非常浪费。严峻的事实是,全球推理是最昂贵的。它涉及人们安排会议并查看所有可能的事务集。然后他们必须审查其他程序员提出的事务。而最昂贵的部分,远远超过了员工在这个过程中投入的工时。
尽管如此,弱隔离并不是完全排除的对象。想象你在全球最大公司之一的前所未有规模的分布式基础设施上工作。构建定制的高吞吐量基础设施,做一些为性能而进行的仔细权衡可能是合理的。
FBI和CIA都有复杂的协议来保持谎言不被揭穿。但这对数据库程序员来说是理想的追求吗?有一种更简单的方法来保持答案的正确性。你可以构建一个过程来确保所有后续更改不会产生任何无意的异常。然而,这不是随便可以接受的事情:这是一个最后的手段,当你真正达到了强隔离系统的性能瓶颈时。
大多数开发数据基础设施的开发人员都有向上展示的任务。他们从事构建类似数据库的内部服务。一旦他们开始构建自己的带有流处理器、Redis缓存或队列的反向数据库,他们就必须提供隔离保证。至少,他们必须正确记录并帮助团队正确使用数据库。
足够的异常
在流处理的特定情况下,隔离特别困难。流处理器通常部署在输入无界且计算连续的情况下。许多具有弱隔离保证的系统设计的非正式目标是最终一致性(即我们会在某个时候达到真相……)。
但这与流处理不太匹配:如果输入从未定下来,最终一致性可能会导致输出永远无法定下来。这与大多数人的预期有很大差距。最终一致性设定了一个潜在可接受的预期,即偏差是有界且临时的。这与偏差是永久且无界的情况有很大不同。
使用流处理器、缓存、键值存储和自定义程序,可以构建一个为最终用户提供明确正确性保证的系统。但这绝非易事。这种保证是严格的可串行性。严格可串行性是与人们关于并发控制的自然直觉最吻合的隔离保证。
如果你厌倦了记住所有那些谎言,请利用强一致性。
本文译自 materialize,由 BALI 编辑发布。