Redis 后台内存分配问题影响 Lua 脚本运行

我最近在一个 Django 应用中添加了一些 Lua 脚本(用于与 Redis 后端进行交互)。在生产环境中(但不是在开发环境中),设置最终会给我以下错误:

Error running script (call to f_8c07b227bc796743f66bad8dbe75a5bf8fcc8cd6): @user_script:2: @user_script: 2: -MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

发生这种情况时,如果我查看 Redis 日志,就会看到 can't save in background: fork: Cannot allocate memory

我知道发生了什么。我的问题是,为什么在无法为后台保存分配内存时,Redis 仍然正常运行,但 Lua 脚本会崩溃?有方法可以避免这种情况吗?


FYI,以下是我的 lua_scripts.py 模块的布局:

import redis
from location import REDLOC2 #location of relevant unix socket

my_server = redis.StrictRedis(unix_socket_path=REDLOC2)

# Get recent
lualatestlogins = """
local l = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1]-600, '+inf') -- returns key:value pairs
local c = {}
for _, v in pairs(l) do
  local p = v:find(':')
  local i = v:sub(1,p-1)
  c[#c+1] = i
end
return c"""
getlatestlogins = my_server.register_script(lualatestlogins)

# Evict old
luacleanselogins = """
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', '(' .. ARGV[1]-600)"""
cleanselogins = my_server.register_script(luacleanselogins)

# -- Store new
luastorelogin = """
redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2] .. ':' .. ARGV[3])
redis.call('SET',KEYS[2],ARGV[3])
redis.call('EXPIRE',KEYS[2],600)"""
storelogin = my_server.register_script(luastorelogin)

# Retrieve collisions
luaretrieveclones = """
local q = redis.call('GET',KEYS[2])
if q == nil or q == '' then
  return nil
else
  local l = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1]-600, '+inf')
  local c = {}
  for _, v in pairs(l) do
    local p = v:find(':')
    local i = v:sub(1,p-1)
    local n = v:sub(p+1)
    if n == q then
      c[#c+1] = i
    end
  end

  return c
end"""
retrieveclones = my_server.register_script(luaretrieveclones)
点赞
用户5384363
用户5384363

错误提示告诉你一切:禁止修改数据集的命令已被禁用

当 Redis 尝试创建一个新进程进行后台保存时,由于内存不足而失败。在这种情况下,Redis 禁用对数据库的任何更新操作。否则,内存中的数据和磁盘上的数据将不一致。

为什么 Redis 在无法分配内存进行后台保存时仍能正常运行?

Redis 仍然可以服务只读请求,并拒绝任何可能修改数据库的请求。

但是 Lua 脚本会崩溃吗?

当你的 Lua 脚本尝试修改数据库时,例如 redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2] .. ':' .. ARGV[3]),会失败。

有没有办法避免这种情况?

  1. 你应该监视这种错误,并将 Redis 移动到一个具有更多内存的新机器以进行扩展,或者使用 Redis 集群进行扩展。

  2. 在你的 Lua 脚本中,使用 redis.pcall 而不是 redis.call。如果 redis.call 失败,整个脚本将终止。但是,如果 redis.pcall 失败,它会捕获错误并返回一个包含错误消息的 Lua 表。你可以检查返回的表来查看你的调用是否成功执行。

2017-02-28 02:27:14