热点新闻
mysql之limit分页优化
2023-08-01 18:55  浏览:2102  搜索引擎搜索“手机闹展网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在手机闹展网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

准备工作







# 总记录数为500000 mysql> select count(id) from edu_test; +-----------+ | count(id) | +-----------+ | 500000 | +-----------+ 1 row in set (0.05 sec)

分析过程

从0开始查询10条:

mysql> select * from edu_test limit 0, 10; 10 rows in set (0.05 sec)

从20万开始查询10条:

mysql> select * from edu_test limit 200000, 10; 10 rows in set (0.14 sec)

从50万开始查询10条:

mysql> select * from edu_test limit 499000, 10; 10 rows in set (0.21 sec)

  • 现象:随着分页越深入,查询的时间也越来越长。

mysql> explain select * from edu_test limit 200000, 10; +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+-------+ | 1 | SIMPLE | edu_test | NULL | ALL | NULL | NULL | NULL | NULL | 499483 | 100.00 | NULL | +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+-------+ 1 row in set (0.09 sec)

  • 思考:limit分页做了一个全表扫描,扫描后将从200000开始往后取10条记录返回。

优化


思路:

  • 快速定位到要访问的数据行,缩小扫描范围。
    方案1
  • 延迟查询(先定位再查询).
    方案2、方案3

方案1:通过有序唯一索引缩小扫描范围
前提必须要id有序,要不然结果会漏掉一部分数据的。

mysql> select * from edu_test where id > 499000 order by id asc limit 10; 10 rows in set (0.14 sec) mysql> explain select * from edu_test where id > 499000 order by id asc limit 10; +----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | edu_test | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1000 | 100.00 | Using where | +----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set (0.16 sec) # 再缩小扫描范围 mysql> select * from edu_test where id between 499000 and 499020 order by id asc limit 10; 10 rows in set (0.09 sec) mysql> explain select * from edu_test where id between 499000 and 499020 order by id asc limit 10; +----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | edu_test | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 21 | 100.00 | Using where | +----+-------------+----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set (0.08 sec)

方案2:子查询

mysql> SELECT * FROM edu_test WHERe id >= (SELECt id FROM edu_test ORDER BY id LIMIT 499000, 1) LIMIT 10; 10 rows in set (0.16 sec) mysql> explain SELECt * FROM edu_test WHERe id >= (SELECt id FROM edu_test ORDER BY id LIMIT 499000, 1) LIMIT 10; +----+-------------+----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+ | 1 | PRIMARY | edu_test | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1000 | 100.00 | Using where | | 2 | SUBQUERY | edu_test | NULL | index | NULL | PRIMARY | 4 | NULL | 499001 | 100.00 | Using index | +----+-------------+----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+ 2 rows in set (0.14 sec)

方案3:join查询

mysql> select * from edu_test s, (select id from edu_test order by id limit 499000, 10) t where s.id = t.id; 10 rows in set (0.16 sec) mysql> explain select * from edu_test s, (select id from edu_test order by id limit 499000, 10) t where s.id = t.id; +----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+ | 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 499010 | 100.00 | NULL | | 1 | PRIMARY | s | NULL | eq_ref | PRIMARY | PRIMARY | 4 | t.id | 1 | 100.00 | NULL | | 2 | DERIVED | edu_test | NULL | index | NULL | PRIMARY | 4 | NULL | 499010 | 100.00 | Using index | +----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+ 3 rows in set (0.10 sec)

实际业务场景


  • 场景:
    在我们设计数据库id的时候,可能采用字符串格式、有顺序的id,带有一定的业务逻辑这样的分布式id。
  • 解决:
    如果我们分页想要优化时候,根据减少扫描思路,可以通过where id like '10289%' 方式,先缩小范围再分页。
  • 启示:
    在设计数据库id主键的时候,尽量保持主键唯一且有序最好能解决热点业务问题(如果依赖很多的非主键值,那么我们可能还需要回表操作),而且主键本身就是一种唯一索引,这种唯一有序特性可以便于帮助我们后期优化,减少扫描记录范围。
发布人:67a1****    IP:61.145.10.***     举报/删稿
展会推荐
  • 放牛
  • 2023-08-01浏览:2281
让朕来说2句
评论
收藏
点赞
转发