《Redis深度历险》读书笔记一(redis五种基本类型数据)
五种基础数据结构
String字符串
-
内存分配
redis的字符串是动态字符串,是可以修改的字符串,内部结构的实现类似于java的ArrayList,采取预分配冗余空间的方式来减少内存的频繁分配。内部为当前字符串分配的实际空间capacity一般要高于实际字符串长度len。当字符串长度小于1MB是,扩容都是加倍,当字符串长度超过1MB,扩容时一次只会多扩1MB,字符串最大长度为512MB。
-
计数
如果value是一个整数,可以对他进行自增操作,范围在signed long的最大值和最小值之间,超出这个范围,redis会报错。
127.0.0.1:6379> set codehole 9223372036854775807 OK 127.0.0.1:6379> incr codehole (error) ERR increment or decrement would overflow
-
字符串由多个字节组成,没个字节又由8bit组成,如此便可以将一个字符串看成很多bit的组合,这便是bitmap位图的数据结构。
list列表
redis的列表相当于java里的LinkedList,是链表而不是数组,意味着list的插入和删除操作非常快,索引定位很慢。
-
用途
redis的列表结构常用来做异步队列使用,将需要延后处理的任务结构体序列化成字符串,塞进redis列表,另一个线程从这个列表中轮询数据进行处理。
-
慢操作
lindex相当于java链表中get方法,需要对链表进行遍历,性能随着index增大而变差。-1表示倒数第一个,-2倒数第二个。
ltrim可以指定两个参数start,end,在这个区间内的值,ltrim保留,区间外的都不要,可以通过ltrim实现一个定长的链表。
ltrim listName 1 0 // 清空整个列表,区间长度为负数。
-
快速列表
redis底层存储的实际上是一个快速链表(quicklist)结构。
在列表元素较少的情况下,会使用一块连续的内存存储,这个结构式ziplist,即压缩列表。将所有的元素彼此紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist,因为普通的链表需要的附加指针空间太大,会浪费空间,而且会加重内存的碎片化,比如某普通链表每个数据都要有prev和next指针,所以redis将链表和ziplist结合起来编程quicklist,既满足了快速的插入删除性能,又不会出现太大的冗余空间。
hash字典
redis的字典只能是字符串,和java的hashmap rehash的方式不一样。
java的HashMap在字典很大时,rehash是个耗时操作,需要一次性全部rehash,redis为了追求高性能,不能堵塞服务,采用了渐进式rehash策略。
渐进式rehash
保留新旧两个hash结构,查询时会同时查询两个hash结构,然后再后续的定时任务以及hash操作指令中,循序渐进的将就hash的内容一点点迁移到新的hash结构中。
set集合
相当于java语言里面的hashSet,内部的键值对事无序的,唯一的,内部实现了一个特殊的字典,字典中所有的value都是一个值null。
zset有序列表
类似于java的SortedSet和HashMap的结合体,一方面是一个set,保证了value的唯一性,另一方面,为每一个value赋予了一个score权重,内部实现用的是一种叫做“跳跃列表”的数据结构。
跳跃列表
因为zset要支持随机的插入和删除,所以不宜用数组来表示。但是链表无法做到快速位置定位。数组可以用二分法快速定位。
跳跃列表类似于层级之,最下面一层所有的元素都会穿起来,然后每隔几个元素挑选出一个代表,再将这几个代表用另外一级指针串起啊来,然后在这些代表里再跳出二级代表,最终形成了金字塔结构。
跳跃列表之所以跳跃,是因为内部元素可能身兼数职,同时处于L0,L1,L2层中,可以快速的在不同层次之间进行跳跃。
定位插入点时,先在顶层进行定位,然后下潜到下一级定位,一直下潜到最底层找到合适的位置,将新元素插进去。