MySQL索引优化实战二

分页查询优化

很多时候我们业务中实现分页功能时可能会用如下SQL来实现:

select * from employees LIMIT 10000,10

表示从表中中区从10001行开始的10行记录,看似只查了10条记录,但是这条SQL是先读取10010条记录,然后抛弃前10000条记录,然后读到后面10条想要的数据,因此要查询一张大表比较靠后的数据,执行效率是很低的。

1、根据自增且连续的主键排序的分页查询
select * from employees LIMIT 90000,5

在这里插入图片描述
在这里插入图片描述

select * from employees where id>90000 limit 5

在这里插入图片描述
在这里插入图片描述
改写后的SQL走了索引,且看执行时间,改写后的时间更快。
但是这种方式在很多场景中不适用,因为表中某些数据被删后造成主键空缺,导致结果不一致。
上面改写必须满足两个条件:

  • 主键自增且连续
  • 结果是按照主键排序的
2、根据非主键字段排序的分页查询
select * from employees order by name  LIMIT 90000,5

在这里插入图片描述

explain select * from employees order by name  LIMIT 90000,5

在这里插入图片描述

没有使用索引,是因为:扫描整个扫一年病查找到没索引的行比扫描全表的成本更高。

如何优化呢?
关键是让排序返回的字段尽可能的少,所以可以让排序和分页操作先查出主键,然后根据主键查找到对应的记录,如下SQL:

select * from employees e INNER JOIN (SELECT id FROM employees ORDER BY name limit 9000,5) ed on e.id = ed.id

在这里插入图片描述
执行计划:

explain select * from employees e INNER JOIN (SELECT id FROM employees ORDER BY name limit 9000,5) ed on e.id = ed.id

原SQL使用的是filesort排序,优化后的SQL使用的是索引排序
在这里插入图片描述

Join关联查询优化

‐‐ 示例表:CREATE TABLE `t1` (`id` int(11) NOT NULL AUTO_INCREMENT,`a` int(11) DEFAULT NULL,`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),KEY `idx_a` (`a`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;create table t2 like t1;‐‐ 插入一些示例数据‐‐ 往t1表插入1万行记录drop procedure if exists insert_t1;delimiter ;;create procedure insert_t1()begindeclare i int;set i=1;while(i<=10000)doinsert into t1(a,b) values(i,i);set i=i+1;end while;end;;delimiter ;call insert_t1();‐‐ 往t2表插入100行记录drop procedure if exists insert_t2;delimiter ;;create procedure insert_t2()begindeclare i int;set i=1;while(i<=100)doinsert into t2(a,b) values(i,i);
set i=i+1;end while;end;;delimiter ;call insert_t2();

MySQL表关联常见有两种算法

  • Nested-Loop Join 算法
  • Block Nested-Loop Join算法

嵌套循环连接算法

一次一行地循环地从第一张表(成为驱动表)中读取行,在这行数据中取到关联字段,根据关联字段在另一张表(被驱动表)中取出满足的行,然后取出两张表的结果合集。

 explain select * from t1 inner join t2 on t1.a=t2.a

在这里插入图片描述

  • 驱动表是t2,被驱动表是t1。先执行的是驱动表。优化器一般会优先选择小表做驱动表,所以使用inner Join 时,排在前面的不一定是驱动表。
  • 当使用left Join时,左表是驱动表。当使用right Join时,右表是驱动表;当使用join时,MySQL会选择数量较小的表作为驱动表。
  • 使用了NLJ算法。一般join语句中,如果执行计划extra中未出现 Using join buffer则表示使用join算法是NLJ。

上面SQL执行流程如下:
1、从表t2中读取一行数据(如果t2表有过滤条件的,会从过滤条件中取出一行数据)
2、从第一步骤的数据中,取出关联字段a到表t1中查找;
3、取出表t1满足条件的行,跟t2中获取的结果合并,作为结果返回给客户端;
4、重复上面3步。

整个过程会读取t2表的所有数据(扫描100行),遍历这每行数据中字段a的值,根据t2表中a的值索引扫描t1表中对应行。(扫描100次t1表的索引,1次扫描可以认为最终只扫描t1表一行完整数据,也就是总共t1表也扫描了100行)。

如果被驱动表的关联字段没索引使用NLJ算法性能会比较低,MySQL会选择Block Nested-Loop Join算法

基于块的嵌套循环连接算法

把驱动表的数据读到join buffer中,然后扫描被驱动表,把被驱动表每一行取出来跟join buffer 中的数据做对比。

explain select * from t1 inner join t2 on t1.b=t2.b

在这里插入图片描述
Extra中的Using join buffer (Block Nested Loop)说明该关联查询用的是BNL算法。

上面SQL的大致流程如下:
1、把t2的所有数据放入到join buffer中
2、把t1中每一行取出来跟join buffer中的数据做对比
3、返回满足join条件的数据

整个过程对两个表都做了一次全表扫描,因此扫描的总行数为10000(t1的数据总量)+100(表t2的数据总量)=10100.并且join buffer中的数据是无序的,因此对表t1中的每一行都要做100次判断,所以内存中的判断次数是100*10000=100万次
如果t2表很大,join buffer放不下怎么办呢?
join buffer的大小是由参数join_buffer_size设定的,默认值为256k,如果放不下表t2的所有数据的话,就分段放
比如t2表中有10000行记录,join_buffer一次只能放800行数据,那么执行过程就是先往join_buffer中放800条记录,然后从t1表中取数据跟join_buffer中数据对比得到部分结果,然后清空join_buffer,再放入t2表剩余200条记录,再次从t1表中取数据跟join buffer中数据对比,所以就多扫了一次表。

被驱动表的关联字段没有索引为什么要算则使用BNL算法而不使用NLJ算法呢?

如果上面第二条SQL使用NLJ算法,那么扫描行数为100*10000=100万次,这个是磁盘扫描。显然,BNL磁盘扫描次数少很多,相比与磁盘扫描,BNL内存扫描会快很多。
因此MySQL对于被驱动表的关联字段没有索引的关联查询,一般都会使用BNL算法,如果有索引一般选择NLJ算法,有索引的情况下,NLJ算法比BNL算法性能更高。

对关联SQL的优化
  • 关联字段加索引: 让MySQL做join操作时尽量选择NLJ算法
  • ==小表驱动大表:==如果明确知道那张是小表可以用straight_join写法固定连接驱动方式,省去MySQL优化器自己判断的时间。
小表的定义

按照各自的条件过滤,过滤后哪个数据量小 哪个是小表

in和exsits 优化

原则:=小表驱动大表,即小的数据集驱动大的数据集

in:

当B表的数据集小于A的数据集时,in优于exists

select * from A where id in (select id from B )
等价于:
forselect id from B){select * from A where A.id = B.id
}
exists:

当A表的数据集小于B的数据集时 exists 优于in
当主查询A的数据,放到子查询B中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留

select * from A where exists(select 1 from B where B.id = A.id)

先查询A中的数据,然后判断是否符合与B的条件

count(*)查询优化
explain select count(1) from employees;
explain select count(id) from employees;
explain select count(name) from employees;
explain select count(*) from employees;

以上四条SQL只有根据某个字段count不会统计字段为null值的数据行
字段有索引时查询效率:
count(星)≈ count(1)>count(字段)>count(主键id)
字段有索引,count(字段)统计走二级索引,二级索引存储数据比主键索引少,所以count(字段)>count(id)
字段无索引时查询效率:
count(*)≈ count(1)>count(主键id)>count(字段)
字段无索引,count(字段)统计走不了索引,count(id)还可以走主键索引,所以count(主键)>count(id)

  • count(1)跟count(字段)执行过程类似,不过count(1)不需要取出字段统计,就用常量1做统计,

  • count(字段)还需要取出字段,索引理论上count(1)比count(字段会快一点)

  • count(*)是例外,MySQL并不会把全部字段取出来,而是专门做了优化,不取值,按行累加,效率很高,所以不需要用count(列名)或count(常量)来代替count(星)

  • 为什么对于count(id),MySQL最终选择辅助索引而不是主键聚集索引?
    因为二级索引相对于主键存储数据更少,检索性能应该更高,mysql内部做了点优化(应该是5.7)

常见优化方法

1、查询MySQL自己维护的总行数

myisam存储引擎做不带where条件的count查询性能是很高的,因为myisam存储引擎的表的总行数总会被MySQL存储在磁盘上,查询不需要计算。
对于innodb存储引擎的表MySQL不会存储表的总记录,因为有mvcc机制,查询count需要实时计算。

2、show table status

如果只需要知道表总行数的估计值可以用下面SQL,性能很高。

show table status like 'employes'

在这里插入图片描述

3、将总数维护到redis

但是很难保证表操作和redis的事务一致性

4、增加数据库计数表

插入或删除表数据行的时候同时维护计数表,让他们在同一个事务里操作

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring事务管理介绍

文章目录 Spring事务管理1 Spring事务简介【重点】问题导入1.1 Spring事务作用1.2 需求和分析1.3 代码实现【前置工作】环境准备【第一步】在业务层接口上添加Spring事务管理【第二步】设置事务管理器(将事务管理器添加到IOC容器中)【第三步】开启注解式事务驱动【第四步】运行…

智能优化算法应用:基于黄金正弦算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于黄金正弦算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于黄金正弦算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.黄金正弦算法4.实验参数设定5.算法结果6.参考…

Nginx配置反向代理与负载均衡

Nginx配置反向代理与负载均衡 一、代理服务1.正向代理2.反向代理 二、实战场景-反向代理1.修改nginx配置 -> nginx.conf文件2.修改前端路径 三、实战场景-负载均衡1.热备2.轮询3.加权轮询4.ip_hash ​ Nginx (“engine x”) 是一个高性能的HTTP和反向代理服务器&#xff0c;…

10 分钟解释 StyleGAN

一、说明 G在过去的几年里&#xff0c;生成对抗网络一直是生成内容的首选机器学习技术。看似神奇地将随机输入转换为高度详细的输出&#xff0c;它们已在生成图像、生成音乐甚至生成药物方面找到了应用。 StyleGAN是一种真正推动 GAN 最先进技术向前发展的 GAN 类型。当Karras …

力扣题:字符串的反转-11.23

力扣题-11.23 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;557. 反转字符串中的单词 III 解题思想&#xff1a;先读取单词&#xff0c;然后将单词进行翻转即可 class Solution(object):def reverseWords(self, s):""":type s…

2024年AMC8美国初中数学竞赛最后一个月复习指南(附资料)

还有一个半月的时间&#xff0c;2024年AMC8&#xff08;大家默认都直接叫这个比赛的英文名&#xff0c;而不叫中文名美国数学竞赛或美国初中数学竞赛了&#xff09;就要开始了。 有志于在2024年AMC8的比赛中拿到奖项的孩子已经在“磨拳霍霍”了。那么最后一个半月的时间该如何…

LeetCode刷题---反转链表

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏&#xff1a;http://t.csdnimg.cn/ZxuNL http://t.csdnimg.cn/c9twt 前言&#xff1a;这个专栏主要讲述递归递归、搜索与回溯算法&#xff0c;所以下面题目主要也是这些算法做的 我讲述…

Linux 进程状态

操作系统学科的进程状态 新建态&#xff1a;刚刚创建的进程&#xff0c; 操作系统还未把它加入可执行进程组&#xff0c; 它通常是进程控制块已经创建但还未加载到内存中的新进程。就绪态&#xff1a;进程做好了准备&#xff0c;只要有机会就开始执行。阻塞态&#xff1a;进程在…

Qt+ROS+ubuntu18.04配置教程(带界面)

1. 安装ROS Qt Creator Plug-in 首先安装ROS Qt Creator Plug-in&#xff0c;这其实是一个带有ROS插件的Qt Creator&#xff1a;去下面的网址https://ros-qtc-plugin.readthedocs.io/en/latest/_source/How-to-Install-Users.html#qt-installer-procedure&#xff0c;根据自己…

Java数据结构 之 包装类简单认识泛类

生命不息&#xff0c;奋斗不止 目录 1. 什么是包装类&#xff1f; 1.1 装箱和拆箱 1.2 自动装箱和自动拆箱 2. 什么是泛型 3. 引出泛型 3.1 语法 4 泛型类的使用 4.1 语法 4.2 示例 4.3 类型推导(Type Inference) 5. 裸类型(Raw Type) &#xff08;了解&#xff09…

IPv6是趋势!如何在Windows上禁用或启用IPv6?有3种简单的方法

IPv6是IPv4的一个更加安全、可扩展和可靠的继任者。然而&#xff0c;这种较新的互联网协议与IPv4不向后兼容&#xff0c;并且大多数VPN服务提供商不支持IPv6协议。 Microsoft不建议用户禁用IPv6或其组件&#xff0c;除非他们需要解决网络问题。但是&#xff0c;如果你计划禁用…

MATLAB实战 | 求水仙花数

循环结构的基本思想是重复&#xff0c;即利用计算机运算速度快以及能进行逻辑控制的特点&#xff0c;重复执行某些语句&#xff0c;以满足大量的计算要求。虽然每次循环执行的语句相同&#xff0c;但语句中一些变量的值是变化的&#xff0c;而且当循环到一定次数或满足条件后能…

阿里云服务器活动:免费试用ECS,轻松搭建WordPress博客平台,送午睡毯及猫超卡

阿里云服务器免费试用3个月 &#xff0c;搭建WordPress博客平台&#xff0c;还送午睡毯及猫超卡。活动时间截止至12月8日 网址&#xff1a; 阿里云服务器薅羊毛 送午睡毯

mac M系列芯片安装chatGLM3-6b模型

1 环境安装 1.1 mac安装conda. 下载miniconda&#xff0c;并安装 curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh sh Miniconda3-latest-MacOSX-arm64.sh1.2 创建虚拟环境并激活 创建名为chatglm3的虚拟环境&#xff0c;python版本为3.10…

Stream API 方法使用总结

文章目录 1.1、Stream介绍1.2、Stream创建对象&#xff08;1&#xff09;empty()方法&#xff08;2&#xff09;of()方法&#xff08;3&#xff09;Arrays.stream()方法&#xff08;4&#xff09;list.stream()方法 1.3、Stream中间方法&#xff08;1&#xff09;filter()方法&…

算法设计与实现--贪心篇

贪心算法 贪心算法是一种在每一步选择中都采取当前状态下最优决策的算法&#xff0c;以期望能够通过一系列局部最优的选择达到全局最优。贪心算法的关键是定义好局部最优的选择&#xff0c;并且不回退&#xff0c;即一旦做出了选择&#xff0c;就不能撤销。 一般来说&#xf…

pybind11教程

pybind11教程 文章目录 pybind11教程1. pybind11简介2. cmake使用pybind11教程3. pybind11的历史 1. pybind11简介 项目的GitHub地址为&#xff1a; pybind11 pybind11 是一个轻量级的头文件库&#xff0c;用于在 Python 和 C 之间进行互操作。它允许 C 代码被 Python 调用&am…

C语言-内存分配

内存分配 1. 引入 int nums[10] {0}; //对int len 10; int nums[len] {0}; //错是因为系统的内存分配原则导致的2. 概述 在程序运行时&#xff0c;系统为了 更好的管理进程中的内存&#xff0c;所以有了 内存分配机制。 分配原则&#xff1a; 2.1 静态分配 静态分配原…

CSS——复合选择器、CSS特性、背景属性、显示模式

1、复合选择器 复合选择器&#xff1a;由两个或多个基础选择器&#xff0c;通过不同的方式组合而成。 作用&#xff1a;更准确、更高效的选择目标元素&#xff08;标签&#xff09; 1.1 后代选择器 后代选择器&#xff1a;选中某元素的后代元素 选择器写法&#xff1a;父选…

Reactor实战,创建一个简单的单线程Reactor(理解了就相当于理解了多线程的Reactor)

单线程Reactor package org.example.utils.echo.single;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; import java.util.Iterator; import java.util.Set;public class EchoServerReactor implements Runnable{Selector sele…