我正在为嵌入式 NoSQL 数据库编写 C++ 中的数据库 LRU 缓存以解决性能问题,并且我试图理解其背后的正确假设行为和理念。
假设有一个处于某个状态 X 的 NoSQL 数据库。我们从相同的隔离状态 X 启动事务 1 (tx1) 和事务 2 (tx2)。两个事务都尝试更改相同的键/值对。每笔交易都将值更改为某个值,并且两个值不相等。Tx1 提交,然后 tx2 提交。数据库的正确行为是什么?
- 新值是从 tx2 提交的值,因为它覆盖了 tx1
- 新值是从 tx1 提交的值,因为提交 tx2 应该会失败
还是答案是别的?
如果它符合 ACID,有人可以详细说明应该如何编程这样的系统吗?
我要缓存的数据库是LMDB,它声称符合 ACID 标准。
背景
这个问题询问了 ACID 中的 I,它代表隔离:
最隔离的标准隔离级别称为serializable。
SQL-92 标准中对可序列化隔离级别的定义包含以下文本:
在真正的序列化执行(每个事务实际上在下一个事务开始之前完全运行到完成)和可序列化隔离之间有一个重要的区别,其中事务只需要具有与串行执行相同的效果(在一些未指定的顺序)。
只要这些事务的影响仍然对应于串行执行的某些可能顺序,真实的数据库系统就可以在物理上及时地重叠可串行事务的执行(增加并发性)。
问题
两种串行执行中的任何一种都是可能的:
两个事务提交后的观察值可能是 V1或V2。
根据不同的串行时间表(T1 然后 T2 或 T2 然后 T1),两者都是正确的。
作为第二个示例,考虑两个事务写入取决于原始值的值。例如,原始值为 100,交易 T1 增加 10%,交易 T2 增加 50%:
可序列化时间表 A:
可序列化时间表 B:
现在,更复杂的操作可能会产生与不可能的串行事务调度相对应的效果。在这种情况下,一个事务将因错误而失败并回滚。
(当引擎无法确定串行调度是可能的时,一些数据库引擎也可能引发错误并中止事务,即使它在逻辑上是可能的。)
根据LMDB的文档:
这证实了 LMDB 实现了可序列化的隔离级别。
另一个问题
您可能打算询问有关Lost Update的问题,其中 T1 所做的更改(不希望地)被 T2 覆盖,因此“丢失”。如果是这样,请查看 Q & A UPDATE 语句行为。