MySQL分页查询的工作原理

  1. 前言
    MySQL 的分页查询在我们的开发过程中还是很常见的,比如一些后台管理系统,我们一般会有查询订单列表页、商品列表页等。

示例:

SELECT * FROM goods order by create_time limit 0,10;
在了解order by和limit的工作原理之前,我们首先回顾下 MySQL 的执行流程和索引结构。

注: 下面没有特别说明默认 MySQL 的引擎为 InnoDB 为讲述方便使用 select * ,生产环境不建议使用
1.1. 执行流程
在这里插入图片描述
MySQL 可以分为 Server 层和存储引擎层两部分,对于这个就不展开讲了。只需要知道一条 SQL 语句是从客户端发起请求到 Server 层,Server 层处理之后选出成本最低的执行计划去存储引擎层进行数据查询,查询出来的数据返回给 Server 层处理,最后返回给客户端。(存储引擎层根据扫描区间定位拿到数据给到 Server 层,剩下的过滤、排序、分页等操作是在 Server 层载进行处理的)。

1.2 索引结构

在这里插入图片描述
InnoDB 存储引擎的索引是一颗 B+ 树,只有主键索引树会存储全部的行记录数据,二级索引只会存储该记录对应的主键 id。所以我们使用二级索引查询数据时,如果查询的字段在二级索引没办法完全覆盖,则需要回表。

  1. order by 工作原理
    准备工作
    创建一张商品表,并且给价格字段设置索引
REATE TABLE goods (
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(255) NOT NULL comment '商品名称',
        price DECIMAL(10,2) NOT NULL comment '售价',
        create_time  DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
        update_time  DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
);
create index idx_price on goods (price);

插入测试数据

delimiter $$
DROP PROCEDURE IF EXISTS proc_batch_insert;
CREATE PROCEDURE proc_batch_insert()
BEGIN
    DECLARE pre_name BIGINT;
    DECLARE priceVal INT;
    DECLARE i INT;
    SET pre_name=1;
    SET priceVal=30;
    SET i=1;
    WHILE i < 1000000 DO
            INSERT INTO goods(`name`,price,create_time,update_time) VALUES(CONCAT('商品',pre_name),(priceVal+i)%100,NOW(),NOW());
            SET pre_name=pre_name+1;
            SET i=i+1;
        END WHILE;
END $$

delimiter ;
call proc_batch_insert();

2.1 索引扫描排序
EXPLAIN SELECT * FROM goods where price > 10 and price < 13 ORDER BY price;
图片

我们发现这条 sql 使用了索引 idx_price,索引结构如下:
在这里插入图片描述
首先会根据二级索引idx_price进行查询找到满足price > 10 and price < 13区间的主键值,因为我们的查询条件是SELECT *,所以需要回表查询到对应的行记录。由于ORDER BY price,我们是需要对查询出来的结果按照价格从小到大进行排序。我们刚刚的数据就是从二级索引idx_price查询出来的,本就是根据price字段排序的,所以无需再排序,直接把查询的数据返回给客户端就行了。
在这里插入图片描述
2.2 文件排序(filesort)
以下 3条 sql 语句都会使用文件排序

-- Using where; Using filesort
EXPLAIN SELECT * FROM goods where price > 10 and price < 30 ORDER BY price;
-- Using where; Using filesort
EXPLAIN SELECT * FROM goods where name like '商品 1%' ORDER BY price;
-- Using index condition; Using filesort
EXPLAIN SELECT * FROM goods where price > 10 and price < 13 ORDER BY name;

对于第二条 sql ,我们很容易判断出来,使用不了二级索引idx_price,只能全表扫描查询出符合条件的行记录再去进行文件排序。

那么第一条 sql 只是查询范围比之前的更大了,为什么就不走二级索引了呢?

我们前面介绍执行流程时说道:MySQL 会选择执行成本最低的执行计划。这条 sql 的查询范围是price > 10 and price < 30,满足这个条件的数据是很多的,每一条数据都需要进行回表查询。这样大量的回表查询,MySQL 认为是很慢的,所以没有使用二级索引。

第三条 sql 虽然可以使用到二级索引idx_price,但是需要排序的字段是name,那么二级索引的作用就只是帮助我们加快查询,而排序操作还是需要使用文件排序。

什么是文件排序呢?

文件排序分成两种:全字段排序、rowid 排序。接下来,我们分别讲解这两种排序工作原理。

2.2.1 全字段排序
MySQL 会给每个线程分配一块内存用于排序 sort_buffer。sort_buffer_size,就是 MySQL 为排序开辟内存(sort_buffer)的大小。

如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件来辅助排序。

EXPLAIN SELECT * FROM goods where price > 10 and price < 13 ORDER BY name;

在这里插入图片描述
我们根据这条 sql 来分析查询过程:

先根据二级索引idx_price查询出满足过滤条件的数据

根据主键 id 进行回表操作查询出对应的行记录

数据汇总到 sort_buffer 按照name进行排序

将排序好的结果集返回给客户端

2.2.2 rowid 排序
我们发现全字段排序会存在一个问题:如果表中的字段非常多,我们把整个行记录放入 sort_buffer 里面进行排序时,能够放入的行记录就会很少,排序性能差。

max_length_for_sort_data ,是 MySQL 中专门控制用于排序的行数据的长度的一个参数。它的意思是,如果单行的长度超过这个值,MySQL 就认为单行太大,要换一个算法。使用 rowid 排序:需要排序的字段 + 主键 id 放入 sort_buffer 进行排序。

在这里插入图片描述
还是使用上述 sql 分析:

可以使用二级索引,所以先根据二级索引idx_price查询出满足过滤条件的数据
根据主键 id 进行回表操作查询出对应的行记录
将排序字段name和主键 id 一起放入 sort_buffer 进行排序
根据主键 id 再去主键索引查询全部字段返回给客户端
如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到 sort_buffer 中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。

这也就体现了 MySQL 的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问。

对于 InnoDB 表来说,rowid 排序要求回表多造成磁盘读,因此不会被优先选择。

2.2.3 排序算法
我们前面说在 sort_buffer 进行排序,但是没有说明具体是什么排序算法,其实我们这个排序算法是需要分情况的,具体如下:

若排序内容能全部放入内存,则仅在内存中使用快速排序;
若排序内容不能全部放入内存,则分批次将排好序的内容放入文件,然后将多个文件进行归并排序
若排序中包含 limit 语句,则使用堆排序优化排序过程
3. limit 工作原理
Server 层维护了一个称作 limit_count 的变量用于统计已经跳过了多少条记录。limit m , n 工作原理就是先读取前面 m+n 条记录,然后抛弃前 m条,读后面 n条想要的,所以 m越大,偏移量越大,性能就越差。

EXPLAIN SELECT * FROM goods where price > 10 and price < 13 ORDER BY name limit 500,10;

如上述 sql 语句,MySQL 先查询 510 条数据,按照ORDER BY的工作原理进行条件查询和排序,最后汇总的结果在返回给客户端之前,MySQL 会截取第 501 到 510 条数据,最后把这 10 行记录返回给前端。

  1. 常见问题分析
    4.1 排序字段使用非唯一字段导致乱序问题
    我们平常使用的分页查询,如果没有用到索引排序底层的排序算法是堆排序,由于是堆排序是不稳定排序,会产生乱序问题。

分页查询商品表,根据创建时间进行排序
SELECT * FROM goods order by create_time limit 0,10;
但是如果此时数据库的商品数据都是通过 Excel 导入进去的,那么它们的创建时间都是一样的,那就会乱序。
在这里插入图片描述

在这里插入图片描述
5 个商品的顺序是不一定的(堆排序:不稳定排序),当我们从第一页到第二页时,商品 3又到了第一页。

那么我们就一直找不到商品 3。对于这个问题,我们可以改成按照主键 id 排序。

4.2 深度分页问题

SELECT * FROM goods ORDER BY price LIMIT 80000,10

在这里插入图片描述

这样的 sql 就是深度分页了,我们之前讲到,MySQL 的底层会查询出 80010 条数据进行文件排序(因为查询数量太多,回表次数过多,MySQL 便不使用二级索引),然后再截取第 80001 到 第80010 条数据返回给客户端。

要解决这种深度分页问题首先应该在产品的设计方面避免这种情况,还有就是我们在查询分页数据时应该需要根据时间做好限制,减少数据,以及对前端传进来的 start、limit 字段进行判断限制。如果还是需要深度分页,就需要利用子查询来实现。

SELECT * FROM goods g
INNER JOIN
(SELECT id FROM goods ORDER BY price LIMIT 80000,10) AS d
ON g.id=d.id;

在这里插入图片描述
子查询使用二级索引查出满足条件的主键,然后进行分页过滤出我们需要主键 id,再去主键索引查询数据(因为排序字段就是我们的二级索引字段,所以查询出来的数据直接就是有序的,无需再进行文件排序)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/152775.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[DB] (数据库工具) navicat 平替 jookdb

jookdb 官方下载 csdn下载(免积分) 解压后直接可以使用 测试数据库适配性. mysql.mariadb.oracle.sqlserver免费使用 除外的提供20天免费试用驱动添加是java的jdbc驱动,可以通过 https://developer.aliyun.com/mvn/view 进行下载数据库的基本操作.包含数据库的新增, 库表新增…

SQLSERVER 遍历循环的两种方式很详细有源码(1)

1.普通循环 Create table WS_Student ( [Id] int primary key not null, [My_Cocode] [int], [My_SCocode] [int], [userId] [bigint], [SetCName] [varchar](50) NULL, [SetEName] [varchar](50) NULL, [SetPcode] [varchar](50) NULL, [Se…

单相浪涌保护器和三相浪涌保护器的区别

浪涌保护器&#xff0c;也称为防雷器&#xff0c;是一种为各种电子设备、仪器仪表、通讯线路提供安全防护的电子装置&#xff0c;主要用于限制过电压和泄放电涌电流。浪涌保护器的核心元件是内部的一个非线性元件。根据非线性元件的不同&#xff0c;浪涌保护器可以分为开关型&a…

​如何使用ArcGIS Pro制作渐变河流效果

对于面要素的河流水系&#xff0c;制作渐变效果方法比较简单&#xff0c;如果是线要素的河流有办法制作渐变效果吗&#xff0c;答案是肯定的&#xff0c;这里为大家介绍一下制作方法&#xff0c;希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图中下载的水系数…

kubernetes部署jenkins

参考&#xff1a; ​​​​​​第七篇&#xff1a;kubernetes部署jenkins-CSDN博客 1、当前kubernetes集群已部署nfs服务 showmount -e 创建jenkins目录 2、添加jenkins的pvc cd /opt/dockerfile/jenkins/ touch jenkins-pv.yaml apiVersion: v1 kind: PersistentVolume m…

软件测试/人工智能丨深入人工智能软件测试:PyTorch引领新时代

在人工智能的浪潮中&#xff0c;软件测试的角色变得愈发关键。本文将介绍在人工智能软件测试中的一些关键技术&#xff0c;以及如何借助PyTorch深度学习框架来推动测试的创新与升级。 PyTorch&#xff1a;深度学习的引擎 PyTorch作为一种开源的深度学习框架&#xff0c;为软件…

【2021集创赛】Arm杯二等奖-基于Arm核的智慧病房手势识别方案

团队介绍 参赛单位&#xff1a;上海交通大学 队伍名称&#xff1a;芯灵手巧 指导老师&#xff1a;王琴、景乃锋 参赛队员&#xff1a;林圣凯、林新源、莫志文 总决赛奖项&#xff1a;二等奖 1.项目概述 1.1 选题背景 我们的选题背景是考虑到很多卧床病人不便于独自向医护人…

.NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试

2023年11月15日&#xff0c;对.net的开发圈是一个重大的日子&#xff0c;.net 8.0正式版发布。 圈内已经预热了有半个月有余&#xff0c;性能不断超越&#xff0c;开发体验越来越完美&#xff0c;早在.net 5.0的时候就各种吹风Aot编译&#xff0c;直到6.0 7.0使用仍然比较麻烦…

比一比国内的现货白银交易所

上海黄金交易所(SGE)是国内最大的白银交易平台之一。它作为中国金融期货市场的重要组成部分&#xff0c;能够提供完善的交易机制和规范的交易环境。SGE的交易品种丰富&#xff0c;包括现货白银&#xff0c;白银延期等的多种交易方式。SGE也具有较高的流动性和交易深度&#xff…

WireGuard 组网教程:快速构建安全高效的私密网络并实现内网穿透

文章目录 1 引言1.1 什么是WireGuard1.2 WireGuard可以用来做什么1.3 WireGuard原理1.4 WireGuard安装 2 WireGuard组网实现内网穿透2.1 前提条件2.2 网络拓扑结构2.3 具体步骤2.3.1 中继服务器配置2.3.2 其他peer2.3.3 测试 2.4 WireGuard配置文件说明 3 WireGuard工具3.1 wg-…

Numpy数组进阶_Python数据分析与可视化

Numpy数组进阶 Numpy的广播机制高级索引整数数组索引布尔索引花式索引 数组迭代 Numpy的广播机制 广播 (Broadcast) 是 numpy 对不同形状 (shape) 的数组&#xff0c;进行数值计算的方式。 对数组的算术运算通常在相应的元素上进行&#xff0c;当运算中的 2 个数组的形状不同时…

轻松实现文件改名:让新文件名与目录名称一致

在日常工作中&#xff0c;我们经常需要处理文件改名的问题。有时候&#xff0c;我们需要将新文件名设置为与目录名称相同&#xff0c;以方便管理和查找。然而&#xff0c;这个过程可能很繁琐&#xff0c;尤其是当你有大量的文件需要改名时。幸运的是&#xff0c;现在有一种简单…

Windows2019部署IIS后,浏览文件变成下载的问题解决过程

1、反复重起服务器、重建应用程序、建应用程序并改名才好使&#xff01; 2、总体上&#xff0c;角色为&#xff1a; 一、在Windows2019服务器上&#xff0c;填加角色&#xff0c;一路下一步&#xff0c;到填加服务器角色时&#xff0c;勾选了【Web服务器(IIS)】 二、然后在【…

阿里云2核2G3M服务器e实例、40G ESSD Entry系统盘性能测评

阿里云99元服务器新老用户同享2核2G经济型e实例、3M固定带宽和40G ESSD Entry系统盘&#xff0c;老用户也可以买&#xff0c;续费不涨价依旧是99元一年&#xff0c;阿里云百科aliyunbaike.com分享阿里云3M带宽服务器40G ESSD Entry云盘性能说明&#xff1a; 阿里云99元服务器配…

el-table操作栏添加el-dropdown获取当前行的数据

0 效果 点击子合同获取到当前行的id 1 代码 beforeHandleCommand(row, childCommand) {return { row: row, childCommand: childCommand }; }, addChildBtn(command) {const row command.row;if (command.childCommand 0) {// todo} else {// todo} },

el-select下拉框只回显value不回显label的原因以及解决方法

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 原因分析&#xff1a; 提示&#xff1a;这里填写问题的分析&#xff1a; el-select的采用的是map的key value结构&#xff0c;因此只显示value而不显示label的原因是&#xff0c;value的类型不正确&…

【Effect C++ 笔记】(四)设计与声明

【四】设计与声明 条款18 &#xff1a; 让接口容易被正确使用&#xff0c;不易被误用 Item 18: 让接口容易被正确使用&#xff0c;不易被误用 Make interfaces easy to use correctly and hard to use incorrectly. “让接口容易被正确使用&#xff0c;不易被误用”&#xff0…

Abaqus2023新功能:分析技术

隐式耦合的松弛和加速器方法 产品&#xff1a;Abaqus/Standard SIMULIA协同仿真引擎现在支持Aitkens松弛方法以及Anderson和Broyden加速器方法&#xff0c;为强耦合物理场提供稳健且省时高效的解决方案。此功能在 2022 FD04 &#xff08;FP.2232&#xff09;版本中首次提供。…

vue的常用指令

1.使用双花括号( {{}} )对变量输出,内部可以写简单的表达式用于对数据的处理 2..v-text&#xff1a;相当于js的innerText, 3.v-html&#xff1a;相当于js的innerHTML 4.v-bind&#xff1a;动态绑定属性,简写是冒号( : ) 5.绑定class&#xff1a;操作元素的 class 列表和内联样式…

Moka人事:实现无代码开发的API连接,打通电商平台与用户运营系统

无代码开发的API连接&#xff1a;Moka人事的核心优势 Moka人事&#xff0c;是北京希瑞亚斯科技有限公司于2015年推出的一款数据驱动的智能化HR SaaS产品。这款产品的主要优势在于其无需进行API开发即可实现系统的连接和集成&#xff0c;这不仅大大提升了企业的工作效率&#x…
最新文章