3

这听起来像是一个显而易见的问题,但我是 CouchDB 的新手,所以我认为值得一问,以防 CouchDB 的结构有什么改变了我不知道的情况。由于我无法控制的原因,我必须在 CouchDB 之外构建一个类似队列的结构。为简单起见,假设我正在为稍后执行的作业排队 ID。请注意,不会有重复项。

我试图弄清楚构建它的最佳方法是什么。正如我目前所看到的,我有几个选择:

  1. 将队列项目作为条目存储queue在 ID 为 的数据库中_id,并将出队的项目存储在 ID 为 的类似dequeued数据库中_id。每个数据库中的每条记录除了(强制性)_id_rev.
  2. 有一个排队数据库,该数据库将包含一条记录_id = 'queue'和一条记录_id = 'dequeued'。在这两条记录中的每一条中,都会有任意数量的键,每个键都是要执行的(或已经执行的)作业的 ID。数据库中与键关联的值将是无关紧要的,可能只是一个布尔值。
  3. 有一个排队数据库,并且在该数据库中,有一个名为queue. 在该记录中,有两个键:queuedequeued。这些键中的每一个都将具有一个任意长度的作业执行 ID 列表作为其关联值。

1稍微不太理想,因为它需要两个数据库,而2让我觉得这是一个糟糕的选择,因为它需要加载排队或出队项目的整个列表才能读取列表项或进行任何更改。但是,3很好,因为它允许整个 ID 列表是一个有序列表而不是键/值对,这使得从列表中选择一个随机项目作为下一个要执行的作业变得更容易,因为我实际上不需要知道任何键名(因为没有)。

我正在寻找提供最佳性能的那个。对此有什么想法吗?

更新

对于将来阅读这个问题的人,我已经构建了我的 CouchDB 队列模块CouchQueue,一个正在进行中的工作。

你可以得到它npm install couchqueue

Github 上查看(并请评论、请求请求等) 。

4

2 回答 2

1

队列中每个元素使用一个文档,并保留一个队列数据库。

我建议使用一个字段来对元素进行排序,例如.created_at使用 ISO 8601 格式的时间戳。

.visible您可以使用标志切换元素的可见性。

我推荐一个 map/reduce 视图,像这样

function(doc) {
  if(doc.visible)
    emit(doc.created_at, doc)
}

现在您可以查询此视图,无论是最早的优先还是最新的优先 ( ?descending=true)。您可以通过更新元素来标记元素完成,设置visible = false

我编写了一个 CouchDB 队列CQS,它与 Amazon SQS API 相同。它类似于我描述的,除了有一个签出状态的消息可以,在超时期间在队列中不可见。我在生产环境中使用 CQS 大约两年了,更新了数亿次。

于 2012-09-04T13:48:05.900 回答
1

我建议为每个队列条目使用单独的文档,这样可以避免冲突。

如果您只需要一个带有接口,的队列来添加插入元素并获取一个元素,那么解决方案可能非常简单(如果您想要带有的列表,或者访问第 n 个元素,它会变得更加复杂)。对于具有线性顺序的调度算法(如 FIFO、FILO),您可以将其实现为插入新文档:push()pop()top()next()push()

{ type: "queue", inserted: CURRENT_TIME, ... }

top()作为地图:

function (doc) {
  if (doc.type == "queue" && doc.inserted) {
    emit(doc.inserted, doc);
  }
}

并减少作为一个聚合(例如,对于 FILO 的最大值,对于 FIFO 的最小值)。因为pop()您可以要求查看,top()然后删除该文档。Map/reduce 必须是确定性的,所以如果你想选择随机元素,你可以使 reduce 依赖于伪随机(由服务器选择)_id

我预计有两个问题:

  1. 注意并发:两个进程可以使用 请求同一个文档top(),第一个将删除该文档作为 的一部分pop(),第二个将尝试获取已删除的文档。

  2. CouchDB 从不真正删除文档,只会标记为已删除。为每个push()/添加和删除pop()将增加数据库。您将不得不以某种方式重用这些文档。也许您对任务进行了一些轮询,这些任务被插入和删除,或者在队列中重新排序。然后您可以添加queued: true到任务文档,而不是使用type: "queue".

于 2012-08-30T10:13:36.093 回答