Redis做为一个非关系型数据库,除了在访问速度上拥有显著优点外,其自己支持的多种数据类型也很是有用,能覆盖系统开发中的不少应用场景。下面列举的场景有的是从网上其余人的博客里看到的,有的本身开发时尝试过的一些解决方案后记录下来的,但愿能给之后的开发带来启发。面试
在说应用场景前先说一些是否以为使用Redis的建议算法
RDB
和AOF
两种持久化方式,可是广泛仍是认为 Redis 的持久化并非很靠谱。很是重要的数据不要依赖Redis来开发,或者最起码不要只在Redis中持久化做为Key-Value
形态的内存数据库,Redis 最早会被想到的应用场景即是做为数据缓存。而使用 Redis 缓存数据很是简单,只须要经过string
类型将序列化后的对象存起来便可,不过也有一些须要注意的地方:数据库
缓存内容与数据库的一致性,这里通常有两种作法:缓存
Redis 中list
的数据结构实现是双向链表,因此能够很是便捷的应用于消息队列(生产者 / 消费者模型)。消息的生产者只须要经过lpush
将消息放入 list,消费者即可以经过rpop
取出该消息,而且能够保证消息的有序性。若是须要实现带有优先级的消息队列也能够选择sorted set
。而pub/sub
功能也能够用做发布者 / 订阅者模型的消息。不管使用何种方式,因为 Redis 拥有持久化功能,也不须要担忧因为服务器故障致使消息丢失的状况。服务器
list
做为双向链表,不光能够做为队列使用。若是将它用做栈即可以成为一个公用的时间轴。当用户发完微博后,都经过lpush
将它存放在一个 key 为LATEST_WEIBO
的list
中,以后即可以经过lrange
取出当前最新的微博。数据结构
list
还能够做为循环链表使用 RPOPLPUSH source destination
分布式
命令 RPOPLPUSH
在一个原子时间内,执行如下两个动做:性能
source
中的最后一个元素(尾元素)弹出,并返回给客户端。source
弹出的元素插入到列表 destination
,做为 destination
列表的的头元素。若是 source
和 destination
相同,则列表中的表尾元素被移动到表头,并返回该元素,能够把这种特殊状况视做列表的旋转(rotation)操做。优化
好比有个进程来完成派单任务,须要将用户发送过来的申请依次派发给工做人员,那么就能够把工做人员的身份标示维护在循环列表中,从列表尾部读取每次读取身份标示后相应的标示都会被放到列表头如此循环往复。设计
使用sorted set
和一个计算热度的算法即可以轻松打造一个热度排行榜,zrevrangebyscore
能够获得以分数倒序排列的序列,zrank
能够获得一个成员在该排行榜的位置(是分数正序排列时的位置,若是要获取倒序排列时的位置须要用zcard
-zrank
)。
计数功能应该是最适合 Redis 的使用场景之一了,由于它高频率读写的特征能够彻底发挥 Redis 做为内存数据库的高效。在 Redis 的数据结构中,string
、hash
和sorted set
都提供了incr
方法用于原子性的自增操做,下面举例说明一下它们各自的使用场景:
string
做为计数器,设定一个名为REGISTERED_COUNT_TODAY
的 key,并在初始化时给它设置一个到凌晨 0 点的过时时间,每当用户注册成功后便使用incr
命令使该 key 增加 1,同时当天天凌晨 0 点后,这个计数器都会由于 key 过时使值清零。hash
进行计数会更好,将该计数器的 key 设为weibo:weibo_id
,hash
的 field 为like_number
、comment_number
、forward_number
和view_number
,在对应操做后经过hincrby
使hash 中
的 field 自增。sorted set
吧,将集合的 key 设为POST_RANK
。当用户发帖后,使用zincrby
将该用户 id 的 score 增加 1。sorted set
会从新进行排序,用户所在排行榜的位置也就会获得实时的更新。这个场景最开始是是一篇介绍微博 Redis 应用的 PPT 中看到的,其中提到微博的 Redis 主要是用在在计数和好友关系两方面上,当时对好友关系方面的用法不太了解,后来看到《Redis 设计与实现》中介绍到做者最开始去使用 Redis 即是但愿能经过set
解决传统数据库没法快速计算集合中交集这个功能。后来联想到微博当前的业务场景,确实可以以这种方式实现,因此姑且猜想一下:
对于一个用户 A,将它的关注和粉丝的用户 id 都存放在两个 set 中:
A:follow
:存放 A 全部关注的用户 idA:follower
:存放 A 全部粉丝的用户 id那么经过sinter
命令即可以根据A:follow
和A:follower
的交集获得与 A 互相关注的用户。当 A 进入另外一个用户 B 的主页后,A:follow
和B:follow
的交集即是 A 和 B 的共同专一,A:follow
和B:follower
的交集即是 A 关注的人也关注了 B。
在 Redis 2.6.12 版本开始,string
的set
命令增长了三个参数:
EX
:设置键的过时时间(单位为秒)PX
:设置键的过时时间(单位为毫秒)NX
| XX
:当设置为NX
时,仅当 key 存在时才进行操做,设置为XX
时,仅当 key 不存在才会进行操做因为这个操做是原子性的,能够简单地以此实现一个分布式的锁,例如:
set key "lock" EX 1 XX
若是这个操做返回false
,说明 key 的添加不成功,也就是当前有人在占用这把锁。而若是返回true
,则说明得了锁,即可以继续进行操做,而且在操做后经过del
命令释放掉锁。而且即便程序由于某些缘由并无释放锁,因为设置了过时时间,该锁也会在 1 秒后自动释放,不会影响到其余程序的运行。
倒排索引是构造搜索功能的最多见方式,在 Redis 中也能够经过set
进行创建倒排索引,这里以简单的拼音 + 前缀搜索城市功能举例:
假设一个城市北京
,经过拼音词库将北京
转为beijing
,再经过前缀分词将这两个词分为若干个前缀索引,有:北
、北京
、b
、be
…beijin
和beijing
。将这些索引分别做为set
的 key(例如:index:北
)并存储北京
的 id,倒排索引便创建好了。接下来只须要在搜索时经过关键词取出对应的set
并获得其中的 id 便可。
我的能力局限目前只知道这些数据类型的应用场景,若是各位有其余场景的应用经验欢迎交流补充,另外面试时被问到为什么使用Redis不要简单的说由于快, 若是在系统中只使用了缓存这一个应用场景那么最起码能够提供一些MySQL的QPS和Redis的QPS数据或者程序在Redis使用先后的平均响应时长来印证你的观点。