Redis中的模式迁移

时间:2014-02-01 14:20:15

标签: redis

我有一个使用redis的应用程序。我使用密钥名user:<id>来存储用户信息。然后在本地我更改了我的应用代码,以便为此目的使用密钥名称user:<id>:data

我很害怕,如果我把这个新代码推送到我的生产服务器,事情就会破裂。其原因在于,由于我的生产redis服务器已经拥有密钥,因此旧的密钥名称。

所以我认为唯一的方法是停止我的应用,将所有旧的密钥名称更改为新的密钥名称。然后重新启动它。你有更好的选择吗?感谢您的帮助:)

2 个答案:

答案 0 :(得分:1)

您可以使用RENAME以Redis客户端语言运行迁移脚本。

如果您对密钥总数没有任何其他控制权,则首先发出KEYS user:*列出所有密钥,然后发出子字符串以获取数字ID,然后重命名。

您可以在transaction

中发出所有这些内容

所以有点伪代码和redis命令:

MULTI
KEYS user:*
For each key {
    id = <Get id from key>
    RENAME user:id user:id:data
}
EXEC

知道了吗?

答案 1 :(得分:1)

将新代码推送到您的生产环境总是一件可怕的事情(这就是为什么只有最艰难的人才能在这个行业中生存;))。我强烈建议您在更改生产代码和数据库之前,确保在本地测试工作流及其结果。

几乎所有对应用程序的更新都需要停止 - 即使只是替换相关文件。由于您提到的原因,这对涉及数据库的任何更改都更加真实。

即使您可以在不停止应用程序本身(例如PHP页面)的情况下部署代码更改,您仍然希望“原子地”完成数据库更改 - 即,没有任何应用程序请求介入和可能中断。虽然某些数据库可以脱机进行维护,但即使这样,您通常也会停止应用程序,否则将会在整个地方生成错误。

如果情况确实如此,无论数据库发生什么变化,您都会停止应用程序(或将其置于维护模式),因此我们将您的问题实际意味着:重命名全部/部分密钥的最快方法是什么在我的数据库中?

要回答这个问题,与上面建议的伪代码类似,我建议您使用Lua脚本,例如以下内容,并在停止应用后对其进行评估:

for _,k in ipairs(redis.call('keys', 'user:*')) do 
    if k:sub(-5) ~= ':data' then 
        redis.call('rename', k, k .. ':data')
    end
end

关于此脚本的一些注意事项,您应该记住:

  1. 虽然KEYS命令在生产中不安全,但由于您正在进行维护,因此可以安全地使用它。对于您需要扫描密钥的所有其他用例,Redis的SCAN更为可取。

  2. 由于Lua脚本是“原子”的,理论上你可以在不停止应用程序的情况下运行这个脚本 - 只要脚本运行(这取决于数据集的大小),应用程序的请求就会被阻止。换句话说,这种方法解决了获取混合键名称(旧的和新的)的问题。然而,这可能不是你想要做的任何情况,因为a)你的应用程序在那段时间内可能仍然是错误/超时,但主要是因为b)它需要能够处理两种类型的键名(即使用旧密钥运行 - &gt;短/长暂停 - &gt;使用新密钥运行)使您的代码更加复杂。

  3. 如果您只打算运行一次脚本并成功,则不需要if条件。

  4. 根据数据库的实际内容,您可能希望进一步过滤掉不应重命名的密钥。

  5. 为了确保兼容性,请避免硬编码/计算生成密钥名称 - 相反,它们应作为参数传递给脚本。