新手问题 will_paginate 分页过多 (大概 10000 页),点击最后几页的时候,速度明显变慢

suxiaohun · 2016年01月08日 · 最后由 suxiaohun 回复于 2016年01月14日 · 3764 次阅读

我用 will_paginate 分页, @questions = Question.paginate(page: params[:page], per_page: 15) db 中数据大概在 15w 条左右,在点击最后几页的时候速度很慢,我从 console 中 得到 sql 在数据库中执行,确实就很慢。

我从网上查了查,说是 offset 过大,导致 sql 查询过慢,有什么好的方法解决这个问题吗? 看了一些优化,好像要写 sql?

使用 id 来做分页,如

@questions = Question.where("questions.id > ?", 100).limit(15)

我从网上查了查,说是 offset 过大,导致 sql 查询过慢,有什么好的方法解决这个问题吗?

是的。 底层的话可以为表建合适的索引。还有一个最简单的方法,直接隐藏最后几页的 button~很多网站都是这么做的,防止用户随意乱点

#1 楼 @hz_qiuyuanxin id? 表中由于一些事务逻辑,id 并不一定是连续的,如何根据 page,per_page 得到基数的这个 id? 而且,我没把一些条件和排序贴出来,这样的话,id 都是无序的

offset 过大就是这种效果,不过每次只取一条结果的话,速度还不错,如果一页 15 个结果,可以分 15 次取出,你可以试一下,有没有效果不敢保证。

N 年前遇到过相同问题,因为数据不常变动,后来我做了冗余,多开一个 text 字段,然后从头到尾遍历一次,在新开的 text 字段存接下来的 14 个结果。跑一遍还是要一点时间的,但这之后只要查一条信息,就得到 15 条数据,多靠后都没关系。

如果更新一条数据,要把包含这条数据的所有相邻 text 字段都更新。这个过程慢一点没关系,不常更新。

不给查太大。

#6 楼 @rei #2 楼 @bright #4 楼 @qinfanpeng

门户新闻可能不用查,但某些情况下,是必须查的。我的一个使用场景是 40 万数据,要查一条数据的前 15 条,后 15 条记录,我就用了冗余,更新和插入麻烦一点,实际效果还是相当好的。

#1 楼 @hz_qiuyuanxin #2 楼 @bright #5 楼 @peter #6 楼 @rei 谢谢各位的回答,我看看能不能说服需求 不显示最后几页,有点悬。 另外我测试优化的时候,遇到一个问题,速度非常快,但结果不一样

我本来打算先查 id,然后 inner join 出来, select * from questions inner join (select id from questions limit 150000,15) as lim using(id); 后来发现是 只查 id,并不是按照 id 自增顺序展示结果的,网上好像也没找到类似的情况, 不知道各位遇到过这种问题没? 如果加上 排序 select * from questions inner join (select id from questions ORDER BY id limit 150000,15) as lim using(id); 结果正常了,可惜速度又下去了 #5 楼 @peter 只查一条试了一下,速度没有明显提高。

#2 楼 @bright 没太听懂你的意思,是只取一部分数据吗?

#8 楼 @suxiaohun 我的场景和你的不一样,我是 select * from table where keyword='foobar',然后在 foobar 这条记录里冗余其它 15 条结果。

你这种情况下,如果知道某页的第一个 id 是什么,那也可以用冗余的方法了。

#10 楼 @peter 额 ,不好意思,我理解错了,你说的冗余我不是很明白,我这边的新数据更新的很频繁, 而且有两个字段内容特别多,应该不合适吧。

@rei 能给具体的实现细节说一下吗?"不给查太大" 是指每页的数吗?感觉效果一样啊

#12 楼 @easonlovewan 页数大于某个值就直接返回空。

ruby-china 的分页只能查看前 69 页,也是出于性能考虑吗?我一直在看老帖子啊。

#13 楼 @rei 这样做确实可以,不过给用户的体验是不是不太好啊

#15 楼 @easonlovewan 一般来说用户要的是搜索和过滤。

@postids = posts.paginate(:page => params[:page]).order('created_at DESC').select('id').to_a
@posts = Post.where('posts.id' => @postids).order('posts.created_at DESC').eager_load [:user]

专门针对 MySQL 傻叉的分页效率做优化。

#18 楼 @msg7086 这样效果好吗?

#9 楼 @easonlovewan 是的,比如只显示前十页

#19 楼 @pathbox 至少比直接分页好多了。 MySQL 就是这个问题很大,limit 数据量一大就死。

#21 楼 @msg7086 唉,还是限制个页数吧,剩下的给搜索

#22 楼 @pathbox 嘛,随意。 反正我们系统里就是这个代码,5000 页都是秒出的。

每次查询做 limit,大于某个值,重新做查询。

@suxiaohun 分页条件,过大的 offse 提供了一种解决方式,可以试一试

#26 楼 @ibugs thanks ,我去试下

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册