原文地址http://oldblog.antirez.com/post/take-advantage-of-redis-adding-it-to-your-stack.html 
@(syoka)[redis|]

如何更好的利用redis

redis相比于其他数据库有诸多不同之处:

  • 它使用内存作为主存储,而硬盘存储则仅仅用来提供持久化
  • 数据模型相对比较单一(Redis数据类型)
  • 它是一个单线程等等
  • 另一个比较大的差异是:为了获取Redis的优势而将Redis应用于你的生产环境中并不要求你j切换Redis作为主库 
    你可以使用它去完成一些之前不可能的新功能或者去修复以前的问题

当然,切换使用Redis也是一个可选项,大部分开发者把Redis作为主数据库主要是为了新特性、写速度和延时功能,但是,试想对一个已经运行在线上的产品进行Redis切换是需要迈出很大的一步。同时对于一些特定场景的应用,Redis可能就不是一个好的选择。举一个例子,因为Redis数据大小不能超过可用内存,所以如果你有一些大数据应用,并且几乎全部都是读操作的话,Redis并不是一个好的选择。

我最喜欢redis是因为它可以帮我解决很多问题,比如将其加入栈中来处理那些在当前已有数据库条件下难以实现或是执行速度缓慢问题。在这个方面,Redis可是信心十足,让我们开始使用Redis去优化你的程序或是为你的程序点缀几笔吧。这篇博客将列举几个例子来说明如何在现有环境下加入Redis并充分利用Redis的一系列特性。我不会使用具体配置和站点名来举例,而是列举几个在Redis不作为主库的条件下能够解决的一些类别问题

需求显示最近的主页上的条目

你能想出一个解决存在大量数据情况导致查询缓慢的办法吗

SELECT * FROM foo WHERE ... ORDER BY time DESC LIMIT 10
  • 1

就像列出”最近被用户添加的条目”,或者”最近发生的事”之类的,在web应用中是非常常见的,也是一个可衡量的问题。直觉告诉我们,如果你想它们按照被创建的顺序进行显示的话,你就需要对其进行排序。

同样的问题可以使用Redis模式进行修复,举个例子吧, 当前有一个web应用用来显示最近20条用户所发布的评论,在最近评论的旁边还有一个显示全部的按钮,点下它,我们就可以看到超过20条的评论信息,并且也是按照分页进行显示的,同时我们还能看到整个评论的时间线排布

我们假定每一个评论都存在我们数据库,并且有一个唯一自增主键 
我们可以使用一种简单的Reids模式使数据可以快速的在主页框和评论时间线页面上进行分页 
+ 每当一个新的评论被追加时,就将它的ID加入Redis的list中,Lpush(FIFO类似队列)lastest.comment 
+ 我们可以限定这个list的容量,这样的redis就只会存最近5000条数据 LTRIM latest.comments 0 5000 
+ 每当我们想要获得一段区分范围内最近评论的话,我们可以使用下面伪代码

 FUNCTION get_latest_comments(start,num_items):
id_list = redis.lrange("latest.comments",start,start+num_items-1)
IF id_list.length < num_items
id_list = SQL_DB("SELECT ... ORDER BY time LIMIT ...")
END
RETURN id_list
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们所做的其实很简单,在Redis我们经常说到实时缓存,最近的ID总是被更新着。但是,我们限制了它的长度是5000个,并且当系统第一次启动的时候,这些ID甚至可以全部为0,因此当前list并不存在。因此当我们获取这些最近时间的评论的ID时,总是会先去访问redis。如果我们开始或者计数的参数超过了这个限定范围的话,我们再回到原有数据库。

我们重来不需要刷新缓存,因为sql 数据库(或者其他一些在硬盘存储数据类型)只有在用户想看到间隔比较远的时候才会被ping通,因此,不要直接从sql数据库中读取。(感觉这句话不好理解!!!)

正如你所见,Redis正在作为一个新的螺丝钉,它并不是像传统的缓存,Redis不需要进行缓存刷新,因为实例总是一致的。它并不是作为一个数据库工作,正如你可以flush你的key。同时所有一切都将继续工作。我称呼它为活缓存,但是我打赌肯定还有更好的名字。

删除和过滤

我们可以通过LREM来删除评论,如果删除的仅仅是很少的一部分,那么有一个可选择的方案就是过滤掉要被删除的评论,因为我们的数据库会通过ID来获取评论,此时DB就会报告说该评论已经不存在了。

有多少次,你想要使用不同的过滤方法来过滤不同的List。当某些过滤器受限于通过数字来筛选的话(比如目录),你可以使用另一个Redis链表来充当你的过滤器(取交集),毕竟你只需要将每5000个数据放进一个list中,这样Redis就仅仅用了一少部分内存却获得了百万条数据,通过来说你不得不做出妥协,发挥你的创造力吧!

排行榜以及相关问题

另一个常见的需求就是很难利用现有模型在那些按照分数排序且每秒都有很多次更新又不能从内存中获取列表的那些数据。

一个经典的例子就是线上比赛的积分榜,比如一个脸书游戏,这种模式不能应用于各种场景。比如在游戏例子中,你收到了来自许多用户的积分,这些积分时时刻刻都在被这些用户刷新,对于这些积分,你可能想要: 
+ 展现榜单前100名 
+ 展示用户当前的全球积分 
对于这些零散操作使用带排序的set来说是最好不过的,即使你有在每分钟有百分用户的百万新积分需要应付。

就是这样一种模式,每当收到一个用户的新积分的时候,我们这样做:

ZADD leaderboard <score> <username>
  • 1

备注:你可能会用用户id来替代名字,这个由你决定

为了获取前100用户的积分,只需ZREVRANGE leaderboard 0 99.

同样的,告诉某个用户他的全球积分,你只需ZRANK leaderboard .

现在你可以做的更多,比如显示当前用户积分附近的用户信息,也就是说你可以显示积分榜中一部分顾客的积分了。

按投票次数和时间排序

像上述那样排行模式的只是一个类似ReDDIT或黑客新闻这样的网站的实现。他们都可以根据相同的公式

score = points / time^alpha
  • 1

这样用户通过投票方式来提升新闻的占比,随着时间而慢慢下降占比,具体算法由你决定,但是不会改变我们的模式

这种工作模式适用于这种情况:只从最近的开始观察,比如,1000条非常适合放在首页的新闻,我们可以忽略掉其他所有,实现也是非常简单:

  • 每当一个新闻被发布的时候我们将它的ID加入一个List,使用LPUSH+LTRIM去只取最近的1000项
  • 这里有一个工人去获得这个list并且不断计算最终这1000条新闻的最终积分。最终结果使用ZADD填充至一个排序set。老的新闻将会被移除当前set。

这是我们有一个由1000个新闻通过积分排序的set。这个set可以以每秒查询100000次的速度显示最高积分的新闻,这个方式可以以一种相当间的方式衡量

关键的地方在于我们的排序,它是由后台工作者创建的,与当前正在看新闻网站的用户来说不成比例

对于刚才发布的那个,我们可以使用原始的IDS列表,或者使用本文博客中的第一个模式

实现条目的过期 
另一种使用排序Set的方法是使用时间索引,我们可以把单个时间比作分数,通常可以按照时间来索引某些东西,但是一个值得注意的地方就是当超过某个给定时间的时候,需要过期我们的主库

这种模式:

  • 每当一个新的数据被加载我们数据库时,我们把它加入排序set集,就像操作分数一样我们使用时间来决定哪些项应该被过期,用另一句话说还有当前时间+残留时间有效
  • 这里有一个后台工作者在排序集合中使用ZRANGE….WITHSCORES去查询最近10项。如果有分数代表unix times已经过期的话,那么我们将它从数据库中删除。

统计事物 
Redis是一个很好的计数器,感谢INCRBY和其他相似的命令

有多少次你想要在你的数据库里面添加一个机器数,用来统计或者暂时你用户新信息,因为它对于数据库来说是一项需要大量写入的任务所以不得不避免使用它,以前困扰了我太多次

但是,使用Redis并不需要考虑它,使用原子自增的它,你可以使用于所有你需要计数的地方,使用原子性操作的GETSET对它不断重置,在你的计数器中设置过期,以便只有在这些事件之间的时间差小于给定的秒数时才可以进行事件计数。

像下面这样:

INCR user:<id>
EXPIRE user:<id> 60
  • 1
  • 2

您可以计算用户最近做了多少页面浏览量,而页面浏览量之间没有超过60秒的停顿时间。例如,当这个计数达到20时,是时候展示一些横幅,提醒,提示或者你想要的东西。

在给定的时间内唯一N项

统计数据的另一个有趣的例子是使用Redis,但使用其他类型的数据库看有多少不同的用户在给定的时间内访问指定的资源是非常困难的。例如,我想知道在线报纸上访问特定文章的唯一注册用户数量或IP地址数量。

每次我想要查看一个新的页面浏览量时,我会这么做

SADD page:day1:<page_id> <user_id>
  • 1

当然,你可能想要使用今天的第一秒,作为unix时间,如:time() - (time()%3600 * 24),或类似的东西

如何知道用户的数量,使用 SCARD page:day1:.

想要测试制定的用户是不是已经进入这个页面了,使用SISMEMBER page:day1:

实时分析正在发生的事情,统计数据,反垃圾邮件或其他内容

我们只举了一小部分例子,但是如果您学习Redis命令集并以有趣的方式组合这些数据结构,则可以毫不费力地为大量实时统计数据建模,以便为增强你的反垃圾邮件系统或提升服务质量。

发布/订阅

你知道Redis包含相当高性能的发布和订阅的实现吗吗?

Redis 发布和订阅的使用非常简单,稳定且快速,支持模式匹配,能够订阅/取关那些运行的频道等等。您可以在Redis PubSub官方文档中阅读关于它的更多信息

队列

您可能已经注意到Redis命令如List推送和List弹出是如何使用链表取实现队列的,但您可以做的不仅仅于此,Redis具有当执行链表弹出命令时,如果此时列表为空,它将阻塞阻止列表的变体

Redis作为队列的常见用法是Resque库,由Github的人员实施和推广

通过我们的http://redis.io/commands/rpoplpush list旋转命令,可以实现具有有趣语义的队列,这将使您的后台工作者更快乐! (例如,您可以实施一个轮换列表来一次又一次获取RSS提要,这样每个工作人员都可以选择过去提取的RSS,因此需要尽快更新)。同样,使用排序集可以轻松实现优先级队列。

缓存 
这一节本身就值得一个特定的博客文章…所以简而言之,我会说Redis可以用作memcached的替代品,以便将缓存变成能够以更简单的方式存储数据的东西,所以每次都不需要重新生成数据。请参阅本文中发布的第一个模式以供参考

Redis无所不能,吹一波

你可以立马使用Redis来完成让用户更爽,让你的系统更简单,让你的网站响应的更快。你不需要为了使用Redis而替换你现有的配置,仅仅使用Redis来完成其他方式无法完成或难以完成或着代价过高的新事物。

05-11 13:07