Redis基础数据结构&基本应用
读书多了,容颜自然改变,许多时候,自己可能以为许多看过的书籍都成了过眼云烟,不复记忆,其实他们仍是潜在的。在气质里,在谈吐上,在胸襟的无涯,当然也可能显露在生活和文字里。by 三毛
Redis有5中基础的数据结构:string(字符串),list(列表),hash(字典),set(集合)和zset(有序集合);这些是Redis中最基础和最重要的部分
string是Redis中最简单的字符串,其内部表示就是一个数组,并且它是一个动态字符串,是可修改的字符串,其内部结构的实现类似于ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,即当前字符串分配的实际空间一般要高于实际字符串长度
注意:当字符串长度小于1MB时,扩容都是加倍现有的空间.如果字符串长度超过1MB,扩容时一次只会多扩1MB的空间(字符串最大长度是512MB).
使用:将要存入的信息使用JSON序列化成字符串,然后将序列化后的字符串塞进Redis,取信息时会经过一次反序列化的过程
键值对set name codeOKget name"code"exists name(integer) 1del name(integer) 1get name(nil)批量键值对:对多个字符串进行读写,节省网络开销set name1 codeOKset name2 superOKmget name1 name2 name31) "code"2) "super"3) (nil)mset name1 boy name2 girl name3 unknowmget name1 name2 name31) "boy"2) "girl"3) "unknow"过期和set命令扩展set name codeget name"code"expire name 5 #5s后过期setex name 5 code #5s后过期,等价于set+expiresetnx name code #如果name不存在就执行set创建,如果已经存在则创建不成功计数:如果value是一个整数,还可以进行自增操作(范围是signed long最大值和最小值之间,超出范围Redis会报错)set age 30OKincr age(integer) 31incrby age 5(integer) 36incrby age -5(integer) 31
字符串由多个字节组成,每个字节又由8个bit组成,如此便可以将一个字符串看成很多bit 的组合,这便是bitmap(位图)数据结构,后面会讲.
Redis的list就相当于LinkedList(是链表,不是数组),这意味着list的插入和删除操作快,时间复杂度是O(1),同时也意味着索引定位很慢,时间复杂度是O(n).列表中每个元素都使用双向指针顺序,串起来可以同时支持向前向后遍历
Redis列表结构常用来做异步队列使用.将需要延后处理的任务结构体序列化成字符串,塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理
队列:常用于消息排队和异步逻辑处理> rpush books java golang(integer) 2> llen books(integer) 2> lpop books"java"> lpop books"golang"> lpop books(nil)栈:不常用> rpush books java golang(integer) 2> rpop books"golang"> rpop books"java"> rpop(nil)
慢操作:
lindex相当于java链表的get(int index)方法,其实际操作是对链表进行遍历,性能随参数增大而变差
ltrim(保留),ltrim的两个参数start_index和end_index定义了一个区间,在这个区间内的值,ltrim要保留,其它的都被砍掉,可以使用ltrim实现一个定长链表
注意:index可以为负数,表示倒数的元素
rpush books java python golang(integer) 3lindex books 1"python"lrange books 0 -11) "java"2) "python"3) "golang"ltrim books 1 -1OKlrange books 0 -11) "python"2) "golang"ltrim books 1 0 #清空了整个列表OKllen books(integer) 0以上命令皆谨慎使用
快速列表:
实际上Redis列表的底层存储的不是一个简单的LinkedList,而是称之为"快速链表"(quicklist)的一个数据结构(不使用普通链表的原因是普通链表需要附加的指针空间太大,会浪费空间,还会加重内存的碎片化)
首先在列表元素较少的情况下,会使用一块连续的内存存储,这个结构是压缩列表(ziplist),它将所有的元素彼此紧紧的挨着一起存储,分配的是一块连续的内存;当数据量较多时才会采用quicklist
quicklist的组成是将链表和ziplist结合起来,也就是将多个ziplist使用双向指针串起来使用
Redis的hash就相当于HashMap,是一个"数组"+"链表"的二维结构,当hash数组碰撞时就会将碰撞的元素使用链表串接起来;与HashMap不同的是,Redis字典的值只能是字符串
HashMap在字典很大时候,rehash是个耗时操作,需要一次性全部rehash;Redis为了追求高性能,不能堵塞服务,所以采用渐进式rehash策略
渐进式rehash会在rehash的同时,保留新旧两个hash结构,查询时会同时查询两个hash结构,然后在后续定时任务以及hash操作指令中,循序渐进的将旧hash中的内容一点点迁到新的hash结构中,当搬迁完成,新的hash结构就会取而代之
hash结构在存储信息时,与字符串一次性全部序列化整个对象不同,hash可以对结构中每个字段单独存储;这样获取信息时就可以进行部分获取;但是存储消耗也要高于字符串
hset books java "think in java" #如果有空格要用引号包起来(integer) 1hset books golang "think in go"(integer) 1hgetall books1) "java"2) "think in java"3) "golang"4) "think in golang"hlen books(integer) 2hget books java"think in java"hset books golang "learning golang" #更新操作,所以返回0(integer) 0hmset books pyhton "learning python" c "c premiere" #批量setOK
同字符串一样,hash结构中单个子key也可以进行计数,对应的指令hincrby,其用法和字符串的incr用法一致
Redis的set集合相当于HashSet,它内部的键值对是无序的,唯一的;它的结构就相当于一个特殊的字典,字典中所有的value都是Null
> sadd books java(integer) 1> sadd books java #不允许重复(integer) 0> sadd books c golang(integer) 2> smembers books #实际行和插入的顺序并不一致,set是无序的1) "c"2) "golang"3) "java"> sismember books java #查询是否存在(integer) 1> sismember books r(integer) 0> scard books #获取长度(integer) 3> spop books #弹出一个"c"
类似于SortedSet和HashMap结合体,一方面具有set的value唯一性,另一方面可以给每个value赋予一个score,代表这个value的排序权重,其内部实现实际上是"跳跃列表"
zadd books 9.0 java(integer) 1zadd books 8.9 c(integer) 1zadd books 8.0 golang(integer) 1zrange books 0 -1 #按score排序列出,参数区间为排名范围1) java2) c3) golangzrevrange books 0 -1 #逆序列出zcard books #相当于count()zscore books c #获取指定value的score"8.9000000000000004" #内部score使用double存储,所以存在小数点精度问题zrank java #获取排名zrangebyscore books 0 8.91 #根据分值区间遍历zset1) golang2) czrangebyscore books -inf 8.91 withscores #根据分值区间(-inf, 8.91]遍历zset,同时返回分值(inf代表infinite,无穷大d的意思)zrem books java #删除value
跳跃列表:
跳跃列表类似于一种层级制,最下面一层所有元素都会串起来,然后每个几个元素挑出一个代表,然后再将这几个代表使用另外一级指针串起来,然后在这些代表里面挑出二级代表,再串起来,以此类推
-
create if not exists:如果容器不存在,那就创建一个,再进行操作
-
drop if not elements:如果容器中元素没有了,那么立即删除容器,释放内存
Redis所有数据结构都可以设置过期时间,时间到了,Redis会立即删除相应的对象.
需要注意的是,过期是以对象为单位的,比如一个zset结构的过期就是zset对象的过期,而不是其中的某个元素过期
如果一个字符串设置了过期时间,在经过set修改之后并且没有重新设置过期时间,它的过期时间会消失
