在主页中显示最新的项目列表
在Web应用中,“列出最新的XXX”非常普遍,这通常会带来可扩展性问题。 类似的问题就可以用Redis来解决。比如说,一个Web应用想要列出用户贴出的最新20条评论。在最新的评论边上我们有一个“显示全部”的链接,点击后就可以获得更多的评论。假设数据库中的每条评论都有一个唯一的递增的ID字段,就可以使用分页来制作主页和评论页,使用Redis的模板,每次新评论发表时,我们会将它的ID添加到一个Redis列表。操作如下:
- Redis保存最新的评论LPUSH latest.values xxx_id
- Redis删除多余的列表数据,只保存最新的5000条LTRIM latest.values 0 5000
- 获取最新评论的范围数据时,通过一次遍历获取需要的评论redis.lrange(“latest.values”,0,100)
Redis中我们的最新ID使用了常驻缓存,这是一直更新的,但是我们做了限制不能超过5000个ID,因此我们的获取ID函数会一直询问Redis。只有在start/count参数超出了这个范围的时候,才需要去访问数据库
排行榜相关
一个很普遍的需求是排行榜(leader board)按照得分进行排序,各种数据库的数据并非存储在内存中,因此在按得分排序以及实时更新这些要求在数据库的性能不够理想;而这些操作对于Redis来说很简单,即使你有几百万个用户,每分钟都会有几百万个得分更新。比如那些在线游戏的排行榜,比如一个Facebook的游戏,根据得分你通常想要:列出前100名高分选手,列出某用户当前的全球排名
- 使用zset存储用户及得分 ZADD leaderboard
<score>
<username>
- 得到得分最高的100名用户 ZREVRANGE leaderboard 0 99
- 得到得分最低的100名用户 ZRANGE leaderboard 0 99
- 得到个人总排名 ZRANK leaderboard
<username>
按照用户投票和时间排序
针对得分随时间变化的排序问题,如热点新闻等。新闻按照类似下面的公式根据得分来排序,计算方式:
score = points / time^alpha
因此用户的投票会相应的把新闻挖出来,但会按照时间的指数将新闻埋下去。 每次新的新闻贴上来后,我们将ID添加到列表中,使用LPUSH + LTRIM,确保只取出最新的1000条项目
- 存储最新的新闻列表LPUSH latest.news
<news>
- 限制保存最新的1000条LTRIM latest.news 0 1000
- 新闻被点击后使用zset存储得分及新闻 ZADD score.news
<score>
<news>
,得分=点击数/发布时长^2 - 删除ZADD中非最新的新闻 ZREM score.news
<news>
[<news>
] - 得到得分最低的100名用户 ZRANGE score.news 0 99
- 得到得分最高的100名用户 ZREVRANGE score.news 0 99
- 得到新闻总排名 ZRANK score.news
<news>
过期项目处理
还有一种常用的排序是按照时间排序,项目中使用unix时间作为得分,用来保持列表按时间排序,周期检查排序列表,发现过期的项目则删除。每次有新记录添加时,把它加入到排序集合中,用时间属性current_time和time_to_live;使用ZRANGE…SCORES通过分数返回有序集合指定区间内的成员。如果发现unix时间已经过期,则在持久化数据库中删除。
- 存储最新的项目,用户时间作为得分 ZADD last.items
<current_time+time_to_live>
<item>
- 检查项目过期,获取小于当前时间的项目 ZRANGEBYSCORE last.items -inf
<current_time>
- 删除过期项目 zrem
计数
各种数据的计数和统计需求用途广泛,比如想知道什么时候封锁一个IP地址。INCRBY命令让这些变得很容易,通过原子递增保持计数;GETSET用来重置计数器;过期属性用来确认一个关键字什么时候应该删除。
比如我想要知道某些特定的IP地址,到底有多少访问了某篇文章:
- 获得页面每日浏览量时需要这样做 INCR page:
<day>
:<page_id>
- 获得用户访问页面量 INCR user:
<user_id>
特定时间内的特定项目
Redis统计在某段特点时间里有多少特定用户访问了某个特定资源。比如我想要知道某些特定的注册用户或IP地址,他们到底有多少访问了某篇文章。
- 每次获得一次新的页面浏览时只需要这样做
SADD page:
- 获取特定用户的数量
SCARD page:<time>:<page_id>
- 测试某个特定用户是否访问了这个页面
SISMEMBER page:<time>:<page_id> <user_id>
- 获取特定页面的所有访问用户
SMEMBERS page:<time>:<page_id>
队列
Redis可以方便的实现消息队列。现代的互联网应用大量地使用了消息队列(Messaging)。消息队列不仅被用于系统内部组件之间的通信,同时也被用于系统跟其它服务之间的交互。消息队列的使用可以增加系统的可扩展性、灵活性和用户体验。非基于消息队列的系统,其运行速度取决于系统中最慢的组件的速度(注:短板效应)。而基于消息队列可以将系统中各组件解除耦合,这样系统就不再受最慢组件的束缚,各组件可以异步运行从而得以更快的速度完成各自的工作。当服务器处在高并发操作的时候,比如频繁地写入日志文件。可以利用消息队列实现异步处理。从而实现高性能的并发操作。
- 加入数据到队列
LPUSH <key> <value>
- 获取数据从队列
LPOP <key>
- 获取队列范围数据
LRANGE <key> 0 10
缓存
Redis能够替代memcached,让你的缓存从只能存储数据变得能够更新数据,因此你不再需要每次都重新生成数据了。网络应用不能无休止地进行模型的战争,看看这些Redis的原语命令,尽管简单但功能强大,把它们加以组合,所能完成的就更无法想象。当然,你可以专门编写代码来完成所有这些操作,但Redis实现起来显然更为轻松