ITPub博客

首页 > 数据库 > 数据库开发技术 > redis系列之“万金油”的String,为什么不好用了?

redis系列之“万金油”的String,为什么不好用了?

原创 数据库开发技术 作者:greencoatman 时间:2023-04-01 17:40:04 0 删除 编辑

Redis 的 string 类型虽然非常灵活,但是它所占用的内存确实比较大。这是因为 Redis 在存储字符串数据时,会为每个字符串数据分配额外的内存空间来存储一些元数据,例如字符串长度、引用计数等等。

另外,当字符串数据被修改时,Redis 会为其分配一个新的内存空间,而不是直接在原有内存空间上进行修改。这种写时复制的策略可以提高 Redis 的性能和可靠性,但同时也会增加内存的消耗。

Redis 的 string 类型是最简单、最基础的数据类型之一,它用于存储字符串或二进制数据。Redis 的 string 类型采用了简单动态字符串(Simple Dynamic String)的数据结构,它具有以下特点:

  1. 内部实现

Redis 的 string 类型采用的是 C 语言中的字符串实现方式,即使用一个字符数组来存储字符串,同时还记录了字符串的长度信息。在 Redis 中,字符串类型的结构体定义如下:

typedef struct sdshdr {    // 记录 buf 数组中已使用的字节数量    int len;    // 记录 buf 数组中未使用的字节数量    int free;    // 字节数组,用于保存字符串内容    char buf[];} sdshdr;
  1. 动态扩容

由于 Redis 的字符串类型需要动态存储字符串或二进制数据,因此需要动态扩容。在 Redis 中,当字符串需要扩容时,Redis 会根据当前字符串的长度和预留空间的大小,计算出字符串需要扩容的大小,并且重新分配一块新的内存空间,然后将原有的字符串内容拷贝到新的内存空间中。

  1. 二进制安全

Redis 的字符串类型是二进制安全的,它能够存储任意二进制数据,包括图片、音频、视频等非文本格式的数据。由于 Redis 使用了动态字符串的数据结构,因此它没有对存储的数据进行编码,而是直接存储原始的字节序列,这使得 Redis 的字符串类型非常适合存储二进制数据。

如果你的应用场景需要频繁地读写大量的字符串数据,并且对内存的消耗比较敏感,那么可以考虑使用 Redis 的其他数据类型来替代字符串类型,例如:

  1. Redis Hash:如果你需要存储一些具有结构化的数据,例如用户信息、商品信息等等,可以使用 Redis 的 hash 类型来代替字符串类型。使用 hash 类型可以节省内存空间,并且可以方便地进行字段级别的读写操作。

  2. Redis List:如果你需要存储一些列表数据,例如用户的购物车信息、消息队列等等,可以使用 Redis 的 list 类型来代替字符串类型。使用 list 类型可以支持列表的插入、删除、截取等操作,并且可以通过索引进行访问。

  3. Redis Set:如果你需要存储一些集合数据,例如用户的好友列表、标签信息等等,可以使用 Redis 的 set 类型来代替字符串类型。使用 set 类型可以去重,并且可以支持集合的交、并、差等操作。

  4. Redis Sorted Set:如果你需要存储一些有序集合数据,例如排行榜、时间线等等,可以使用 Redis 的 sorted set 类型来代替字符串类型。使用 sorted set 类型可以按照分数进行排序,并且可以进行范围查询、排名查询等操作。


当我们需要存储一些结构化数据时,例如用户信息,我们可以使用 Redis 的 hash 类型来替代字符串类型。

举个例子,假设我们要存储一个用户的信息,包括用户名、年龄、性别、邮箱等字段,如果使用 Redis 的字符串类型来存储这些信息,我们可能会这样写

SET user:1 "name:张三, age:20, gender:男, email:zhangsan@example.com"


这样的话,每次需要访问用户信息时,我们需要将字符串进行解析,提取出需要的字段值,比较繁琐。

而如果我们使用 Redis 的 hash 类型来存储这些信息,我们可以这样写:

HMSET user:1 name 张三 age 20 gender 男 email zhangsan@example.com


这样的话,我们就可以将每个字段作为 hash 的一个 key-value 对来存储,访问用户信息时,我们只需要通过 key 来获取对应的 value,非常方便。例如,我们可以这样获取用户的姓名:

HGET user:1 name

这样的写法不仅更加直观,而且可以节省内存空间,因为 Redis 的 hash 类型使用的是一张哈希表来存储数据,相比于字符串类型,可以更加高效地存储结构化数据。

Redis 的 hash 类型之所以比字符串类型更加节省内存,主要有以下两个方面的原因:

  1. 散列表的数据结构

Redis 的 hash 类型使用了散列表(Hash Table)的数据结构,它是一种非常高效的数据结构。散列表通过将键(Key)映射到散列桶(Hash Bucket)中的索引来存储数据,因此能够快速地进行插入、删除和查找操作。

在 Redis 的实现中,每个 hash 类型的键值对都会存储在散列表中,因此相对于使用字符串类型存储结构化数据,使用 hash 类型可以更加高效地利用内存空间。

  1. 只存储字段和值

Redis 的字符串类型存储结构化数据时,需要将每个字段名和值都存储为字符串,而这些字符串本身也需要占用内存空间。而 Redis 的 hash 类型只需要存储字段名和对应的值,因此可以大大减少存储数据所需的内存空间。

举例说明如何用集合类型保存单值的键值对?

在保存单值的键值对时,可以采用基于 Hash 类型的二级编码方法。

这里说的二级编码,就是把一个单值的数据拆分成两部分,前一部分作为 Hash 集合的 key,后一部分作为 Hash 集合的 value,这样一来,我们就可以把单值数据保存到 Hash 集合中了。以图片 ID 1101000060 和图片存储对象 ID 3302000080 为例,我们可以把图片 ID 的前 7 位(1101000)作为 Hash 类型的键,把图片 ID 的最后 3 位(060)和图片存储对象 ID 分别作为 Hash 类型值中的 key 和 value。按照这种设计方法,我在 Redis 中插入了一组图片 ID 及其存储对象 ID 的记录,并且用 info 命令查看了内存开销,我发现,增加一条记录后,内存占用只增加了 16 字节,如下所示:


127.0.0.1:6379> info memory# Memoryused_memory:1039120127.0.0.1:6379> hset 1101000 060 3302000080(integer) 1127.0.0.1:6379> info memory# Memoryused_memory:1039136

在使用 String 类型时,每个记录需要消耗 64 字节,这种方式却只用了 16 字节,所使用的内存空间是原来的 1/4,满足了我们节省内存空间的需求。

原文作者:二进制跳动;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2022-07-08

  • 博文量
    312
  • 访问量
    141081