mysql 近千万级数据表,在分页时有什么好的方案吗。

2020-04-12 20:53:21 +08:00
 hyd8323268

单表 800w 条,myisam 引擎。 需要根据一个值为时间戳的字段进行排序,时间戳有重复值,普通索引。 刚开始是采用的:select 字段 from 表名 order by 时间戳 desc,id asc limit 0,10; 但是特别慢,前几页还好,越到后面越慢。 百度了几个方案试了都感觉没什么特别大的变化。

9536 次点击
所在节点    MySQL
56 条回复
gz911122
2020-04-12 20:59:54 +08:00
假设时间戳字段叫 ctime,
那么直接 ctime > ? limit ? 就行
jugelizi
2020-04-12 20:59:56 +08:00
不要分页 page 传最后最大 ID
odirus
2020-04-12 21:00:54 +08:00
有主键吧?

"select * from t limit 10000, 10" 变为 "select * from t where pk > ${上一页的最大值} limit 10" 这种方案试过没有呢?
gz911122
2020-04-12 21:01:31 +08:00
@gz911122 order by ctime,id where ctime > 上次查询最后一条的 ctime id > 最后一条的 id limit 分页大小
odirus
2020-04-12 21:43:20 +08:00
如果使用最后一条记录 ID 来查询的话貌似要漏数据,假设数据如下
ctime, id
1, 8
1, 9
2, 10
2, 11
3, 12
3, 13

如果按照 order by ctime desc, id asc 的话查询结果是 ( 3, 12 )
后续页面按照 "order by ctime desc, id asc where ctime < ${最后一条记录的 ctime} and id < ${最后一条记录的 id}" 的话查询结果是 ( 2, 10 )

所以我建议查询方式是 order by ctime desc, id desc,第一页查询结果是 ( 3, 13 )
后续页面按照 "order by ctime desc, id desc where ctime <= ${最后一条记录的 ctime} and id < ${最后一条记录的 id}" ,查询结果是 ( 3, 12 ),特别注意那个等号
Aresxue
2020-04-12 21:58:43 +08:00
查询语句可以带主键(趋势递增才可以), 比如原来的语句是 select * from table limit 1000,10(单页 10 条), 现在给改成
1.select * from table where id >= (select id from table 1imit 1000,1) limit 10
2.Select id from table limit 10000, 10;Select * from table where id in (123,345....);
3.select * from table INNER JOIN (select id from table limit 10000,1) using (id);
4.在代码里记录最后一条的 id, select * from table where id >= ? limit10;
简单来说就是只扫描主键不扫描行记录, 减少一次 B+树中的搜索
hooopo
2020-04-12 22:14:25 +08:00
现在还有人用 myisam ?
mornone
2020-04-12 22:19:35 +08:00
@Aresxue 这个方法是可行的,使用主键条件缩小查询范围
eq06
2020-04-12 22:21:37 +08:00
另建分页表,将分页和时间戳关联起来
iffi
2020-04-12 22:24:29 +08:00
大数据量下分页优化 - 延迟关联

select id,name from user limit 500000,10
优化后:
select id,name from user u inner join (select id from user limit 500000,10)as tmp
on tmp.id = u.id
littlewing
2020-04-12 22:37:42 +08:00
有一个疑问,InnoDB 下,select * from t order by idx_1 limit 100, 10 这种语句,idx_1 是索引,扫描前 100 行的时候会回表扫描主键索引拿到所有列吗?还是只是扫描 idx_1 这个索引的前 100 行,然后后面从 101 行开始取 10 行再分别回表用 id 去主键索引拿到所有列的值?
xiaoidea
2020-04-13 00:07:04 +08:00
@iffi 这是啥原理,mark 下明天试试
soseek
2020-04-13 02:08:07 +08:00
@xiaoidea 减少了数据体积了👀
bigbigroll
2020-04-13 08:14:08 +08:00
@hooopo 可能数据库做了读写分离?
ddup
2020-04-13 08:48:41 +08:00
用分区表 根据时间范围建立分区表试试
atonku
2020-04-13 08:50:09 +08:00
全查出来,代码分
ohao
2020-04-13 08:57:50 +08:00
my.oschina.net/luanwu/blog/3138110
可以参考这个

上次这么多数据还是弄过期域名的时候 亿级的
min max 的方式来分页 效率还可以的
luckylvke
2020-04-13 08:58:34 +08:00
@xiaoidea 只查主键索引减少回表,缩小范围后,内连出来(我猜的。。。
hyd8323268
2020-04-13 09:28:49 +08:00
@iffi 最重要的问题是要排序,需要根据时间戳排序,并且时间戳是有重复的。
用你优化后的 sql 中的子查询的话是不用回表的,但是如果得加排序。
select id,name from user u inner join (select id from user order by 时间戳 desc,id asc limit 500000,10)as tmp
on tmp.id = u.id 。这样的话覆盖索引是不生效的,特别慢。
pumily
2020-04-13 09:35:28 +08:00
我前几天刚遇到过,但是数据量还没楼主的那么大,但是感觉可以提供一个参考思路。
https://blog.csdn.net/qq_41348754/article/details/105422383

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

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

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

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

© 2021 V2EX