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的更多相关文章

  1. redis存储对象

      redis主要存储类型最常用的五种数据类型: String Hash List Set Sorted set redis存储对象序列化和反序列化 首先来了解一下为什么要实现序列化 为什么要实现序列 ...

  2. redis存储对象与对象序列化详解

    redis主要存储类型最常用的五种数据类型: String Hash List Set Sorted set redis存储对象序列化和反序列化 首先来了解一下为什么要实现序列化 为什么要实现序列化接 ...

  3. Java操作Redis存储对象类型数据

    背景描述      关于JAVA去操作Redis时,如何存储一个对象的数据,大家是非常关心的问题,虽然官方提供了存储String,List,Set等等类型,但并不满足我们现在实际应用.存储一个对象是是 ...

  4. Redis | 使用redis存储对象反序列化异常SerializationFailedException

    案例 使用Redis进行对象存储,在处理业务逻辑的时候,丛Redis获取对象发现反序列化失败,抛出如下异常: Caused by: org.springframework.data.redis.ser ...

  5. Redis存储对象(序列化和反序列化)

    代码以及实例: package com.hp.test; import redis.clients.jedis.Jedis; import java.io.*; public class Test3 ...

  6. redis存储对象,实体类新加字段空指针问题处理

    redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...

  7. cookie存储对象信息

    最近看到某公司某项目中用于保存多个城市信息到cookie中的方法,该方法的逻辑是按时间顺序记录最近访问过的三个城市的名字及id,逻辑包插入与含排重.插入与排重的代码如下: 1 2 3 4 5 6 7 ...

  8. Redis:存储对象的两种方式(序列化和json字符串)

    方式一:序列化操作   public class SerializeUtil {   /*   * 序列化   * */   public static byte[] serizlize(Object ...

  9. django-登录和退出及redis存储session信息

    登录 1.视图函数views.py # 登录 from django.contrib.auth import authenticate, login # authenticate:user认证 log ...

随机推荐

  1. JavaWeb开发中的分层思想(一)

    JavaWeb开发分层思想(一) 一.认识DAO.Service.Controller层 DAO(Data Access Object) 1.直接看英文意思就是"数据访问对象",也 ...

  2. Linux 自定义快捷命令

    Linux中一些比较常用的命令总是重复敲很麻烦,这个时候就可以使用 alias 来自定义快捷命令,用以简化操作.系统会有一些预定义的快捷命令,比如 ll 的效果就和 ls -l 一样. 可以使用 al ...

  3. gsoap多wsdl集成

    gsoap常规用法: 通过wsdl文件创建头文件 //通过wsdl文件创建头文件 wsdl2h [options] -o file.h ... WSDL and XSD files or URLs t ...

  4. 【Azure 应用程序见解】 Application Insights 对App Service的支持问题

    问题描述 Web App 发布后, Application Insights 收集不到数据了 问题分析 在应用服务(App Service)中收集应用的监控数据(如Request,Exception, ...

  5. 前端vue使用高德地图

    首先,注册Key 1.注册开发者账号,成为高德开放平台开发者 2.登陆之后,在进入「应用管理」 页面「创建新应用」 3.为应用添加 Key,「服务平台」一项请选择「 Web 端 ( JSAPI ) 」 ...

  6. 热更新基础--AssetBundle学习笔记

    一.简介 AssetBundle简称AB包,特定平台的资产压缩包(包括模型.贴图.预设体.音效.材质球等资产). 作用:Resources下的资源只读且打包后不可修改,而AB包存储位置自定,后期可以动 ...

  7. 设计原则:开闭原则(OCP)

    1.什么是开闭原则 开闭原则的英文是Open Closed Principle,缩写就是OCP.其定义如下: 软件实体(模块.类.方法等)应该"对扩展开放.对修改关闭". 从定义上 ...

  8. PBFT共识算法详解

    PBFT(Practical Byzantine Fault Tolerance,实用拜占庭容错) 一.概述 拜占庭将军问题最早是由 Leslie Lamport 在 1982 年发表的论文<T ...

  9. Google不兼容ShowModalDialog()弹出对话框的解决办法

    <script type="text/javascript"> //弹窗函数 function openDialog() { var url = "https ...

  10. MySQL数据库高级五:主从复制

    主机只能一台 从机去复制 单表500万上限