Redis字符串缩短后,何时回收多余内存?
Redis字符串缩短后的内存回收时机
比如,当一个30M的字符串被修剪到只剩3字节时,Redis会在什么时候释放那些闲置的内存?留着大量未使用的空间显然会造成资源浪费。
Redis字符串(SDS)结构
Redis的字符串采用简单动态字符串(SDS)实现,64位版本的结构如下:
struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; /* 已使用字节数 */ uint64_t alloc; /* 已分配字节数(不包含头部和终止符) */ unsigned char flags; /* 低3位表示类型,高5位未使用 */ char buf[]; /* 字符串内容缓冲区 */ };
sdstrim的实现(无内存回收逻辑)
以下是字符串修剪函数sdstrim的代码,它只负责修剪字符串内容,没有回收闲置内存的逻辑:
/* 移除字符串首尾连续出现的、属于cset(以null结尾的C字符串)的字符 * * 调用后,原sds字符串不再有效,所有引用必须替换为调用返回的新指针。 * * 示例: * * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); * s = sdstrim(s,"Aa. :"); * printf("%s\n", s); * * 输出结果为 "HelloWorld"。 */ sds sdstrim(sds s, const char *cset) { char *end, *sp, *ep; size_t len; sp = s; ep = end = s+sdslen(s)-1; while(sp <= end && strchr(cset, *sp)) sp++; while(ep > sp && strchr(cset, *ep)) ep--; len = (ep-sp)+1; if (s != sp) memmove(s, sp, len); s[len] = '\0'; sdssetlen(s,len); return s; }
内存回收的触发时机
从sdstrim的代码能看到,它仅更新了SDS的len字段(标记实际使用长度),但并没有调整alloc字段或释放多余的内存——这是Redis惰性内存回收策略的体现,具体回收时机分为三种:
- 主动调用收缩API:需要显式调用
sdsshrinktofit函数(对应Redis命令层面可以通过重新执行SET赋值,或使用MEMORY PURGE辅助),该函数会将SDS的内存分配缩小到实际使用的大小,直接释放闲置空间。比如在sdstrim执行后调用sdsshrinktofit(s),就能立刻回收30M到3字节之间的闲置内存。 - 后续写操作触发扩容或覆盖:如果后续对该字符串执行追加、修改等操作,当需要的内存超过当前
alloc时,Redis会重新分配新的内存块,旧的闲置内存会被内存分配器回收;但如果只是小范围修改,仍然会保留原有大内存块。 - 键被清理:当该字符串键被
DEL命令删除、过期自动清理,或者触发内存淘汰策略被移除时,整个SDS的内存会被Redis的内存分配器完整回收。
Redis的这种设计是为了平衡性能与内存利用率——保留闲置内存可以避免频繁内存分配/释放带来的系统开销,适合字符串可能再次扩容的场景;但如果确定不会再用到闲置空间,主动调用收缩操作就能避免资源浪费。
内容的提问来源于stack exchange,提问作者Name Null
相关产品推荐
相关产品推荐

