MySQL笔记11

一、SQL优化

1.概念

在应用的的开发过程中,由于初期数据量小,开发人员写 SQL 语句时更重视功能上的实现,但是当应用系统正式上线后,随着生产数据量的急剧增长,很多 SQL 语句开始逐渐显露出性能问题,对生产的影响也越来越大,此时这些有问题的 SQL 语句就成为整个系统性能的瓶颈,因此我们必须要对它们进行优化.

优化方法

从设计上优化:如合理设计表结构、数据类型等。

从查询上优化:优化SQL查询语句的写法等。

从索引上优化:合理创建和使用索引,提升查询效率。

从存储上优化:如优化存储引擎、表空间等。

2.优化技巧

(1)原则

减小查询的数据量、提升SQL的索引命中率

(2)查询时尽量不要使用 *

开发中为方便会用 * 代替所有字段,但这种写法对数据库性能不友好。

原因:

分析成本变高:增加SQL解析器的额外解析成本。

网络开销变大:返回无意义数据多,网络数据包体积大。

内存占用变高:SQL查到的结果集更大,占用内存也越多。

维护性变差:增加业务维护量。

(3)连表查询时尽量不要关联太多表

关联表过多会导致执行效率变慢、执行时间变长。

建议:

交互型业务:关联表数量控制在5张以内。

后台型业务:虽对用户体验要求低且业务复杂,但最好控制在16~18张以内(阿里开发规范要求控制在3张以内)。

(4)多表查询时一定要以小驱大

先查询小表,再用小表的结果去大表中检索数据。写SQL时最好将小表放在前面,大表放在后面。

(5)不要使用like左模糊和全模糊查询

原因:

like 以 % 开头(如 %xxx 、 %xxx% )会导致索引失效,触发全表查询,降低查询效率。

解决方案:

如需模糊查询,应避免左模糊和全模糊;若确实需要,可建立全文索引,或在数据量较大时使用ES、Solr等搜索引擎替代。

(6)查询时尽量不要对字段做空值判断

对字段进行 is null 或 not is null 判断时,索引会失效,导致全表扫描。

如:

优化建议:

设计字段时尽量用 not null 定义,若字段需为空,可用 0 、空字符串 "" 等空字符代替,这样查询空值时可通过空字符走索引检索。

(7)不要在条件查询 = 前对字段做任何运算

即使字段建立了索引,这类操作还是会导致索引失效,触发全表查询。因为如果MySQL优化器字段前有运算,生成执行计划时不会为运算后的字段选择索引。

如:

(8)!=、!<>、not in、not like、or...要慎用

这些操作可能会导致索引失效。

如:

上述SQL虽然变长了,但查询效率会而更高,因为后面的SQL可以走索引

(9)避免频繁创建、销毁临时表

临时表是数据缓存,对常用查询结果建立临时表可基于内存快速查询(速度远快于磁盘检索)。

注意事项:

仅对频繁查询的数据建立临时表,盲目无限制创建、销毁会给MySQL带来较大负担。

(10)从业务设计层面减少大量数据返回的情况

一次性返回大量数据会引发网络阻塞、内存占用过高、资源开销大等问题。

优化建议:

若业务存在一次性返回全量数据的情况,需拆分业务逻辑,例如分批返回数据给客户端。

(11)尽量避免深分页的情况出现

如:

这类深分页SQL,MySQL会先查询出100010条数据,再丢弃前10万条,仅返回最后10条,极其浪费资源。

改进方法:

情况1(结果集有递增连续字段):基于有序字段筛选后再分页,如:

情况2(搜索分页,结果无序):从业务上限制深分页,例如参考百度搜索,一般只提供前30页,避免用户进行极深分页操作。

(12)SQL务必要写完整,不要使用缩写法

示例:

注意:

所有隐式的这种写法,在底层都需要做一次转换,将其转换为完整的写法,因此简写的SQL会比完整的SQL多一步转化过程,如果需要极致程度的优化,切记将SQL写成完整语法。

(13)明确仅返回一条数据的语句可以使用 limit 1

示例:

上述这两条SQL语句都是根据姓名查询一条数据,但后者大多数情况下会比前者好,因为加上limit 1关键字后,当程序匹配到一条数据时就会停止扫描,如果不加的情况下会将所有数据都扫描一次。所以一般情况下,如果确定了只需要查询一条数据,就可以加上 limit 1提升性能。

(14)客户端的一些操作可以批量化完成

批量新增某些数据、批量修改某些数据的状态...,若使用上述接口的结构,即insert语句嵌入在for循环中,由于每次都需要往MySQL发送SQL语句,则会带来额外的网络开销以及耗时,应更改为如下:

上述修改,会组合成一条SQL发送给MySQL执行,能够在很大程度上节省网络资源的开销,提升批量操作的执行效率。

3.查看SQL执行频率

(1)作用

MySQL 客户端连接成功后,通过 show [session|global] status 命令可以查看服务器状态信息。通过查看状态信息可以查看对当前数据库的主要操作类型。

(2)格式

查看当前会话统计结果(7个下划线):

查看自数据库上次启动至今统计结果:

查看针对Innodb引擎的统计结果:

(3)重要参数分析

|----------------------|----------------------------------------|
| 参数 | 含义 |
| Com_select | 执行 select 操作的次数,一次查询只累加1 |
| Com_insert | 执行 insert操作的次数,对于批量插入的 insert 操作,只累加一次 |
| Com_update | 执行 update 操作的次数 |
| Com_delete | 执行 delete 操作的次数 |
| Innodb_rows_read | select 查询返回的行数 |
| Innodb_rows_inserted | 执行 insert 操作插入的行数 |
| Innodb_rows_updated | 执行 update 操作更新的行数 |
| Innodb_rows_deleted | 执行 delete 操作删除的行数 |
| Connections | 试图连接 MySQL 服务器的次数 |
| Uptime | 服务器工作时间 |
| Slow_queries | 慢查询的次数 |

(4)定位低效率执行SQL

可以通过以下两种方式定位执行效率较低的 SQL 语句:

慢查询日志 : 通过慢查询日志定位那些执行效率较低的 SQL 语句。

show processlist:该命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。

慢查询:

查看慢日志配置信息:

开启慢日志查询:

查看慢日志记录SQL的最低阈值时间(单位为秒) :

修改慢日志记录SQL的最低阈值时间,需要重启mysql服务:

show processlist:

分析:

id列------用户登录mysql时,系统分配的"会话id",可以使用函数 connection_id() 查看

user列------显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句

host列------显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户

db列------显示这个进程目前连接的是哪个数据库

command列------显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等

time列------显示这个状态持续的时间,单位是秒

state列------显示使用当前连接的sql语句的状态,很重要的列。state描述的是语句执行中的某一个状态。一个sql语句,以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成

info列------显示这个sql语句,是判断问题语句的一个重要依据

(5)explain 分析执行计划

作用:

通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN 命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。

格式:

explain sql

示例:

分析:

|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| 字段 | 含义 |
| id | select 查询的序列号,是一组数字,表示的是查询中执行 select 子句或者是操作表的顺序。 |
| select_type | 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个SELECT)等 |
| table | 输出结果集的表 |
| partitions | 匹配的分区信息,若表未分区则为NULL,用于展示查询涉及的分区情况 |
| type | 表示表的连接类型,性能由好到差的连接类型为(system --> const --> eq_ref --> ref --> ref_or_null --> index_merge --> index_subquery --> range --> index --> all ) |
| prossible_keys | 表示查询时,可能使用的索引 |
| key | 表示实际使用的索引 |
| key_len | 索引字段的长度 |
| ref | 表示与索引列进行比较的列或常量,例如某列与另一列的关联、或列与常量的比较。NULL表示没有使用到基于列的索引关联 |
| rows | 扫描行的数量 |
| extra | 执行情况的说明和描述 |

参数使用:

key:如果该值为空,则表示未使用索引查询,此时需要调整SQL或建立索引。

type:这个字段决定了查询的类型,如果为index、all就需要进行优化。

rows:这个字段代表着查询时可能会扫描的数据行数,较大时也需要进行优化。

filtered:这个字段代表着查询时,表中不会扫描的数据行占比,较小时需要进行优化。

Extra:这个字段代表着查询时的具体情况,在某些情况下需要根据对应信息进行优化。

(6)show profile 分析SQL

作用:

show profiles 能够在做SQL优化时帮我们了解时间都耗费到哪里。

查看是否开启:

设置开启:

示例:

通过 show profile for query query_id 语句可以查看到该SQL执行过程中每个线程的状态和消耗的时间:

在获取到最消耗时间的线程状态后,MySQL 支持进一步选择 all、cpu、block io 、context switch、page faults 等明细类型类查看 MySQL 在使用什么资源上耗费了过高的时间。例如,选择查看CPU的耗费时间 :

分析:

|------------|-------------------|
| 字段 | 含义 |
| Status | SQL 语句执行的状态 |
| Duration | SQL 执行过程中每一个步骤的耗时 |
| CPU_user | 当前用户占有的 CPU |
| CPU_system | 系统占有的 CPU |

(7)trace 分析优化器执行计划

作用:

MySQL 提供了对 SQL 的跟踪 trace , 通过 trace 文件能够进一步了解为什么优化器选择 A 计划, 而不是选择 B 计划。

方法:

打开 trace ,设置格式为 JSON,并设置 trace 最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整展示。

示例:

(8)使用索引优化

作用:

索引是数据库优化最常用也是最重要的手段之一, 通过索引通常可以帮助用户解决大多数的MySQL的性能优化问题。

建立原则:

一般针对数据分散的关键字进行建立索引,比如ID、QQ,像性别、状态值等建立索引没有意

对大数据量表建立聚集索引,避免更新操作带来的碎片

尽量使用短索引,一般对int、char/varchar、date/time 等类型的字段建立索引

需要的时候建立联合索引,但是要注意查询SQL语句的编写

谨慎建立 unique 类型的索引(唯一索引)

大文本字段不建立为索引,如果要对大文本字段进行检索,可以考虑全文索引

频繁更新的列不适合建立索引

order by 字句中的字段,where 子句中字段,最常用的sql语句中字段,应建立索引。

对于只是做查询用的数据库索引越多越好,但对于在线实时系统建议控制在5个以内。

(9)架构优化

业务拆分:搜索功能,like ,前后都有%,一般不用MySQL数据库

业务拆分:某些应用使用nosql持久化存储,例如memcahcedb、redis、ttserver 比如粉丝关注、好友关系等;

数据库前端必须要加cache,例如memcached,用户登录,商品查询

动态数据静态化。整个文件静态化,页面片段静态化

数据库集群与读写分离;

单表超过2000万,拆库拆表,人工或自动拆分(登录、商品、订单等)

相关推荐
半夏知半秋2 小时前
skynet.newservice接口分析
笔记·后端·学习·安全架构
立志成为大牛的小牛3 小时前
数据结构——十四、构造二叉树(王道408)
数据结构·笔记·学习·程序人生·考研
稚辉君.MCA_P8_Java3 小时前
WebSocket 是什么原理?为什么可以实现持久连接?
网络·数据库·websocket·网络协议
RanceGru3 小时前
LLM学习笔记5——本地部署ComfyUI和Wan2.1-T2V-1.3B文生视频模型
笔记·学习·stable diffusion·transformer
小光学长3 小时前
基于Vue的图书馆座位预约系统6emrqhc8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
koo3644 小时前
李宏毅机器学习笔记18
笔记
code小毛孩4 小时前
如何简单的并且又能大幅度降低任务队列的锁粒度、提高吞吐量?
java·jvm·数据库
x_lrong4 小时前
个人AI环境快速搭建
人工智能·笔记
teeeeeeemo4 小时前
Webpack 模块联邦(Module Federation)
开发语言·前端·javascript·笔记·webpack·node.js