Redis基础

1.Redis为什么要自己实现一个SDS?

因为在c语言中,没有类似Java类型中的string类型,字符只能存储在char [ ]中,而char数组中的字符串分割使用“” ,这样就存在二进制的安全问题:图片视频音频存储时候使用二进制,所以如果使用char[ ]存储图片视频音频的二进制数据的话,就会出问题。所以自定义一种存储结构,就没有安全问题,并且可以自动根据字符长度进行动态扩容,内存也会随着字符串的变更重新分配,不需要担心内存溢出。 并且,用sds存储字符串,查找字符串长度的时候直接从sds的结构里取,不用遍历数组,时间复杂度为o(1)

2.基于Set如何实现用户关注模型?

为每个用户定义一个set,存储该用户关注的用户集合,集合存储用户的唯一标识id,有了用户的关注人信息后可以做以下几个操作:
相互关注:用集合里自己关注的人的id,去查找该用户的关注人集合,看自己是否在集合中,如果在里面,说明自己关注的人也关注了自己。
我关注的人也关注了他: 用自己关注的人的集合,跟自己关注的人关注的集合做交集,那么就能计算出自己关注的人跟自己都共同关注了谁。
查找可能认识的人:可能认识的人首先要有一个规则,怎么才算可能认识的人,暂定,自己关注的人里面,有两个人以上共同关注了某个人,就认为算是可能认识的人吧。按这个规则来设计的话:遍历自己关注的人列表后,用数学上的组合方式,分别对每个人的关注列表做交集,其结果就是自己可能认识的人。

3.总结Redis五大数据类型,和编码转换的条件

#3.1 String:

存储键值对。操作命令比较多,可以一次设置一个键值对,也可以一次设置多个键值对,可以指定键值对的过期时间。总之string字符串的操作命令能满足各种对字符串的操作。String数据类型定存储结构是通过hashtable实现的,源码中结构是dictEntry,该结构中有要存储的键和值引用。键的存储使用sds结构,直接避免了内存分配可能出现的问题和性能问题。
如果键存储的值是整型数值,且不大于long的范围(2^63-1=9223372036854775807)时,键类型为int;当键的长度小于44时,类型为embstr;当长度大于44时,自动转化为raw类型。Embstr类型和raw类型的内存结构稍有区别,embstr的存储内存是连续的,一次性分配就完成键初始化,raw需要两次(redisobject和sds分别需要分配一次内存,导致raw类型的键删除也需要多释放一次内存)。但是embstr 只读的(因为设置为长度增加要重新分配内存,redisobject和sds),所以embstr类型的键修改时要先转化为raw并且键从embstr转变为raw后,不可逆转不会因为键长度再变短而变回embstr。
String可用来做网页缓存,分布式锁,计数,限流等等。

##3.2 hash:

可以将多个键值对添加到一个键对应的值中。 类似java中的hashmap。
数据结构:
当对象下所有的键值对的键和值的长度都小于64个字节时,或者该对象下存放的键值对个数总数小于512个时,使用压缩列表ziplist存储键值对。它的结构是一个双向链表,链表的每个节点存有指向前一个节点和下一个节点的指针,以及前一个节点和当前节点的长度。
当对象下键值对个数总数大于512个或者有键值的长度超过64字节时,键值对转换成哈希表存储。这个哈希表的存储结构大致上,思想和java的hashMap一样,都是采用数组加链表存储键值对,解决键的hash冲突问题。可用来存储一个对象关联的多条数据的信息,这种有子父关系且子数据比较多的业务场景数据都可以考虑hash。

 3.3 List:

链表,先进先出。可以左进右出也可以右进左出。
设计思想上和java数据结构中的链表一样。
存储时,当数据量较小时用ziplist存储,当数据量大到一定程度,转换为Lindedlist存储。3.2版本后,list类型的统一用quicklist来存储,每个节点都是一个zipList。可以用来存储那种需要按序存储和取出展示的场景的业务数据。比如时间线。

3.4 set:

集合。无重复元素。Redis提供了对两个集合的交并差的操作。
数据结构:当元素都是整数类型时,用intset存储,否则使用hashtable的键存储集合元素。如果元素超过512个,也会使用hashtable存储。
可用来存储不需要重复元素的业务场景数据,比如社交网站上的用户之间的关注信息。

3.5 sortedset:

和set数据类型的作用类似,只不过多了个排序的功能,存入的元素都多了个分数。取出时可以按分数排序。
存储结构:元素数量小于 128 个 或者所有 member 的长度都小于 64 字节 时用ziplist存储,否则用skiplist+dict存储。可用来做排行榜这种业务场景下的应用数据存储,只要那种需要排名的场景都可以考虑使用sortedset来存储。

4 dict里面为什么要定义两个哈希表ht[0] ht[1]?hash扩容是怎么实现的?

因为哈希表存储数据时采用琏地址法,当元素多时,哈希表的性能会下降,性能取决于哈希表的长度和保存的元素个数的比率。为了提高哈希表的性能,当哈希表存储的元素个数很多时,就把元素都取出来重新计算一下存储位置存放到另一个哈希表里,并清空当前哈希表。所以用两个哈希表的目的就是为了提高哈希表的性能,存一个清空一个,并且每次rehash时都重新计算元素位置进行存储。 hash扩容怎么做的:
ratio = used / size,已使用节点与字典大小的比例超过1:5触发扩容。扩容时创建一个新的hashtable,大小是另一个hashtable中已使用的元素长度的二倍,把老的元素重新计算hash值和索引后放置到新的hashtable里面,然后完成后清空另一个哈希表。

原文地址:https://www.cnblogs.com/mike-mei/p/14663698.html