一)String类型:可以使用object encoding name就可以查看字符串的编码
SDS,flags的值不同,那么len和alloc所表示的值的数据范围也不同,所以flags的只是为了标识SDS头的总大小;
alloc和len刚开始进行申请内存空间的时候都是相同的
String是redis中最常见的数据结构类型:
1)最基本的编码方式是RAW,是基于简单动态字符串来实现的,存储上线是512mb,每一次存储一个String类型的数据的时候,需要先申请内存创建一个RedisObject的内存空间,再申请SDS的内存空间,申请的时候有两次内存的申请,释放的时候也是有两次内存的释放;
2)如果存储的SDS的长度是小于44个字节的,那么会直接采用EMBSTR编码,此时的ObjectHead和SDS是一段连续的内存空间,申请内存的时候只是需要调用一次内存分配函数即可(因为每一次分配内存都要涉及到用户态和内核态的切换)
SDS一共占用48个字节,RedisObject占用16个字节,一共是64个字节(不会超过64字节),Redis在底层采用的内存分配算法是以2^N来做内存分配的,64恰好是一个分片大小,就不会产生内存碎片,所以实际使用String类型的时候,尽量字符串长度小于44字节
3)如果进行存储的字符串是整数值,况且在Long_Max范围内,底层就会使用INT编码,直接将数据存储在RedisObject的指针位置(指针刚好8个字节),就不需要SDS了
Redis中的List数据结构
Redis的List类型可以从首和尾进行操作元素
哪一个数据结构可以满足以上特征呢
1)使用普通双向链表,可以从双端进行访问内存占用比较高,内存碎片比较多
2)使用ZipList,可以从双端进行访问,内存的占用比较低,存储的上限比较低,双端访问不是基于指针实现的,而是记录每一个结点的大小,推算出内存地址的,因为他是申请连续内存空间的,如果数据量不多还可以,但是如果数据量过大,申请较大的连续内存空间效率可能非常低,所以是不建议使用ZipList来存储过多的数据的,只能存储少量数据
3)QuickList:底层是使用双端链表+压缩列表,可以从双端进行访问,内存占用是比较低的,包含多个Ziplist,存储上限较高,双端列表的每一个节点不是数据而是一个ZipList
3.1)在3.2版本之前,Redis使用ZipList(只能存储少量数据)和LinkedList来实现List,当元素数量小于512况且元素大小小于64字节的时候采用ZipList编码,超过使用LinkedList编码;
3.2)在3.2版本以后,Redis统一使用QuickList来实现List;
1)上面这个方法是通用的插入方法,where是队首或者是队尾,lpush和rpush底层都是调用的pushGenericCommand方法,最后的这个xx参数,如果你进行传递了,也就是true,只有当前的key存在,才会执行push命令,如果不存在,一般是不会传递的,也就是当你第一次进行插入的时候,key不存在,就会自动创建这个key,并分配内存空间;
2)第一个参数client代表客户端,当redis的客户端和服务端建立连接的时候,都会被封装成一个client对象,里面包含着客户端的各种信息,包括客户端要执行的命令,每一个命令都会被分成N段(lpush key value l1 l2 l3),命令本质上就是一个一个的字符串,每一个字符串会使用空格隔开,分成多个命令段放到一个数组中,也就是argv数组,其实最后执行命令的时候,就是从c里面的argv数组中取出一个一个的参数命令段,拼接成完整命令执行;
3)j=2开始遍历是在寻找未来可以存放在list中的元素,里面的if语句是针对用户传递过来的针对于元素大小的校验,argc就是整个argv数组元素的个数;
4)lookupkeywrite方法是在寻找你要向list集合中的哪一个key进行push,第一个参数是c->db,是客户端要进行访问哪一个数据库,取出当前客户端要访问的数据库,第二个参数在数据库中去选择对应key去寻找对应的value(list);
最终方法的返回值是一个RedisObject对象(list)
5)然后去进行检查当前类型是否是一个List类型,根据RedisObject中的type属性来检查当前对象的类型,如果不是list类型直接返回;
6)如果RedisObject不存在并且xx为true,说明没有list就直接返回了;
7)否则就会在createQuickList中创建一个全新的List:
8)createObject方法底层创建了一个全新的RedisObject对象,并将RedisObject中数据的指针指向创建的新的list,并在方法底层针对RedisObject设置全新的编码和类型;
9)最后的quicklistsetoptions中限制QuickList中每一个ZipList的大小,是否压缩,server.list_compress_depth是压缩深度,如果是0表示不进行压缩,如果是1表示QuickList的首尾各有一个不压缩,中间节点压缩,如果是2表示QuickList的首尾各有两个节点不压缩,中间节点压缩
10)dbAdd方法是真正的添加元素
Redis中的Set数据结构:对元素的查询效率非常高
Set是Redis中的单列集合,包括以下特点:
1)不保证有序性
2)保证元素唯一
3)求并集,差集,补集
1)首先就是跳表, 查询性能比较不错,但是不太合适,首先跳表要去根据score值进行排序,但是Set集合不需要有序,但是Set集合中还没有score值;
2)Set是Redis中的集合,不一定保证元素有序性,但是需要保证元素唯一,对查询性能要求极高,为了保证查询效率和唯一性,set使用dict编码,dict中的key用来存储元素,value统一使用null;
3)当存储的Set集合里面全部都是整数的时候,并且元素数量不超过set-max-intset-entries的时候,底层默认使用的是IntSet编码,是连续存储空间,元素数量也不能太多,从而来节省内存;
1)subject就是一个RedisObject对象,里面的编码可能是Inset或者是dict,第二个参数是要插入的值
2)进行新增元素的时候,如果RedisObject中的编码是HT编码,那么直接插入元素value即可
3)如果RedisObject中的编码类型是IntSet那么直接判断当前value是否是整数,如果是整数,直接添加元素到IntSet里面,如果此时添加元素成功了,直接判断当前元素个数是否大于了set-max-intset-entries的值,如果大于,那么直接将编码转换成HT
4)如果value值不是整数直接转换成HT编码;