文章转载地址:http://www.cnblogs.com/wuxl360/p/5465670.html
文章写实,很符合技术人员特点。又加上作者的描述清晰易懂。难得的佳作。
分页查询和redis
问题
我在做论坛的是时候遇到了如下的问题。论坛里可以有很多的主题topic,每个topic对应到很多回复reply。现在要查询某个topic下按照replyTime升序排列的第pageNo页的reply,每页pageSize个reply。
reply是存放在mysql中的。以前的实现是利用mysql的limit查询
1
2
3
4
|
select * from reply
where topicId = ?
order by replyTime asc
limit (pageNo - 1) * pageSize, pageSize |
由于现在有很多的主题的回复很多,当有人查询第几百甚至几千页的时候,mysql性能表现很不好。“select limit offset, size” 只要offset太大,传统的关系型数据库的性能表现都不好。
如果能够利用带索引的查询条件先过滤掉一部分数据,就可以大大提高性能,比如:
1
2
3
4
5
|
select *
from reply
where topicId = ? and replyId > lastReplyIdOfCurrentPage
order by replyTime asc
limit (pageNo - currentPageNo) * pageSize, pageSize |
lastReplyIdOfCurrentPage 是当前页的最后一个reply的id。currentPageNo是当前页的页号。这里用replyId过滤条件,把前面页的内容过滤掉,这样减少了 offset的大小。但是当用户需要跳转到很远的一个页面的时候,offset还是会很大。比如,当前是第10页,要跳转到第1000页,offset = 990 * pageSize,还是会很大,性能依旧不行。尽管目前很多产品,都不提供这样的跳转能力了,但是我们的产品团队还是认为这个功能在我们的产品里面不可或 缺。
迁移到cassandra
后来我们把reply数据全部迁移到了cassandra上。cassandra的数据结构和mysql不一样。我们创建了一个topic_reply 列簇,每一行的行号是topicId,每一列是这个topic的replyId,这样得到类似如下结构
1:1,2,5,33,245,663,780...
2:36,78,89,94,235,345...
在cassandra中列是自然排序的,形成了一个从topic到reply的索引。查询的时候只能查询topicId行的列大于(或小于)replyId的size个replyId,相当于sql:
1
|
select * from topic_reply where replyId > ? limit size
|
, 不能够 “limit offset, size”。这意味着如果要查询第一千页,而我不知道第一千页开始的replyId是多少,我就得取出这一千页的数据,这显然是行不通的。所以得想办法从靠近我要取的数据的某个replyId处开始取数据。
redis的SortedSet
无 论是mysql还是cassandra,都不能很好地解决从一个很长的序列中取出任意一段数据的问题,而造成这一问题的根源在于这些数据是存放在磁盘上 的,磁盘不适合做此类的随机读的操作。所以想,如果能有一个程序,管理一些很大很大的放在内存中排序数组就好了,因为对内存中的数组做下标访问,是非常快 速的。做了一下调查正好发现,redis提供了此类的功能。
redis将数据存放到内存中,所以既便是随机读写,速度都是非常快。 redis支持的SortedSet结构正好适合于做分页查询。SortedSet按照给定的score给member排序,允许通过下标或者score 去查询。把同一个topic的replyId作为member,以replyId本身为score存放到SortedSet后,就可以通过下标取值了,例 如:
//存入数据
zadd tr:1 1 1
zadd tr:1 2 2
zadd tr:1 5 5
zadd tr:1 33 33
zadd tr:1 245 245
zadd tr:1 663 663
//pageSize = 3 取 第二页,即下标 3 到5的元素
zrange tr:1 3 5
其中 tr:1 是这个SortedSet的key,”tr:”只是用来区分其它key用的前缀,1是topicId。更详细的内容看redis官网http://redis.io
如此一来,就可以实现任意分页查询了,而且性能非常好。
缓存索引
redis 的数据全部存放到内存中,如果把所有topic到reply的关系都放到内存中,要耗费很多内存,而且这么多的内存实际上很多是浪费的,毕竟大部分的 topic是不活跃的。再者topic到reply的映射关系是非常重要的,所以我们需要把这种关系持久化。最后我们决定,这个映射关系,或者称为索引还 是存放在cassandra里面,只是在需要的时候,才从cassandra里面把索引载入到redis内,然后再利用redis分页查询。如此一 来,redis成了一个支持分页查询的强大的缓存。
分片缓存
对于超长的主题,全新载入到redis一次也是相当的耗时的,我们采取分片来解决这个问题。我们把索引每4800个值分成一片,用另外一个数据结构记录索引长度和索引从第二片开始的每片的开始值。
<iframe id="iframe_0.878788665934626" style="border-width: initial; border-style: none; width: 20px; height: 20px;" src="data:text/html;charset=utf8,%3Cstyle%3Ebody%7Bmargin:0;padding:0%7D%3C/style%3E%3Cimg%20id=%22img%22%20src=%22http://sulong.me/wp-content/uploads/2013/05/index_slice.png?_=5465670%22%20style=%22border:none;max-width:1132px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.878788665934626',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no"></iframe>
更新的索引的时候更新这个分片信息,记录各分片的头部是为了便于从cassandra载入分片。
查 询的时候把分页查询转化成某个片上某段索引的值。当分片大小大于pageSize并且能被pageSize整除时,这个转化是很简单的,因为分页正好会全 部落在某一个分片中。我们之所以把分片大小设置成4800正是因为这个值能被10 15 20 25 30 40 50 60 80 100 200 等很多常用分页大小整除。分片太大浪费内存,分片太小分片就太多。
只要算出这一页所在的分片,然后把需要的索引段载入到redis,再利用redis的分页查询查出结果。这样,只有活跃的索引分段才会被载入到redis内存中。
如果用mysql来持久化索引效果也是类似的,而且查询更加便利能力更强。
总结
只要产品能接受,就不要使用任意分页,任意跳转。确实需要高速分页查询的时候可以使用redis的SortedSet,但是得注意内存大小和持久化问题。
相关推荐
所有KOR库
2-1+Cassandra时空应用场景
本文将通过示例介绍Springboot,mybatis,maven,oracle,cassandra,aop事务,定时任务等框架的集成,因此业务不会复杂,供学习使用。
卡桑德拉模板Project 旨在用作 Web 项目模板,其中包括与 mysql 和 cassandra 的持久层集成。 还使用了 Spring 数据和 spring mvc。 Velocity 被选择并配置为模板引擎。 为了运行程序 cassandra.properties 和 mysql...
Cassandra的主要特点就是它不是一个数据库,而是由一堆数据库节点共同构成的一个分布式网络服务,对Cassandra 的一个写操作,会被复制到其他节点上去,对Cassandra的读操作,也会被路由到某个节点上面去读取。...
Stream-Framework:使用 Cassandra 和 Redis 创建动态消息和通知系统
Windows一键启动Eureka和Cassandra和redis-server的脚本.bat
流框架活动流和新闻源 Stream Framework是一个Python库,可让您使用Cassandra和/或Redis构建活动流和新闻提要。 如果您不使用python,请查看 ,它支持Node,Ruby,PHP,Python,Go,Scala,Java和REST。 您可以构建...
Solandra, Solandra = Solr + Cassandra SolandraSolandra是基于 Apache和的实时分布式搜索引擎。它的核心是Solr和Cassandra的紧密集成,这意味着在单个JVM中,Solr和Cassandra都在运行,并且使用Cassandra模型的...
1-7+Cassandra过去,现在,未来
基于Kafka,Cassandra,gRPC,Redis,.NET 5,ASP.NET的10亿活跃用户的聊天的和。 基于Docker,OpenTelemetry,Fluentd,ElasticSearch,Kibana,Jaeger且环境,具有可容器化组件。 我感谢所有评论,请随时使用“ ...
Cassandra(apache-cassandra-3.11.11-bin.tar.gz)是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存收件箱等简单格式数据,集GoogleBigTable的数据模型与Amazon Dynamo的完全分布式的架构于一身...
DevCenter cassandra客户端 DevCenter cassandra客户端 DevCenter cassandra客户端
cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战cassandra 实战...
Cassandra(apache-cassandra-4.0.1-bin.tar.gz)是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存收件箱等简单格式数据,集GoogleBigTable的数据模型与Amazon Dynamo的完全分布式的架构于一身...
Cassandra的主要特点就是它不是一个数据库,而是由一堆数据库节点共同构成的一个分布式网络服务,对Cassandra 的一个写操作,会被复制到其他节点上去,对Cassandra的读操作,也会被路由到某个节点上面去读取。...
Cassandra从搭建到数据插入存储 环境在XP下
The rising popularity of Apache Cassandra rests on its ability to handle very large data sets that include hundreds of terabytes -- and that's why this distributed database has been chosen by ...
摘要:Spark,强大的迭代计算框架,在...在对Spark内部实现有了一定了解之后,当然希望将其应用到实际的工程实践中,这时候会面临许多新的挑战,比如选取哪个作为数据仓库,是HBase、MongoDB还是Cassandra。即便一旦