【问题标题】:Is it possible to use a cassandra table as a basic queue是否可以使用 cassandra 表作为基本队列
【发布时间】:2013-12-06 21:30:02
【问题描述】:

是否可以在cassandra中使用一个表作为队列,我认为我在mysql中使用的策略不起作用,即给定这个表:

create table message_queue(id integer, message varchar(4000), retries int, sending boolean);

我们有一个事务将该行标记为“正在发送”,尝试发送,然后删除该行或增加重试次数。该事务确保在任何时候只有一个服务器会尝试处理来自 message_queue 的项目。

有一篇文章on datastax 描述了陷阱以及如何绕过它,但是我不确定放置大量墓碑的影响是什么,它们会保留多长时间?

【问题讨论】:

  • 这是可能的,但不是微不足道的。为此,您需要利用分区和分桶。有一个可用作 docker 容器 github.com/paradoxical-io/cassieq 的实现(免责声明我是该项目的贡献者)

标签: queue cassandra cql cql3


【解决方案1】:

不要这样做。 Cassandra 作为队列后端是一个糟糕的选择,除非你非常非常小心。您可以在Jonathan Ellis blog post "Cassandra anti-patterns: Queues and queue-like datasets" 中阅读更多原因(这可能是您所暗示的帖子)。 MySQL 也不是支持队列的好选择,我们是像 RabbitMQ 这样的真正队列产品,它很棒而且非常易于使用。

使用 Cassandra 作为队列存储的问题在于:每次删除消息时,都会为该消息编写墓碑。每次您查询下一条消息时,Cassandra 都必须遍历这些墓碑和已删除的消息,并尝试确定少数未被删除的消息。对于任何类型的吞吐量,读取值的数量与实际实时消息的数量将是数十万比一。

调整 GC 宽限和其他参数将无济于事,因为这仅适用于墓碑在压缩后会挂起多长时间,即使您将 CPU 专用于仅运行压缩,您仍然会有数十个死活口粮数千或更多。在某些情况下,即使 GC 优雅为零墓碑也会在压缩后徘徊。

有一些方法可以减轻这些影响,它们在 Jonathan 的帖子中进行了概述,但这里有一个摘要(我写这篇文章并不是为了鼓励您使用 Cassandra 作为队列后端,而是因为它解释了更多关于Cassandra 有效,应该可以帮助您理解为什么它不适合该问题):

为避免墓碑问题,您不能继续使用相同的队列,因为它会比压缩消除它们的速度更快地填满墓碑,并且您的性能将直接陷入困境。如果您向主键添加一个确定性且取决于时间的列,您可以避免一些性能问题,因为更少的 tombstone 有时间建立,并且 Cassandra 将能够完全删除旧行及其所有 tombstone。

对每个队列使用单行也会创建一个热点。单个节点必须处理该队列,其余节点将处于空闲状态。您可能有很多队列,但其中一个可能会比其他人看到更多的流量,这意味着您获得了一个热点。通过向主键添加第二列,将队列分片到多个节点上。它可以是消息的哈希值(例如crc32(message) % 60 将创建 60 个分片,不要使用太小的数字)。当您想查找从所有分片中读取的下一条消息并选择其中一个结果时,忽略其他结果。理想情况下,您可以找到一种将其与依赖时间的东西结合起来的方法,这样您就可以在解决该问题的同时解决该问题。

如果您在到达时间之后对消息进行排序(例如使用TIMEUUID 集群键)并且可以以某种方式跟踪已传递的最新消息,则可以执行查询以查找该消息之后的所有消息。这将意味着更少为 Cassandra 翻墓碑,但这不是灵丹妙药。

然后是确认问题。我不确定它们对您是否重要,但看起来您的架构中有某种锁定机制(我正在考虑 retriessending 列)。这行不通。在 Cassandra 2.0 和它的比较和交换功能之前,没有办法让它正常工作。要实现锁定,您需要读取列的值,检查它是否没有被锁定,然后写下它现在应该被锁定。即使具有一致性级别ALL,另一个应用程序节点也可以同时执行相同的操作,并且最终都认为他们锁定了消息。使用 Cassandra 2.0 中的 CAS,可以原子地执行操作,但会以性能为代价。

StackOverflow 上有更多关于 Cassandra 和队列的答案,请阅读它们(从以下内容开始:Table with heavy writes and some reads in Cassandra. Primary key searches taking 30 seconds

【讨论】:

  • 感谢您的有用/详细的回复。我认为会是这种情况,但我想在最终不得不继续提出替代解决方案之前确定一下。
【解决方案2】:

可以定义宽限期。默认情况下为 10 天:

gc_grace_seconds¶

(默认值:864000 [10 天])指定垃圾前等待的时间 收集墓碑(删除标记)。默认值允许 在删除之前需要大量时间来实现一致性。 在许多部署中,可以缩短此间隔,并且在单节点中 cluster 它可以安全地设置为零。使用 CLI 时,使用 gc_grace 而不是 gc_grace_seconds。

取自 documentation

另一方面,我认为在 Cassandra 中实现队列模式并不是很有用。为了防止您的工作人员两次处理一个条目,您需要强制执行“ALL”读取一致性,这违背了分布式数据库系统的目的。 我强烈建议查看专门的系统,例如原生支持队列模式的消息传递系统。以RabbitMQ 为例。您将立即启动并运行。

【讨论】:

  • 确实,使用为队列设计的产品会更好,但是在这种情况下,现有产品(在 MySQL 上)只有一个低吞吐量队列,必须安装和维护另一个软件。仅此一个队列的产品。
  • 我了解您的预订。如果您需要重新考虑您的 MySQL 队列,我建议您阅读:blog.engineyard.com/2011/… RabbitMQ 非常易于安装、维护和理解。它可能会为您的项目开辟新的机会。
  • ALL 一致性不足以实现锁。您仍然可以最终让两个应用程序节点锁定相同的值(ALL 给您的只是所有节点都同意最新的值,但它不能保证在您编写新值之前该值没有改变值)。
【解决方案3】:

Theo 关于不使用 Cassandra 进行队列的回答是正确的。

只是想补充一点,我们一直在为我们的队列使用 Redis 排序集,并且效果很好。我们的一些队列有数千万个元素,每秒被访问数百次。

【讨论】:

    猜你喜欢
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 2019-01-16
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多