我发现4"正确"方法:
increment
和increment_counter
应该是album.values[:column] -= 1 # or += 1 for increment
和album.update(:counter_name=>Sequel.+(:counter_name, 1))
update_sql
同样效果s[:query_volume].update_sql(:queries => Sequel.expr(3) + :queries)
dataset.update_sql(:exp => 'exp + 10'.lit)
http://sequel.jeremyevans.net/rdoc/classes/Sequel/Dataset.html#method-i-update
但是没有一个解决方案实际更新值并以安全,原子的方式返回结果。
解决方案基于"添加值然后保存"应该,afaik,在多处理环境中不确定地失败,导致错误,例如:
album
Sequel.expr
和Sequel.+
实际上并没有返回一个值,但是Sequel::SQL::NumericExpression
和(afaik)你没有办法解决这个问题另一个DB往返,这意味着可能发生这种情况:
因此,如果没有编写自定义锁定代码,解决方案是什么?如果没有,没有编写自定义锁定代码:)最好的方法是什么?
我一般不满意答案,说我想要太多的生活,正如一个答案所暗示的那样:)
专辑只是文档的一个例子。
想象一下,例如,您在电子商务POS上有一个交易柜台,它可以在不同的主机上同时接受2个交易,并且您需要在24小时内使用整数计数器发送它们(称为systan) ,发送2 trx与同一个systan,1将被拒绝,或者更糟糕的是,计数中的间隙被警告(因为他们提示"缺少事务")因此它不可能使用DB&# 39; s ID值。
一个不太严重的例子,但与我的用例更相关,在后台worker中同时触发了几个文件导出,每个文件目的地都有自己的计数器。计数器中的间隙被警告,工作人员在不同的主机上(因此互斥体没有用)。而且我有一种感觉,我很快就会解决更严重的问题。
数据库序列也不好,因为它意味着在添加每个终端时都要做DDL,而我们在这里说1000。即使在我不太严重的用例中,门户网站上的DDLing操作仍然是PITA,甚至可能无法工作,具体取决于下面的缓存方案(由于ActiveRecord
和Sequel
的实施 - 在我的情况下我使用两者 - 可能只需要重新启动服务器来注册商家。
Redis可以做到这一点,但是当您坐在符合ACID标准的数据库上时,为计数器添加另一个基础架构组件似乎很麻烦。
答案 0 :(得分:6)
如果您使用的是PostgreSQL,则可以使用UPDATE RETURNING:DB[:table].returning(:counter).update(:counter => Sequel.expr(1) + :counter)
但是,如果不支持UPDATE RETURNING或类似的东西,则无法在返回递增值的同时以原子方式递增。
答案 1 :(得分:1)
答案是 - 在多线程环境中,不要使用数据库计数器。面对这种困境时:
在您需要计算专辑的特定情况下 - 您是否有理由在数据库上而不是模型上的派生字段?
更新1:
鉴于您正在处理与多个主机上的工作人员近似文件导出的内容,您需要提前分配ID(即,将具有作业的工作人员和来自单个规范来源的下一个可用ID)或让工作人员呼叫中央服务,以先到先得的方式分配交易ID。
我想不出另一种方法。我从未使用过POS系统,但我所使用的电信网络配置系统通常使用单个事务生成器服务,并在适当时命名为id。