Redis数据结构
redis的数据结构有5种,分别为string(字符串)、set(集合)、zset(有序集合)、hash(哈希)、list(列表)。
1.string 字符串
字符串string是redis最简单的数据结构,redis的数据结构都是以唯一的key作为字符串名称,然后通过唯一的key获取不同的value值,不同类型的数据结构的差异就在于value的结构不一样。字符串的用途非常广泛,一个常见的应用就是缓存用户信息,将用户信息序列化成json字符串,然后将序列化后的字符串缓存到redis中,redis的字符串是动态字符串,是可以修改的字符串,类似java arraylist的数据结构,采用预分配冗余空间的方式来减少内存的频繁分配。如图
内部为当前字符串实际分配的空间,capacity ⼀般要⾼于实际字符串⻓度 len。当字符串⻓度⼩于 1M时,扩容都是加倍现有的空间,如果超过 1M,扩容时⼀次只会多扩1M 的空间。需要注意的是字符串最⼤⻓度为 512M。
使用场景
- 缓存
- 计数器
- 分布式锁
常用api
key 为字符串 value 为字符串、Integer、Bits
- set key 设值 不管key是否存在都设值
- get key 获取值
- del key 删除值
- incr key key自增1.如果key不存在,自增后get(key)=1
- decr
- incrby key k key自增k,如果key不存在,自增后get(key)=k
- decrby
- setnx key不存在才设置
- mget key1、key2批量获取key,原子操作(批量操作,可以节省网络耗时开销)
- mset key1 value1 key2 value2
- getset key newvalue set key newvalue 并返回旧的value
- append key value 将value追加到旧的value上
- strlen key 返回字符串的长度(注意中文)
- getrange key start end 获取字符串指定下标的所有值
- setrange
2. list 列表
redis中的列表相当于java中linkedlist,是一个链表,意味着list的插入和删除非常快时间复杂度为O(1),而定位索引非常慢时间复杂度为O(n),当列表弹出最后一个元素的时候,该数据结构自动被删除,内存被回收。
redis中的key为字符串,value是一个有序的队列
特点:有序、可以重复、左右两边插入弹出
使用场景
Redis 的列表结构常⽤来做异步队列使⽤。将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另⼀个线程从这个列表中轮询数据进⾏处理。
常用api
- rpush key value 从右边插入值
- lpush
- linsert key before|after value newValue 在list指定的值前|后添加新值
- lpop key 从列表的左边弹出一个元素
- rpop
- lrem key count value count>0从左到右,删除最多count个value相等的项 count<0 从右到左,删除最多Math.abs(count)个value相等的项 count=0 删除所有和value相等的值
- ltrim key begin end 按照索引范围修改列表
- lrange key start end(包含end) 返回指定索引的value
- llen 获取列表的长度
- lset key index newvalue指定索引新的值
注意
慢操作:lindex 相当于 Java 链表的get(int index)⽅法,它需要对链表
进⾏遍历,性能随着参数index增⼤⽽变差。
ltrim 和字⾯上的含义不太⼀样,个⼈觉得它叫 lretain(保留) 更合适
⼀些,因为 ltrim 跟的两个参数start_index和end_index定义了
⼀个区间,在这个区间内的值,ltrim 要保留,区间之外统统砍掉。
我们可以通过ltrim来实现⼀个定⻓的链表,这⼀点⾮常有⽤。
index 可以为负数,index=-1表示倒数第⼀个元素,同
样index=-2表示倒数第⼆个元素。
快速列表:
Redis 底层存储的还不是⼀个简单的
linkedlist,⽽是称之为快速链表 quicklist 的⼀个结构。
⾸先在列表元素较少的情况下会使⽤⼀块连续的内存存储,这个结构
是 ziplist,也即是压缩列表。它将所有的元素紧挨着⼀起存储,
分配的是⼀块连续的内存。当数据量⽐较多的时候才会改成
quicklist。因为普通的链表需要的附加指针空间太⼤,会⽐较浪
费空间,⽽且会加重内存的碎⽚化。⽐如这个列表⾥存的只是 int
类型的数据,结构上还需要两个额外的指针 prev 和 next 。所以
Redis 将链表和 ziplist 结合起来组成了 quicklist。也就是将
多个 ziplist 使⽤双向指针串起来使⽤。这样既满⾜了快速的插⼊
删除性能,⼜不会出现太⼤的空间冗余。
3、Hash哈希
Redis中的字典同java中的hashmap,它是无序的,它的实现也和Hashmap一致,同样的数组加上链表,数组用来存储数据,链表用来解决hash碰撞。不同的是redis字典的值只能是字符串,另外它们的rehash方式不一致,java中的hashmap中的rehash是一个耗时的操作,需要一次性全部rehash,redis为了提高性能,不能堵塞服务,采用渐进式rehash。渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,查询时会同时查询两个 hash 结构,然后在后续的定时任务中以及hash 操作指令中,循序渐进地将旧 hash 的内容⼀点点迁移到新的hash 结构中。当搬迁完成了,就会使⽤新的hash结构取⽽代之。当 hash 移除了最后⼀个元素之后,该数据结构⾃动被删除,内存被回收。
hash结构也可以存储用户信息,不同于字符串需要一次性序列化整个对象,hash可以对用户中的每个字段进行单独存储,这样我们可以获取对象的部分信息。
常用api
- hget key field 获取hash key对应的field的value,时间复杂度O(1).
- hset
- hdel
- hexist key field判断hash key是否存在field
- hlen key 获取hash key field的长度
- hmget 批量获取
- hgetall 返回hash key对应的所有key和value
- hvals key 返回hash key对应的所有的field对应值
- hkeys key返回hash key对应的所有的field
- hsetnx key field value 设置hash key对应的field的value,如果field存在,则失败
- hincrby key field counter
- hincrbyfloat
Set
Redis中的集合相当于java中的HashSet,它内部的键值对是无序且唯一的,它的内部实现相当于一个特殊的字典,所有的value都是null。当集合中的最后一个元素被删除后,数据结构自动删除,内存回收。
特点:无序,不允许有重复的元素
常用api
- sadd key element向集合中添加元素,如果element存在,则添加失败
- srem key element删除集合中的某个元素
- scard 计算集合中的大小
- sismember判断是否在集合中
- srandmember从集合中随机挑选m个元素
- smember获取集合中的所有元素
- spop从集合中随机弹出一个元素
Zset
zet是比较特色的数据结构,类似于java中的sortedset和hashmap的结合体,一方面他是一个set保证了value的唯一性,另一方面他给每一个value赋值一个score,代表这个value排序的权重,它的内部实现是跳跃列表的数据结构。
zset最后一个value被删除以后,数据结构自动删除,内存被回收。
zset 可以⽤来存粉丝列表,value 值是粉丝的⽤户 ID,score 是关
注时间。我们可以对粉丝列表按关注时间进⾏排序。
zset 还可以⽤来存储学⽣的成绩,value 值是学⽣的 ID,score 是
他的考试成绩。我们可以对成绩按分数进⾏排序就可以得到他的名
次。
常用api
- zadd key score element
- zrem key element
- zincrby key increScore element 增加元素或者减少元素的分数
- zcard key 返回元素的总个数
- zscore key element 获取元素的分数
- zrank key 获取元素的排名
- zrang key start end 获取指定索引升序排序
- zcount key minScore maxScore 返回某一个分数区间元素的个数
9.zremrangebyrank key start end 删除指定排名内的升序元素
总结
list/set/hash/zset这四种数据结构都是容器类型的数据结构,他们有下面两条的通用规则 - create if not exists
如果容器不存在,那就创建⼀个,再进⾏操作。⽐如 rpush 操
作刚开始是没有列表的,Redis 就会⾃动创建⼀个,然后再
rpush 进去新元素。 - drop if no elements
如果容器⾥元素没有了,那么⽴即删除元素,释放内存。这意
味着 lpop 操作到最后⼀个元素,列表就消失了。
过期时间
Redis 所有的数据结构都可以设置过期时间,时间到了,Redis 会⾃
动删除相应的对象。需要注意的是过期是以对象为单位,⽐如⼀个
hash 结构的过期是整个 hash 对象的过期,⽽不是其中的某个⼦
key。
还有⼀个需要特别注意的地⽅是如果⼀个字符串已经设置了过期时
间,然后你调⽤了 set ⽅法修改了它,它的过期时间会消失。