使用 SWAPDB 命令在线替换 Redis 数据库
正如上一节所说, SWAPDB
命令可以以非阻塞方式互换给定的两个数据库。 因为这个命令的执行速度是如此之快, 并且完全不会阻塞服务器, 所以用户实际上可以使用这个命令来实行在线的数据库替换操作。
举个例子, 假设我们拥有一个 Redis 服务器, 它的 0 号数据库储存了用户的邮件地址以及经过加密的用户密码, 这些数据可以用于登录用户账号。 不幸的是, 因为一次漏洞事故, 这个服务器遭到了黑客入侵, 并且经过确认, 这个服务器储存的所有用户密码均已泄露。 为了保障用户的信息安全, 我们决定立即重置所有用户密码, 具体的做法是: 遍历所有用户的个人档案, 为每个用户生成一个新的随机密码, 并使用这个新密码替换已经泄露的旧密码。
代码清单 11-4 展示了一个用于重置用户密码的脚本, 它的 reset_user_password()
函数会迭代 origin
数据库中的所有用户数据, 为他们生成新密码, 并将更新后的用户信息储存到 new
数据库里面。 在此之后, 函数会使用 SWAPDB
命令互换新旧两个数据库, 并以异步方式移除旧数据库。
代码清单 11-4 用于重置用户密码的脚本代码: /database/reset_user_password.py
import random from redis import Redis from hashlib import sha256 def generate_new_password(): random_string = str(random.getrandbits(256)).encode('utf-8') return sha256(random_string).hexdigest() def reset_user_password(origin, new): # 两个客户端,分别连接两个数据库 origin_db = Redis(db=origin) new_db = Redis(db=new) for key in origin_db.scan_iter(match="user::*"): # 从源数据库获取现有用户信息 user_data = origin_db.hgetall(key) # 重置用户密码 user_data["password"] = generate_new_password() # 将新的用户信息储存到新数据库里面 new_db.hmset(key, user_data) # 互换新旧数据库 origin_db.swapdb(origin, new) # 以异步方式移除旧数据库 # (new_db 变量现在已经指向旧数据库) new_db.flushdb(asynchronous=True)
作为例子, 表 11-5 和表 11-6 分别展示了设置新密码之前和之后的用户数据。 注意, 为了凸显新旧密码之间的区别, 重置之前的用户密码是未经加密的。
表 11-5 重置之前的用户数据
散列键名 |
|
|
---|---|---|
“user::54209” |
“petergogo128” |
|
“user::73914” |
“happyjack256” |
|
“user::98321” |
“tomrocktheworld512” |
|
“user::39281” |
“maryisthebest1024” |
表 11-6 重置之后的用户数据
散列键名 |
|
|
---|---|---|
“user::54209” |
“669a533168e4da2fce34…4d2af” |
|
“user::73914” |
“e0caf7fc1245fa13fb34…a18eb” |
|
“user::98321” |
“1b9f3944bec47bed3527…388c1” |
|
“user::39281” |
“b7f6e3cd4ca27ac67851…75ccf” |