Mysql数据库百万级记录查询分页优化

很多的朋友在面试中会遇到这样的问题,也有很多的项目在运营一段时间后也会遇到MYSQL查询中变慢的一些瓶颈,今天这儿简单的介绍下我常用的几种查询分页的方法,我所知道的也无非就是索引、分表、子查询偏移,所以要是有什么不对或有更好的方法,欢迎大家留言讨论。

效率分析关键词:explain + SQL语句

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

    select * from `table` order by id desc limit 0, 20

在中小数据量的情况下,这样的SQL足够用了,唯一需要注意的问题就是确保使用了索引。随着数据量的增加,页数会越来越多,在数据慢慢增长的过程中,可能就会出现limit 10000,20这样的情况,limit 10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里,如果是limit 100000,100,需要扫描100100行,在一个高并发的应用里,每次查询需要扫描超过10W行,性能肯定大打折扣。

这种方式有几个不足:较大的偏移(OFFSET)会增加结果集,小比例的低效分页足够产生磁盘I/O瓶颈,需要扫描的行多。

简单的解决方法:不显示记录总数,没用户在乎这个数字;不让用户访问页数比较大的记录,重定向他们;避免count(*) ,不显示总数,让用户通过“下一页”来翻页 ,缓存总数;单独统计总数,在插入和删除时递增/递减

二,第二种就是分表,计算HASH值,这儿不做介绍了,我目前也没有在项目中真正使用过这种方法,还停留在理论层次;

三,第三种是偏移:

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

或者

    select * FROM `table` AS t1 JOIN (SELECT id FROM `table` ORDER BY id desc LIMIT 900,1) AS t2 WHERE t1.id<=t2.id order by t1.id desc limit 5

原理就是记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,由于页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减少扫 描的行数。其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。

比如还是SELECT * FROM `table` ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,如果我们只提供”上一页”、”下一页”这样 的跳转(不提供到第N页的跳转),那么在处理”上一页”的时候SQL语句可以是:

    SELECT * FROM `table` WHERE id > 9527 ORDER BY id ASC LIMIT 20;

处理”下一页”的时候SQL语句可以是:

    SELECT * FROM `table` WHERE id < 9500 ORDER BY id DESC LIMIT 20;

不管翻多少页,每次查询只扫描20行。

缺点是只能提供”上一页”、”下一页”的链接形式,但是我一般来说非常喜欢”<上一页 1 2 3 4 5 6 7 8 9 下一页>”这样的链接方式,怎么办呢?

如果LIMIT m,n不可避免的话,要优化效率,只有尽可能的让m小一下,我们扩展前面做法,还是SELECT * FROM `table` ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,比如要跳到第8页,我看的SQL语句可以这 样写:

    SELECT * FROM `table` WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;

跳转到第13页:

    SELECT * FROM `table` WHERE id < 9500 ORDER BY id DESC LIMIT 40,20;

注意SQL语句里面的ASC和DESC,如果是ASC取出来的结果,显示的时候记得倒置一下。

整体来说在面对百万级数据的时候如果使用上面第三种方法来优化,系统性能上是能够得到很好的提升,在遇到复杂的查询时也尽量简化,减少运算量。 同时也尽量多的使用内存缓存,有条件的可以考虑分表、分库、阵列之类的大型解决方案了。
时间: 2024-10-12 18:46:11

Mysql数据库百万级记录查询分页优化的相关文章

mysql 单表百万级记录查询分页优化

insert select (制造百万条记录) 在开始百万级数据的查询之前,自己先动手制造百万级的记录来供我们使用,使用的方法是insert select方法 INSERT 一般用来给表插入一个指定列值的行.但是,INSERT 还存在另一种形式,可以利用它将一条SELECT 语句的结果插入表中.这就是所谓的INSERT SELECT, 顾名思义,它是有一条INSERT语句和一条SELECT语句组成的. 现在,有一个warning_reparied表,有2447条记录,如下: mysql> sel

数据库查询优化方案(处理上百万级记录如何提高处理查询速度)

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引.2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0 3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索

使用redis缓存加索引处理数据库百万级并发

使用redis缓存加索引处理数据库百万级并发 前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1000万条数据,可以参考我之前的文章插入数据,这里不再细说.我大概的做法是这样的,编码使用多线程访问我的数据库,在访问数据库前先访问redis缓存没有的话在去查询数据库,需要注意的是redis最大连接数最好设置为300,不然会出现很多报错. 贴一下代码吧 1 2 3 4 5 6 7 8 9 10 1

[已解决]C#批量高效率导入大数据到数据库[百万级以上]

将几百万条数据导入到数据库中,怎么样高效率的导入?下面我就介绍一个高效率的方法:1.将数据库文件(DB.csv)导入到DataTable中: /// <summary> /// 将CSV文件的数据读取到DataTable中 /// </summary> /// <param name="fileName">CSV文件路径</param> /// <returns>返回读取了CSV数据的DataTable</returns

Java对MySQL数据库进行连接、查询和修改(转)

Java对MySQL数据库进行连接.查询和修改 0. 一般过程: (1) 调用Class.forName()方法加载驱动程序. (2) 调用DriverManager对象的getConnection()方法,获得一个Connection对象. (3) 创建一个Statement对象,准备一个SQL语句,这个SQL语句可以是Statement对象(立即执行的的语句).PreparedStatement语句(预编译的语句)或CallableStatement对象(存储过程调用的语句). (4) 调用

Oracle数据库查询优化方案(处理上百万级记录如何提高处理查询速度)

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引.2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=03.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引

关于mysql处理百万级以上的数据时如何提高其查询速度的方法

关于大数据量处理方法: 1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:     select id from t where num is null     可以在num上设置默认值0,确保表中num列没有null值,然后这

总结:如何使用redis缓存加索引处理数据库百万级并发

前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1000万条数据,可以参考我之前的文章插入数据,这里不再细说.我大概的做法是这样的,编码使用多线程访问我的数据库,在访问数据库前先访问redis缓存没有的话在去查询数据库,需要注意的是redis最大连接数最好设置为300,不然会出现很多报错. 贴一下代码吧 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1

自己动手丰衣足食,夜谈MySQL数据库去除重复记录最快的方法

mysql数据库重复插入了一些数据,想删除保留一条,查找了很多方案,类似这种以及这种的 初试了一下,感觉可能达到效果,但是速度太慢了,几十上百条数据还可以,几十上百万条,mysql.exe 进程基本上就跑满cpu了,半天出不来结果.大家都是程序员,就该有程序员的样子,不能完全依赖于sql语句,可以写个小工具变通下.思路如下:单字段重复的时候,就写个工具查询 这个字段,附带记录这个字段的主键,然后请看代码,我这边是zblog的数据库,手动插入了很多博文数据,重复了一些.批量导入代码 var dat