将Redis事务包装在数据库事务中

时间:2019-11-07 20:02:55

标签: ruby postgresql redis

我正在尝试拉出一堆存储在redis队列中的记录,并将它们以1000的批次写入数据库。

这个想法是将redis事务包装在数据库事务中,这样,如果db提交失败,redis事务也会爆炸,因此我HTML所需要的元素也不会消失,这是一个简单的例子,其中有10个记录:

lpop

该示例使用 REDIS.with do |redis| redis.multi do |multi| MyRailsModel.transaction do 10.times do attrs = JSON.parse(multi.lpop("foo")) MyRailsModel.create(attrs) end end end end 约定,但可用于任何设置。

我得到的问题是,ActiveRecord并没有真正返回值,而是一个multi.lpop("foo")-如果我尝试简单地放入Redis::Future,则会出现一个SON.parse(multi.lpop("foo").value)错误。

我开始从Redis api中感觉到我正在尝试做的事可能是不可行的,但是我发现很难相信像Redis交易这样的基本事情(例如获得价值)是不可能的,所以我希望有人知道我想念的东西

1 个答案:

答案 0 :(得分:1)

Redis中的事务与Postgres中的事务完全不同。来自Redis docs

  

使用MULTI命令输入Redis事务。该命令始终以OK答复。此时,用户可以发出多个命令。 Redis不会执行这些命令,而是将它们排队。一旦调用EXEC,所有命令都将执行。

因此,在您EXEC之前,Redis事务中实际上什么也没有发生,这时它会立即运行所有内容,而您无法在命令之间进行干预。

这些不是ACID交易;他们提供isolation,但不提供atomicity。无法撤消更改并使情况更糟:

  

即使命令失败,队列中的所有其他命令也会被处理 – Redis将不会停止处理命令。

(当然,所有这些都是有充分理由的:Redis是单线程的,因此命令之所以被隔离只是因为它们从不并行运行,并且“事务”无非是保证您的命令块当然,它希望在物理上尽可能快地执行该块,并且不会浪费时间等待往返客户或将所有数据版本化到迎合偶尔的回滚。)


Redis并未真正提供ACID事务之类的高级抽象,它提供了一组低级commands,您可以(希望)将其组合以满足您的需求。而且,如果您需要任务队列的回滚功能,则感兴趣的主要命令是RPOPLPUSH

  

以原子方式返回并删除存储在source上的列表的最后一个元素(尾),并将该元素推入存储在destination上的列表的第一个元素(头)。

文档实际上详细说明了此问题的解决方案(“可靠队列”)。基本上,您可以使用RPOPLPUSH(从原子上)将项目移动到第二个“进行中”列表,在处理完项目后将其删除,并监视列表中是否有孤立的项目,有可能在超时后重新排队(或使用其他方法来检测失效的处理器。

如果您不想自己解决所有麻烦,可以使用a million message queue implementations,其中许多运行在Redis之上。