Redis缓存之String的滥用( 二 )


#### String类型127.0.0.1:6379> set name zhangsanOK127.0.0.1:6379> type namestring#### List类型127.0.0.1:6379> lpush keylist 1 zhangsan(integer) 2127.0.0.1:6379> type keylistlist####  Hash类型127.0.0.1:6379> hmset keyhash name zhangsanOK127.0.0.1:6379> type keyhashhash####  Set类型127.0.0.1:6379> sadd keyset name zhangsan(integer) 2127.0.0.1:6379> type keysetset####  Sort Set类型127.0.0.1:6379> zadd keyzset 1 zhangsan(integer) 1127.0.0.1:6379> type keyzsetzset####  Bitmaps 类型127.0.0.1:6379> setbit keybitmap 10 1(integer) 0127.0.0.1:6379> type keybitmapstring####  Hyperloglogs类型 127.0.0.1:6379> pfadd keyhyperloglogs 2 23 42 2(integer) 1127.0.0.1:6379> type keyhyperloglogsstring####  Geospatial类型127.0.0.1:6379> geoadd keygeo 13.361389 38.115556 test(integer) 1127.0.0.1:6379> type keygeozset####  Stream类型127.0.0.1:6379> xadd keystream * name zhangsan"1650552771376-0"127.0.0.1:6379> type keystreamstream元数据encodingencoding表示当前value值的编码格式有三种int、embstr、raw,可以通过命令object encoding key获取
####  如果值是数字编码类型就是int127.0.0.1:6379> set name 1OK127.0.0.1:6379> object encoding name"int"#### 如果值是字符串同时长度小于等于44那么就是embstr127.0.0.1:6379> set name1 "zhangsan"OK127.0.0.1:6379> object encoding name1"embstr"#### 如果值是字符串同时长度大于44127.0.0.1:6379> set name2 "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"OK127.0.0.1:6379> object encoding name2"raw"元数据refcountrefcount为被引用对象,当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# Memoryused_memory:866600127.0.0.1:6379> hset obj name zhangsan(integer) 1127.0.0.1:6379> info memory# Memory   第一次创建hash结构需要 消耗80字节used_memory:866680127.0.0.1:6379> hset obj addr beijin(integer) 1127.0.0.1:6379> info memory# Memory    后续在hash结构中加入属性  只消耗16字节used_memory:866696#########################String类型###############################127.0.0.1:6379> info memory# Memoryused_memory:866720127.0.0.1:6379> set teststr zhangsanOK127.0.0.1:6379> info memory# Memory  消耗72字节used_memory:866792127.0.0.1:6379> set teststr1 zhangsanOK127.0.0.1:6379> info memory# Memory  消耗72字节used_memory:866864


推荐阅读