向Redis实例写入键值对前校验键值唯一性的实现方法
Redis键值对写入唯一性校验实现
需求说明
通过POST方式向redis_instance提交键值对集合时,正式写入前必须完成全量校验,拦截三类冲突场景:
- 待提交数据内部存在重复键值对
- Redis已存在的键,对应值与待提交的同键新值不匹配
- Redis已存在的值,对应键与待提交的同值新键不匹配
以下为禁止写入的冲突样例,存在键值链式绑定问题:
Fred: Luke Luke: Jim Jim: Jane以下为允许写入的合法样例,无内部和外部冲突:
Fred: Luke Jim: Jane
原有代码问题
原有POST逻辑未做任何前置校验,直接循环写入键值对,会导致冲突数据进入Redis,且写入中途如果报错会产生部分写入的脏数据。
改造后代码
from rest_framework.response import Response from rest_framework.decorators import api_view import json @api_view(['GET', 'POST']) def manage_items(request, *args, **kwargs): if request.method == 'GET': items = {} count = 0 # 生产环境数据量大时建议用scan_iter替代keys("*"),避免阻塞Redis for key in redis_instance.keys("*"): key_str = key.decode("utf-8") val = redis_instance.get(key).decode("utf-8") items[key_str] = val count += 1 response = { 'count': count, 'msg': f"Found {count} items.", 'items': items } return Response(response, status=200) elif request.method == 'POST': item = json.loads(request.body) # 第一层:校验待提交数据内部冲突 submit_keys = list(item.keys()) submit_values = list(item.values()) # 检查提交的键是否重复 if len(set(submit_keys)) != len(submit_keys): return Response({'msg': '提交数据存在重复键,写入失败'}, status=400) # 检查提交的值是否重复 if len(set(submit_values)) != len(submit_values): return Response({'msg': '提交数据存在重复值,不允许一个值绑定多个键,写入失败'}, status=400) # 检查提交数据内部键值交叉冲突:键和值集合不能有交集,避免出现A→B、B→C的链状关系 if set(submit_keys) & set(submit_values): return Response({'msg': '提交数据存在键值交叉冲突,写入失败'}, status=400) # 第二层:拉取Redis存量数据,校验和存量的冲突 exist_kv = {} exist_vk = {} # 存值到键的反向映射,快速校验值重复 for key in redis_instance.keys("*"): key_str = key.decode("utf-8") val_str = redis_instance.get(key).decode("utf-8") exist_kv[key_str] = val_str exist_vk[val_str] = key_str # 校验键冲突:已存在的键值必须和提交的完全一致 for k, v in item.items(): if k in exist_kv: if exist_kv[k] != v: return Response({'msg': f'键{k}已存在且绑定值为{exist_kv[k]},与提交值{v}冲突,写入失败'}, status=400) # 校验值冲突:已存在的值对应的键必须和提交的完全一致 if v in exist_vk: if exist_vk[v] != k: return Response({'msg': f'值{v}已绑定键{exist_vk[v]},与提交键{k}冲突,写入失败'}, status=400) # 所有校验通过,批量写入 for k, v in item.items(): redis_instance.set(k, v) response = { 'msg': f"{submit_keys} successfully set to {submit_values}" } return Response(response, 201)
注意事项
- 生产环境如果Redis存储的键值对量级超过1万,禁止使用
keys("*")全量扫库,替换为redis_instance.scan_iter("*")游标遍历,避免阻塞Redis主线程 - 写入环节如果对一致性要求极高,可以加Redis事务包裹批量写入逻辑,保证原子性
- 所有校验失败的请求统一返回400状态码,明确提示冲突原因,方便前端排查
内容的提问来源于stack exchange,提问作者Emm
相关产品推荐
相关产品推荐

