redis各类型实现&命令

比较基础的命令不做过多赘述(例如string的set、get等),主要讲述一下各类型的大概实现方式和需要费力记忆的命令,帮助理解和更好地运用Redis,解决工作中的问题。

需要注意的是:redis中各数据类型是不能嵌套的。不像编程语言中A类可以拥有成员属性B类。

  • string:

string类型是二进制安全的,因为在redis底层的实现中,string类型是一个结构体,内容如下:

1
2
3
4
5
struct sdshdr{
long len; //记录总长度
long free; //记录buf剩余可用长度
char buf[];//实际存储字符串
}

因为使用len记录了总长度,使用free记录了buf数组中剩余可用长度,所以不需要使用nil字符作为结束,从而实现二进制安全

在redis中,也使用string类型保存数字,从而拥有incr、decr等方法,具体实现细节隐藏在底层,不细表了。

mset命令:一次set多个key的值,但若失败一个,则整个命令失效(类似于事务机制,要么都成功,要么都不成功),不会出现部分成功部分失败的情况。

msetnx 命令:参考mset,但比mset多一个限制条件:若其中一个key已存在,则也视为事务失败


  • list列表:

列表在redis中使用双链表实现,但并不提供精确查找(精确查找需要遍历链表,时间复杂

度为O(n) ),只能范围查找,所以更加适用于做各类排行榜、队列等。

lrange:获取指定区间的元素(只读)

ltrim:截取指定区间元素(只写),若成功,返回OK(不返回截取后的内容),会修改该列表

blpop/brpop,阻塞式地pop,但它有个特点是可以一次pop多个list,并且pop的顺序是从左到右(例如从list1-list2-list3的顺序),若list1存在内容,就会永远pop出list1的内容直到list1为空,再去pop出list2的内容,所以可用于实现优先级队列,同时它带有timeout属性(以秒为单位),故可结合起来实现带超时的优先级队列。若超时,返回的是nil(编程语言中为NULL或None,视语言而定)

list类型一般用于存储可重复的值,例如用户的购买记录、全校师生的名单等,或者作为简单的消息队列使用


  • set集合:

集合内的元素是无序的,在redis中集合实现的方式是通过hashtable,所以它查找和删除元素的时间复杂度为O(1)

spop:删除并返回set中的一个随机元素

smove:将一个集合中的某元素移动到另一个集合中,整个操作是原子的

set类型主要应用场景是快速查找元素是否存在、存储不可重复的数据,例如维护一个网站的用户登录标识,可快速获取该用户的在线状态;或者是用于做记录,例如网站中用户一天只能投票一次,就可以使用集合存储已经投过票的用户id,从而避免重复投票


  • sorted set 有序集合:

有序集合类似于集合,但它和集合的不同点是:拥有一个double类型的整数score。通过score这个属性实现有序的特性。

而在redis底层中,有序集合使用SkipList(跳跃表)和HashTable组合而成,SkipList负责排序,HashTable负责保存数据。
有序集合相比普通集合,可以覆盖普通集合的功能,并可用于构建具有优先级的队列(普通集合无法实现)

zincrby :对指定元素的score做自增

zrank :返回指定key在集合中的排序下标,按照score升序排列

zrevrank :同上,但排序方式是按照score降序

zrange :返回指定两个下标之间的元素,按照score升序排列

zrevrange :同上,但按照score降序

rangebyscore :返回在集合中分数score在指定区间大小内的元素

zcount :返回在集合中分数在指定区间大小内的元素个数

zremrangebyrank:删除集合中排名在给定区间内的元素

zremrangebyscore:删除集合中分数在给定区间内的元素

sorted set可用于实现排行榜(例如知乎的各回答点赞数排行,用户每次点赞时,执行zincrby进行添加点赞数)

  • Hash:

Hash类型就是一个hashtable,可以用于存储对象,例如存储一个用户对象:把用户ID作为key,将用户信息存入hash中。

【注意】:新建Hash类型对象时,为了节省内存,redis使用zipmap存储数据,zipmap并不是真正的hashtable,添加、删除和修改操作的时间复杂度都是O(n),但是相比hashtable来说,zipmap节省不少内存。当zipmap中的key或者value超过一定限制,redis会自动将它替换成真正的hashtable。(这是典型的优化手段,虽然zipmap时间复杂度更高,但由于只会在数据较少的时候使用,所以实际上所花费的时间并不多。这样做是为了在尽量不影响redis性能的情况下节省内存)

hmset:一次设置key对应的hash对象内多个属性的值,如果key对应的hash对象不存在,则会创建此对象

hlen :返回key对应hash对象的属性数量

hkeys :返回hash对象中所有的键列表

hvals :返回hash对象中所有的值列表

Hash类型的用途很多,一般用于存储多个简单的映射关系,例如保存简单的用户信息(电话、性别、年龄等)