Concurrency 在Cassandra中使用轻量级事务(CA)时,如何避免写操作丢失?

Concurrency 在Cassandra中使用轻量级事务(CA)时,如何避免写操作丢失?,concurrency,transactions,cassandra,compare-and-swap,optimistic-concurrency,Concurrency,Transactions,Cassandra,Compare And Swap,Optimistic Concurrency,我正在Cassandra上做一些测试,看看是否可以将其用于支持乐观并发的可伸缩键值存储 由于一个键值存储只需要一个表,每个项都由KEY访问,它似乎可以很容易地为我们的问题提供技术基础。 但是,只要检测到并发性,在运行和重试时,就会看到写操作丢失 该测试将创建一个表: 创建表对象键文本、版本int、主键; 并使用以下命令插入多个键: 插入对象键,版本值?,0(如果不存在); 然后,使用CAS操作将这些项目的版本增加若干次: -客户端检索当前版本 从对象中选择版本,其中键=?; -并使用检索到的版本

我正在Cassandra上做一些测试,看看是否可以将其用于支持乐观并发的可伸缩键值存储

由于一个键值存储只需要一个表,每个项都由KEY访问,它似乎可以很容易地为我们的问题提供技术基础。 但是,只要检测到并发性,在运行和重试时,就会看到写操作丢失

该测试将创建一个表:

创建表对象键文本、版本int、主键; 并使用以下命令插入多个键:

插入对象键,版本值?,0(如果不存在); 然后,使用CAS操作将这些项目的版本增加若干次:

-客户端检索当前版本 从对象中选择版本,其中键=?; -并使用检索到的版本作为版本检查更新项目 更新对象集版本=?其中key=?如果版本=?; 对于更新,客户端代码实际上如下所示:

专用异步任务CompareAndSetstring键,int currentCount,PreparedStatement updateStatement { //增加版本 IStatement语句=updateStatement.BindCurrentCount+1,key,currentCount; //执行该语句 行集结果=wait Session.ExecuteAsyncstatement; //检查结果 行=result.GetRows.SingleOrDefault; 如果行==null 在更新结果中抛出新异常无行。; //检查是否应用了CAS操作 返回row.GetValue[应用]; } 如您所见,由于并发性,无法应用CAS操作。因此,此操作将重试,直到成功为止。写入超时异常也会被处理

专用异步任务Updatestring密钥,PreparedStatement selectStatement,PreparedStatement updateStatement { bool done=false; //尝试更新增加版本,直到成功 好啦!完成了 { //获取当前版本 TestItem=null; 而item==null item=等待GetItemkey,选择Statement; 尝试 { //使用轻量级事务更新版本 完成=等待比较设置键,item.Version,updateStatement; //轻型事务CA失败,因为比较失败->未更新 如果!完成 联锁。增量REF中止更新; } 捕获WriteTimeoutException wte { //部分写入超时部分已更新,因此最终必须全部更新,因为这是一个CAS操作 如果wte.ReceivedAcknowledgets>0 { 联锁。增量REF部分写入输出; 完成=正确; } 其他的 //完成写入超时->对此不确定。。。 联锁。递增参考TotalWriteTimeout; } } } 以下是使用100个项目并将每个项目更新10次的测试的输出:

Running test with 100 items and 10 updates per item.

Number of updates: 1000
Number of aborted updates due to concurrency: 3485
Number of total write timeouts: 18
Number of partial write timeouts: 162

LOST WRITES: 94 (or 9,40%)

Results: 

Updates | Item count
     10 |         35
      9 |         43
      8 |         17
      7 |          3
      6 |          2

Xunit.Sdk.EqualExceptionAssert.Equal() Failure
Expected: 0
Actual:   94
如您所见,这是一个高度并发的测试。请查看必须重试更新的中止操作的数量。但是,坏消息是我们正在失去写作。客户端认为应该执行1000次更新,但在本例中丢失了94次写入

丢失写入的数量与写入超时的数量的数量级相同。因此,它们似乎是联系在一起的。问题是:

我们是否需要以更好的方式处理超时异常? 在Cassandra上执行CAS操作时,有没有办法避免写操作丢失?
WriteTimeoutException表示Cassandra无法及时执行该操作。在您的测试中,您将Cassandra置于重载下,任何操作都可能因超时异常而失败。因此,您需要做的是重做操作,并通过反复尝试从问题中恢复。它类似于SQLTimeoutException。您还需要对此进行防御。

这看起来像是一张优秀的JIRA票据-Cassandra版本、使用的JVM和DEBUG system.log可能也会有帮助:好,首先,我们将使用2.0.9版之前使用的最新Cassandra版本进行测试。问题与2.1.2版相同。我为此创建了一个JIRA票证:无法从WriteTimeoutException判断LWT是否真的会成功?是吗?这个问题是另一回事,如果你公司的成功建立在数据完整性的基础上,那么应该阻止你使用Cassandra,这也是一些人在某些情况下仍然使用关系数据库的原因,即使这些数据库不能扩展。这个错误甚至使我的建议无效。在任何情况下都不应该重做事务,所以基本上是的,就超时而言,你永远不会知道。在这个bug事件之前,发生超时但调用成功的可能性很小。超时时窗口已关闭,但有未完成的ACK。这就是为什么我们在美国
sed将所有碎片都设置为本地,并且超时合理的高。因此,除了争用和系统损坏之外,基本上不存在超时的机会,如果您重试,您会发现。问题还在于,如果您有写入超时,其他副本可能会赶上已经接受该值的副本。重复写入值就像将其播种到所需的碎片中一样。这就是为什么我们将Cassandra用于媒体数据和日志,而不是用于我们需要信任的100%正确性的数据。您是否可以执行串行读取以了解实际发生的情况,或者LWT是否基本上已损坏?