面试题答案
一键面试方案设计
- Redis 特性利用:
- 命名空间隔离:利用 Redis 的键空间来为每个租户创建独立的命名空间。例如,对于租户
tenant1
,所有的键都以tenant1:
作为前缀。这样不同租户的数据在键空间层面就相互隔离,降低对象共享导致的数据泄露风险。 - 数据库隔离:Redis 支持多个数据库(默认 16 个,编号 0 - 15)。可以为每个租户分配一个独立的数据库。不过需要注意,在 Redis 集群模式下,数据库隔离功能受限。
- 命名空间隔离:利用 Redis 的键空间来为每个租户创建独立的命名空间。例如,对于租户
- 配置:
- 客户端配置:在客户端连接 Redis 时,根据租户信息选择对应的数据库或者设置正确的键前缀。例如,在 Python 的
redis - py
库中,连接时可以这样设置:
- 客户端配置:在客户端连接 Redis 时,根据租户信息选择对应的数据库或者设置正确的键前缀。例如,在 Python 的
import redis
# 假设 tenant_id 是租户 ID
tenant_id = "tenant1"
r = redis.Redis(host='localhost', port=6379, db=int(tenant_id[-1])) # 假设根据租户 ID 最后一位选择数据库
# 或者设置键前缀
prefix = f"{tenant_id}:"
- 服务器端配置:对于 Redis 服务器,确保开启了多数据库功能(默认开启)。如果使用云 Redis 服务,查看是否支持自定义命名空间或者数据库隔离,并进行相应配置。例如,在阿里云 Redis 中,可以通过控制台或者 API 来创建和管理不同租户的资源。
- 访问控制:
- 使用 Redis 认证:设置 Redis 密码,只有通过认证的客户端才能连接 Redis 服务器。在 Redis 配置文件(
redis.conf
)中设置requirepass yourpassword
,客户端连接时需要提供密码:
- 使用 Redis 认证:设置 Redis 密码,只有通过认证的客户端才能连接 Redis 服务器。在 Redis 配置文件(
r = redis.Redis(host='localhost', port=6379, db=0, password='yourpassword')
- 细粒度权限控制:可以使用 Redis 的 ACL(访问控制列表)功能,为每个租户或者租户组设置不同的权限。例如,限制某些租户只能进行读操作,某些租户可以读写。在 Redis 配置文件中配置 ACL 规则:
# 示例:创建一个只读用户
acl setuser readonly on >readonlypassword ~* +GET
- 数据加密:
- 客户端加密:在将数据存入 Redis 之前,在客户端对敏感数据进行加密。例如,使用 Python 的
cryptography
库:
- 客户端加密:在将数据存入 Redis 之前,在客户端对敏感数据进行加密。例如,使用 Python 的
from cryptography.fernet import Fernet
# 生成密钥
key = Fernet.generate_key()
cipher_suite = Fernet(key)
data = "sensitive information"
encrypted_data = cipher_suite.encrypt(data.encode())
# 存入 Redis
r.set(prefix + "sensitive_key", encrypted_data)
- 读取时解密:在从 Redis 读取数据后,在客户端进行解密:
retrieved_encrypted_data = r.get(prefix + "sensitive_key")
decrypted_data = cipher_suite.decrypt(retrieved_encrypted_data).decode()
代码示例
以下是一个完整的 Python 示例,结合了命名空间隔离和简单的访问控制(密码认证):
import redis
from cryptography.fernet import Fernet
# 租户 ID
tenant_id = "tenant1"
prefix = f"{tenant_id}:"
# 连接 Redis
r = redis.Redis(host='localhost', port=6379, db=0, password='yourpassword')
# 数据加密
key = Fernet.generate_key()
cipher_suite = Fernet(key)
data = "tenant1 specific data"
encrypted_data = cipher_suite.encrypt(data.encode())
# 存入 Redis
r.set(prefix + "data_key", encrypted_data)
# 读取数据
retrieved_encrypted_data = r.get(prefix + "data_key")
if retrieved_encrypted_data:
decrypted_data = cipher_suite.decrypt(retrieved_encrypted_data).decode()
print(decrypted_data)