支持五种数据类型:
* string(字符串)
* hash(哈希) =》 散列表 O(1)
* list(列表) =》 双向链表 (两端O(1),查询O(n)
* set(集合) =》 值为空的hash table 散列表 ,所有 O(1)
* zset(sorted set:有序集合) =》 散列表 O(1)+ 跳跃表 O ( logN)
命令参考:
http://redisdoc.com/index.html
使用场景:
https://www.cnblogs.com/yy1234/p/7809551.html
redis过期策略:https://blog.csdn.net/xiangnan129/article/details/54928672
Redis进阶不得不了解的内存优化细节:https://cloud.tencent.com/developer/article/1162213
把Redis当作队列来用,真的合适吗?:https://www.51cto.com/article/659208.html
String
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
常规key-value缓存应用;
常规计数:微博数,粉丝数等。
SET key value
SETNX key value 只有在 key 不存在时设置 key 的值。
SETEX key seconds value 将值 value 关联到 key ,并将 key 的过期时间设为 seconds
PSETEX key milliseconds value 和 SETEX 命令相似,但它以毫秒为单位
MSET key value [key value ...]
GET key
MGET key1 [key2..]
INCR key
INCRBY key increment
DECR key
DECRBY key decrement
hash
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
存储部分变更的数据,如用户信息等。
HSET key field value
HMSET key field1 value1 [field2 value2 ]
HSETNX key field value
HINCRBY key field increment
HEXISTS key field
HGET key field
HMGET key field1 [field2]
HGETALL key
HLEN key
HDEL key field1 [field2]
list
每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样List即可以作为栈,也可以作为队列。
lpush/rpush key value [value…]
lpop/rpop key
llen key
lrange key start stop 左开右也开,和js不同 ,-1表示倒数第一个
lindex key index
lset key index value
ltrim key start end 保留指定片段, 左开右也开同lrange
取最新N个数据的操作
//把当前登录人添加到链表里
ret = r.lpush("login:last_login_times", uid)
//保持链表只有N位
ret = redis.ltrim("login:last_login_times", 0, N-1)
//获得前N个最新登陆的用户Id列表
last_login_list = r.lrange("login:last_login_times", 0, N-1)消息队列系统
使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。
比如:将Redis用作日志收集器
实际上还是一个队列,多个端点将日志信息写入Redis,然后一个worker统一将所有日志写到磁盘。
set
set就是一个集合,集合的概念就是一堆不重复值的组合。set中的元素是没有顺序的。
sadd key member [member …]
srem key member [member …]
smembers key 列举所有
ismember key member 是否成员
sdiff key [key..] 差集 =》 sdiffstore destkey key [key…] 差集并存储在destkey
sinter key [key ..] 交集 ...
sunion key [key..] 并集 ...
scard key 个数
spop key 随机弹出一个
srandmember key [count]随机取n个元素
案例:
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。
可以非常方便的实现如共同关注、共同喜好、二度好友等功能
交集,并集,差集
//即属于ruby又属于web的书?
inter_list = redis.sinter("tag:web", "tag:ruby")
//即属于ruby,但不属于web的书?
diff_list = redis.sdiff("tag:ruby", "tag:web")
//属于ruby和属于web的书的合集?
union_list = redis.sunion("tag:ruby", "tag:web”)获取某段时间所有数据去重值
这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就行了,set意为集合,所以会自动排重。
sorted set
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。zset使用了两种不同的存储结构,分别是 zipList(压缩列表)和 skipList(跳跃列表)
zipList(压缩列表)
当 zset 满足以下条件时,使用压缩列表:
1、成员的数量小于128 个;
2、每个 member (成员)的字符串长度都小于 64 个字节。
当 zset 使用压缩列表保存数据时,entry 的第一个节点保存 member,第二个节点保存 score。依次类推,集合中的所有成员最终会按照 score 从小到大排列。
skipList(跳跃列表)
底层是一个命名为zset的结构体,而一个zset结构同时包含一个字典和一个跳跃表。跳跃表按score从小到大保存所有集合元素。而字典则保存着从member到score的映射,这样就可以用O(1)的复杂度来查找member对应的score值。
插入、删除、查找的复杂度均为O(logN)
/*
* 有序集合
*/
typedef struct zset {
// 字典,键为成员,值为分值
// 用于支持 O(1) 复杂度的按成员取分值操作
dict *dict;
// 跳跃表,按分值排序成员
// 用于支持平均复杂度为 O(log N) 的按分值定位成员操作
// 以及范围操作
zskiplist *zsl;
} zset;
zadd key score member [score member … ] 增加member,重复这条命令(不同score) 可以修改member的score
zincrby key incr member 对指定成员的分数加上增量 increment
ZCARD key 返回集合的成员数
ZCOUNT key min max 返回区间分数的成员数
zscore key member 返回有序集中,成员的分数值
zrank/zrevrank key member 返回有序集合中指定成员的索引
ZREM key member [member ...] 移除有序集合中的一个或多个成员
ZREMRANGEBYRANK key start stop 按照排名范围删除元素
ZREMRANGEBYSCORE key min max 按照分数范围删除元素
zrange/zrevrange key start stop [withscores] 根据元素score从小到大顺序返回索引从start到stop之间所有元素(包含两端)
等价: `select * from where id>=start and id<=stop order by id asc|desc`
zrangebyscore/zrevrangebyscore key min max [withscores] [limit offset count] 根据score分数范围 查member列表
等价: `select * from where score>=min and score<=max order by score asc|desc`
排行榜应用,取TOP N操作
将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。
//将登录次数和用户统一存储在一个sorted set里
zadd login:login_times 5 1
zadd login:login_times 1 2
zadd login:login_times 2 3
//当用户登录时,对该用户的登录次数自增1
ret = r.zincrby("login:login_times", 1, uid)
//那么如何获得登录次数最多的用户呢,逆序排列取得排名前N的用户
ret = r.zrevrange("login:login_times", 0, N-1)-
比如在线游戏的排行榜,根据得分你通常想要:
- 列出前100名高分选手
- 列出某用户当前的全球排名
模式是这样的,每次获得新得分时,我们用这样的代码:
ZADD leaderboard
得到前100名高分用户很简单:
ZREVRANGE leaderboard 0 99
用户的全球排名也相似,只需要:
ZRANK leaderboard
需要精准设定过期时间的应用
比如你可以把上面说到的sorted set的score值设置成过期时间的时间戳,那么就可以简单地通过过期时间排序,定时清除过期数据了,
不仅是清除Redis中的过期数据,你完全可以把Redis里这个过期时间当成是对数据库中数据的索引,用Redis来找出哪些数据需要过期删除,然后再精准地从数据库中删除相应的记录。范围查找
他有一个IP范围对应地址的列表,现在需要给出一个IP的情况下,迅速的查找到这个IP在哪个范围,也就是要判断此IP的所有地。
这个问题引来了Redis作者Salvatore Sanfilippo(@antirez)的回答。解答如下:
例如有下面两个范围,10-20和30-40
- A_start 10, A_end 20
- B_start 30, B_end 40
我们将这两个范围的起始位置存在Redis的sorted set数据结构中,基本范围起始值作为score,范围名加start和end为其value值:
redis 127.0.0.1:6379> zadd ranges 10 A_start
redis 127.0.0.1:6379> zadd ranges 20 A_end
redis 127.0.0.1:6379> zadd ranges 30 B_start
redis 127.0.0.1:6379> zadd ranges 40 B_end
这样数据在插入sorted set后,相当于是将这些起始位置按顺序排列好了。
现在我需要查找15这个值在哪一个范围中,只需要进行如下的zrangbyscore查找:
redis 127.0.0.1:6379> zrangebyscore ranges (15 +inf LIMIT 0 1
- "A_end"
这个命令的意思是在Sorted Sets中查找大于15的第一个值。(+inf在Redis中表示正无穷大,15前面的括号表示>15而非>=15)
查找的结果是A_end,由于所有值是按顺序排列的,所以可以判定15是在A_start到A_end区间上,也就是说15是在A这个范围里。至此大功告成。
当然,如果你查找到的是一个start,比如咱们用25,执行下面的命令:
redis 127.0.0.1:6379> zrangebyscore ranges (25 +inf LIMIT 0 1
- "B_start"
返回结果表明其下一个节点是一个start节点,也就是说25这个值不处在任何start和end之间,不属于任何范围。
当然,这个例子仅适用于类似上面的IP范围查找的案例,因为这些值范围之间没有重合。如果是有重合的情况,这个问题本身也就变成了一个一对多的问题。
Pub/Sub
Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
使用场景
Pub/Sub构建实时消息系统
Redis实现访问频率限制 :
https://segmentfault.com/a/1190000004287708
Redis各种命令时间复杂度一览表
https://blog.csdn.net/qq_23564667/article/details/110917900
Redis集群详解
https://blog.csdn.net/miss1181248983/article/details/90056960/
Redis有三种集群模式,分别是:
- 主从模式
- Sentinel模式
- Cluster模式