您当前的位置:首页 > 电脑百科 > 数据库 > Redis

Redis缓存之String的滥用

时间:2022-04-24 10:40:28  来源:  作者:Java面试365

在我们日常开发中如果使用redis做缓存,那么使用最多的可能为String类型,String类型使用简单而且容易理解但这只是开发方面,如果业务数据量过大使用String类型存储可行性是否还是最高,我们可以依靠在线Redis内存预估统计工具http://www.redis.cn/redis_memory/如下统计

模拟1亿个String类型的键值对,key占用4个字节value占用4个字节,仅key,value占用内存800M,那Redis的String类型需要占用多少呢?如下所示

Redis缓存之String的滥用

 

key和value单纯的内存消耗只占据了Redis的String类型所需总内存的十分之一,也是说有十分之九是存储其它信息,那到底是什么呢?如下分析。

简单动态字符串SDS

Redis使用的String类型底层实现就是SDS简单动态字符串,为什么Redis需要封装而不是c自带的字符串呢?

SDS的优势

  • SDS获取字符串的长度时间复杂度为O(1),而C语言自带的需要遍历数组时间复杂度为O(N)。
  • SDS有效避免缓冲区溢出(在长度不足时可以扩容)。
  • SDS可以减少修改字符串带来的内存分配(C语言字符串修改N次都需要重新分配内存,SDS最多需要重新分配N次内存)。

SDS结构

SDS底层结构从3.x到6.x版本变化挺大需要分开学习,3.x结构简单如下所示

typedef char *sds;
struct sdshdr {
    // 记录buf数组已使用的长度
    unsigned int len;
    
    // 记录buf数组没有使用的长度
    unsigned int free;
    
    // 字符串保存位置
    char buf[];
};
Redis缓存之String的滥用

 

需要注意的是buf结尾是结束符'''是一定存在的,占用一个字节,但是在计算len时是不会计算结束标识符''的。

6.x版本SDS结构代码如下所示

typedef char *sds;
/* 
 * Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. 
 * sdshdr5未使用,其余都有使用
 */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used  已使用长度*/
    uint8_t alloc; /* 分配长度 不包括报头和空终止符,1个字节存储 */
    unsigned char flags; /* 高3位存储、低5位预留 */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; 
    uint16_t alloc; /* 分配长度 不包括报头和空终止符,2个字节存储 */
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; 
    uint32_t alloc; /* 分配长度 不包括报头和空终止符,4个字节存储 */
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; 
    uint64_t alloc; /* 分配长度 不包括报头和空终止符,8个字节存储 */
    unsigned char flags; 
    char buf[];
};

结构图如下所示

Redis缓存之String的滥用

 

RedisObject结构

Redis存在不同的数据类型,在这些不同的数据类型中又需要记录一些相同的信息如key最后访问时间、引用次数等所以需要将其封装为一个结构体(JAVA中的对象)来存储这些元素这就是RedisObject结构图如下所示。

Redis缓存之String的滥用

 

元数据type

元数据中的type为数据类型目前存在六种数据类型:string,hash,set,list,zset,stream可以通过命令type {key}获取类型

#### String类型
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> type name
string
#### List类型
127.0.0.1:6379> lpush keylist 1 zhangsan
(integer) 2
127.0.0.1:6379> type keylist
list
####  Hash类型
127.0.0.1:6379> hmset keyhash name zhangsan
OK
127.0.0.1:6379> type keyhash
hash
####  Set类型
127.0.0.1:6379> sadd keyset name zhangsan
(integer) 2
127.0.0.1:6379> type keyset
set
####  Sort Set类型
127.0.0.1:6379> zadd keyzset 1 zhangsan
(integer) 1
127.0.0.1:6379> type keyzset
zset
####  Bitmaps 类型
127.0.0.1:6379> setbit keybitmap 10 1
(integer) 0
127.0.0.1:6379> type keybitmap
string
####  Hyperloglogs类型 
127.0.0.1:6379> pfadd keyhyperloglogs 2 23 42 2
(integer) 1
127.0.0.1:6379> type keyhyperloglogs
string
####  Geospatial类型
127.0.0.1:6379> geoadd keygeo 13.361389 38.115556 test
(integer) 1
127.0.0.1:6379> type keygeo
zset
####  Stream类型
127.0.0.1:6379> xadd keystream * name zhangsan
"1650552771376-0"
127.0.0.1:6379> type keystream
stream

元数据encoding

encoding表示当前value值的编码格式有三种int、embstr、raw,可以通过命令object encoding key获取

####  如果值是数字编码类型就是int
127.0.0.1:6379> set name 1
OK
127.0.0.1:6379> object encoding name
"int"

#### 如果值是字符串同时长度小于等于44那么就是embstr
127.0.0.1:6379> set name1 "zhangsan"
OK
127.0.0.1:6379> object encoding name1
"embstr"

#### 如果值是字符串同时长度大于44
127.0.0.1:6379> set name2 "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
OK
127.0.0.1:6379> object encoding name2
"raw"

元数据refcount

refcount为被引用对象,当refcount=0表示可回收对象,可以通过命令refcount key查看引用次数。

RedisObject指针ptr

如果值的类型为int,那么ptr直接存储的就是这个int类型的值,不会去指向其它内存地址,如下所示。

Redis缓存之String的滥用

 

当值为字符串类型,同时字符串的长度小于等于44时,数据采用embstr编码格式编码,将RedisObject对象的元数据、指针、SDS分配到一片连续的内存空间,避免内存碎片。

为什么字符串长度需要小于等于44呢?

Redis中的内存分配器jemalloc认为超出64字节就是一个大字符串所以就以64为界,而元数据占8字节、指针占8字节,SDS分两种情况

1、如果是6.x版本SDS其它内存消耗4个字节(1B(len)+1B(alloc)+1B(flag)+1B(''))所以是64-8-8-4=44。

2、如果是3.x版本SDS其它内存消耗9个字节(4B(len)+4B(free)+1B(''))所以是64-8-8-9=39。

版本不同编码格式判断的临界值会有稍微不同。

Redis缓存之String的滥用

 

当值是字符串但是长度大于44时,编码格式变为raw,SDS和RedisObject的内存分配不再连续,SDS内存空间将独立分配,如下所示。

Redis缓存之String的滥用

 

dictEntry结构

那么除了SDS动态字符串和RedisObject结构,一个简单的String操作还会涉及到哪些内存分配呢?当然是有的那就是哈希桶中的元素dictEntry,dictEntry中包含key、value、next等值如下所示。

Redis缓存之String的滥用

 

总结

String使用虽然简单但不是万金油哪里都能使用,在数据量大的时候我们需要选择合适的数据结构来避免这种情况的发生,如list、set、sort set、hash等这些数据结构就能节省dictEntry所需要的内存,下面以6.x版本演示如下所示( info memory可以查看内存使用情况)。

#########################hash集合类型#############################
127.0.0.1:6379> info memory
# Memory
used_memory:866600
127.0.0.1:6379> hset obj name zhangsan
(integer) 1
127.0.0.1:6379> info memory
# Memory   第一次创建hash结构需要 消耗80字节
used_memory:866680
127.0.0.1:6379> hset obj addr beijin
(integer) 1
127.0.0.1:6379> info memory
# Memory    后续在hash结构中加入属性  只消耗16字节
used_memory:866696

#########################String类型###############################
127.0.0.1:6379> info memory
# Memory
used_memory:866720
127.0.0.1:6379> set teststr zhangsan
OK
127.0.0.1:6379> info memory
# Memory  消耗72字节
used_memory:866792
127.0.0.1:6379> set teststr1 zhangsan
OK
127.0.0.1:6379> info memory
# Memory  消耗72字节
used_memory:866864

如果开发中需要存储业务数据到Redis中,对数据类型的选择一定要慎重,一味的滥用String在数据量大时对Redis的负担将是巨大的,会影响RDB持久化、故障转移、主从同步等。



Tags:Redis缓存   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
看完Redis缓存穿透、缓存击穿、缓存雪崩来吊打面试官!
一、前言「Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。」Redis在缓存应用中还是很广泛的,项目中也经常使用。基本上面试中肯定...【详细内容】
2023-12-06  Search: Redis缓存  点击:(158)  评论:(0)  加入收藏
Redis缓存使用的三种模式
在互联网应用中,缓存技术是提高系统性能和稳定性的重要手段之一。Redis作为一种高性能的缓存数据库,被广泛应用于各种互联网应用中。本文将介绍Redis缓存使用的三种模式,包括Ca...【详细内容】
2023-10-19  Search: Redis缓存  点击:(127)  评论:(0)  加入收藏
面试为啥都问Redis缓存?赶紧补一下
大家好,我是哪吒。我第一次接触缓存的时候,是用map做的,当时做一个实时数据同步的功能。需求看似简单,一取一传 当时是通过websocket获取服务端数据。 然后根据数据类别,将数据缓...【详细内容】
2023-09-06  Search: Redis缓存  点击:(239)  评论:(0)  加入收藏
Redis缓存之String的滥用
在我们日常开发中如果使用Redis做缓存,那么使用最多的可能为String类型,String类型使用简单而且容易理解但这只是开发方面,如果业务数据量过大使用String类型存储可行性是否还...【详细内容】
2022-04-24  Search: Redis缓存  点击:(418)  评论:(0)  加入收藏
一文弄懂Redis缓存一致性最佳实践参考案例
背景概述最近团队里我们在密集的讨论 Redis 缓存一致性相关的问题,电商核心的域如商品、营销、库存、订单等实际上在缓存的选择上各有特色,那么在这些差异的业务背后,我们有没...【详细内容】
2022-01-12  Search: Redis缓存  点击:(394)  评论:(0)  加入收藏
利用AOP自定义Redis缓存注解
背景在查询类开发中我们有使用缓存的场景,一般可以使用Redis作为缓存,来缓解数据库如MySQL的压力。使用缓存的步骤为:“(1)从Redis缓存中获取数据,如果存在数据,直接返回值。(2)如果...【详细内容】
2020-08-28  Search: Redis缓存  点击:(245)  评论:(0)  加入收藏
帮你解读什么是Redis缓存穿透和缓存雪崩(包含解决方案)
作为一个内存数据库,redis也总是免不了有各种各样的问题,这篇文章主要是针对其中两个问题进行讲解:缓存穿透和缓存雪崩。并给出一些解决方案。这两个问题是基本问题也是面试常...【详细内容】
2020-03-14  Search: Redis缓存  点击:(505)  评论:(0)  加入收藏
Redis缓存知识问题
缓存穿透:条件:缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要...【详细内容】
2020-02-21  Search: Redis缓存  点击:(346)  评论:(0)  加入收藏
Redis缓存击穿、缓存穿透、缓存雪崩
本篇文章主要谈谈Redis中很容易出现的三大问题现象:缓存击穿、缓存穿透以及缓存雪崩。不过在介绍这三个问题现象之前,我们首先需要先来了解下Redis中key的过期淘汰机制。众所...【详细内容】
2019-10-12  Search: Redis缓存  点击:(712)  评论:(0)  加入收藏
redis缓存穿透,缓存击穿,缓存雪崩原因
01前言在我们日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如...【详细内容】
2019-08-26  Search: Redis缓存  点击:(790)  评论:(0)  加入收藏
▌简易百科推荐
兄弟,王者荣耀的段位排行榜是通过Redis实现的?
在王者荣耀中,我们会打排位赛,而且大家最关注的往往都是你的段位,还有在好友中的排名。作为程序员的你,是否思考过这个段位排行榜是怎么实现的?了解它的实现原理,会不会对上分有所...【详细内容】
2024-04-15    dbaplus社群  Tags:Redis   点击:(3)  评论:(0)  加入收藏
16个Redis常见使用场景总结
来源:blog.csdn.net/qq_39938758/article/details/105577370目录 缓存 数据共享分布式 分布式锁 全局ID 计数器 限流 位统计 购物车 用户消息时间线timeline 消息...【详细内容】
2024-04-11    书圈  Tags:Redis   点击:(8)  评论:(0)  加入收藏
Linux获取Redis 性能指标方法
一、监控指标Ø 性能指标:PerformanceØ 内存指标: MemoryØ 基本活动指标:Basic activityØ 持久性指标: PersistenceØ 错误指标:Error二、监...【详细内容】
2024-04-11  上海天正信息科技有限    Tags:Redis   点击:(10)  评论:(0)  加入收藏
Redis与缓存一致性问题
缓存一致性问题是在使用缓存系统,如Redis时经常遇到的问题。当数据在原始数据源(如数据库)中发生变化时,如何确保缓存中的数据与数据源保持一致,是开发者需要关注的关键问题。一...【详细内容】
2024-04-11  后端Q    Tags:Redis   点击:(8)  评论:(0)  加入收藏
Redis 不再 “开源”,未来采用 SSPLv1 和 RSALv2 许可证
Redis 官方于21日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause...【详细内容】
2024-03-27  dbaplus社群    Tags:Redis   点击:(23)  评论:(0)  加入收藏
Redis“叛逃”开源,得罪了几乎所有人
内存数据库供应商Redis近日在开源界砸下了一块“巨石”。Redis即将转向双许可模式,并实施更为严格的许可条款。官方对此次变更的公告直截了当:从Redis 7.4版本开始,Redis将在Re...【详细内容】
2024-03-25    51CTO  Tags:Redis   点击:(13)  评论:(0)  加入收藏
如何使用 Redis 实现消息队列
Redis不仅是一个强大的内存数据存储系统,它还可以用作一个高效的消息队列。消息队列是应用程序间或应用程序内部进行异步通信的一种方式,它允许数据生产者将消息放入队列中,然...【详细内容】
2024-03-22  后端Q  微信公众号  Tags:Redis   点击:(22)  评论:(0)  加入收藏
Redis不再 “开源”
Redis 官方今日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause 开...【详细内容】
2024-03-21  OSC开源社区    Tags:Redis   点击:(14)  评论:(0)  加入收藏
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  编程技术汇    Tags:Redis   点击:(50)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  楼仔  微信公众号  Tags:Redis   点击:(46)  评论:(0)  加入收藏
站内最新
站内热门
站内头条