示例:使用 SWAPDB 命令实行在线替换数据库

正如上一节所说,SWAPDB 命令可以以非阻塞方式互换给定的两个数据库。因为这个命令的执行速度是如此之快,并且完全不会阻塞服务器,所以用户实际上可以使用这个命令来实行在线的数据库替换操作。

举个例子,假设我们拥有一个 Redis 服务器,它的 0 号数据库储存了用户的邮件地址以及经过加密的用户密码,这些数据可以用于登录用户账号。不幸的是,因为一次漏洞事故,这个服务器遭到了黑客入侵,并且经过确认,这个服务器储存的所有用户密码均已泄露。为了保障用户的信息安全,我们决定立即重置所有用户密码,具体的做法是:遍历所有用户的个人档案,为每个用户生成一个新的随机密码,并使用这个新密码替换已经泄露的旧密码。

代码清单 11-4 展示了一个用于重置用户密码的脚本,它的 reset_user_password() 函数会迭代 origin 数据库中的所有用户数据,为他们生成新密码,并将更新后的用户信息储存到 new 数据库里面。在此之后,函数会使用 SWAPDB 命令互换新旧两个数据库,并以异步方式移除旧数据库。


代码清单 11-4 用于重置用户密码的脚本代码:/database/reset_user_password.py

  1. import random
  2.  
  3. from redis import Redis
  4. from hashlib import sha256
  5.  
  6. def generate_new_password():
  7. random_string = str(random.getrandbits(256)).encode('utf-8')
  8. return sha256(random_string).hexdigest()
  9.  
  10. def reset_user_password(origin, new):
  11. # 两个客户端,分别连接两个数据库
  12. origin_db = Redis(db=origin)
  13. new_db = Redis(db=new)
  14.  
  15. for key in origin_db.scan_iter(match="user::*"):
  16. # 从源数据库获取现有用户信息
  17. user_data = origin_db.hgetall(key)
  18. # 重置用户密码
  19. user_data["password"] = generate_new_password()
  20. # 将新的用户信息储存到新数据库里面
  21. new_db.hmset(key, user_data)
  22.  
  23. # 互换新旧数据库
  24. origin_db.swapdb(origin, new)
  25.  
  26. # 以异步方式移除旧数据库
  27. # (new_db 变量现在已经指向旧数据库)
  28. new_db.flushdb(asynchronous=True)

作为例子,表 11-5 和表 11-6 分别展示了设置新密码之前和之后的用户数据。注意,为了凸显新旧密码之间的区别,重置之前的用户密码是未经加密的。


表 11-5 重置之前的用户数据

散列键名email 字段(邮箱地址)password 字段(未加密密码)
“user::54209”peter@spam.mail“petergogo128”
“user::73914”jack@spam.mail“happyjack256”
“user::98321”tom@spam.mail“tomrocktheworld512”
“user::39281”mary@spam.mail“maryisthebest1024”

表 11-6 重置之后的用户数据

散列键名email 字段(邮箱地址)password 字段(已加密密码)
“user::54209”peter@spam.mail“669a533168e4da2fce34…4d2af”
“user::73914”jack@spam.mail“e0caf7fc1245fa13fb34…a18eb”
“user::98321”tom@spam.mail“1b9f3944bec47bed3527…388c1”
“user::39281”mary@spam.mail“b7f6e3cd4ca27ac67851…75ccf”