玖叶教程网

前端编程开发入门

Redis zadd导致的一次线上问题排查和处理

链接:https://juejin.cn/post/7106113486366703624

背景

最近有用户反馈,主播收了1881数值礼物,头像下数字显示881, 正常来说,应该显示1881的数值。

经过排查,是因为redis zadd在并发情况下导致数据不一致的问题。

问题排查

经过对送礼日志的排查,发现mongodb数据更新正常,但是redis数据异常,查看业务代码后发现了问题。

业务代码如下:

这里的逻辑是先更新 mongodb, 然后对redis数值进行zadd覆盖. 一般情况下没啥问题, 但如果稍微遇到高并发:

  1. A线程跟B线程同时对mongo进行修改操作, 在数据库层面, B的修改后于A的修改.
  2. 但在进程中, 线程B先于线程A调用redis修改, 线程A对redis的修改覆盖了B的数值.
  3. 导致redis中数据维护错误.

问题修复

使用Lua脚本修改redis值,并发情况下不处理覆盖逻辑, 避免旧值覆盖新值,利用mongodb自增保证原子性。 代码如下:

Lua脚本如下:

SAFE_ZADD_SCRIPT = """
    local key = KEYS[1]
    local field = ARGV[1]
    local new_value = tonumber(ARGV[2])
    local old_value = redis.call("zscore", key, field)
    local res = nil
    
    if (not old_value) or (tonumber(old_value) < new_value)
    then
        res = redis.call('zadd', key, new_value, field)
    end
    
    return res
"""
复制代码

为啥不使用zincrby?

zincrby虽然保证不会有并发问题,但是如果key失效或者被清理,会导致排行榜从0开始计数,导致数据错误。



发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言