在 Rails 应用程序中,用户访问我显示弹出窗口的页面。我想在用户每次看到该弹出窗口时更新记录。
为了避免竞争条件,我使用乐观锁定(所以我在弹出表中添加了一个名为 lock_version 的字段)。
代码很简单:
# inside pages/show.html.erb
<%= render @popup %>
# and inside the popup partial...
...
<%
Popup.transaction do
begin
popup.update_attributes(:views => popup.views + 1)
rescue ActiveRecord::StaleObjectError
retry
end
end
%>
问题是很多用户访问该页面,并且mysql超过了锁定超时。
所以网站冻结了,我收到了很多这些错误:超出锁定等待超时;尝试重新启动事务
这是因为有许多挂起的请求试图使用过时的lock_version值更新记录。
如何解决我的问题?
increment_counter,因为它会生成一个 SQL UPDATE 查询而不会锁定。
但我认为在您的情况下,使用任何键值数据库(如 Redis)来存储和更新您的弹出计数器会更好,因为它可以比 SQL DB 更快地完成。
如果您不能采用@maxd回复中指出的方法,则可以利用异步库(例如 Sidekiq)来处理此类请求(其中它们只会在作业队列中备份)。
lib/some_made_up_class.rb
def increment_popup(popup)
Popup.transaction do
begin
popup.update_attributes(:views => popup.views + 1)
rescue ActiveRecord::StaleObjectError
retry
end
end
end
然后,在另一段代码(控制器、服务或视图(不太理想地将逻辑放在视图层)中。
SomeMadeUpClass.delay.increment_popup(popup)
# OR you can schedule it
SomeMadeUpClass.delay_for(1.second).increment_popup(popup)
从本质上讲,这将产生排队插入的效果,同时释放您的页面,并且理论上有助于减少您点击的超时等。
虽然它肯定不仅仅是添加像 Sidekiq 这样的库(gem)和我在这里的示例代码,但我认为异步库/工具将有很大帮助。