单表三千万数据分页优化方案

2017-08-09 15:08:17 +08:00
 Aluhao
直接用变通的查询方式,查询时间高的吓人,因为有些数据查询是不连贯的,不能用大于多少或等于多少来查询;
[sql] => SELECT * FROM `article_comments` ORDER BY id DESC LIMIT 199980, 20;
[time] => 0.438656
[sql] => SELECT * FROM `article_comments` ORDER BY id DESC LIMIT 1999980, 20;
[time] => 26.835051
[sql] => SELECT * FROM `article_comments` ORDER BY id DESC LIMIT 19999980, 20;
[time] => 31.343988
[sql] => SELECT * FROM `article_comments` ORDER BY id DESC LIMIT 29999980, 20;
[time] => 32.138655

现在的做法是通过先查询取出 id
SELECT id FROM `article_comments` LIMIT 19999980, 20;

然后用 id 去取数据
SELECT * FROM `article_comments` WHERE id IN('1','2'....);
虽然这样优化了很多,但是也不是很理想,如果取的 ID 间隔大,也会进行全表扫描;
看 V2 能人很多,不知道还有没更优化的方法?
8293 次点击
所在节点    MySQL
36 条回复
af463419014
2017-08-09 15:20:36 +08:00
数据库查询规范第一条 : 绝对不允许使用 select *
yzongyue
2017-08-09 15:27:05 +08:00
在实际的项目中会有这种需求么
> SELECT id FROM `article_comments` LIMIT 19999980, 20;
连个 where article_id = xxx 都没有
Aluhao
2017-08-09 15:30:32 +08:00
@yzongyue 上面只是一个显示所有留言的列表,等同于留言管理吧,加判断也是有需求的;
cxh116
2017-08-09 15:34:03 +08:00
改分页查询条件吧, limit offset 几十万,能不慢.
用当前页最大评价 id ,下一页查询大于此 id 即可.

https://juejin.im/entry/5865c81fac502e006d5e72de
Aluhao
2017-08-09 15:36:41 +08:00
@cxh116 这方法不适用有些场景的,如,需要查询用户回复的留言,这个留言 ID 非顺序的,很乱的,在几千万条数据中断断的出现几千条;
w0000
2017-08-09 15:40:14 +08:00
@Aluhao 这个 id 是建了索引的么,这么慢吗
Aluhao
2017-08-09 15:41:18 +08:00
@w0000 肯定建了索引了,因为数据太多了,单表三千多万,如果只是几十万就很快。
XiaoFaye
2017-08-09 15:45:06 +08:00
论一定时间后关闭评论的重要性
nullcc
2017-08-09 15:46:32 +08:00
采用 limit offset 这种方式,如果表数据量一大就会坑爹,如果索引没建好,一个很大的 offset 需要让数据库先扫描前面的很多数据,到后期效率和全表扫描也没什么区别了。
你可以考虑在 article_comments 的创建日期上创建聚集索引,文章评论满足“既不能绝大多数都相同,又不能只有极少数相同”的条件,比较适合在这种列上创建聚集索引。查询的时候可以根据日期先做筛选,然后再跟上你的其他查询条件。
Aluhao
2017-08-09 15:46:37 +08:00
@XiaoFaye 这个不太好吧
nullcc
2017-08-09 15:48:41 +08:00
补充下,文章评论的创建日期满足“既不能绝大多数都相同,又不能只有极少数相同”的条件,上面笔误。
Sunshow
2017-08-09 15:54:20 +08:00
把翻页都通过 id > 的条件转换成取第一页的请求
sujin190
2017-08-09 15:55:25 +08:00
大量数据要翻页一般不使用页码,而是使用游标,游标那么久可以包含很多信息了,当前页码,翻页方向,当前 id 值,每页条数等等,使用的方式 where id>last_id offset per_page_count,这样就可以充分使用索引来翻页了
Immortal
2017-08-09 15:59:28 +08:00
@Aluhao 上面说的根据 id 大于的方式查询 我想了下没问题吧 虽然是断断续续的 limit 后数量还是准确的吧?
daimazha
2017-08-09 16:16:38 +08:00
@Immortal #14 没问题, 而且这么多数据 可以考虑分表了。
vipvideoshare
2017-08-09 16:18:47 +08:00
试试给 id 加反向 index
```
CREATE INDEX id_desc_index ON article_comments (id DESC)
```
Immortal
2017-08-09 16:20:12 +08:00
@daimazha 是的 如果用户 id 顺序 可以直接取模分表 也简单的
zhaopengme
2017-08-09 16:27:49 +08:00
肯定是根据业务场景来优化了,单纯的用 sql 么有意义.
eg.
1. 比如把数据分为常用数据和历史数据
2. 比如做数据统计,增加中间表
3. 数据快速处理,使用数据库编程,游标很有效率
等等等,具体问题具体对象.
gouchaoer
2017-08-09 16:38:39 +08:00
select * 没有问题,limit 那种写法不可以,你需要这样给 article_comments 弄个自增的 int 健,然后 select * frome table where id_inc > 10000 and id_inc<10100;
suconghou
2017-08-09 17:43:22 +08:00
ID 加索引了吧,可以这样 分两次查询 select id from mytable order by id limit 13456901,1; 先查出分页的首页的 ID

得到 ID x

然后 select * from mytable order by id where id > x limit 10;
得到数据.

由于 ID 有索引,整体时间就在于第一条 SQL 的时间. 性能能提升 2-3 倍.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/381671

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX