Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 533|回复: 0

MySQL高效分页解决方案集(转)

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-17 03:03:08 | 显示全部楼层 |阅读模式

    很久以前的一次面试中,被面试官问到这个问题,由于平时用到的分页方法不多,只从索引、分表、使用子查询精准定位偏移以外,没有使用到其它方法。

    后来在看其它博客看到了一些不同的方案,也一直没有整理。今天有时间,整理出来,分享给大家。

    一,最常见MYSQL最基本的分页方式:

    select * from content order by id desc limit 0, 10

    在中小数据量的情况下,这样的SQL足够用了,唯一需要注意的问题就是确保使用了索引。随着数据量的增加,页数会越来越多,查看后几页的SQL就可能类似:

    select * from content order by id desc limit 10000, 10

    一言以蔽之,就是越往后分页,LIMIT语句的偏移量就会越大,速度也会明显变慢。
    此时,我们可以通过2种方式:

     


    一,子查询的分页方式来提高分页效率,飘易用的SQL语句如下:

    SELECT * FROM `content` WHERE id> (SELECT id FROM `content` ORDER BY id desc LIMIT ".($page-1)*$pagesize.", 1) ORDER BY id desc LIMIT $pagesize

    为什么会这样呢?因为子查询是在索引上完成的,而普通的查询时在数据文件上完成的,通常来说,索引文件要比数据文件小得多,所以操作起来也会更有效率。(via)通过explain SQL语句发现:子查询使用了索引!

    id select_type table type possible_keys key key_len ref rows Extra
    1 PRIMARY content range PRIMARY PRIMARY 4 NULL 6264 Using where
    2 SUBQUERY content index NULL PRIMARY 4 NULL 27085 Using index

    经过飘易的实测,使用子查询的分页方式的效率比纯LIMIT提高了14-20倍!

     

    二,JOIN分页方式

    select * FROM `content` AS t1
    JOIN (SELECT id FROM `content` ORDER BY id desc LIMIT ".($page-1)*$pagesize.", 1) AS t2
    WHERE t1.id

    经过我的测试,join分页和子查询分页的效率基本在一个等级上,消耗的时间也基本一致。explain SQL语句:

    id select_type table type possible_keys key key_len ref rows Extra
    1 PRIMARY system NULL NULL NULL NULL 1
    1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 6264 Using where
    2 DERIVED content index NULL PRIMARY 4 NULL 27085 Using index

    三,使用MYSQL的FOUND_ROWS()函数
    Mysql FOUND_ROWS() 函数结合SQL_CALC_FOUND_ROWS在SELECT中可以得到两个结果:
    1. 得到Limit的内容
    2. 得到去除Limit以后所有行数

    SELECT语句中经常可能用LIMIT限制返回行数。有时候可能想要知道如果没有LIMIT会返回多少行,但又不想再执行一次相同语句。那么,在SELECT查询中包含SQL_CALC_FOUND_ROWS选项,然后执行FOUND_ROWS()就可以了:

    select SQL_CALC_FOUND_ROWS * FROM tbl_name WHERE id > 100 LIMIT 10;
    SELECT FOUND_ROWS();

    其中SQL_CALC_FOUND_ROWS 告诉Mysql将sql所处理的行数记录下来,FOUND_ROWS() 则取到了这个纪录。 虽然也是两个语句,但是只执行了一次主查询,所以效率比原来要高很多。

    1. 如果在前一条语句中使用SQL_CALC_FOUND_ROWS选项,FOUND_ROWS()将返回第一条语句没有LIMIT时返回的行数。
    2. 如果在前一条语句中没有使用SQL_CALC_FOUND_ROWS选项,FOUND_ROWS()将返回前一条语句实际返回的行数。
    如果使用 SELECT SQL_CALC_FOUND_ROWS,MySQL必须计算所有结果集的行数。尽管这样,总比再执行一次不使用LIMIT的查询要快多了吧,因为那样结果集要返回客户端滴。(另外:应该不单是没有将结果集返回的原因,还有原因可能是比如LIKE之类比较费劲的SQL不需要再去劳累一次。)

    -- 注意下面语句中的条件 LIKE
    SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name WHERE Name LIKE '%string%' id > 100 LIMIT 10;
    SELECT FOUND_ROWS();

    -- 上面语句等价于下面语句,但性能方面应该提升非常非常的明显:
    SELECT COUNT(*) FROM tbl_name WHERE Name LIKE '%string%' ;
    SELECT * FROM tbl_name WHERE Name LIKE '%string%' id > 100 LIMIT 10;

    参考博客:

    http://blog.hexu.org/archives/1328.shtml

    http://hi.baidu.com/thinkinginlamp/blog/item/17476d22d66876a14623e81d.html

    http://www.piaoyi.org/php/MySQL-SUBQUERY-index.html

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 18:54 , Processed in 0.056885 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表