珍惜当下 展望未来!

mysql调优(new)

mysql调优(new)

针对mysql5.7版本

目的:

针对业务,实际场景的问题来对mysql服务端进行优化。

解决实际中的各类问题。

1.基础架构

总体架构

总体分为三层

连接,服务,存储引擎

image.png

详细模块

image.png

1.Connector:用来支持各种语言和 SQL 的交互,比如 PHP,Python,Java 的 JDBC
2.Management Serveices & Utilities:系统管理和控制工具,包括备份恢复、MySQL 复制、集群等等
3.Connection Pool:连接池,管理需要缓冲的资源,包括用户密码权限线程等等
4.SQL Interface:用来接收用户的 SQL 命令,返回用户需要的查询结果
5.Parser:用来解析 SQL 语句
6.Optimizer:查询优化器
7.Cache and Buffer:查询缓存,除了行记录的缓存之外,还有表缓存,Key 缓存,权限缓存等等。
8.Pluggable Storage Engines:插件式存储引擎,它提供 API 给服务层使用,跟具体的文件打交道。

2.一条查询sql的执行流程(详解)

image.png

建立连接 查询缓存

1.建立连接

2.查询缓存

3.语法解析(解析器)

4.查询优化--》执行计划 索引选择 (优化器)

5.执行(执行器)

建立连接

首先运行mysql服务,监听端口3306(默认)

通信协议

MySQL 支持多种通信协议。

  • TCP/IP协议,用的最多,我们java中的数据库驱动就是用的这个
  • Unix Socket,要用到服务器上的一个物理文件(mysql.sock)。
  • 另外还有命名管道(Named Pipes)和内存共享(Share Memory)的方式

通信方式

单工:数据单向传输

半双工:双向传输,但不能同时传输

全双工:双向传输,可以同时传输

mysql用的半双工。因此客户端和服务端不能同时发送数据。

所以客户端发送 SQL 语句给服务端的时候,(在一次连接里面)数据是不能分成小块发送的,不管
你的 SQL 语句有多大,都是一次性发送。
如果发送给服务器的数据包过大,我们必须要调整 MySQL 服务器配置 max_allowed_packet 参数的值
(默认是 4M)。

show variables like 'max_allowed_packet';

对于服务端返回数据也是,数据包别太大,能分页就分页。

连接方式

MySQL 既支持短连接,也支持长连接。短连接就是操作完毕以后,马上 close 掉。长连接可以保持 打开,后面的程序访问的时候还可以使用这个连接。

长时间不活动的连接,MySQL 服务器会断开

#默认都是28800s   8小时
show global variables like 'wait_timeout'; (非交互式超时时间,如 JDBC 程序)
show global variables like 'interactive_timeout'; (交互式超时时间,如数据库工具)

MySQL 默认的最大连接数是 151 个(5.7 版本),最大是 16384(2^14)。

#最大连接数
show variables like 'max_connections';

#查看 3306 端口当前连接数
netstat -an|grep 3306|wc -l;

查询缓存

MySQL 内部自带了一个缓存模块。默认是关闭的。主要是因为 MySQL 自带的缓存的应用场景有 限,第一个是它要求 SQL 语句必须一模一样。第二个是表里面任何一条数据发生变化的时候,这张表所有缓存都会失效。

在 MySQL 5.8 中,查询缓存已经被移除了。 ==》用的场景少 还难维护

语法解析(解析器)

是对 SQL 语句进行词法和语法分析和语义的解析。

检查sql是否合法,如果不合法的话,,会在预处理器处理时报错。

查询优化(优化器)

优化器开展工作。

根据sql的解析结果生成不同的执行计划,选择最优的一条来执行。

MySQL 里 面使用的是基于开销(cost)的优化器,那种执行计划开销最小,就用哪种。

优化器常见的优化处理

  • 是否使用索引
  • 查询数据,是不是能直接从索引里面取到值。
  • count()、min()、max(),比如是不是能从索引里面直接取到值。
  • ……

优化器最终会把解析树变成一个查询执行计划,查询执行计划是一个数据结构。

当然,这个执行计划是不是一定是最优的执行计划呢?不一定,因为 MySQL 也有可能覆盖不到所有的执行计划。

我们可以通过explain关键字来查询sql的执行计划

EXPLAIN select name from user where id=1;

存储引擎

基本介绍

我们的数据是放在哪里的?执行计划在哪里执行?是谁去执行?

插件式的存储引擎,根据业务可以对不同表使用不同的存储引擎。

不同的存储引擎通过提供不同的存储机制、索引方式、锁定水平等功能,来满足我们的业务需求。

#查看库里所有表的存储引擎
show table status from `ihrm`;

#查看表的存储引擎
show table status like 'bs_city'

各种存储引擎

官网:https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html

可以查一下

SHOW ENGINES ;

常用的有InnoDB MyISAM

MyISAM和InnoDB区别

MyISAM: 5.5之前默认存储引擎

3个文件存数据,

适用于以读为主的表。

存储了表的行数(count 速度更快)。

支持表锁(更新时其他线程不能读写表,查询时其他线程不能写),不支持行锁

不支持事务

innodb: 5.5之后默认存储引擎(包括5.5)

2个文件存数据

支持事务,外键

支持表锁,行锁(默认行锁)

安全性比较高

其他存储引擎,不怎么用

Memory CSV Archive

Memory (1个文件)

所有数据都保存在内存,适合做临时表,读写快

默认使用哈希索引

3.一条更新的sql执行流程

大致过程和查询差不多,解析器-》优化器-》执行器

区别就在于拿到符合条件的数据之后的操作。

InnoDB 里面有个内存的缓冲池(buffer pool)。

更新时不会直接写到磁盘(IO代价大),而是先写入到这个缓冲的内存中,innodb有专门的线程来将buffer pool的数据写入到磁盘。内存的数据页和磁盘数据不一致的 时候,我们把它叫做脏页。

(每隔一段时间一次性把多个修改写到磁盘。--》刷脏)

image.png

这里就会有问题:内存数据没有同步到磁盘,mysql服务就挂了咋办?--》持久化机制

持久化机制通过日志文件来实现:redo log,bin log

redo log

记录更新后的值。 日志文件

特点:
记录修改后的值(事务的持久性就是通过redo实现)
文件大小是固定的,前面的内容会被覆盖,所以不能用于回滚。
innodb存储引擎实现的,并不是所有存储引擎都有。


bin log

相当于全量日志,记录所有更新操作

MySQL Server 层也有一个日志文件,叫做 binlog,它可以被所有的存储引擎使用。

binlog 以事件的形式记录了所有的 DDL 和 DML 语句(因为它记录的是操作而不是数据值,属于逻 辑日志),可以用来做主从复制和数据恢复。

文件内容可以追加,没有固定大小限制

image.png

update -- >redolog--> binlog

4.mysql索引原理

索引介绍

就是一种数据结构,用于快速检索。(目录)

image.png

索引类型

innoDb/MyISAM存储引擎有4种类型:

normal:普通索引

unique:唯一索引,值唯一但是可以允许一条数据为null (主键索引是特殊的唯一索引 值不能为null)

fullText:全文索引,针对比较大的数据创建,防止like效率慢的问题(只支持字段为文本类型:比如char、varchar、text)

spatial:空间索引(用得少)

索引的数据结构

索引结构发展演变:

二叉树 (Binary Search Tree)

取决于tree的深度,最坏的情况和全表扫描没区别……(斜树)

平衡二叉树(AVL Tree)

插入数据时会自动计算调整,保证平衡

每个节点存单个东西:索引的键值,自己的地址,左右指定节点的地址

问题:

  • 数据量大的话还是比较深,需要多次磁盘IO。--- 还是慢

  • InnoDB 操作磁盘的最小的单位是一页(或者叫一个磁 盘块),大小是 16K(16384 字节)。意思就是你1个节点数据存的东西太少的话,还是一个节点就要一次IO --- 》 浪费IO的空间(理论来讲一个节点能把16k存完最好)

    #查询表对应 索引和数据的空间大小
    select
    CONCAT(ROUND(SUM(DATA_LENGTH/1024/1024),2),'MB') AS data_len,
    CONCAT(ROUND(SUM(INDEX_LENGTH/1024/1024),2),'MB') as index_len
    from information_schema.TABLES
    where table_schema='test' and table_name='t_user';
    

image-20221128185357469

**Btree:多路平衡查找树(分裂、合并)

也存三个东西:索引键值),自己的地址,指向的节点地址,数据

  • 索引值就是具体的数据:主键索引保存数据,非主键索引保存主键值

和AVL不一样的是,指向的节点地址永远比关键字数多 1,如下:

image-20221128185328198

每个节点存更多的索引数据。

相对于AVL解决的问题:数据存储的多,分叉多

高瘦--》矮胖

B+树(加强版多路平衡查找树)

改良版的BTREE,解决的问题比BTREE更全面

image-20221128231909548

特点:

关键字的数量是跟路数相等的;

只有叶子节点才存数据。所以匹配到关键字不会直接返回,还会往下搜索,一直到叶子节点。

B+Tree 的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个 叶子节点的第一个数据,形成了一个有序链表的结构。

优势:

每个节点能存储更多索引值(因为数据都在叶子节点了)

  • 数据多的话,一次磁盘IO能够加载的索引关键字更多

  • 分叉更多 ----》更矮更胖

效率稳定:因为都要到叶子节点拿数据,所以io次数比较稳定

排序能力更强(因为叶子节点上有下一个数据区的指针,数据形成了链表)

mysql使用的索引结构

一般有两种:常用的是B+Tree(innodb 和 myisam)

哈希索引

还有一种是hash索引,特点如下:

image-20221129002733739

查询速度快,通过hashcode直接匹配。

只能进行等值匹配,不能范围查询和模糊查询

数据量大或者字段值重复多的话,会造成hash冲突,(采用拉链法解决),效率会降低。

memory存储引擎支持哈希索引

B+Tree落地

不同存储引擎存储的文件也不一样

show VARIABLES LIKE 'datadir';  #查询相关数据文件存储的位置

innodb:2个文件frm(表结构) ibd(数据+索引)

myisam: 3个文件 frm (表结构) MYD (数据) MYI(索引)

MYISAM的B+TRee索引:

MyISAM 的 B+Tree 里面,叶子节点存储的是数据文件对应的磁盘地址。

分了两个文件分别存索引和数据

image-20221129005357504

innodb的B+TRee索引:

innodb索引和数据存在同一个文件中,数据在b+tree的叶子节点上存储。

是这里会有一个问题,一张 InnoDB 的表可能有很多个多索引,数据肯定是只有一份的,那数据 在哪个索引的叶子节点上呢?。。

这里涉及到聚集索引的概念。

聚集索引不是索引的类型,而是数据存储的一种方式

就是索引键值的逻辑顺序跟表数据行的物理存储顺序是一致的。(比如字典的目录是按拼音排序 的,内容也是按拼音排序的,按拼音排序的这种目录就叫聚集索引)。--- 比如数据的内容按照主键排序。

innodb就是使用聚集索引的树结构来存储数据的。(一般以主键索引作为聚集索引,数据存储在主键索引树上的叶子结点上,决定数据行的物理存储顺序。)

问题又来了?数据都存在聚集索引的叶子节点上,那非聚集索引怎么拿到完整的数据? --- 回表

非聚集索引的叶子节点数据存的是聚集索引的key,拿到key之后再回表,去聚集索引树上的叶子节点拿到完整数据。(回表能避免就避免,加快效率)

image-20221129013223163

我们一般用什么来作为聚集索引:

  • 主键

  • 没有主键的话,不包含有 NULL 值的唯一索引作

  • 都没有的话,则 InnoDB 会选择内置 6 字节长的 ROWID 作为隐藏的聚集索引, 它会随着行记录的写入而主键递增。

    select _rowid name from t_user1;
    

5. 索引使用原则

索引并不是越多越好,更新数据时需要重新维护

不建议在离散度低的字段建索引

离散度=count(distinct(column_name)) : count(*)---》重复值越多,离散度就越低,太低的话导致扫描行数过多

联合索引最左匹配:

  • 针对多个字段可以建立联合索引 ,单列索引是特殊的联合索引
  • 查询sql匹配的字段也要根据索引的联合索引的顺序一样,否则不会走索引。(建立联合索引一定要把常用的字段放到最左边),如果多个字段都匹配到了联合索引的话,效率会比单列索引高

image-20221129091403814

覆盖索引: 如果是非主键索引的情况下,是需要回表获取数据的

- select的数据可以直接从索引中获得,不需要回表,这时候使用的索引就叫做覆盖索引,
- 能使用就使用,大大提高了检索效率(减少了IO次数)

什么字段上创建索引?

用于 where 判断 order 排序和 join 的(on)字段上创建索引

频繁更新的值,别建索引

什么情况索引会失效

  • 用不用索引,最终都是优化器说了算。基于开销的原则,开销怎么小怎么来
索引列上使用函数,表达式……
explain SELECT * FROM `t2` where id+1 = 4;

字符串不加引号,出现隐式转换
explain SELECT * FROM `user_innodb` where name = '136';

like 条件中前面带%

负向查询可能不走索引:(看开销   有时候全表反而更快 取决于优化器) --注意跟数据库版本、数据量、数据选择度都有关系。
not in  ,!=  ,not like

6.mysql优化思路和工具

mysql调优的最终目的:

  • 目的:提高系统吞吐量,减少响应时间(压榨资源)

我们说到性能调优,大部分时候想要实现的目标是让我们的查询更快。一个查询的动作又是由很多 个环节组成的,每个环节都会消耗时间,我们在第一节课讲 SQL 语句的执行流程的时候已经分析过了。 我们要减少查询所消耗的时间,就要从每一个环节入手。

image-20221129094458157

连接--配置优化

服务端:

设置合适的可用连接数

及时释放不活动的连接

-- 修改最大连接数,当有多个应用连接的时候
show variables like 'max_connections'; 

--及时释放不活动的连接,注意不要释放连接池还在使用的连接
-- 交互式和非交互式的客户端的默认超时时间都是 28800秒,8 小时,我们可以把这个值调小。
show global variables like 'wait_timeout'; 


客户端

使用连接池,复用连接,避免每次执行sql都要新建连接,用完又销毁

具体怎么配置,还是要压测来确定。 不同的硬件条件可以要针对不同的配置

架构优化

缓存

缓存减轻数据库压力

集群(主从复制)

加机器,分担压力

主从复制: master slave (读写分离哦,主写从读)

主从复制如何实现,怎么解决数据同步的问题的?

binlog

有了这个 binlog,从服务器会获取主服务器的 binlog 文件,然后解析里面的 SQL 语句,在从服务
器上面执行一遍,保持主从的数据一致。
这里面涉及到三个线程,连接到 master 获取 binlog,并且解析 binlog 写入中继日志,这个线程叫
做 I/O 线程。
Master 节点上有一个 log dump 线程,是用来发送 binlog 给 slave 的。
从库的 SQL 线程,是用来读取 relay log,把数据写入到数据库的。
这个是主从复制涉及到的三个线程。

image-20221129095723394

image-20221129095901417

分库分表

我们在做了主从复制之后,如果单个 master 节点或者单张表存储的数据过大的时候,比如一张表 有上亿的数据,单表的查询性能还是会下降,我们要进一步对单台数据库节点的数据进行拆分,这个就 是分库分表。

优化器(执行计划)

优化器就是对我们的 SQL 语句进行分析,生成执行计划。

问题:在我们做项目的时候,有时会收到 DBA 的邮件,里面列出了我们项目上几个耗时比较长的查 询语句,让我们去优化,这些语句是从哪里来的呢? 我们的服务层每天执行了这么多 SQL 语句,它怎么知道哪些 SQL 语句比较慢呢? 第一步,我们要把 SQL 执行情况记录下来。

慢查询日志 slow query log
# 慢查询日志默认关闭  开启是有代价的(会影响性能)
show variables like 'slow_query%';

# 超过多长时间的 SQL 才记录到慢日志,默认是 10 秒
show variables like 'long_query%';

# 可以动态修改(重启失效)
set @@global.slow_query_log=1; -- 1 开启,0 关闭,重启后失效
set @@global.long_query_time=3; -- mysql 默认的慢查询时间是 10 秒,另开一个窗口后才会查
到最新值
#也可以配置文件my.cnf修改,持久
slow_query_log = ON
long_query_time=2
slow_query_log_file =/var/lib/mysql/localhost-slow.log

#模拟慢查询
select sleep(10);


#查看有多少慢查询
show global status like 'slow_queries'; 
#获取慢查询日志文件位置 查看内容
show variables like 'slow_query%';
#日志内容多的话 难以分析 可以使用工具来分析---MySQL 提供了mysqldumpslow的工具,在MySQL 的bin目录下。
#查询用时最多的 10 条慢 SQL:
./mysqldumpslow -s t -t 10 -g 'select' /usr/local/mysql/data/iZwz984mbcsaw0ihvbfzfpZ-slow.log
show profile

可以查看SQL 语句执行的 时候使用的资源,比如 CPU、IO 时间的消耗情况。

#开启
select @@profiling;
set @@profiling=1;

#查询profile用法
help profile;

#所有sql执行时间
show profiles;


#查询某条sql的各个过程消耗的时间  1代表queryId
show profile for query 1;
#某条sql的各个过程cpu消耗
 SHOW PROFILE CPU FOR QUERY 2;


其他系统命令
#show processlist;   查询连接线程状态  可以拿到id  kill掉异常线程
show processlist; 
select * from information_schema.processlist; #查表效果同上


#show status 查看服务器运行状态
SHOW GLOBAL STATUS LIKE 'com_select'; -- 查看 select 次数


#show engine 存储引擎运行信息
#show engine 用来显示存储引擎的当前运行信息,包括事务持有的表锁、行锁信息;事务的锁等待
#情况;线程信号量等待;文件 IO 请求;buffer pool 统计信息。
show engine innodb status;

explain

上面的工具都是定位问题

定位到问题后使用explain来分析sql

来进行优化

字段类型优化

posted @ 2022-11-29 16:54  嘿嘿-  阅读(791)  评论(0编辑  收藏  举报