MySQL 进阶(三)【SQL 优化】

news2024/9/23 21:22:01

1、SQL 优化

1.1、插入数据优化

1.1.1、Insert 优化

1、批量插入

插入多条数据时,不建议使用单条的插入语句,而是下面的批量插入:

INSERT INTO tb_name VALUES (),(),(),...;

批量插入建议一次批量 500~100 条,如果数据量比较大,建议通过多条批量插入语句来插入;

2、手动提交事务

        MySQL 默认会开启事务,但是默认每执行一次插入语句就开启和关闭一次事务,所以会有大量的事务启动关闭开销;建议使用手动提交事务

START TRANSACTIONS;
INSERT INTO tb_name VALUES (),(),(),...;
INSERT INTO tb_name VALUES (),(),(),...;
INSERT INTO tb_name VALUES (),(),(),...;
...
COMMIT;
3、主键顺序插入

在插入数据时,建议尽量顺序插入主键,而不是乱序插入:

主键顺序插入(高效):1 2 3 4 5 ...

主键乱序插入(低效):5 3 4 2 1 ...

1.1.2、load 大批量插入数据

使用 load 指令将本地磁盘文件中的数据插入到数据库表当中:

# 客户端连接MySQL服务器时,加上参数 --local-infile
mysql --local-infile -uroot -p
# 设置全局参数 local_infile = 1
set global local_infile = 1;
# 执行 load 指令将准备好的数据加载到表中
load data local infile '/path_to_data' into table table_name fields terminated by ',' lines terminated by '\n';

这里的 load 指令有点像 HQL 中的 load:

LOAD DATA [LOCAL] INPATH '/opt/module/data/xxx.txt' TO TABLE table_name;

HQL 的 load 命令并不需要指定分隔符,因为在建表的时候我们已经在 row format 中设置的文件的分隔符了;此外,这里想到今天使用 HQL load 命令的一些需要注意的问题:

  • 在向分桶表 load 数据的时候,不能从 local 直接 load,而是得先上传到 hdfs 上,再从 hdfs load 到分桶表才行

1.2、主键优化

1.2.1、数据组织方式

在 InnoDB 存储引擎当中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(IOT)

之前我们在学习索引的时候知道,InnoDB 存储引擎中的索引可以分为聚集索引和二级索引,而聚集索引正是由主键构成的一颗B+Tree,它的叶子节点存储的是行数据;

接下来,我们看一下当我们往数据库表中插入数据的时候,它的流程是什么样的?

1.2.2、页分裂

page 可以填满,也可以为空,但在 InnoDB 存储引擎规定,在一个 page 中至少包含 2 行数据;

主键顺序插入

当主键顺序插入时,一切都非常平静:

主键乱序插入

下面,我们看一下主键乱序插入时的情况:

上面,我们的主键都是乱序插入的,可以看到,现在来了一个主键为 50 的 row,按道理它是应该放到主键为 47 的前面,主键为 23 的后面(也就是 page 1)的,但是显然 page 1 现在已经存满了,那么接下来就会发生页分裂:

首先,page 1 (因为主键为 23 的数据当前理应插入到 page 1)会把自己 50% 之后的数据移动到一个新的 page 当中,然后将要主键为 23 的新数据页添加进去,最后将原本页之间的连接断开,重新建立页的连接;

所以,不难想到如果是大量数据的场景下,主键乱序插入时会出现频繁的页分裂现象,性能很低;与新增数据相反的是删除数据,删除数据又会引起页合并:

1.2.3、页合并

在 MySQL 中,当删除一行记录时,实际上数据并不会被物理删除,只是记录被标记为删除并且它的空间变得允许被其它记录回收使用;

当页中被删除的记录达到了 50%,InnoDB 会开始寻找最靠近的页(前或后),看看能否将两个页合并以优化空间使用;

 1.2.4、主键设置原则

  • 满足业务需求的情况下,尽量降低主键的长度。(因为在一张表中,聚集索引只有一个,而二级索引可以有多个,如果主键很长,二级索引也有很多,那么就会在存储时会消耗大量的磁盘空间,查询时也会消耗大量的磁盘IO)
  • 插入数据时,尽量选择顺序插入,选择使用 AUTO_INCREMENT 自增主键;(不会出现页分裂现象)
  • 尽量不要使用 uuid 做主键或者是其它自然主键,如身份证号;(主键长度太长,而且无序)
  • 尽量避免主键的修改;

1.3、order by 优化

  • Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区 sort buffer 中完成排序操作;所有不是通过索引直接返回排序结果的排序都是 FileSort 排序;
  • Using index:通过有序索引顺序扫描直接返回有序数据,这种情况就是 using index,不需要额外排序,操作效率高;

也就是说,我们在优化 order by 的时候,尽量优化成 using index;下面我们看一条普通的 order by 语句:

可以看到,在未建立索引时,order by 语句默认走的是 filesort;下面我们为 age 字段建立一个索引:

CREATE INDEX idx_student_no ON student(no);

注意:这里搜索时,我设置投影的字段是 id 和 no,其中 id 是主键,no 我们刚创建了索引,不能使用 select * ,因为只有使用覆盖索引(查询使用了索引,并且需要返回的列在该索引中全部能够找到对应的值)才能命中索引,不然索引不生效;

可以看到,即使是降序排序也是索引也是可以命中优化的;

 现在我们创建一个联合索引:idx_student_no_name,做一个测试:

可以看到,当 order by 的字段顺序和联合索引相反时,只有字段 no 能被索引命中,name 不可以,这是因为违背了最左前缀法则;继续:

可以看到,当 order by 的两个字段分别升序和降序排列时,降序的字段无法被索引命中,这是因为:

这是因为默认创建索引时,索引字段都是按照升序进行排列的,所以我们可以根据之后的需求在创建索引时,根据排序规则进行创建:

CREATE INDEX idx_student_no_name_ad ON student(no asc,name desc);

此时,再次查看查询计划:

可以看到,这样就解决了字段排序规则不同的问题;

 注意

  • 只有联合索引需要注意创建时的排序规则,单列索引不需要,因为单列索引默认升序,反向扫描只需要反向扫描即可。
  • 如果不可避免 filesort ,大数据排序时,可以适当增加排序缓冲区的大小 sort_buffer_size (默认 256 K)

1.4、group by 优化

这里我们同样讨论的是索引对于 group by 的影响,因为分组也是通过索引来提高查询效率的:

可以看到,在未建立索引前,使用 group by 语句时效率很低(临时表),但是创建索引后就可以走索引了;

可以看到,这次当我们用 age 字段 group by 时,又出现了 temporary ,效率依然不够好,这是因为违背了联合索引的最左前缀法则;可当我们同时使用 profession 和 age 进行 group by 时,索引再次命中;

但是有时候业务逻辑不需要我们去 group by 那么多的字段怎么办?

其实,就像上面这样,我们也可以通过前面给联合索引左边的字段加个 where 条件,让它满足最左前缀法则即可;

1.5、limit 优化

在大数据场景下,比如 limit 10000000,10 ,此时 MySQL 需要排序前 100000010 条记录(存储引擎会把这 100000010 条记录返回给服务层的缓存),并返回后 10 条记录,其它丢弃,查询排序的代价非常大;这一类问题也叫做深度分页

MySQL 深度分页是指在分页查询数据量比较大的表时,需要访问表中的某一段数据,而这段数据的位置非常靠后,需要通过较大的 offset 来获取目标数据。

阿里巴巴《Java 开发手册》:

1.5.1、覆盖索引 + 子查询

1.5.2、inner join 延迟关联

上面这两种方式没什么区别,下面是 inner join,上面是笛卡尔积,但是因为设置的过滤条件,所以等价于一个 inner join;对于 limit 的优化原理,简单来讲就是:控制返回的总页数。

        对于上面的 limit 10000000,10 来说,它会返回给服务端 10000010 条记录,然后再根据 offset 挨个抛弃前 10000000 条记录,返回给客户端剩余的 10 条记录。

        可以看出,当offset非0时,server层会从引擎层获取到很多无用的数据,而当select后面是*号时,就需要拷贝完整的行信息,拷贝完整数据只拷贝行数据里的其中一两个列字段耗时是不同的,这就让原本就耗时的操作变得更加离谱。

        因为前面的offset条数据最后都是不要的,就算将完整字段都拷贝来了又有什么用呢,所以我们可以将sql语句修改成下面这样:

select * from tb_user where id >=(select id from tb_user order by id limit 10000000, 1) order by id limit 10;

上面这条sql语句,里面先执行子查询 select id from page order by id limit 6000000, 1, 这个操作,其实也是将在innodb中的主键索引中获取到6000000+1条数据然后server层会抛弃前6000000条,只保留最后一条数据的id。

但不同的地方在于,在返回server层的过程中,只会拷贝数据行内的id这一列,而不会拷贝数据行的所有列,当数据量较大时,这部分的耗时还是比较明显的。

在拿到了上面的id之后,假设这个id正好等于10000000,那sql就变成了

select * from tb_user where id >=(10000000) order by id limit 10;

这样 innodb 再走一次主键索引,通过B+树快速定位到id=6000000的行数据,时间复杂度是lg(n),然后向后取10条数据。

关于深度分页,知乎这篇文章讲的很不错,我也是受启发于这篇文章;

1.6、count 优化

  • count 是一个聚合函数,对于返回的结果集,一行一行进行判断,只有不是 NULL 才会计数(count(字段)的时候,count(*)或者count(id)依然计算null)
SELECT COUNT(*) FROM table_name;

上面的 SQL  是查询当前表的总行数,不同的存储引擎的效率是不一样的:

  • 对于 MyISAM 而言,它会把表的总行数存到磁盘上,所以不加条件直接查询 count(*) 会直接返回结果,O(1)的时间复杂度;
  • 而对 InnoDB 而言,它就只能遍历整张表了,性能很低;

可以看到,如果 count(*) 的查询语句中包含 where 过滤条件,不管是 MYISAM 还是 InnoDB ,性能都很差,所以我们需要对它进行优化:

1.6.1、count 的几种用法

对于 count,我们使用的无非就是那几种:

  • count(主键)
    •  InnoDB 会遍历整张表,把每一行主键取出来返回给服务层,服务层拿到主键后直接进行累加(主键不可能为空);
  • count(字段)
    •  InnoDB 会遍历整张表,把每一行字段值取出来返回给服务层,如果字段有 not null 约束,那么就直接按行累加;如果没有,那么就对非 null 的值进行计数
  • count(1)
    •  InnoDB 遍历整张表,但是不取值。服务层会对返回的每一行放一个数字 1 进去,直接按行进行累加
  • count(*)
    •  InnoDB 同样遍历整张表,但是不会把字段取出来,而是专门做了优化。服务层直接按行进行累加

按照排序效率:count(字段) < count(主键) < count(1) < count(*) ,所以尽量使用 count(*),因为数据库专门对它做了优化;

1.7、update 优化

关于 update 语句需要注意的就是,update 的条件一定要是索引字段(比如主键),因为只有更新条件的字段是索引列才会是行锁,否则将是表锁

可以看到,当我们的更新条件是 no 字段时(不是索引列),当另一个客户端去更新数据时直接被阻塞,最后甚至超时更新失败;

所以,当我们在使用 update 语句的时候,一定要注意尽量使用索引字段做为更新条件去更新,否则就会出现行锁升级为表锁,并发性能就会降低;因为 InnoDB 的行锁是针对索引加的锁,而不是针对记录加的锁! 

总结

  • 插入数据
    • inset 语句,大数据量(分成500~1000的记录批量插入)建议使用批量插入,而且建议手动事务(避免频繁创建销毁事务开销)、主键顺序插入(避免页分裂,顺序插入性能高)
    • 大批量数据:load data local infile
  • 主键优化
    •  主键设计应尽量短、顺序插入(建议 auto_increment 而不是 uuid,比如身份证号,不仅长度长,而且无序)
  • order by 优化
    •  using index:直接返回数据(不需要再去服务层缓冲区排序),性能高
    • using filesort:需要将查询返回的结果去服务层缓冲区去排序
    • 所以在对 order by 进行优化时,其实是使用索引来进行优化的;涉及导排序的字段尽量建立索引,同时注意创建索引时的升序降序问题
    • 尽量使用覆盖索引而不是 select *
  • group by 优化
    •  索引,多字段分组时遵循最左前缀法则
  • limit 优化
    •  深度分页性能低,使用覆盖索引 + 子查询
  • count 优化
    •  count(字段) < count(主键) < count(1) < count(*)
  • update 优化
    •  update 的条件字段尽量使用索引字段(尽量主键),InnoDB 的行锁是针对索引加的锁,而不是针对记录加的锁!

所以,总之我们在做 SQL 优化的时候,其实基本都是在针对索引进行优化;

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

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

相关文章

Web3时代的教育技术革新:智能合约在学习管理中的应用

随着区块链技术的发展和普及&#xff0c;Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一&#xff0c;不仅在金融和供应链管理等领域展示了其巨大的潜力&#xff0c;也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…

Linux中运用xsync实现免密集群分发

一、前言 今天搭建了三台虚拟机的集群&#xff0c;在集群中部分操作在三台虚拟机上的操作都一致&#xff0c;为了提高效率&#xff0c;就需要配置xsync实现集群分发。 二、设置免密登录 1.生成公钥和私钥 ssh-keygen -t rsa一直敲回车&#xff0c;会生成两个文件&#xff0c…

Yolov8网络结构学习

详解YOLOv8网络结构/环境搭建/数据集获取/训练/推理/验证/导出/部署 深入解析YOLOv8&#xff1a;网络结构与推理过程 YOLO? You Know! --YOLOV8详解 一&#xff1a;yolov8总体结构 1.Backbone:它采用了一系列卷积和 反卷积层只来提取特征&#xff0c;同时也使用了残差连接和…

酷炫末世意境背景404单页HTML源码

源码介绍 酷炫末世意境背景404单页HTML源码&#xff0c;背景充满着破坏一切的意境&#xff0c;彷佛末世的到来&#xff0c;可以做网站错误页或者丢失页面&#xff0c;将下面的代码放到空白的HTML里面&#xff0c;然后上传到服务器里面&#xff0c;设置好重定向即可 效果预览 …

滑动窗口题目

题目描述&#xff1a; 计算两个字符串str1和str2在给定的含有n个元素的字符串数组strs中出现的最短距离。 详细解释&#xff1a; 定义整数变量n&#xff0c;用于存储字符串数组strs的长度。定义一个vector<string>类型的变量strs&#xff0c;用于存储输入的字符串。定义…

Kafka消息队列python开发环境搭建

目录 引言 Kafka 的核心概念和组件 Kafka 的主要特性 使用场景 申请云服务器 安装docker及docker-compose VSCODE配置 开发环境搭建 搭建Kafka的python编程环境 Kafka的python编程示例 引言 Apache Kafka 是一个分布式流处理平台&#xff0c;由 LinkedIn 开发并在 2…

Linux--网络基础

计算机网络背景 计算机网络背景是一个复杂而丰富的领域&#xff0c;涵盖了从计算机单机模式到网络互联的演变过程&#xff0c;以及网络技术的不断发展和创新。 计算机单机模式和独立发展 在早期&#xff0c;计算机主要以单机模式存在&#xff0c;即每台计算机都是独立的&…

Jupyter Notebook安装及基本使用

Jupyter Notebook安装及基本使用 目录 Jupyter Notebook安装及基本使用方式一&#xff1a;Anaconda直接安装方式二&#xff1a;pip命令安装Jupyter使用虚拟环境 方式一&#xff1a;Anaconda直接安装 安装Anaconda 下载地址&#xff0c;输入邮箱&#xff0c;Windows下载 开始安…

探索Puppeteer的强大功能:抓取隐藏内容

背景/引言 在现代网页设计中&#xff0c;动态内容和隐藏元素的使用越来越普遍&#xff0c;这些内容往往只有在特定的用户交互或条件下才会显示出来。为了有效地获取这些隐藏内容&#xff0c;传统的静态爬虫技术往往力不从心。Puppeteer&#xff0c;作为一个强大的无头浏览器工…

成都亚恒丰创教育科技有限公司 【插画猴子:笔尖下的灵动世界】

在浩瀚的艺术海洋中&#xff0c;每一种创作形式都是人类情感与想象力的独特表达。而插画&#xff0c;作为这一广阔领域中的璀璨明珠&#xff0c;以其独特的视觉语言和丰富的叙事能力&#xff0c;构建了一个又一个令人遐想连篇的梦幻空间。成都亚恒丰创教育科技有限公司 在众多插…

【深度学习】InternVL2-8B,图转文,docker部署

文章目录 基础fastapi服务请求fastapi接口 基础 https://huggingface.co/OpenGVLab/InternVL2-8B#%E7%AE%80%E4%BB%8B InternVL2-26B应该更好&#xff0c;但显存没那么大&#xff0c;只能跑InternVL2-8B了。 下载&#xff1a; cd /ssd/xiedong/InternVL2-26B git clone htt…

Xline 异步运行时IO问题分析

Table of Contents 1. Xline运行时性能问题 2. 异步运行时和阻塞操作 3. Runtime调度问题 4. 性能测试 4.1 测试结果分析 5. 如何正确实现&#xff1f; 6. 何时能够在Runtime上阻塞 7. 总结 在异步运行时上进行编程经常是很困难的&#xff0c;在本篇文章中&#xff0c;我…

万界星空科技电线电缆MES系统实现线缆全流程追溯

MES系统通过高度集成的数据平台&#xff0c;对电线电缆的生产全过程进行实时监控与记录&#xff0c;从原材料入库开始&#xff0c;到生产过程中的各个关键控制点&#xff0c;再到成品出库&#xff0c;每一步操作都被详细记录并可追溯。这种全流程追溯能力主要体现在以下几个方面…

React学习笔记02-----React基本使用

一、React简介 想实现页面的局部刷新&#xff0c;而不是整个网页的刷新。AJAXDOM可以实现局部刷新 1.特点 &#xff08;1&#xff09;虚拟DOM 开发者通过React来操作原生DOM&#xff0c;从而构建页面。 React通过虚拟DOM来实现&#xff0c;可以解决DOM的兼容性问题&#x…

Android10.0 锁屏分析-KeyguardPatternView图案锁分析

首先一起看看下面这张图&#xff1a; 通过前面锁屏加载流程可以知道在KeyguardSecurityContainer中使用getSecurityView()根据不同的securityMode inflate出来&#xff0c;并添加到界面上的。 我们知道&#xff0c;Pattern锁所使用的layout是 R.layout.keyguard_pattern_view&a…

【ESP32】打造全网最强esp-idf基础教程——18.ESP32连接MQTT Broker

ESP32连接MQTT Broker 一、MQTT Broker 在开始ESP32编程之前&#xff0c;我们要先来看看公共主流的MQTT服务器可供使用&#xff0c;所谓的公共MQTT服务器就是一些网站给我们提供了在线的MQTT Broker&#xff0c;我可以直接利用其进行 MQTT 学习、测试甚至是小规模使用&…

表格竖向展示

最近在做手机端web页面&#xff0c;页面中需要有个表格来显示数据&#xff0c;但是由于数据太多页面太窄&#xff0c;table展示横向滑动的话感觉很丑。所以让表格竖向显示了 具体页面如下: 实现代码&#xff1a;当然代码里面绑定的数据啊什么的你都可以修改为自己的内容&#…

【软件建模与设计】-05-软件建模和设计方法概览

目录 1、COMET基于用例的软件生命周期 1.1、需求建模 1.2、分析建模 1.3、设计建模 1.4、增量软件构建 1.5、增量软件集成 1.6、系统测试 2、COMET与其他软件过程比较 2.1、与RUP对比 2.2、与螺旋模型对比 3、需求、分析和设计建模 3.1、需求建模活动 3.2、分析建…

机器学习入门【经典的CIFAR10分类】

模型 神经网络采用下图 我使用之后发现迭代多了之后一直最高是正确率65%左右&#xff0c;然后我自己添加了一些Relu激活函数和正则化&#xff0c;现在正确率可以有80%左右。 模型代码 import torch from torch import nnclass YmModel(nn.Module):def __init__(self):super(…

【香橙派】Orange pi AIpro开发板评测,与树莓派的横向对比以及实机性能测试

一、前言 在人工智能领域飞速发展的时代&#xff0c;国产厂商们也是紧随时代的步伐&#xff0c;迅龙公司联合华为推出了一款全新的开发板 Orange pi AIpro 作为一款建设人工智能新生态的开发板&#xff0c;它可广泛适用于AI边缘计算、深度视觉学习及视频流AI分析、视频图像分析…