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()
 begin
 declare i int;
 set i=1;
 while(i<=10000)do
 insert 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()
 begin
 declare i int;
 set i=1;
 while(i<=100)do
 insert 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、增加数据库计数表

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

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

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

相关文章

[MySQL]日期和时间函数

文章目录 1 获取日期、时间 CURDATE() &#xff0c;CURRENT_DATE()CURTIME() &#xff0c; CURRENT_TIME()NOW() / SYSDATE() / CURRENT_TIMESTAMP() / LOCALTIME() / LOCALTIMESTAMP()UTC_DATE()UTC_TIME()代码示例2 日期与时间戳的转换 UNIX_TIMESTAMP()UNIX_TIMESTAMP(date)…

ArcGIS制作某村土地利用现状图

1. 根据坐落单位名称属性选择并提取作图数据 (1) 将“作图线状地物”、“作图图班”和“村庄”图层加入ARCGIS&#xff08;右键Layers-Add data&#xff09;&#xff0c;选择相应路径下的文件加载即可。 (2) 按属性来提取作图村庄的地类图班、线状地物和村界文件&#xff08;…

Flutter PK jetpack compose区别和选择

Flutter诞生于Chrome团队&#xff0c;是一帮做Web的开发做的跨平台框架&#xff0c;从最开始的设计初衷&#xff0c;就是指向了跨平台这条路&#xff0c;而Compose&#xff0c;则是诞生于Android团队&#xff0c;是为了解决当前View的架构体系不能再继续适应申明式编程的范式而…

【BLE基础知识】--Slave latency设置流程及空中包解析

1、Slave latency基本概念 当BLE从设备对耗电量要求较高时&#xff0c;若需要节省耗电量&#xff0c;则可以通过设置Slave Latency参数来减少BLE从设备的耗电。 Slave Latency&#xff1a;允许Slave&#xff08;从设备&#xff09;在没有数据要发的情况下&#xff0c;跳过一定…

SCAU:分期还款(加强版)

分期还款(加强版) Time Limit:1000MS Memory Limit:65535K 题型: 编程题 语言: G;GCC;VC 描述 从银行贷款金额为d&#xff0c;准备每月还款额为p&#xff0c;月利率为r。请编写程序输入这三个数值&#xff0c;计算并输出多少个月能够还清贷款&#xff0c;输出时保留1位小…

【iOS控件】—— UIPickerView的使用

【iOS控件】—— UIPickerView的使用 一. 简述UIPickerView1. 什么是UIPickerView2. UIPickerView遵守的协议 二. 测试Demo三. 总结 一. 简述UIPickerView 先看一下UIPickerView的效果图&#xff1a; 1. 什么是UIPickerView UIPickerView是iOS平台上的一个用户界面元素&am…

百马百担c语言编程

以下是一个百马百担问题的C语言编程实现&#xff1a; #include <stdio.h>int main() { int n, m, k; scanf("%d%d%d", &n, &m, &k); int a[n], b[m], c[k]; for (int i 0; i < n; i) { scanf("%d", &a[i]);…

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

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

使用WPF设计时绑定加快开发速度

知识来源&#xff1a;B站up主 香辣恐龙蛋 第一步 第二步

小心处理 C++ 静态变量中的陷阱

小心处理 C 静态变量中的陷阱 函数中的 static 变量 static 变量的作用 C 中 static 关键字的最后一个用途是在函数内创建局部变量&#xff0c;这些变量在其作用域内退出和进入时保持其值。函数内的 static 变量类似于只能从该函数访问的全局变量。static 变量的一个常见用途…

【合集】从Java基础到JavaWeb网络开发——Java基础文章合集 JavaWeb网络开发文章合集

前言 本篇博客是Java开发的合集文章&#xff0c;内容涵盖了Java基础相关的博客&#xff0c;JavaWeb开发相关的博客&#xff0c;并且给出了小项目的案例。 目录 前言引出Java基础1、基本数据类型2、数组和集合List3、运算符4、逻辑控制5、IO流6、面向对象初步7、数据库入门8、J…

微服务实战系列之EhCache

前言 书接前文&#xff0c;继续深耕。上一篇博主对Redis进行了入门级介绍&#xff0c;大体知道了Redis可以干什么以及怎么使用它。 今日博主继续带着大家学习如何使用EhCache&#xff0c;这是一款基于Java的缓存框架。 微服务实战系列之Redis微服务实战系列之Cache微服务实战…

uni-app 微信小程序之自定义navigationBar顶部导航栏

文章目录 1. 实现效果2. App.vue3. pages.json 配置自定义4. 顶部导航栏 使用 微信小程序自定义 navigationBar 顶部导航栏&#xff0c;兼容适配所有机型 1. 实现效果 2. App.vue 在App.vue 中&#xff0c;设置获取的 StatusBar&#xff0c;CustomBar 高度&#xff08;实现适配…

FL Studio 21.2.1.3859中文破解激活版2024免费下载安装图文教程

FL Studio 21.2.1.3859中文破解激活版是我见过更新迭代最快的宿主软件&#xff0c;没有之一。FL Studio12、FL Studio20、FL Studio21等等。有时甚至我刚刚下载好了最新版本&#xff0c;熟悉了新版本一些好用的操作&#xff0c;Fl Studio就又推出了更新的版本&#xff0c;而且F…

【附代码】Python函数性能测试(perfplot)

文章目录 相关文献测试电脑配置展开元素是list的list在numpy数组上映射函数的最有效方法数组numpy中唯一值的最有效频率计数方法反转numpy数组的最有效方法如何向 numpy 数组添加额外的列将 numpy 矩阵初始化为零或一以外的值 作者&#xff1a;小猪快跑 基础数学&计算数学&…

P2 Linux系统目录结构

前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_ChenPi的博客-CSDN博客》✨✨✨ &#x1f6f8;推荐专…

【STL】手撕 string类

目录 1&#xff0c;string类框架 2&#xff0c;string&#xff08;构造&#xff09; 3&#xff0c;~string&#xff08;析构&#xff09; 4&#xff0c;swap&#xff08;交换&#xff09; 5&#xff0c;string&#xff08;拷贝构造&#xff09; 1&#xff0c;常规法 2&a…

【数据结构】AOV网与拓扑排序

一.AOV网的概念&#xff08;Activity On Vertex Network&#xff09; 在一个表示工程的有向图中&#xff0c;用顶点表示活动&#xff0c;用弧表示活动之间的优先关系。这样的有向图为顶点表示活动的网&#xff0c;我们称为AOV网&#xff08;Activity On Vertex Network&#xf…

【系统运维】Centos部署Haproxy+Keepalived+RabbitMQ高可用集群

1.RabbitMQ高可用集群方案 &#xff08;1&#xff09;RabbitMQ搭建集群的作用&#xff1a;提高可用性、可靠性和处理能力&#xff0c;确保系统提供高效的消息传递服务 高可用性&#xff1a;通过集群&#xff0c;即使其中一个节点发生故障&#xff0c;其他节点仍然可以继续提供…