能否使用HSET将嵌套字典以哈希结构存储在Redis中?
结论
Redis 原生哈希(Hash)结构不支持直接存储嵌套字典,其设计为单key下对应若干平级字段(field)和值(value),所有value仅支持字符串、整数、浮点数这类标量类型,无法直接存储字典、列表等复合结构,这就是你运行报错的根本原因。
替代方案(无需全局序列化整个字典)
方案1:字段路径扁平化存储
将嵌套字典的层级路径用分隔符(如冒号:、点.)拼接作为哈希的字段名,把所有嵌套结构拍平为平级键值对后存入哈希,读取时再按路径规则还原为嵌套字典即可。
示例实现代码:
from redis import StrictRedis def flatten_dict(d, parent_key='', sep=':'): items = [] for k, v in d.items(): new_key = f"{parent_key}{sep}{k}" if parent_key else k if isinstance(v, dict): items.extend(flatten_dict(v, new_key, sep=sep).items()) elif isinstance(v, list): for idx, item in enumerate(v): list_key = f"{new_key}{sep}{idx}" if isinstance(item, (dict, list)): items.extend(flatten_dict(item, list_key, sep=sep).items()) else: items.append((list_key, item)) else: items.append((new_key, v)) return dict(items) foo = { "host_data": { "hostname": "some_host", "mac_address": "82:fa:8e:63:40:05", "root_password": { "is_crypted": True, "password": "sha512_password" }, "network": { "ip_address": "192.168.0.10/24", "default_gateway": "192.168.0.1", "vmnic": "vmnic3", "vlan": 20 }, "dns_servers": [ "dns01.local", "dns02.local" ] }, "installation_type": "esxi", "image_data": { "type": "vCenter_contentlib", "host": "vcenter01", "credential": { "username": "some_user", "password": "some_password" }, "content_library": "the_content_lib_name", "image_name": "some_image" }, "host_short_name": "esxi021" } redis_connection = StrictRedis(host='localhost', port=6379, charset="utf-8", decode_responses=True) # 拍平后存入哈希 flat_foo = flatten_dict(foo) redis_connection.hset("test", mapping=flat_foo) # 读取单个嵌套字段示例,比如取host_data下的ip地址 print(redis_connection.hget("test", "host_data:network:ip_address")) # 输出:192.168.0.10/24
这个方案完全基于原生Hash实现,不需要额外模块,还支持单独读写任意层级的字段,不需要加载整个字典。
方案2:使用RedisJSON模块存储
RedisJSON是Redis官方推出的JSON结构存储模块,原生支持完整的嵌套JSON结构读写,支持按路径查询、修改单个嵌套字段,性能远高于全局序列化的方案,你的Redis 6.2.5版本完全兼容RedisJSON 2.x版本。
示例实现代码:
from redis import StrictRedis redis_connection = StrictRedis(host='localhost', port=6379, charset="utf-8", decode_responses=True) # 直接存储整个嵌套字典 redis_connection.json().set("test_json", "$", foo) # 单独查询嵌套字段 print(redis_connection.json().get("test_json", "$.host_data.network.ip_address")) # 输出:['192.168.0.10/24'] # 单独修改嵌套字段 redis_connection.json().set("test_json", "$.host_data.network.vlan", 30)
这个方案是最贴合需求的实现,完全不需要处理序列化逻辑,原生支持嵌套结构操作。
方案3:局部序列化
如果既不能安装RedisJSON模块,也不想把整个结构拍平,可以仅把嵌套层级的value序列化为JSON字符串,平级字段保持原生存储:
import json from redis import StrictRedis redis_connection = StrictRedis(host='localhost', port=6379, charset="utf-8", decode_responses=True) # 仅嵌套字段序列化 foo_to_save = { "host_data": json.dumps(foo["host_data"]), "image_data": json.dumps(foo["image_data"]), "installation_type": foo["installation_type"], "host_short_name": foo["host_short_name"] } redis_connection.hset("test", mapping=foo_to_save) # 读取平级字段直接拿 print(redis_connection.hget("test", "installation_type")) # 读取嵌套字段反序列化对应value即可 host_data = json.loads(redis_connection.hget("test", "host_data"))
内容的提问来源于stack exchange,提问作者AlexK
相关产品推荐
相关产品推荐

