Redis 存储对象信息用 Hash 和String的区别

目录
  • 前言
  • String
  • Hash
  • String 和 Hash 占用内存的比较
  • 网友讨论
  • 总结

前言

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 时获取不到想要的数据

总结

以上的环境博主都是部署在3A服务器上的,感兴趣的朋友可以自己也部署一套。本文主要介绍了 Redis 存储对象信息是用 Hash 还是 String,建议是大部分情况下使用 String 存储就好,毕竟在存储具有多层嵌套的对象时方便很多,占用的空间也比 Hash 小。当我们需要存储一个特别大的对象时,而且在大多数情况中只需要访问该对象少量的字段时,可以考虑使用 Hash。

到此这篇关于Redis 存储对象信息用 Hash 和String的区别的文章就介绍到这了,更多相关Redis Hash与String内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • redis删除hash的实现方式

    目录 redis删除hash方式 redis之hash类型 redis中存取hash类型 常用命令 hash命令小结 总结 redis删除hash方式 在工作中遇到删除hash类型的缓存时遇到了,怎样也删不掉redis里面的缓存,后来发现,hash类型和string类型的换成删除方式不一样,所以在这里做一下整理,后面再有遇到其他类型的,还会陆续的完善. 详细如下: 针对hash类型的缓存删除,函数: 因为需要删除很多缓存内容,所以在common.php写的公共函数进行调用, function p

  • Redis基本数据类型哈希Hash常用操作

    目录 Redis数据类型Hash常用操作 一.hset 二.hget 三.hmset 四.hmget 五.hgetall 六.hdel 七.hlen 八.hexists 九.hkeys 十.hvals 十一.hincrby 十二.hsetnx Redis数据类型Hash常用操作 redis里的hash是一个string类型的field(字段)和value(值)的映射表.特别适合用于存储对象,每个hash可以存储40多亿键值对. 熟悉python的童鞋可以想象成字典dict.之前的数据类型存储都是

  • redis如何取hash的值

    目录 redis取hash的值 redis中存值是Hash冲突如何解决 总结 redis取hash的值 命令:可以取得对应的hash中的具体值 hmget s_account:208 account accountname 结果:“www” 如图 命令:取得hash中的所有key hkeys s_account:208 结果: redis中存值是Hash冲突如何解决 Redis 中的 Hash和 Java的 HashMap 更加相似,都是数组+链表的结构.当发生 hash 碰撞时将会把元素追加到

  • RedisTemplate常用操作方法总结(set、hash、list、string等)

    目录 String类型 Hash类型 List类型 Set类型 zSet类型 Redis常用的数据类型: String Hash List Set zSet Sorted set String类型 保存和读取String(最常用的) System.out.println("缓存正在设置........."); redisTemplate.opsForValue().set("key1","value1"); redisTemplate.opsFo

  • Redis Hash序列化存储的问题及解决方案

    目录 SDR序列化方式有多种 对Redis的存储设置是我自己写的 更改序列化方法 更改序列化方式 继续使用JdkSerializationRedisSerializer 这里说的是Spring Data Redis(一下简称SDR)设置Hash存储的序列化. SDR序列化方式有多种 如: StringRedisSerializer JdkSerializationRedisSerializer Jackson2JsonRedisSerializer OxmSerializer 等等 目前我有个需

  • redis 存储对象的方法对比分析

    redis 存储对象的方法对比 问题背景: 原来项目里面全部是直接redis存储对象的json数据,需要频繁的序列化和反序列化,后来考虑更换项目中的redis存储对象为hash对象存储的,但是获取后不能方便的set get操作,很是蛋疼,怎么才能解决这个问题呢? 1.1 直接存储对象的json 存放redis的时候,直接先用fastJson 或者 jackJson或者Gson把对象序列化为json数据,然后用直接存放,key表示用户id或许和openid,value则是对象的json数据 pub

  • Redis如何存储对象

    目录 Redis存储对象 首先引入依赖 创建一个Jedis实例 方式一:将对象转化为JSON字符串存入redis 方式二:将对象序列化后存到redis 方式三:将对象用Hash数据类型存储 Redis可以存储java对象吗 答案:是可以 Redis存储对象 在以往面试中,多次被问到Redis怎么存储对象,我支支吾吾半天回答不上来,毕竟我只用过String类型,为了避免以后的尴尬,今天花点时间来整理一下. 本人用的Jedis客户端 首先引入依赖         <dependency>    

  • Java 实现Redis存储复杂json格式数据并返回给前端

    问题背景 在Java Web项目中,经常需要前端请求数据,后台从数据库中查询并计算最后返回json格式数据给前端. 而每次请求都需要计算一次可能比较浪费时间,这时我们可以将计算好的结果保存在redis中,下次请求时先判断redis中是否已经存在,如果是则直接从redis里取出返回,因为是在内存中,所以比较快. 而自己在项目中遇到的json格式数据比较复杂,下面记录一下redis存储对象和json格式数据的几种方式以及遇到的问题. 存储方式 1. 直接使用String存储 String类型是Red

  • Redis如何存储对象与集合示例详解

    前言 大家都知道在项目中,缓存以及mq消息队列可以说是不可或缺的2个重要技术.前者主要是为了减轻数据库压力,大幅度提升性能.后者主要是为了提高用户的体验度,我理解的是再后端做的一个ajax请求(异步),并且像ribbmitmq等消息队列有重试机制等功能. 这里主要讲redis如何把对象,集合存入,并且取出.下面话不多说了,来一起看看详细的介绍吧. 1.在启动类上加入如下代码 private Jedis jedis;private JedisPoolConfig config;private Je

  • Redis之GEO存储地理位置信息的使用

    目录 常用API geoadd geopos geodist georadiusbymember geohash 在外卖软件中的附近的美食店铺.外卖小哥的距离,打车软件附近的车辆,交友软件中附近的小姐姐.我们都可以利用redis的GEO地理位置计算得出. 1.Redis 的 Geo 是在 3.2 版本才有的 2.使用 geohash 保存地理位置的坐标 3.使用有序集合(zset)保存地理位置的集合 常用API 命令 描述 geoadd 增加某个地理位置的坐标 geopos 获取某个地理位置的坐

  • 浅谈Redis存储数据类型及存取值方法

    Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合) String存取值: 是 redis 最基本的类型 一个 key 对应一个 value.value其实不仅是String,也可以是数字.string 类型是二进制安全的.意思是 redis 的 string 可以包含任何数据.比如jpg图片或者序列化的对象.string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512M

  • C#中Cookie之存储对象

    本文通过代码详细给大家做讲解,具体内容如下: 做项目过程中,用户登陆之后,需要将用户的信息存到Cookie中,但因为Cookie中只能存储字符串,所以想到了先把用户实体序列化成Json串,存储在Cookie中,用到的时候再拿出来反序列化. 道理很简单,网上的例子也很多,但还是遇到一些小困难.下面与大家分享成果.(我的开发环境为VS2012,.net framework版本为4.0,) C#中Json与对象之间的互相转换 下载并引用Newtonsoft.Json.dll 定义一个简单的用户实体:

  • 详解Spring Cloud Gateway 数据库存储路由信息的扩展方案

    动态路由背景 ​ 无论你在使用Zuul还是Spring Cloud Gateway 的时候,官方文档提供的方案总是基于配置文件配置的方式 例如: # zuul 的配置形式 routes: pig-auth: path: /auth/** serviceId: pig-auth stripPrefix: true # gateway 的配置形式 routes: - id: pigx-auth uri: lb://pigx-auth predicates: - Path=/auth/** filte

  • 详解在Java程序中运用Redis缓存对象的方法

    这段时间一直有人问如何在Redis中缓存Java中的List 集合数据,其实很简单,常用的方式有两种: 1. 利用序列化,把对象序列化成二进制格式,Redis 提供了 相关API方法存储二进制,取数据时再反序列化回来,转换成对象. 2. 利用 Json与java对象之间可以相互转换的方式进行存值和取值. 正面针对这两种方法,特意写了一个工具类,来实现数据的存取功能. 1. 首页在Spring框架中配置 JedisPool 连接池对象,此对象可以创建 Redis的连接 Jedis对象.当然,必须导

随机推荐