当前位置: 首页 > 知识库问答 >
问题:

在Cassandra中原子更新共享状态

齐振
2023-03-14

我计划使用cassandra作为我的应用程序的nosql数据存储。我的用例之一是更新用户的“余额”。假设每个用户的余额存储为一个关键UID_balance。现在,如果我的应用程序想要更新多个用户的余额,我将如何处理原子性?

我想,在某个时刻,应用程序基本上将执行以下操作:

1. for each user u
2.    current_balance = read_users_balance(u);
3.    new_balance     = current_balance + delta_for_user(u);
4.    write_users_balance(u, new_balance);
5. end

现在,这里有几个问题:

  1. 与cassandra的连接可能会中断,导致代码只更新少数用户的余额
  2. 在步骤2和4之间,可能有另一个进程可以更新用户的余额,我将更新一个过时的余额,使用户的余额处于“损坏”状态

RDBMS解决了这些问题,因为它们提供了ACID属性,而Cassandra没有。我看到Cassandra最近(2012年10月)开始提供原子批次。我不确定这是否是解决这个问题的正确方法。

这是我和一个朋友头脑Storm的结果。我们实际上并不更新用户的余额,而是创建一个记录,将更新增量附加到另一个记录。例如:

UID1_balance = {100}
UID1_deltas  = {10,20,-40}

为了获得当前余额,我们只需将增量应用于余额即可。我们可以有一个脱机过程,将增量应用于用户的余额并修剪增量列表。

这个解决方案可以工作并减少腐败状态的可能性,但我认为这是一个矫枉过正。有没有更好的方法来解决这个问题?

共有3个答案

隆康平
2023-03-14

>

  • 正如Richard所指出的,目前最好的方法是使用原子批处理来更新许多增量。如果出现问题,只需重播批处理。

    另一种可能的解决方案是将 ZooKeeper 用作协调和分布式锁定服务:http://ria101.wordpress.com/tag/zookeeper/

    另一种可能的解决方案是使用计数器,这样您就不需要这样做

    your current_balance = read_users_balance(u);
    new_balance     = current_balance + delta_for_user(u);
    

    因为有了计数器,你不需要在更新前读取余额。http://www.datastax.com/dev/blog/whats-new-in-cassandra-0-8-part-2-counters

    但是计数器有一个问题,它们不是幂等的,所以如果您没有收到确认您的增量/减量成功,您就不能重放该计数器,因为它会导致超数。

    新的计数器将解决这个问题。

  • 澹台展鹏
    2023-03-14

    您最初的“读html" target="_blank">修改写”方法无法在卡桑德拉中工作,因为没有锁。卡桑德拉计数器部分解决了这个问题,但在两个地方不足以满足您的要求:

    1. 您不能使用多个计数器执行原子批处理,这意味着您最终可能会应用某些更新,而其他更新则不会。
    2. 如果出现错误,您无法判断计数器是否递增,因此最终可能会得到不准确的值。

    这意味着将增量存储为单独列的可能解决方案是获得所需保证的唯一方法,结合Cassandra 1.2中的原子批处理(参见http://www.datastax.com/dev/blog/atomic-batches-in-cassandra-1-2)。您的解决方案就像实现计数器,其中每个计数器位于一行中,每个增量是一列。要读取,您需要将一行中列的所有值相加。

    正如你所说,这里的问题是处理垃圾,因为这些增量列表将随着时间的推移而增长。如果没有太多的更新,这是可以的,但如果余额更新频繁,它会变得太慢读取。

    您的离线“垃圾收集”过程可以通过读取delta,然后自动删除它们并为整个值添加一个delta来确保安全。使用原子批处理和单线程进程可以确保安全。

    邢杰
    2023-03-14

    我建议阅读论文“建立在流沙上”,这将使您从帐户的角度思考,他们甚至引用了这样的银行帐户示例。注意:蔡斯和富国银行不会在交易中转移资金,因此他在那篇文章中解释了我们如何在微观层面上做同样的事情,就像宏观层面;)一样。

    这在为卡珊德拉写季后赛的时候帮助很大,因为现在在季后赛维基上也有一个模式页面。

     类似资料:
    • 使用时有点卡住了。如果我把它放在before窗口上,它似乎可以正常工作,但如果放在窗口的“apply”函数之后,它就会失败。 我正在测试两个流,主功能在不断摄取数据和控制流模型在更改模型的要求。 我能够在中正确设置并查看b0/b1,但是始终可以看到b0和b1在初始化时设置为0。 我是不是漏了什么明显的东西?

    • 我试图了解如何构建具有不同"页面"或"视图"的ReactJS应用程序。 我将以下组件作为我的基础应用程序,并且我正在React状态中使用currentState属性在视图中的活动组件之间切换。 它执行此任务,但在启动dataLoaded回调时,组件从不接收更新的recipes数组。 如何根据应用程序中的更新状态更新道具? 还是我处理这整件事的方式错了?

    • 你说的批处理语句在卡桑德拉中是原子的是什么意思?确切地说,文档在本质上有点令人困惑。这是否意味着查询在群集中的节点之间是原子的? 例如,我有一批100个查询。如果批处理中的第40个查询失败,那么批处理中执行的39个查询会发生什么? 我知道有一个在后台创建的批处理日志,它将负责部分批处理的一致性。它是否删除了其余39个条目,并提供了批处理查询所需的原子性质。 在MYSQL中,我们将自动提交设置为fa

    • 我正在学习如何应对,我整天都在试图找到解决问题的办法,但都没有成功。然后决定在这里提出我的第一个问题。 我有子组件,包括React-Datepicker组件和单独的“第二天”和“前一天”按钮来更改选定的日期。 SelectedDate存储在父组件的状态中。 我试图从子组件更新父组件的状态,当状态更新时,子组件应该重新呈现,因为该状态作为道具传递给同一个子组件。 我已设法从child更改父状态,但子

    • SyntaxException:第1:34行在输入'(')处没有可行的替代方案(UPDATE mytable SET mycolumn=cast 我看到了关于cast函数的文档,对于任何其他类型,都没有在输出类型下列出。 https://docs.datastax.com/en/dse/5.1/cql/cql/cql_reference/refcqlfunction.html#refcqlfunc

    • 是否可以在ElasticSearch中进行批量原子更新? 我知道定期批量更新不是原子的,正如这里所指出的:https://www.elastic.co/guide/en/elasticsearch/guide/current/bulk.html#bulk 还有其他方法可以自动更新多个文档吗?即。要么所有更新都发生,要么没有更新。