1

我正在深入研究 CouchDB 2,并且发现了一些带有序列号的意外排序。在一种情况下,我发现 _changes 提要中的早期更改具有序列号

99-g1AAAAI-eJyd0EsOgjAQBuAGiI-dN9C9LmrBwqzkJtrSNkgQV6z1JnoTvYneBEvbhA0aMU1mkj6-_NMSITTJfYFm2anOcsFT10mpTzyG-LxpmiL32eqoN8aEAcWE9dz_jPCFrnzrHGQchiFM4kSgaV0JqQ6VFF-AtAV2DggMgCEGxrNhQfatc3bOyDiKUalg2EBVoCu66KapazcUh41e69-GssjNIvcWWRokk2oNofwj0MNazy4QFURhGQ0J9LKI-SHPIBHEgiak51nxBhxnrRk

对于同一个数据库,我的 _changes 提要中的最后一个序列号是

228-g1AAAAJFeJyd0EkOgjAUBuAGTJCdN9AjlIKFruQm2jFAEFes9SZ6E72J3gQ7JW7QCGnyXtLhy-vfAgCWVSjAip96XglW-o5afRJQwNbDMDRVSOuj3ogQJRgiOnL_O8I2urKdd4B1KCRpkRcCxH0npKo7KX4ApQH2HogsAElOKOPTBjkY5-yd2DqKYqnItA91C13BRTdNXY0VWouRrV7JDOvmrLuxlLW4VAlJ5Qzr4aznJ2wskIIy-y9sh7wcYoMKLJKRXOACjTxr3uHcsBE

在浏览器控制台中,以下是错误的

'228-g1AAAAJFeJyd0EkOgjAUBuAGTJCdN9AjlIKFruQm2jFAEFes9SZ6E72J3gQ7JW7QCGnyXtLhy-vfAgCWVSjAip96XglW-o5afRJQwNbDMDRVSOuj3ogQJRgiOnL_O8I2urKdd4B1KCRpkRcCxH0npKo7KX4ApQH2HogsAElOKOPTBjkY5-yd2DqKYqnItA91C13BRTdNXY0VWouRrV7JDOvmrLuxlLW4VAlJ5Qzr4aznJ2wskIIy-y9sh7wcYoMKLJKRXOACjTxr3uHcsBE' > '99-g1AAAAI-eJyd0EsOgjAQBuAGiI-dN9C9LmrBwqzkJtrSNkgQV6z1JnoTvYneBEvbhA0aMU1mkj6-_NMSITTJfYFm2anOcsFT10mpTzyG-LxpmiL32eqoN8aEAcWE9dz_jPCFrnzrHGQchiFM4kSgaV0JqQ6VFF-AtAV2DggMgCEGxrNhQfatc3bOyDiKUalg2EBVoCu66KapazcUh41e69-GssjNIvcWWRokk2oNofwj0MNazy4QFURhGQ0J9LKI-SHPIBHEgiak51nxBhxnrRk'

这是一个错误还是我需要使用其他方法来比较序列号?

在查看我的 _changes 提要中的其他序列号时,看起来它们通常按照我的预期排序,但在这种情况下,当第一个数字(例如 99)从 2 位跳到 3 位时,排序中断. 如果将其归结为一个简单的字符串比较示例,您可以看到 '228' > '99' => false

4

1 回答 1

4

以下答案包含来自@rnewson 的电子邮件线程的摘录。我希望它可以帮助其他人理解 CouchDB 2 中的序列号。谢谢,罗伯特!

的背景:

在 2.0 中没有简单的方法来比较它们,也不需要它们按顺序排列。简而言之,它们并不是为了在 couchdb 之外进行检查或比较而设计的;不透明地对待它们。

前面的数字是第二部分中编码的各个更新序列的总和,仅用于欺骗较旧版本的 couchdb 复制器进行检查点。

序列字符串的后半部分是 {node, range, seq} 元组的编码列表(其中 seq 是您从 2.0 之前的版本中知道的整数值)。当一个序列字符串被传回时,作为 since= 参数,couchdb 解码这个字符串并将适当的整数 seq 值传递给各个分片。

综上所述,总的来说,前面的数量应该增加。完整的字符串本身是不可比较的,因为编码列表没有定义的顺序(因此可以生成两个编码不同但解码为相同元组列表的字符串,只是顺序不同)。

另一个方面是更改提要不是完全有序的。对于给定的分片,它完全有序的(一个分片与具有整数序列的 2.0 之前的数据库相同),couchdb 不会打乱该输出(尽管如果这样做,复制的正确性将被保留)。但是,集群数据库由多个分片组成(“q”值,默认为 4 iirc)。集群的更改提要将这些单独的更改提要合并为一个,但不会对其施加总顺序。我们不这样做是因为它既昂贵又不必要。

如果您需要收听 _changes 提要,然后从稍后停止的位置重新启动,则解决方案:

正确使用更改提要的算法是:

  1. 读取 /dbname/_changes
  2. 幂等处理每一行
  3. 定期(每 X 秒或每 X 行)存储您处理的最后一行的“seq”值

如果您崩溃了,或者您没有使用 Continuous=true,您可以再次执行相同的过程,但在步骤 1 中进行了修改;

修改 1. 阅读 /dbname/_changes?since=X

其中 X 是您在步骤 3 中保存的值。如果您不使用连续模式,那么您可以在使用非连续响应结束时记录“last_seq”值。但是,您冒着重新处理更多物品的风险。

使用此方案(复制器和所有索引器都遵循该方案),您不必关心结果是否乱序,您无需比较任何两个 seq 值。

确实需要确保可以多次正确处理相同的更改。例如,考虑复制器,当它从更改提要中看到一行时,它会询问目标数据库是否包含该行中的 _id 和 _rev 值。如果是,则复制器移动到下一行。如果没有,它会尝试将该行中的文档写入目标数据库。如果发生崩溃,因此在处理该行之前使用 seq 值调用 _changes,它将再次询问目标数据库是否具有 _id/_rev,只有这一次目标会说是。

于 2017-06-09T00:26:03.640 回答