1. 缓存 比如我要从数据库查看最新的5000条评论: SELECT comments FROM user ORDER BY time DESC LIMIT 5000 这样的操作随着数据的增加会变得越来越慢,因为要进行排序操作。而且这种排序本身不应该发生:因为我们存的时候是按时间存进去的。 我们可以使用redis的列表对象来实现,此时列表由Linkedlist数据结构实现。 每次新评论发表时,我们会将它的ID添加到一个Redis列表LPUSH latest.comments,然后将列表裁剪到5000,LTRIM latest.comments 0 5000 每次我们需要获取最新评论的项目范围时,我们调用一个函数来完成(使用伪代码): FUNCTION get_latest_comment(num_items): id_list=redis.lrange("latest.comments",0,num_items-1) IF id_list<num_items id_list = MySQL("SELECT ... ORDER BY time LIMIT ...") END RETURN id_list END 只有超过了5000这个限制时,才会去访问数据库。 2. 排行榜 选手报名参加活动,观众可以对选手进行投票,每个观众对同一名选手只能投一票,活动期间最多投四票。后台需要提供以下接口: 接口1:返回TOP 10的选手信息及投票数 接口2:返回活动总参与选手数及总投票数 接口3:对于每个选手,返回自己的投票数,排名,距离上一名差的票数 如果是Mysql方案,需要建立一个表来记录投票信息。这个表在入表时首先就需要判断是否重复刷票,有两种方法 在查询时需要组合查询:将投票表和选手信息表组合,统计投票信息,然后排序输出。耗时较长。 新建一个排行榜表,每隔一段时间做组合查询,维护这个表。缺乏实时性。 redis方案可以采用有序集合对象,我们创建一个有序集合vote_activity,然后使用ZINCRBY key increment memeber命令给指定成员的分数加上增量increment, redis> ZINCRBY vote_activity 1 Bob "1" redis> ZINCRBY vote_activity 1 Tim "1" redis> ZINCRBY vote_activity 1 Bob "2" 有序列表入队时,按分值排好序了,我们可以方便的用ZSCORE key member查询分数。 redis> zscore vote_activity Bob "2" 以及获取某人的排名,获取前10名,获取前10名分数等等, #获取Alice排名(从高到低,zero-based) redis> zrevrank vote_activity Alice (integer) 0 #获取前10名(从高到低) redis> zrevrange vote_activity 0 9 1) "Alice" 2) "Bob" #获取前10名及对应的分数(从高到低) redis> zrevrange vote_activity 0 9 withscores 1) "Alice" 2) "2" 3) "Bob" 4) "1" 3. 消息队列 一般来说,消息队列有两种场景:一种是发布者订阅者模式;一种是生产者消费者模式。利用redis这两种场景的消息队列都能够实现。定义: 生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息;即对于每个消息只能被最多一个消费者拥有。(常用于处理高并发写操作) 发布者订阅者模式:发布者生产消息放到队列里,多个监听队列的消费者都会收到同一份消息;即正常情况下每个消费者收到的消息应该都是一样的。(常用来作为日志收集中一份原始数据对多个应用场景) 发布者订阅者模式可以直接使用pub/sub指令实现。 生产者消费者模式分两种: 普通的 带有优先级的 普通模式下使用brpop指令,可以以阻塞的形式返回数据列表中新添加的参数: while(true) { List<string> msgs = redis.brpop(BLOCK_TIMEOUT,listKey); Handle(msgs); } 如果是优先级模式,当优先级不是很多是,可以分为两组: while(true) { List<string> msgs = redis.brpop(['high_task_queue', 'low_task_queue'],0); Handle(msgs); } brpop命令可以输入多个键,如果同时都有元素可读,读先输入的那个键。 如果优先级划分很多,就需要再用列表排序的办法了(有序集合不好,因为没有阻塞模式)。假如有1000个优先级,我们可以先分组,分为10组,每组按优先级顺序排列,查找时二分查找。 4. 时间轴 所谓时间轴系统就是典型的微博模式:用户在自己的主页可以看到其关注的博主发表的信息列表(按时间排序);而其它用户可以一个用户的个人主页看到这个人发布的信息列表(按时间排序)。 解决方案主要有两种: 推模式:某人发布内容之后推送给所有粉丝,空间换时间,瓶颈在写入; 拉模式:粉丝从自己的关注列表中读取内容,时间换空间,瓶颈在读取; 以推模式为例: (1)博主发布博文 我们创建一个哈希对象post,键为博文ID,值为博文内容字符串。存储博文。 redis> HSET post 4396 "hahahahah" 再使用一个列表,按先后顺序存储该博主的博文: redis>LPUSH Dasima 4396 然后使用一个集合,存储该博主的所有粉丝,利用SMEMBERS获取这些粉丝的名单。 redis> SMEMBERS Dasima 每一个粉丝拥有一个timeline列表,存取所有推送博文的ID。之后对所有粉丝的推送列表进行写入。 (2)用户读取博文推送 利用LRANGE从推送中拉取一定数量的博文,根据拉到的博文ID,读取哈希表的内容。 redis>LRANGE timeline 0 30 redis>HGETALL(4396) 5. 实现分布式锁 (1)什么是分布式锁? 对于单进程的程序,采用普通锁即可防止竞争,而对于多进程分布式系统来说需要采用分布式锁来保证一致性。 (2)redis如何实现分布式锁 在 Redis 2.6.12 版本开始,set命令增加了三个参数,替换以前的setnx命令: EX:设置键的过期时间(单位为秒) PX:设置键的过期时间(单位为毫秒) NX | XX:当设置为NX时,仅当 key 存在时才进行操作,设置为XX时,仅当 key 不存在才会进行操作 我们可以以此实现简单的分布式锁: set key "lock" EX 1 XX 如果这个操作返回false,说明 key 的添加不成功,也就是当前有人在占用这把锁。而如果返回true,则说明得了锁,便可以继续进行操作,并且在操作后通过del命令释放掉锁。并且即使程序因为某些原因并没有释放锁,由于设置了过期时间,该锁也会在 1 秒后自动释放,不会影响到其他程序的运行。 del "lock"