Redis 并无直接使用数据结构来实现键值对数据库, 而是基于这些数据结构建立了一个对象系统, 这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象, 每种对象都用到了至少一种咱们前面所介绍的数据结构。html
经过这五种不一样类型的对象, Redis 能够在执行命令以前, 根据对象的类型来判断一个对象是否能够执行给定的命令。 使用对象的另外一个好处是, 咱们能够针对不一样的使用场景, 为对象设置多种不一样的数据结构实现, 从而优化对象在不一样场景下的使用效率。git
除此以外, Redis 的对象系统还实现了基于引用计数技术的内存回收机制: 当程序再也不使用某个对象的时候, 这个对象所占用的内存就会被自动释放; 另外, Redis 还经过引用计数技术实现了对象共享机制, 这一机制能够在适当的条件下, 经过让多个数据库键共享同一个对象来节约内存。github
Redis 使用对象来表示数据库中的键和值, 每次当咱们在 Redis 的数据库中新建立一个键值对时, 咱们至少会建立两个对象, 一个对象用做键值对的键(键对象), 另外一个对象用做键值对的值(值对象)。redis
Redis 中的每一个对象都由一个 redisObject
结构表示, 该结构中和保存数据有关的三个属性分别是 type
属性、 encoding
属性和 ptr
属性:数据库
1 typedef struct redisObject { 2 3 // 类型 4 unsigned type:4; 5 6 // 编码 7 unsigned encoding:4; 8 9 // 指向底层实现数据结构的指针 10 void *ptr; 11 12 // ... 13 14 } robj;
咱们能够看到一个对象中主要包含了三种字段。缓存
type: 表示对象的类型。好比String,List,Hash等等服务器
encoding:表示对象底层用的是什么数据结构。如INT(整数),EMBSTR(简洁版sds),RAW(sds),HT(map)等等数据结构
ptr:ptr是一个指针,指向对象所用的数据结构。函数
以下图所示: post
set k v
k是String类型,embstr数据结构,也就是简洁版的sds,后续讲。
以前咱们讲数据结构,都没有见到过embStr,是的,我也是看到这一节才知道有这个东西的。
Redis为了优化,搞了一个embStr,他是为了专门存短字符串的一种编码优化方式。
embstr
编码将建立字符串对象所需的内存分配次数从 raw
编码的两次下降为一次。raw
编码会调用两次内存分配函数来分别建立 redisObject
结构和 sdshdr
结构, 而 embstr
编码则经过调用一次内存分配函数来分配一块连续的空间, 空间中依次包含 redisObject
和 sdshdr
两个结构。由于一个连续,一个不连续。embstr
编码的字符串对象只须要调用一次内存释放函数, 而释放 raw
编码的字符串对象须要调用两次内存释放函数。理由同上embstr
编码的字符串对象的全部数据都保存在一块连续的内存里面, 因此这种编码的字符串对象比起 raw
编码的字符串对象可以更好地利用缓存带来的优点。总的来讲,由于embstr分配的是一段连续的内存,使得它分配释放内存都是一次,因此效率会有所提升。同时embste <==> sds 为44个字节。
从下图中,咱们能够明确看到。 len <= 44 都是embster的数据结构,若是len > 44 则转变为raw。至于为啥44。
你们能够去算一下。参考文章:
https://zhuanlan.zhihu.com/p/67876900
https://xiaoyue26.github.io/2019/01/19/2019-01/redis%E7%9A%84embstr%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF39B/
Redis为了节省内存,真的是操碎了心。
c语言不像Java,Go等语言,自己不具有自动回收内存机制。Java的内存回收致使STW一直被人诟病,最近看了ZGC的数据,Java真的是崛起了。
所以Redis 在本身的对象系统中构建了一个引用计数(reference counting)技术实现的内存回收机制, 经过这一机制, 程序能够经过跟踪对象的引用计数信息, 在适当的时候自动释放对象并进行内存回收。
但熟悉JVM的都知道,引用计数他有一种缺陷就是,解决不了循环引用的问题。
如 A <==> B 但已经没有其余任何节点引用AB了,但AB因为相互引用,计数为1,永远不会被回收。因此Java用了GC ROOT。
但Redis不知道为啥不存在这个问题,找了资料,也没找出什么缘由。大多都说Redis没有复杂的结构,因此?有大佬能解答下不?
引用计数咱们能够经过 OBJECT refcount token 命令,查询到token被引用了几回,若是为0,那么则能够回收了。
还有最重要的一点是,Redis对整数 0-9999(共1W个整数)作了缓存。相似于Java对-128-127作缓存同样。
可是没有对值的字符串,如aaaaa的这种缓存,毕竟判断一个字符串是否在库里面,须要扫整个库,很是耗时,而且cpu压力很是的大。
处于优化,折中的考虑,也就缓存了0-9999吧。其实看看淘宝商品的价格,缓存0-100足矣,毕竟0-100占据了99%的商品。
具体可看:http://redisbook.com/preview/object/share_object.html
参考: