Redis 存储对象信息是用 Hash 还是 String
Redis 内部使用一个 RedisObject 对象来表示所有的 key 和 value,RedisObject 中的 type,则是代表一个 value 对象具体是何种数据类型,它包含字符串(String)、链表(List)、哈希结构(Hash)、集合(Set)、有序集合(Sorted set)。

日常工作中我们存储对象信息的时候,一般有两种做法,一种是用 Hash 存储,另一种是 String 存储。但好像并没有所谓的最佳实践,那么实际上到底用什么数据结构存储更好呢?
首先简单回顾下,Redis 的 Hash 和 String 结构。
String
String 数据结构是简单的 key-value 类型,value 其实不仅是 String,也可以是数字。Redis 中的 String 可以表示很多语义:
字符串(bits)
整数
浮点数
这三种类型,Redis 会根据具体的场景完成自动转换,并且根据需要选取底层的承载方式。String 在Redis 内部存储默认就是一个字符串,被 RedisObject 所引用,当遇到 incr、decr 等操作时会转成数值型进行计算,此时 RedisObject 的 encoding 字段为int。
在存储过程中,我们可以将用户信息使用 Json 序列化成字符串,然后将序列化后的字符串存入 Redis 进行缓存。

由于 Redis 的字符串是动态字符串,可以修改,内部结构类似于 Java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。如上图所示,内部为当前字符串实际分配的空间 capacity,一般高于实际字符串长度 len。
假设我们要存储的结构是:
{
"name": "xiaowang",
"age": "35"
}
如果此时将此用户信息的 name 改为“xiaoli”,再存到 Redis 中,Redis 是不需要重新分配空间的。而且我们在读取和存储数据的时候只需要对做 Json 序列化与反序列化,比较方便。
Hash
Hash 在很多编程语言中都有着很广泛的应用,而在 Redis 中也是如此。在 Redis 中,Hash 常常用来缓存一些对象信息,如用户信息、商品信息、配置信息等,因此也被称为字典(dictionary),Redis 的字典使用 Hash table 作为底层实现, 一个 Hash table 里面可以有多个哈希表节点,而每个哈希表节点保存了字典中的一个键值对。实际上,Redis 数据库底层也是采用 Hash table 来存储键值对的。
Redis 的 Hash 相当于 Java 的 HashMap,内部结构实现与 HashMap 一致,即数组+链表结构。只是 reHash 方式不一样。

前面说到 String 适合存储用户信息,而 Hash 结构也可以存储用户信息,不过是对每个字段单独存储,因此可以在查询时获取部分字段的信息,节省网络流量。不过 Redis 的 Hash 的值只能是字符串,存储上面的那个例子还好,如果存储的用户信息变为:
{
"name": "xiaowang",
"age": 25,
"clothes": {
"shirt": "gray",
"pants": "read"
}
}
那么该如何存储"clothes"属性又变成了该用 String 还是 Hash 的问题。
String 和 Hash 占用内存的比较
既然两种数据结构都可以存储结构体信息。到底哪种更加合适呢?
首先我们用代码先插入 10000 条数据,然后用可视化工具来看看内存的占用情况。
const Redis = require("ioRedis");
const Redis0 = new Redis({port: 6370});
const Redis1 = new Redis({port: 6371});
const user = {
name: 'name12345',
age: 16,
avatar: 'https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=256767015,24101428&fm=26&gp=0.jpg',
phone: '13111111111',
email: '1111111@11.email',
lastLogon: '2021-04-28 10:00:00',
}
async function main() {
for (let i = 0; i < 10000; i++) {
await Redis0.set(`String:user:${i}`, Json.Stringify(user));
await Redis1.hmset(`Hash:user:${i}`, user);
}
}
main().then(process.exit);
先看 Redis0:

再来看看 Redis1:

可以看到还是有点差距的,但是差距并不明显。
网友讨论
网上的用户也有同样的疑问, 因为值的长度是不确定的,所以不知道采用 String 还是 Hash 存储更有效率。

这里我主要给大家翻译下该问题下优质的答案:
适合用 String 存储的情况:
每次需要访问大量的字段
存储的结构具有多层嵌套的时候
适合用 Hash 存储的情况:
在大多数情况中只需要访问少量字段
自己始终知道哪些字段可用,防止使用 mget 时获取不到想要的数据
总结
本文主要介绍了Redis 存储对象信息是用 Hash 还是 String,建议是大部分情况下使用 String 存储就好,毕竟在存储具有多层嵌套的对象时方便很多,占用的空间也比 Hash 小。当我们需要存储一个特别大的对象时,而且在大多数情况中只需要访问该对象少量的字段时,可以考虑使用 Hash。
Redis 存储对象信息是用 Hash 还是 String的更多相关文章
- redis存储对象
redis主要存储类型最常用的五种数据类型: String Hash List Set Sorted set redis存储对象序列化和反序列化 首先来了解一下为什么要实现序列化 为什么要实现序列 ...
- redis存储对象与对象序列化详解
redis主要存储类型最常用的五种数据类型: String Hash List Set Sorted set redis存储对象序列化和反序列化 首先来了解一下为什么要实现序列化 为什么要实现序列化接 ...
- Java操作Redis存储对象类型数据
背景描述 关于JAVA去操作Redis时,如何存储一个对象的数据,大家是非常关心的问题,虽然官方提供了存储String,List,Set等等类型,但并不满足我们现在实际应用.存储一个对象是是 ...
- Redis | 使用redis存储对象反序列化异常SerializationFailedException
案例 使用Redis进行对象存储,在处理业务逻辑的时候,丛Redis获取对象发现反序列化失败,抛出如下异常: Caused by: org.springframework.data.redis.ser ...
- Redis存储对象(序列化和反序列化)
代码以及实例: package com.hp.test; import redis.clients.jedis.Jedis; import java.io.*; public class Test3 ...
- redis存储对象,实体类新加字段空指针问题处理
redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...
- cookie存储对象信息
最近看到某公司某项目中用于保存多个城市信息到cookie中的方法,该方法的逻辑是按时间顺序记录最近访问过的三个城市的名字及id,逻辑包插入与含排重.插入与排重的代码如下: 1 2 3 4 5 6 7 ...
- Redis:存储对象的两种方式(序列化和json字符串)
方式一:序列化操作 public class SerializeUtil { /* * 序列化 * */ public static byte[] serizlize(Object ...
- django-登录和退出及redis存储session信息
登录 1.视图函数views.py # 登录 from django.contrib.auth import authenticate, login # authenticate:user认证 log ...
随机推荐
- Srping源码之XMLBeanFactory
本文是针对Srping的XMLBeanFactory来进行解析xml并将解析后的信息使用GenericBeanDefinition作为载体进行注册,xmlBeanFactory已经在Spring ...
- Fishing Master HDU - 6709
题目链接:https://vjudge.net/problem/HDU-6709 题意:给出n条鱼的抓捕时间k和煮鱼时间a[i],你要在最短时间把所有鱼都煮了.(注意你只有一口锅) 思路:首先我们必须 ...
- 【linux】驱动-8-一文解决设备树
目录 前言 8. Linux设备树 8.1 设备树简介 8.2 设备树框架 8.2.1 设备树格式 8.2.1.1 DTS 文件布局 8.2.1.2 node 格式 8.2.1.3 propertie ...
- Dynamics CRM产生公共签名,避免每次插件换环境重新输入签名密钥账号密码
在Dynamcs CRM项目维护交接过程中,我们经常会使用其他合作者的插件代码.但是每次拿到别人代码编译的时候插件密钥都要重新输入密钥的账号密码.而且如果密钥都是的话比较麻烦.所以这里就针对这个问题做 ...
- 京东效率专家带你快速落地DevOps
行业内的公司纷纷在招聘DevOps工程师,企业的DevOps转型看起来迫在眉睫,公司内部也要设计和开发DevOps平台,DevOps已经成为了所有IT从业人员应知应会的必备技能. 为你提供一套清晰的D ...
- 自动化kolla-ansible部署centos7.9+openstack-train-超融合单机架构
自动化kolla-ansible部署centos7.9+openstack-train-超融合单机架构 欢迎加QQ群:1026880196 进行交流学习 环境说明: 1. 满足一台电脑一个网卡的环 ...
- 自动化kolla-ansible部署ubuntu20.04+openstack-victoria之镜像制作ubuntu16.04-16
自动化kolla-ansible部署ubuntu20.04+openstack-victoria之镜像制作ubuntu16.04-16 欢迎加QQ群:1026880196 进行交流学习 制作Ope ...
- 2. IntelliJ Idea 常用快捷键列表
Ctrl+E,最近的文件 Ctrl+Shift+E,最近更改的文件 Shift+Click,可以关闭文件 Ctrl+[ OR ],可以跑到大括号的开头与结尾 Ctrl+F12,可以显示当前文件的结构 ...
- Redis持久化——AOF日志
最新:Redis内存--内存消耗(内存都去哪了?) 最新:Redis持久化--如何选择合适的持久化方式 最新:Redis持久化--AOF日志 更多文章... 上一篇文章Redis持久化--内存快照(R ...
- Cobalt-Strike Office宏利用与免杀
1.打开Cobalt-Strike生产Office宏病毒. 首先需要设置监听器.因为钓鱼的目标比较单纯,在这里就不采用域前置技术. 然后使用攻击模块,生产Office宏病毒. 设置好监听器. 生成宏病 ...