初识Redis
什么是Redis?
Redis是一种非关系型数据库,在高并发和海量数据操作环境下相对传统的关系型数据库有着很大的优势。Redis的基本数据类型有字符串String,列表List,字典HashMap,集合Set,有序集合ZSet。
下面来讲一下两种数据类型底层的数据结构与实现。
- String字符串
由于Redis是用C语言实现的,这就不得不说到C语言是怎么储存字符串的。C语言通过一个字符数组来保存一个字符串,其结尾为’ ’。显然,如果Redis直接使用C语言的方式来储存字符串的话很容易造成溢出和不必要的时间开销(获取字符串长度)。Redis使用经过封装的SDS简单动态字符串。其有一个header,header包含了字符串的信息:该字符串的长度len,以及最大长度malloc,然后就是负责储存字符的buf字符数组了。
特此搬来网上大神的总结:
C字符串 |
SDS动态简单字符串 |
获取字符串长度为O(n) |
获取字符串长度为O(1) |
API是不安全的,可能会造成缓存区溢出 |
API是安全的,不会造成缓冲区溢出 |
修改字符串N次必然要执行N次内存分配 |
修改字符串N次至多执行N次内存分配 |
只能保存文本数据 |
可以保存文本或者二进制数据 |
可以使用所有<string.h>的所有函数 |
可以使用<string.h>的部分函数 |
- HashMap字典
Hash字典储存键值对的原理是首先利用Hash函数通过键来作为参数来计算index值,再根据index值选择数组的位置,在数组的index位置下储存节点。如果是index值冲突的话,会以链表的形式储存,操作的方法是将新来的键值对节点插入到最前面。
由于hash的键值对是动态变化的,为了维持hash的负载均衡因子在合理的范围之内,必须进行rehash过程。这个过程一共分为3个步骤:1.为字典里的另一个表分配空间,扩容的话其大小为大于user(user为目前保存的键值对数量)*2的最小的2^n的数,缩容的话其大小为大于user(user为目前保存的键值对数量)的最小的2^n的数 2.将原来使用的表的键值对复制到新的表中去 3.释放原来的表,并将新扩容或缩容的表标记为使用的表,完成扩容。
以上是rehash的普通操作,但是Redis的rehash却是渐进式的,第一步和普通的一样,完成对扩容目标表的分配空间,区别在于第二步:将rehashidx的值设置为0,表示将对rehash开始工作时,将从第一个元素开始,并且逐渐递增,在rehash进行期间,当字典进行增删改查操作时,顺带将原来的表rehash到新表中去,然后rehashidx+1,直到完成所有键值对rehash,将rehashidx设置为-1.
渐进式rehash的优势是将rehash所耗费的操作分散到各个增删改查操作中去,避免了集中进行rehash,如果储存的数据过大大话会导致服务中断。