redis-内存模型
简单描述
c语言编写,K-V形式存储,K是String类型的,V支持5种不同的数据类型,分别是:string,list,hash,set,sorted set; 底层数据结构有:简单动态字符串(SDS),链表,字典,跳跃表,整数集合,压缩列表,对象。
内存模型
虽然有那么多种类型,但总的来说是K-V结构,所以所有的数据都是在一个hashtable中,详细的存储如下图
来自https://www.cnblogs.com/kismetv/p/8654978.html这里之所以要2个dictht有一个很重要的原因就是为了rehash,bucket是一个数组,存放指向dictEntry结构的指针。
value是redisObject类型:
type :具体的类型
encoding :对象的内部编码,redis支持的每种类型,都有至少两种内部编码
lru:对象最后一次被命令程序访问的时间
refcount:该对象被引用的次数,这里有个共享对象的概念。默认0-9999
ptr:指向具体的数据的指针
字符串—SDS结构(三个参数)
buf :字节数组,用来存储字符串
len :buf已使用的长度
free:buf未使用的长度
列表
压缩列表(ziplist):列表中元素数量小于512个;列表中所有字符串对象都不足64字节。
双端链表(linkedlist):不满足压缩列表时。
哈希
压缩列表(ziplist):哈希中元素数量小于512个;哈希中所有键值对的键和值字符串长度都小于64字节
哈希表(hashtable):不满足压缩列表时。另外redis的最外层K-V哈希,只有这种结构。
集合
整数集合(intset):集合中元素数量小于512个;集合中所有元素都是整数值。
哈希表(hashtable):不满足整数集合时。
有序集合
压缩列表(ziplist):有序集合中元素数量小于128个;有序集合中所有成员长度都不足64字节。
跳跃表(skiplist):不满足压缩列表时。
观察redis内存状态是否健康的几个参数
输入info memory:
used_memory :redis分配器分配的内存总量
used_memory_rss :redis进程占据操作系统的内存
mem_fragmentation_ratio:内存碎片比率,该值是used_memory_rss / used_memory的比值
mem_allocator:Redis使用的内存分配器,在编译时指定,默认是jemalloc。
内存碎片比率这个值一般大于1,且值越大表示内存碎片越多,redis还没怎么存入数据时例外,因为redis进程本身占用的内存不在used_memory里。如果该值比率小于1,表示redis使用了虚拟内存,这个时候内存已经不够用了。
如何减少内存碎片?
1.使用合理的内存分配器
2.如果内存碎片已经很大,可以考虑安全重启的方式,因为重启之后,redis会重新从备份文件读取数据,在内存中重排。
redis内存使用量评估
不应该只考虑key,value的大小,其实分配的内存远远不止,参考redis内存结构,除了key,value占用的空间外,dictEntry,redisObject,SDS的非buf参数占用部分,bucket都会占用,而且还有一点得注意,sds由jemalloc分配的空间应该是2的部分倍数,比如key是7字节的时候,sds占用16个字节(其他参数共9字节),但key是8字节时,sds占用17个字节,这个时候jemalloc会分配32。同理bucket列表的长度是2的指数倍。
所以基于上述,该如何优化呢?
1.利用jemalloc特性进行优化。比如key 8个字节变 key 7个字节。
2.尽量使用长整型/整型。
3.利用共享对象,可根据需求调整REDIS_SHARED_INTEGERS参数提高共享对象的个数。
4.关注内存碎片率。
rehash
跟hashmap rehash有什么不同?
redis是渐进式迁移,按entry为单位顺序迁移,有字段记录了迁移进度。
内部也是两个两张hash表,一张旧表,一张新表,迁移的时候旧表往新表迁移数据,当迁移完后,当前的新表又变成了旧表。
与Memcached比较
1. redis支持持久化,memecache 把数据全部存在内存之中,
2. redis支持更多类型