我用 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 字段都更新。这个过程慢一点没关系,不常更新。
#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 只查一条试了一下,速度没有明显提高。
#8 楼 @suxiaohun 我的场景和你的不一样,我是 select * from table where keyword='foobar'
,然后在 foobar 这条记录里冗余其它 15 条结果。
你这种情况下,如果知道某页的第一个 id 是什么,那也可以用冗余的方法了。
@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 傻叉的分页效率做优化。