用Redis解决互联网项目的数据读取难点
Redis在很多方面与其他数据库解决方案不同:它使用内存提供主存储支持,而仅使用硬盘做持久性的存储;它的数据模型非常独特,用的是单线程。另一个大区别在于,你可以在开发环境中使用Redis的功能,但却不需要转到Redis。
转向Redis当然也是可取的,许多开发者从一开始就把Redis作为首选数据库;但设想如果你的开发环境已经搭建好,应用已经在上面运行了,那么更换数据库框架显然不那么容易。另外在一些需要大容量数据集的应用,Redis也并不适合,因为它的数据集不会超过系统可用的内存。所以如果你有大数据应用,而且主要是读取访问模式,那么Redis并不是正确的选择。
然而我喜欢Redis的一点就是你可以把它融入到你的系统中来,这就能够解决很多问题,比如那些你现有的数据库处理起来感到缓慢的任务。这些你就可以通过Redis来进行优化,或者为应用创建些新的功能。在本文中,我就想探讨一些怎样将Redis加入到现有的环境中,并利用它的原语命令等功能来解决 传统环境中碰到的一些常见问题。在这些例子中,Redis都不是作为首选数据库。
1、显示最新的项目列表
下面这个语句常用来显示最新项目,随着数据多了,查询毫无疑问会越来越慢。
SELECT * FROM foo WHERE ... ORDER BY time DESC LIMIT 10
在Web应用中,“列出最新的回复”之类的查询非常普遍,这通常会带来可扩展性问题。这令人沮丧,因为项目本来就是按这个顺序被创建的,但要输出这个顺序却不得不进行排序操作。
类似的问题就可以用Redis来解决。比如说,我们的一个Web应用想要列出用户贴出的最新20条评论。在最新的评论边上我们有一个“显示全部”的链接,点击后就可以获得更多的评论。
我们假设数据库中的每条评论都有一个唯一的递增的ID字段。
我们可以使用分页来制作主页和评论页,使用Redis的模板,每次新评论发表时,我们会将它的ID添加到一个Redis列表:
LPUSH latest.comments <ID>
我们将列表裁剪为指定长度,因此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
这里我们做的很简单。在Redis中我们的最新ID使用了常驻缓存,这是一直更新的。但是我们做了限制不能超过5000个ID,因此我们的获取ID函数会一直询问Redis。只有在start/count参数超出了这个范围的时候,才需要去访问数据库。
我们的系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,而主页或第一个评论页是不会麻烦到硬盘上的数据库了。
2、删除与过滤
我们可以使用LREM来删除评论。如果删除操作非常少,另一个选择是直接跳过评论条目的入口,报告说该评论已经不存在。
有些时候你想要给不同的列表附加上不同的过滤器。如果过滤器的数量受到限制,你可以简单的为每个不同的过滤器使用不同的Redis列表。毕竟每个列表只有5000条项目,但Redis却能够使用非常少的内存来处理几百万条项目。
3、排行榜相关
另一个很普遍的需求是各种数据库的数据并非存储在内存中,因此在按得分排序以及实时更新这些几乎每秒钟都需要更新的功能上数据库的性能不够理想。
典型的比如那些在线游戏的排行榜,比如一个Facebook的游戏,根据得分你通常想要:
- 列出前100名高分选手
- 列出某用户当前的全球排名
这些操作对于Redis来说小菜一碟,即使你有几百万个用户,每分钟都会有几百万个新的得分。
模式是这样的,每次获得新得分时,我们用这样的代码:
ZADD leaderboard <score> <username>
你可能用userID来取代username,这取决于你是怎么设计的。
得到前100名高分用户很简单:ZREVRANGE leaderboard 0 99。
用户的全球排名也相似,只需要:ZRANK leaderboard <username>。
4、按照用户投票和时间排序
排行榜的一种常见变体模式就像Reddit或Hacker News用的那样,新闻按照类似下面的公式根据得分来排序:
score = points / time^alpha
因此用户的投票会相应的把新闻挖出来,但时间会按照一定的指数将新闻埋下去。下面是我们的模式,当然算法由你决定。
模式是这样的,开始时先观察那些可能是最新的项目,例如首页上的1000条新闻都是候选者,因此我们先忽视掉其他的,这实现起来很简单。
每次新的新闻贴上来后,我们将ID添加到列表中,使用LPUSH + LTRIM,确保只取出最新的1000条项目。
有一项后台任务获取这个列表,并且持续的计算这1000条新闻中每条新闻的最终得分。计算结果由ZADD命令按照新的顺序填充生成列表,老新闻则被清除。这里的关键思路是排序工作是由后台任务来完成的。
5、处理过期项目
另一种常用的项目排序是按照时间排序。我们使用unix时间作为得分即可。
模式如下:
- 每次有新项目添加到我们的非Redis数据库时,我们把它加入到排序集合中。这时我们用的是时间属性,current_time和time_to_live。
- 另一项后台任务使用ZRANGE…SCORES查询排序集合,取出最新的10个项目。如果发现unix时间已经过期,则在数据库中删除条目。
6、计数
Redis是一个很好的计数器,这要感谢INCRBY和其他相似命令。
我相信你曾许多次想要给数据库加上新的计数器,用来获取统计或显示新信息,但是最后却由于写入敏感而不得不放弃它们。
好了,现在使用Redis就不需要再担心了。有了原子递增(atomic increment),你可以放心的加上各种计数,用GETSET重置,或者是让它们过期。
例如这样操作:
INCR user:<id> EXPIRE
user:<id> 60
你可以计算出最近用户在页面间停顿不超过60秒的页面浏览量,当计数达到比如20时,就可以显示出某些条幅提示,或是其它你想显示的东西。
7、特定时间内的特定项目
另一项对于其他数据库很难,但Redis做起来却轻而易举的事就是统计在某段特点时间里有多少特定用户访问了某个特定资源。比如我想要知道某些特定的注册用户或IP地址,他们到底有多少访问了某篇文章。
每次我获得一次新的页面浏览时我只需要这样做:
SADD page:day1:<page_id> <user_id>
当然你可能想用unix时间替换day1,比如time()-(time()%3600*24)等等。
想知道特定用户的数量吗?只需要使用SCARD page:day1:<page_id>。
需要测试某个特定用户是否访问了这个页面?SISMEMBER page:day1:<page_id>。
8、实时分析正在发生的情况,用于数据统计与防止垃圾邮件等
我们只做了几个例子,但如果你研究Redis的命令集,并且组合一下,就能获得大量的实时分析方法,有效而且非常省力。使用Redis原语命令,更容易实施垃圾邮件过滤系统或其他实时跟踪系统。
9、Pub/Sub
Redis的Pub/Sub非常非常简单,运行稳定并且快速。支持模式匹配,能够实时订阅与取消频道。
10、队列
你应该已经注意到像list push和list pop这样的Redis命令能够很方便的执行队列操作了,但能做的可不止这些:比如Redis还有list pop的变体命令,能够在列表为空时阻塞队列。
现代的互联网应用大量地使用了消息队列(Messaging)。消息队列不仅被用于系统内部组件之间的通信,同时也被用于系统跟其它服务之间的交互。消息队列的使用可以增加系统的可扩展性、灵活性和用户体验。非基于消息队列的系统,其运行速度取决于系统中最慢的组件的速度(注:短板效应)。而基于消息队列可以将系统中各组件解除耦合,这样系统就不再受最慢组件的束缚,各组件可以异步运行从而得以更快的速度完成各自的工作。
此外,当服务器处在高并发操作的时候,比如频繁地写入日志文件。可以利用消息队列实现异步处理。从而实现高性能的并发操作。
11、缓存
Redis的缓存部分值得写一篇新文章,我这里只是简单的说一下。Redis能够替代memcached,让你的缓存从只能存储数据变得能够更新数据,因此你不再需要每次都重新生成数据了。
用Redis解决互联网项目的数据读取难点的更多相关文章
- Redis 单节点百万级别数据 读取 性能测试.
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 这里先进行造数据,向redis中写入五百万条数据,具体方式有如下三种: 方法一:(Lua 脚本) vim ...
- springboot(12)Redis作为SpringBoot项目数据缓存
简介: 在项目中设计数据访问的时候往往都是采用直接访问数据库,采用数据库连接池来实现,但是如果我们的项目访问量过大或者访问过于频繁,将会对我们的数据库带来很大的压力.为了解决这个问题从而redis数据 ...
- Redis在windows实现将数据缓存起来定时更新读取
实现接口的读取存放在内存中,实现了Web网站直接读取内存数据,大大的减少了访问接口带来的等待时间,这个功能是比较实用的 需要下载一下‘类库’及‘Redis-x64-3.2.100程序包’ 百度云材料下 ...
- 解决InputStream中数据读取不完整问题
转载:https://blog.csdn.net/lilidejing/article/details/37913627 当需要用到InputStream获取数据时,这时就需要读取InputStrea ...
- python操作三大主流数据库(14)python操作redis之新闻项目实战②新闻数据的展示及修改、删除操作
python操作三大主流数据库(14)python操作redis之新闻项目实战②新闻数据的展示及修改.删除操作 项目目录: ├── flask_redis_news.py ├── forms.py ├ ...
- Redis:Java链接redis单节点千万级别数据 写入,读取 性能测试
本文是对Redis 单节点,针对不同的数据类型,做插入行测试. 数据总条数为:10058624 环境说明: Redis 未做任何优化, 单节点 (服务器上, 内存64G) ...
- 豌豆夹Redis解决方式Codis源代码剖析:Proxy代理
豌豆夹Redis解决方式Codis源代码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描写叙述: Codis is a proxy b ...
- 【原创】互联网项目中mysql应该选什么事务隔离级别
摘要 企业千万家,靠谱没几家. 社招选错家,亲人两行泪. 祝大家金三银四跳槽顺利! 引言 开始我们的内容,相信大家一定遇到过下面的一个面试场景 面试官:"讲讲mysql有几个事务隔离级别?& ...
- 互联网项目中mysql应该选什么事务隔离级别
引言 开始我们的内容,相信大家一定遇到过下面的一个面试场景 面试官:“讲讲mysql有几个事务隔离级别?” 你:“读未提交,读已提交,可重复读,串行化四个!默认是可重复读” 面试官:“为什么mysql ...
随机推荐
- databaseDesgin-temple
ylbtech-dbs:ylbtech-storebook- A, 返回顶部 1, 2, B,返回顶部 1, 2, C,返回顶部 作者:ylbtech出处:http://storebook.cnblo ...
- Python项目打包成exe文件
这里我们使用pyinstaller这个软件即可,使用pip即可完美安装,在要打包的程序目录下打开cmd输入 pyinstaller -F 文件名.py 即可成功,运行成功后生成一个dict文件夹,东西 ...
- log4j示例-Daily方式(log4j.properties)
log_home=./log log4j.rootLogger=info log4j.category.com.ai.toptea.collection=Console,DailyFile,Daily ...
- DataTables warning: table id=DataTables_Table_0 - Requested unknown parameter '1' for row 0. For more information about this error, please see http://datatables.net/tn/4
今天在做后台的时候,考虑到会员模块和订单模块列表显示差不多,于是乎,偷了个懒,把会员列表显示页面的代码拷贝了过来,修改了一下,变成了订单模块.可是,在订单列表显示的时候老是报下面的错误,截图如下: 后 ...
- SQLSERVER出错提示:此上下文中不允许使用''。此处只允许使用常量、表达式或变量。不允许使用列名。
在执行一段SQL语句时出现了这样的一段错误提示,在网上找了不少答案,都说的不是很详细,反复修改试验,最终解决了此问题.原SQl语句为: insert into shoufei(djbh,sflb,jk ...
- node.js 获取客户端信息
结果:
- 【BZOJ】2819: Nim(树链剖分 / lca+dfs序+树状数组)
题目 传送门:QWQ 分析 先敲了个树链剖分,发现无法AC(其实是自己弱,懒得debug.手写栈) 然后去学了学正解 核心挺好理解的,$ query(a) $是$ a $到根的异或和. 答案就是$ l ...
- 【洛谷】P2983 [USACO10FEB]购买巧克力Chocolate Buying(贪心)
题目描述 Bessie and the herd love chocolate so Farmer John is buying them some. The Bovine Chocolate Sto ...
- pickle示例
my_pickle.py---------------------- #!/usr/bin/env python # encoding: utf-8 # Date: 2018/6/3import p ...
- 02_java之数据类型和基本运算
01变量概述 * A: 什么是变量? * a: 变量是一个内存中的小盒子(小容器),容器是什么?生活中也有很多容器,例如水杯是容器,用来装载水:你家里的大衣柜是容器,用来装载衣裤:饭盒是容器,用来装载 ...