MySQL训练营 <4> Mysql 性能优化

       MySQL训练营 <4> Mysql 性能优化

1. 连接管理

  服务端

  1.  最大连接数,默认是151,这个可以调大。
show variables like 'max_connections';
  1. 长时间不活动的链接,默认是28800s,8小时。这个可以改小一点,尽快的回收链接
-- 非交互式超时时间,如JDBC程序
show global VARIABLES like '%wait_timeout%';
-- 交互式超时时间,如数据库工具
show global VARIABLES like '%interactive_timeout%';

客户端

  1. 客户端链接到服务端,使用长连接。放在连接池中,可以是复用的。连接池可以用来优化客户端连接到服务端的环节druid,c3po,Hikari
  2. 客户端减少服务端的连接数。使用 Redis 作缓存

2. 缓存 - 架构优化

2.1 缓存

在应用系统并发数非常大的时候,如果没有缓存会造成两个问题:

  • 一方面会给数据库造成很大的压力
  • 另一方面,从引用层面来说,操作数据的速度也会受到影响。把查询过的数据放在Redis,实时性不是很高的,

2.2 集群,主从复制

如果单台数据库服务满足不了访问需求,可以做数据库的集群方案。

集群必然会有一个数据一致性的问题。那就需要基于binlog的主从复制。slave 请求 master 同步数据,把DML语句再执行一遍

基于主从复制的方案,然后再做一个路由(到从节点),那就叫做读写分离,增删改 写入 master 节点,查询从 slave 节点。

有了读写分离,那就需要动态数据源的配置,Spring 有个 Abstractroutingdatasource 可以动态配置数据源。或者一些其它的中间件 shardingsphere , mycat 

2.3 分库分表

垂直分库

把数据库按照业务拆分成不同的数据库。 垂直分库,减少并发压力。可以使用: shardingjdbc分库分表, mycat 这些工具来分库。

水平分库分表 

数据库中如果有一张表数据量特别大,有一亿条数据,即使分库了,查询量还是在那一张表上。查询起来会特别慢。那就需要把那张表拆分。

水平分库分表的做法就是把单张表的数据按照一定的规则分布到多个数据库。

分表是按照数据的维度来划分的。在不同的数据库中表结构是一模一样的,只是存的数据不一样。

假设现有客户表,有一亿条数据,分到5个不同数据库中,每个库中数据分2000万条。

DB1:0-2000万    DB2:2-4000万    DB3:4-6000万 


3. 优化器

3.1 慢查询日志   slow query log

-- 慢查询日志,默认是关闭的,开启消耗性能
show variables like '%slow_query%';
Variable_nameValue说明
slow_query_logOFF默认是关闭的
slow_query_log_file/var/log/mysql-slowquery.log慢查询日志存放路径

那么什么样的查询能定义为慢查询日志呢?查询超过多少时间算慢?

show variables like '%long_query%';
Variable_nameValue说明
long_query_time2.000000默认是10 秒,这边改成2秒

修改配置文件建议在 /etc/my.cnf  配置文件中修改。修改如下:

打开慢查询的日志文件: 

3.2 mysqldumpslow

mysql 提供一个分析慢查询日志的工具 叫 mysqldumpslow , 在 mysql 的 bin 目录下。

  • 查看下这个工具在什么位置 :
whereis  mysqldumpslow 

mysqldumpslow --help
  • 例如:查询用时最多的10条慢sql :
mysqldumpslow -s t -t 10 -g 'select' /var/lib/mysql/localhost-slow.log
/var/lib/mysql/localhost-slow.log 是慢查询日志的路径

Count 代表这个sql执行了几次

Time 代表执行的时间,括号里面是累计时间

Lock 表示锁定的时间,括号里面是累计时间

Rows 表示返回的记录数,括号是累计

3.3 show profile

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

  • 查看是否开启 show profile
-- 查看profile是否开启
select @@profiling;
-- 开启
set  @@profiling=1;
  •  查看 profile 统计
-- 查看 profile 统计
show profiles;
  • 也可以根据 ID 查看执行详细信息,在后面带上 for query + id.
show profile for query 127;

4. 其它系统命令

开启标准监控和锁监控:

set Global innodb_status_output=ON;
set Global innodb_status_output_locks=ON

  • 查看 Server 层的运行信息:
show status ;

返回的状态可以去mysql官网搜索下

  • 查看mysql启动以来,执行了多少次的select:
show global status like 'com_select';

  • 查看服务层的连接信息:
    • 这个命令可以用于显示用户运行线程,如果说其中某个线程有问题,可以根据id号kill线程
    • Command代表链接的状态 sleep是不活动的       Time链接保持了多长时间
show processlist;
-- 两者效果等同
select * from information_schema.`PROCESSLIST`;

  • 查看 Innodb 存储引擎的状态
show ENGINE innodb status;

=====================================
2020-09-03 07:53:41 0x24c0 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 47 seconds
-----------------
BACKGROUND THREAD     【后台线程】
-----------------
srv_master_thread loops: 2 srv_active, 0 srv_shutdown, 2850 srv_idle
srv_master_thread log flush and writes: 0
----------
SEMAPHORES     【信号量】
----------
OS WAIT ARRAY INFO: reservation count 11
OS WAIT ARRAY INFO: signal count 11
RW-shared spins 0, rounds 0, OS waits 0
RW-excl spins 1, rounds 30, OS waits 1
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 0.00 RW-shared, 30.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS     【所有的活跃的的事务,以及它们所持有的锁信息】
------------
Trx id counter 155912
Purge done for trx's n:o < 155910 undo n:o < 0 state: running but idle
History list length 3
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281475307737288, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 281475307736448, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 281475307735608, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 281475307734768, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: wait Windows aio (insert buffer thread)
I/O thread 1 state: wait Windows aio (log thread)
I/O thread 2 state: wait Windows aio (read thread)
I/O thread 3 state: wait Windows aio (read thread)
I/O thread 4 state: wait Windows aio (read thread)
I/O thread 5 state: wait Windows aio (read thread)
I/O thread 6 state: wait Windows aio (write thread)
I/O thread 7 state: wait Windows aio (write thread)
I/O thread 8 state: wait Windows aio (write thread)
I/O thread 9 state: wait Windows aio (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
1024 OS file reads, 218 OS file writes, 38 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX     【INSERT BUFFER 就是 内存结构中的channge buffer,改名字了。ADAPTIVE HASH INDEX就是 buffer pool 中为热点数据页建立自适应Hash索引】
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 2 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 2 buffer(s)
Hash table size 34679, node heap has 4 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG     【日志的信息】
---
Log sequence number          281760519
Log buffer assigned up to    281760519
Log buffer completed up to   281760519
Log written up to            281760519
Log flushed up to            281760519
Added dirty pages up to      281760519
Pages flushed up to          281760519
Last checkpoint at           281760519
28 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY      【BUFFER POOL 和 内存 的信息】
----------------------
Total large memory allocated 137363456
Dictionary memory allocated 415238
Buffer pool size   8192
Free buffers       7039
Database pages     1143
Old database pages 441
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1001, created 142, written 155
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1143, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=2260, Main thread ID=00000000000008D0 , state=sleeping
Number of rows inserted 0, updated 315, deleted 0, read 6808
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

5. EXPLAIN   执行计划

现在我们知道哪些SQL慢了,但是为什么慢呢?慢在哪里了?Mysql 提供了一个执行计划工具,通过 Explain 我们可以模拟优化器执行sql语句的过程,来知道mysql是怎么处理一条sql语句的

Mysql 官网可以搜索 Explain : MySQL :: MySQL 8.0 Reference Manual :: 8.8.2 EXPLAIN Output Format

现在准备3张表:课程表,老师表,老师电话表。都没有索引

DROP TABLE IF EXISTS course;
CREATE TABLE `course` (
 `cid` int(3) DEFAULT NULL,
 `cname` varchar(20) DEFAULT NULL,
 `tid` int(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DROP TABLE IF EXISTS teacher;
CREATE TABLE `teacher` (
 `tid` int(3) DEFAULT NULL,
 `tname` varchar(20) DEFAULT NULL,
 `tcid` int(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DROP TABLE IF EXISTS teacher_contact;
CREATE TABLE `teacher_contact` (
 `tcid` int(3) DEFAULT NULL,
 `phone` varchar(200) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `course` VALUES ('1', 'mysql', '1');
INSERT INTO `course` VALUES ('2', 'jvm', '1');
INSERT INTO `course` VALUES ('3', 'juc', '2');
INSERT INTO `course` VALUES ('4', 'spring', '3');

INSERT INTO `teacher` VALUES ('1', 'qingshan', '1');
INSERT INTO `teacher` VALUES ('2', 'jack', '2');
INSERT INTO `teacher` VALUES ('3', 'mic', '3');

INSERT INTO `teacher_contact` VALUES ('1', '13688888888');
INSERT INTO `teacher_contact` VALUES ('2', '18166669999');
INSERT INTO `teacher_contact` VALUES ('3', '17722225555');

5.1    id

  •   查询mysql课程的老师手机号
-- 查询mysql课程的老师手机号
explain select tc.phone from teacher_contact tc where tcid = 
    (select tcid from teacher t where t.tid = 
				(select c.tid from course c where c.cname = 'mysql')
		);

返回结果如下:

id 值不同,先查询 id 值大的(先大后小)。先查询id=3,再查询id=2,然后id=1

  • 查询课程id为2,或者联系表id为3的老师
-- 查询课程id为2,或者联系表id为3的老师		
EXPLAIN	
select t.tname,c.cname,tc.phone 
from teacher t , course c , teacher_contact tc
where t.tid = c.tid and t.tcid = tc.tcid and (c.cid=2 or tc.tcid=3);

结果如下: 

id 值相同时,表的查询顺序是从上往下顺序执行。例如,这次查询的id都是1(说明子查询被优化器转换成了连接查询),查询的顺序是 teacher t(3条)----> course c (4条)----> teacher_course tc (3条)。

在连接查询中,先查询的表叫驱动表,后查询的表叫被驱动表,我们肯定要把小表放前面查询,因为它的中间结果最少。这就是小表驱动大表

如果ID 既有相同 又有不同,那么 ID 不同的先大后小,ID 相同的从上往下

5.2  select type

 select 查询类型有如下几种:

SIMPLE  : 简单查询

PRIMARY:主查询,最外面的那层查询。查询中若包含任何的子部分,最外层查询为PRIMARY,也就是最后加载的就是PRIMARY 。即下图中的 teacher_contact 表

SUBQUERY:内层查询(也叫子查询)。即下图中的 teacher,coures 表

DERVIED:派生的查询,子查询位于FROM子句。表示得到最终结果之前会用到临时表。select  course 把两个查询结果 union 起来。得到一个中间的结果。这个是放在临时表中。

UNION :表示用到了union查询(union会用到临时表) 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED。 UNION ALL 不需要去重,所以不需要用临时表

UNION RESULT :从UNION表获取结果的SELECT。主要显示哪些表之间存在 UNION查询,<union2,3> 表示id为2,3的查询存在 UNION

5.3  type

type 所显示的是查询使用了哪种类型

system > const > eq_ref > ref > range > index > all。以上访问类型除了all都能使用到索引

const : 主键索引或唯一索引一次查询,精准匹配,只找到唯一的一条数据

system:一次查询,精准匹配,但是查询的是系统表。

eq_ref :通常出现在多表 join 查询,被驱动表通过唯一性索引(UNIQUE 或 PRIMARY KEY)进行访问,此时被驱动表的访问方式是 eq_ref。 如下图所示

ALTER TABLE teacher_contact ADD PRIMARY KEY(tcid); -- 创建主键索引

 以上三种: system , const , eq_ref  基本上很难优化到这个状态。

ref :  当查询使用到 非唯一索引时, 就是 ref

ALTER TABLE teacher ADD INDEX idx_tcid (tcid);-- 在 teacher 表创建普通索引

range :  对索引进行范围查询。如果where后面是  between  , and  或   <  ,  >  ,  <=   ,   >=  ,    in  这些,type类型就是 range。如下图

index: 全名叫 full index scan 。就是扫描了全部的索引数据,没有用到过滤条件。 如下图:

All :就是全表扫描

  • 小结:
  • 一般来说,需要保证查询至少到 range 级别,最好能到  ref
  • All 和 index 都是需要优化的

5.4  possible_keys    key

possible_keys    : 可能用到的索引

key : 最终查询用到的索引。

一般要注意的是 key 列是空的,要注意了,就是没有用到索引。有一种情况  possible_keys   是空的,但是 key 有用到索引,那就是查询了联合索引里的非第一个字段。

key_len :使用的索引长度

ref  : 筛选数据的参考

rows : mysql 预估需要扫描的行数,行数越少越好。

filtered  : 过滤的数据。这个字段表示存储引擎返回的数据在 Server 层过滤后,剩下多少满足查询的记录数量的比例,它是一个百分比。如果比率很低,说明存储引擎返回的数据需要经过大量的过滤,只有少部分满足,这个是需要消耗性能的,需要关注。

Extra

  • Using index  :   覆盖索引。查询的列刚好在所用的索引列里面,不需要回表。
  • Using where:   存储引擎层查询出来的数据不全是Server层需要的,到Server层使用where条件过滤。(和用不用到索引没关系)
  • Using Index Condition (ICP):   取数据是在存储引擎层的,过滤数据是在Server层的。当mysql发现在Server过滤数据太大了,就把过滤数据的过程下推到存储引擎层。这个就叫做索引条件下推。
  • Using filesort:    不能直接用索引排序。当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。数据较少时从内存排序,否则从磁盘排序。Using filesort 需要优化
  • Using temporary:   临时表,查询的时候出现了中间的结果。在查询时候,去重、排序之类的工作,可能会用到临时表。举例:
    • DISTINCT  非索引列
    • group by 非索引列
    • 使用 join 的时候,group 任意列
    • Using temporary 需要优化,例如创建复合索引

对于优化有重要参考的字段是:

 type , possible_keys    key   ,  Extra 

Mysql 官方服务器上也有关于优化语句的文章:MySQL :: MySQL 8.0 Reference Manual :: 8.2.1 Optimizing SELECT Statements

让我么你的sql语句 cost更小

数据库自动诊断优化工具

6. 表结构的优化

字段的定义

  • 原则:使用可以正确存储数据的最小数据类型。为每一列选择合适的字段类型。

6.1 整数类型

int 有8种类型,性别可以使用 tinyint , emun 也是用的 tinyint .

6.2 字符类型

变长情况下, varchar 更节省空间。固定长度用 char

6.3  大文件存储

使用专用的文件存储服务器。

6.4 表拆分或字段冗余

将不常用的字段拆分出去,避免列数过多或数据量多大。

字段拆分:比如有张商户信息表,字段越来越多,后来进行拆分:基础信息表,附件信息表,联系人信息表,结算信息表

字段冗余:比如合同表,存有客户ID,每次查询都要关联客户表,通过客户ID查询出客户姓名,如果直接把客户姓名放到合同表里,就不用了每次去关联表,这样也可以提升性能

从哪些维度来优化数据库?从上往下,代价越来越高

其它的可以优化的:

       比如 服务降级,优化

       数据库,分区, 分库

       预售,分流

  • Mysql 一些优化

1.使用limit对查询结果的记录进行限定

2.避免select *,将需要查找的字段列出来

3.使用连接(join)来代替子查询

4.拆分大的delete或insert语句

5.可通过开启慢查询日志来找出较慢的SQL

6.不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边

7.sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库

8.OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内

9.不用函数和触发器,在应用程序实现

10.避免%xxx式查询

11.少用JOIN

12.使用同类型进行比较,比如用'123'和'123'比,123和123比

13.尽量避免在WHERE子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描

14.对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5

15.列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大

  • 此次Mysql 体系架构

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值