深化了解缓存的神奇世界-浅析Redis数据结构 (缓存深度)
Labs导读
(RemoteDictionaryServer)远程字典服务,是一款经过Key-Value存储的NoSql数据库,数据缓存在内存中,支持网络、可耐久化日志,提供多种言语的API,罕用的场景有高速缓存、散布式数据共享、散布式锁、限流和信息队列等。通常名目研发中,联合springframework封装的RedisTemplateAPI经常使用。
Part01、环境搭建
●操作系统:7
●集成环境:CLion
●编译环境:GCC9
●代码版本:redis-6.2.6
1.1环境装置
操作系统和集成环境的可自行装置。由于Centos7自动gcc版本较低,因此须要更新GCC版本,经过如下命令可成功编译环境的更新:
#装置centos-release-scl%yum-yinstallcentos-release-scl#装置devtoolsetGGC9%yum-yinstalldevtoolset-9-gccdevtoolset-9-gcc-c++devtoolset-9-binutils#激活对应的devtoolset%echosource/opt/rh/devtoolset-9/enable>>/etc/profile#检查版本%gcc-v
1.2编译和运转
从官网网站下载源码,解压,编译和运转。
%wget启动Redis%cdsrc%./redis-server#验证%cdsrc%./redis-cli
图片
经常使用Clion建设C工程,并导入源代码,确保GCC9是对应的编译环境,以调试形式启动redis-server模块,经常使用redis-cli客户端衔接服务端,设置断点,键入相应的命令启动调试。
图片
Part02、数据库的组织结构
首先从微观层面了解数据库的结构及组织相关。redisDB,dict,dictht,dictEntry,
redisObject等相关数据库结构定义在server.h,dict.h,sds.h和zipList.h等头文件中。
//server.htypedefstructredisDb{dict*dict;/*ThekeyspaceforthisDB*/dict*expires;/*Timeoutofkeyswithatimeoutset*/dict*blocking_keys;/*Keyswithclientswtingfor/>图片经过上图,可以了解到如下内容:
(1)RedisDB可有多个,经过redis.conf中的databases参数启动性能,自动是16个;
(2)每个RedisDB有两个"dictht"哈希表组成,区分是ht[0]和ht[1],这样做的目标是为了rehash,重要处置扩容和缩容的疑问,经过ht[0]和ht[1]相互搬迁数据成功rehash上班,而且每次命令只搬迁一个索引上方的数据,缩小系统操作期间,防止因数据量过大而影响性能;其实如今dict.c的dictRehash函数中。
(3)HASH表中存储的每个元素是dictEntry结构组成的链表。经过链式,处置两个key的哈希值正好落在同一个哈希桶中的哈希抵触疑问。
intdictRehash(dict*d,intn){intempty_visits=n*10;/*Maxnumberofemptybucketstovisit.*/if(!dictIsRehashing(d))return0;while(n--&&d->ht[0].used!=0){dictEntry*de,*nextde;/*Notethatrehashidxcan'toverflowaswearesuretherearemore*elementsbecauseht[0].used!=0*/assert(d->ht[0].size>(unsignedlong)d->rehashidx);while(d->ht[0].table[d->rehashidx]==NULL){d->rehashidx++;if(--empty_visits==0)return1;}/*在HASH桶中找到非空的索引后,开局链表的数据移动上班*/de=d->ht[0].table[d->rehashidx];/*MoveallthekeysinthisbucketfromtheoldtothenewhashHT*/while(de){uint64_th;nextde=de->next;/*在新的hash表中找到对应键值的索引*/h=dictHashKey(d,de->key)&d->ht[1].sizemask;/*把要参与的数据放在新的hash表对应索引链表的开局*/de->next=d->ht[1].table[h];d->ht[1].table[h]=de;/*更新计数器*/d->ht[0].used--;d->ht[1].used++;/*链表中的下一个Node*/de=nextde;}/*因数据已成功移动,因此清空老的hash表对应的桶*/d->ht[0].table[d->rehashidx]=NULL;/*指向下一个桶*/d->rehashidx++;}/*假设曾经rehashed了一切的表,监禁HT[0]的表空间,将HT[1]设置为以后的表,重置HT[1]*/if(d->ht[0].used==0){zfree(d->ht[0].table);d->ht[0]=d->ht[1];_dictReset(&d->ht[1]);d->rehashidx=-1;return0;}/*Moretorehash...*/return1;}
(4)dictEntry中的"key"由sds(便捷灵活字符串)结构组成。redis依据数据的长度,定义了不同类型的sds结构。例如:sdshdr8,sdshdr16,sdshdr32,sdshdr64;这样的结构定义,既节俭了空间,也处置了二进制安保(例如C言语的‘ ’)缓和冲区溢出(经过alloc-len可计算残余空间)等疑问。
//SDS.Hstruct__attribute__((__packed__))sdshdr8{uint8_tlen;/*used*/uint8_talloc;/*excludingtheheaderandnullterminator*/unsignedcharflags;/*3lsboftype,5unusedbits*/charbuf[];};struct__attribute__((__packed__))sdshdr16{uint16_tlen;/*used*/uint16_talloc;/*excludingtheheaderandnullterminator*/unsignedcharflags;/*3lsboftype,5unusedbits*/charbuf[];};
(5)redis有STRING,LIST,SET,ZSET,HASH,MODULE,STREAM七种数据类型;有sds,quicklist,ziplist,dict,zskiplist,stream七种底层数据结构;每种数据类型依据存储数据的大小,多少等,区分由不同的底层数据结构成功。例如list数据类型,由quicklist,ziplist区分成功;HASH数据类型,由dict,ziplist区分成功。
(6)dictEntry中的"*val"指向redisObject"结构,此结构中redisObject->type存储的是数据类型;redisObject->encoding存储的是底层的数据结构类型;redisObject->ptr存储详细的数据;相应的实如今object.c中。
//object.crobj*createQuicklistObject(void){quicklist*l=quicklistCreate();robj*o=createObject(OBJ_LIST,l);o->encoding=OBJ_ENCODING_QUICKLIST;returno;}robj*createZiplistObject(void){unsignedchar*zl=ziplistNew();robj*o=createObject(OBJ_LIST,zl);o->encoding=OBJ_ENCODING_ZIPLIST;returno;}robj*createSetObject(void){dict*d=dictCreate(&setDictType,NULL);robj*o=createObject(OBJ_SET,d);o->encoding=OBJ_ENCODING_HT;returno;}robj*createIntsetObject(void){intset*is=intsetNew();robj*o=createObject(OBJ_SET,is);o->encoding=OBJ_ENCODING_INTSET;returno;}robj*createHashObject(void){unsignedchar*zl=ziplistNew();robj*o=createObject(OBJ_HASH,zl);o->encoding=OBJ_ENCODING_ZIPLIST;returno;}
Part03、源码调试
3.1入口
正如一切的C代码一样,入口是service.c中的main函数。
图片
3.2redis命令入口
一切的redis命令定义在redisCommandTable数组中,类型为"redisCommand",经过函数指针的形式调用。例如下图中的Get和Set命令。
图片
图片
Part04、总结
以上区分从环境搭建,数据库的结构组织相关和源码调试启动了引见,假设你对redis源代码感兴味,执行起来吧!
redis常用数据结构介绍和业务应用场景分析
redis内置了很多常用数据结构,了解这些数据结构的功能和应用场景能够让我们在需求开发时灵活运用来解决实际问题。 String是redis中最基础的数据结构,你可以把它用作缓存最基础的kv(key-value)类型的缓存(value最大为512MB),只需要把需要缓存的对象进行string的编解码即可。 另外String也可以保存数值类型的数据,就可以来实现计数功能(redi提供了incr等原子操作) 常见应用场景 List列表更多的时候是把它当成队列使用(最大2^32 - 1个元素),使用入队出队功能,如果来使用它作为各种列表的话,很多时候不具备防重功能在使用的时候不是很方便。 常见应用场景 Set是一种无序不重复的集合,添加删除检查是否存在都是O(1)的时间复杂度。 常见应用场景 hash是一个map结构,可以像存储对象的多个字段一样存储一个key的多类数据。 常见应用场景 redis中的pub/sub可以实现广播功能,类似rocketmq中的broadcast 常见应用场景 除了上述最基本的数据结构外,redis还提供了一些其他的数据结构,有的是需要安装相关redis stack来使用的。 bitmap本质上还是使用的string字符串,不过可以通过bit来进行操作,把这个key的value值想象成bit组成的数组。 常见应用场景 bloomfilter(也叫布隆过滤器)可以理解成一种特殊的set集合,它可以用来判断一个值是否在这个集合中,不过不同于普通的set,它的判断存在一定误判的可能(假阳性),如果bloomfilter判断一个值不在这个集合中,那么一定不在,但是如果判断在,那么有可能不在。 常见应用场景 hyperloglog是一种概率性的去重计数数据结构,可以实现一定精度的去重计数 常见应用场景 geohash可以实现距离计算、距离查询等地理位置相关的功能 常见应用场景
redis缓存原理
1、Redis是一种内存高速cache,如果使用redis缓存,那经常被访问的内容会被缓存在内存中,需要使用的时候直接从内存调取,不知道比硬盘调取快了多少倍,并且支持复杂的数据结构,应用于许多高并发的场景中。 2、Redis支持主从同步。 数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。 这使得Redis可执行单层树复制。 存盘可以有意无意的对数据进行写操作。 由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。 同步对读取操作的可扩展性和数据冗余很有帮助。 zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。 可以理解了有两列的mysql表,一列存value,一列存顺序。 操作中key理解为zset的名字。 更多关于redis缓存原理,进入:查看更多内容
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。