谈谈MySql优化的一些事

谈谈MySql优化的一些事

前言概述:

MySql优化一直以来都是一个热议的话题,不管是在面试的过程中,还是企业中真实开发的场景下,可以说这已经是一名成熟程序员的必备之器了!所以今天我们一起总结一下关于MySql优化方向的一些常规手段和方案。

​ 凭借着出色的性能、低廉的成本、丰富的资源,MySql已经成为绝大多数互联网公司的首选关系型数据库。可以看到Google,Facebook,Twitter,百度,新浪,腾讯,淘宝,网易,久游等绝大多数互联网公司数据库都是用的MySQL数据库,甚至将其作为核心应用的数据库系统。虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”、“SQL语句优化”、“了解数据库原理”等要求。我们知道一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之重。

我们将这里进行一个较为全面的分析,让大家了解到MySQL的性能到底与哪些地方有关,以便于让大家寻找出其性能问题的根本原因,而尽可能清楚的知道该如何去优化自己的数据库。

​在我们正式介绍优化之前还是要重点声明一个前提,那就是**一切优化工作都是建立在实际业务场景之上的,没有业务就谈优化就是耍流氓!**这里说明一下,无论多么好的优化方案都是针对相对业务设计的,脱离了业务对MySql优化高谈阔论的话那就是纸上谈兵。所以我们今天就会选取 电商项目中的订单模块的为例子来介绍整个优化思路。下面并以章节的形式来逐步探讨!

一、 优化的维度

​ 首先,我们一起来看一下MySql优化的维度,这一部分决定了我们后面优化方案的取舍,所以值得关注!以下图为例 MySql数据库的优化大概可以考虑4个人维度,分别是 SQL及索引优化数据库表结构优化系统配置硬件优化。这几个维度不难理解 重点在于 如何选择!我们通常会选择 性价比高的方案,由下图所示,这个倒金字塔最上方的应该就是性价比最高的方案,因为 优化效果突出而且成本较低!所以由此可见 我们本次分享也会符合这个思路,重点推出 SQL及索引优化 和 数据库表结构方面的优化![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
在这里插入图片描述

**成本:**需要特别注意一下,优化对整个系统造成的影响的大小决定了这件事的成本的大小,影响越大成本越高!而不是用金钱来衡量这个成本的

二、优化工具和监控Sql的手段的介绍

​ 上一章节我们讲到优化维度,而经过分析重点优化方向就是 SQL及索引优化,那么问题来了,当一个系统已经上线,而我们也无法保证一次性就能写出最佳方案的SQL以及索引的应用。所以经过对项目的使用过程中对SQL的监控就变得尤为关键了,也是我们将来进行优化的重要依据!那么如何来对MySql 中的Sql执行进行监控呢?接下来我们就来接触几个概念:

**慢查询:**一种MySql的自带机制,可以监控MySql中Sql语句执行的具体情况 (日志的形式体现,后面会详解…)

**mysqldumpslow:**分析慢查询日志的工具,其中提供了大量的命令参数,可以让我们对 MySql 的慢查询日志进行归类分析,得到我们最想关注的SQL语句!

explain: 按照上面的思路我们已经可以分析出需要进行优化的Sql语句,那么究竟接下来要怎么优化,此时就要用到explain看查看SQL执行的查询计划,只有清晰的掌握了SQL的整个查询计划 那我们才能对症下药!

小结:

根据以上的介绍我们大概可以得出一个针对 查询优化的思路和流程

  • 利用MySQL的慢查询功能检测MySql执行的情况,并根据自己的业务情况定义慢查询的标准。
  • 利用mysqldumpslow工具对慢查询日志进行精准分析,获取需要优化的SQL语句
  • 然后再利用 explain关键字查看这些执行慢的SQL 的查询计划,从而制定合适的优化策略

三、查询优化

​ 上面一小节我们分析了如何进行优化分析的流程,接下来就一起说一下

查询优化的操作,在实际开发当中要掌握一个规律 通常都是 查多写少,所以查询优化也是我们本次文章分析的一个重点。

​ 首先来了解一下MySql 中一个查询的执行流程是如何,如下图:

1.MySql的查询流程:

​ ① 客户端将查询发送到服务器;

② 服务器检查查询缓存,如果找到了,就从缓存中返回结果,否则进行下一步。

③ 服务器解析,预处理。

④ 查询优化器优化查询

⑤ 生成执行计划,执行引擎调用存储引擎API执行查询

⑥服务器将结果发送回客户端。

在这里插入图片描述

查询缓存
在解析一个查询语句之前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据,如果命中缓存直接从缓存中拿到结果并返回给客户端。这种情况下,查询不会被解析,不用生成执行计划,不会被执行。

语法解析和预处理器
MySQL通过关键字将SQL语句进行解析,并生成一棵对应的“解析树”。MySQL解析器将使用MySQL语法规则验证和解析查询。

查询优化器
语法书被校验合法后由优化器转成查询计划,一条语句可以有很多种执行方式,最后返回相同的结果。优化器的作用就是找到这其中最好的执行计划。

查询执行引擎
在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询执行引擎则根据这个执行计划来完成整个查询。最常使用的也是比较最多的引擎是MyISAM引擎和InnoDB引擎。mysql5.5开始的默认存储引擎已经变更为innodb了。

了解查询流程后,我们就详细的来进行优化操作!

3.1 设置MySql的慢查询规则

**准备业务场景:**以电商项目场景的 订单表为例子!

创建一个新的数据库 --> 执行dianshang.sql --> 执行 order_init.sql

注意: 这里我们模拟了 百万级别的数据,如果数据量不够达不到优化的效 果!

1.1 在Dos命令窗口下操作

  • 登录MySql

  • 查看Mysql的慢查询日志功能是否开启

​ show variables like ‘%slow_query_log%’;

在这里插入图片描述

  • 开启慢查询功能:

    set global slow_query_log = 1/ON

  • 自定义慢查询时间规则

    set long_query_time =1 (单位是 秒)

注意: 通过命令进行设置可能马上不会生效,如果要永久生效,最好在 my.ini 文件中修改 my.cnf 需要从起MySql服务

  • 执行Sql语句 --> 查询统计订单详情表

​ select count(*)from tb_order_detail;

  • 此时打开MySql服务的Data目录

    在这里插入图片描述

    此文件就是慢查询日志文件,打开文件后会发现记录了查询执行超过1秒的SQL语句!

    在这里插入图片描述

OK!到此完成了慢查询的设置!!!

3.2 慢查询日志文件的分析工具 mysqldumpslow

刚才我们成功的记录了慢查询日志,接下来就要对日志进行详细分析!

注意:

mysqldumpslow 工具依赖 Perl环境!

Linux环境下:安装完MySql服务 自带 mysqldumpslow ,因为Linux 环境

自带Perl环境 。

Window环境下:需要咱们自己先安装 Perl环境

1.对慢查询日志经行分析

根据查询时间进行排序:

mysqldumpslow.pl -s t C:/SKY-20180503LQT-slow.log

2.将分析得到的Sql语句记录出来

**小结:**到此我们也就完成了对慢查询日志的分析以及SQL语句的筛选工作

3.3 Explain 分析Sql的执行计划

Explain 分析Sql语句

EXPLAIN select * from tb_order where user_id = 1212741731695472640

在这里插入图片描述

id:不重要,它就是当前记录的一个序号 忽略不计

  • select_type: 表示当前执行的Sql的查询类型

    • SIMPLE: 简单的Sql (只有一个Select)
    • PRIMARY: 子查询中最外层的查询
    • SUB QUERY: 子查询(第二个或者后面的查询)
    • UNION: 查询语句中使用了UNION关键字(合并查询)
  • table : 当前要查询操作的表

  • type: 查询性能级别的体现

    const > eq_ref > ref > ref_or_null > range > index > ALL

    • ALL: 全表扫描,查询的时候没有用到任何优化策略(没有使用任何索引)
    • index: 全索引扫描
    • range: 在索引上进行范围查找
    • ref_or_null : 在索引上进行null值的判断
    • ref : 在索引上进行 等值 查找
    • eq_ref: 索引上只有一个可选项
    • const: 常量查询,通常是主键 唯一索引 经过MySql查询优化器处理后的结果!
  • possible_keys: 查询过程中可能会使用的索引

  • key: 实际查询中用到的索引

  • key_len: 索引的字段的值数据类型的长度

  • ref: 使用索引的等值查询,会显示const

  • **rows: ** 执行查询预计要扫描的行数,这个不精准 仅供参考

  • filtered: 查询的数据 预计占总行数的百分比,这个值不准仅供参考

**小结:**到此我们就可以清晰的查看本次SQL的执行计划,然后对其进行适当 的优化,那么如何优化呢?针对查询优化 索引 是很重要的一个手段,所以下一小结重点就是介绍索引优化…

3.4 索引优化

**前言概述:**索引的使用时MySql查询优化的重要手段,下面我们就详细的介 绍索引优化!

3.4.1 首先给条件字段user_id创建索引

– ALTER TABLE tb_order add INDEX user_idx(user_id);

3.4.2 再通过Explain来看Sql的执行计划 如下:

在这里插入图片描述

​ 发现查询计划中已经用到了索引!

3.4.3 索引的底层数据结构是什么呀,有很多人都会问这个问题,我们一起来 看一下? 如下图所示 :
  • BTree

在这里插入图片描述

  • B-Tree --> 聚集型索引
    在这里插入图片描述

  • B+Tree非聚集型索引

在这里插入图片描述

**小结:**这里介绍索引底层的数据结构,通过图例分析了它的原理

3.4.4 使用索引的基本必备常识
  • 常见的索引类型

普通索引:只能提供查询加速的功能

主键索引:唯一且非空

唯一索引: 要求数据的唯一性,NULL除外

组合索引: 多个字段创建联合索引

hash索引: 全内存查找,和Memory 存储引擎配合的很好

fullText: 全文索引

  • 创建索引的原则

    1.数据的辨识度不能低于70% (辨识度:字段内的数据唯一值的个数)

    ​ age 100条数据–>理想状态 100种情况 > 70%

    ​ sex 100条数据 --> 理想状态 2中情况 < 70%

​ 2.经常作为where 条件的字段适合加索引或者组合索引

3.经常作为多表连接的字段适合加索引

4.经常作为排序字段出现适合加索引
  • 索引的失效场景

    1.不要使用 != / < > /not in / is null/ in not null

    2.不要在索引字段上进行运算 where id + 1 = 100;

    3.不要在字符串列上使用隐式的类型转换 phone = 18811011011,但是反 过来可以

场景一: varchar 类型的字段 phone (字符串)–>给这个字段加了索引

​ where phone = ‘18811011011’; (不失效)

​ where phone = 18811011011; (失效 --> 因为传的参数是 数值 类 型而字段本身是字符串类型,在做等值比较的时候MySql会进行 隐式类型 转换)

​ **场景二:**int 类型字段 phone (数值)—> 不管有没有隐式转换 索引都不 失效

4.使用like关键字不遵循左前缀匹配规则 phone like ‘%000’

5.当使用组合索引的时候 切记 要遵循左前匹配规则,如果不遵循索引就失效啦

​ – 组合索引创建

​ ALTER TABLE tb_order add INDEX user_idx(user_id,role_id);

​ where user_id = 1 and role_id = 2 (不失效)

​ where role_id = 1 and user_id = 2(不失效)

​ where user_id = 1 (不失效 --> 遵循了左前匹配规则)

​ where role_id = 2 (失效 --> 违反了左前匹配规则)

3.5 优化实战-以订单表为例

**前言:**有了以上的知识储备,下面我们就对 订单表 设置两个业务场景进行优化操作!

**场景1:**快年底了,公司要决策来年的战略部署,需要参考很多报表数据!

需求1:统计9月份订单总金额

– 场景:快年底了,公司要决策来年的战略部署,需要参考很多报表数据!

– 需求:统计9月份订单总金额
– Sql:
select sum(total_fee) from tb_order
where pay_time >= ‘2019-09-01 00:00:00’
and pay_time < ‘2019-10-01 00:00:00’
– 执行时间 1.6秒

– 优化1:给pay_time 加索引
create index pay_time_idx on tb_order(pay_time)

– 执行结果 8秒(效率不升反降)
– 问题:在使用多字段查询数据的时候建议使用 组合索引
– 创建组合索引
create index paytime_totalfee_idx on tb_order(pay_time, total_fee)

**场景2:**快年底了,公司要决策来年的战略部署,需要参考很多报表数据!

需求2:统计9月份订单总金额

– 场景2:快年底了,公司要决策来年的战略部署,需要参考很多报表数据!
– 需求:统计9月份销量最好的商品

select sku_id, count(1) cnt from tb_order_detail
where create_time
BETWEEN ‘2019-09-01 00:00:00’
AND ‘2019-10-01 00:00:00’
GROUP BY sku_id
ORDER BY cnt
LIMIT 1

– 执行时间 3.1 秒(需要优化)
– 解决方案:创建组合索引

create
index createtime_skuid_idx
on tb_order_detail(create_time, sku_id)

– 优化结果 0.2秒

总结:以上就本作者对MySql的查询优化中的重点索引优化的一些拙见和精力很高兴和大家分享!

四、数据库结构优化

**前言:**上面是针对查询索引方面的优化,当然MySql优化方案远不止如此,本小节就来聊聊数据库结构方面的优化策略

1.优化表结构

– 尽量将表字段定义为NOT NULL约束

– 定义字段的时候能用数值类型就不要用字符串

– VARCHAR的长度只分配真正需要的空间

– 尽量使用TIMESTAMP而非DATETIME,但TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的范围小得多,而且TIMESTAMP的值因时区不同而不同。

– 单表不要有太多字段,建议在20以内

– 合理的加入冗余字段可以提高查询速度。(建议在冗余字段上加 - 索引)

2.读写分离

**备注:**Mysql中的读写分离和Redis中的一样的!

在这里插入图片描述

3.表拆分
3.1垂直拆分

场景: 在实际开发当中,考虑一个表字段过多或者对字段的值的大小进行区分,通常都会进行垂直拆分。例如 spu 和 spu_detail

在这里插入图片描述

注意事项:

– 插入的时候使用事务,也可以保证两表的数据一致。

3.2 水平拆分

在这里插入图片描述

问题:水平拆分后 当查询的时候如何准确定位 查那张表?

  1. 1.切分键原理拆表
    在这里插入图片描述

2.基因提取法

场景: 注册的时候利用手机号注册,后期可能会出现根据手机号查询用户,那将来根据手机号查询的时候应该查询那一张表?

– 在生成数据的时候就利用到 基因提取法 方案 生成用户id,这样一来 在查询的时候,还是利用取模的方式 就能准确定位要查那一张表!

在这里插入图片描述

4.MySql数据库集群

场景:考虑到高并发和高可用 我们可以采取数据库集群的解决方案!

​ 如下图所示:

在这里插入图片描述

五、存储优化

**前言:**如何把控在存储方面的优化思路,前提得了解MySql中的两大常用存储引擎!

1.在Mysql中查看存储引擎的方式:

show ENGINES;

在这里插入图片描述

2.Innodb和MyISAM的区别
  • – MyISAM是非事务安全型的,而InnoDB是事务安全型的。

  • – MyISAM锁的粒度是表级,而InnoDB支持行级锁定。

  • – MyISAM不支持外键,而InnoDB支持外键

  • – MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用 MyISAM。

  • – InnoDB表比MyISAM表更安全。

  • – MyISAM对聚合查询支持很好,如果频繁的统计计算的需求建议使用MyISAM

3.实际开发中两种存储引擎的取舍!

Innodb一般用于核心业务系统,而且要求支持事务的场景下,MyISAM一般用于服务辅助核心业务系统的边缘化系统,类似于报表统计系统!

在这里插入图片描述

4.存数据的时候需要考虑的优化策略
  • 禁用索引

禁用-- alter table 表名 disable keys;

开启-- alter table 表名 enable keys;

  • 禁用唯一性检查

禁用-- set unique_checks = 0;

开启-- set unique_checks =1;

  • 禁用外键检查

禁用-- set foreign_key_checks = 0;

开启-- set foreign_key_checks = 1;

  • 禁止自动提交

禁用:SET autocommit = 0;

开启:SET autocommit = 1;

  • 批量插入数据

insert into 表名 values (…),(…),(…)

综上所述:提升Insert 效率其实在插入数据的时候尽量少维护其他东西

六、缓存优化

备注: MySql的查询缓存5.7.20版本之后不建议使用MySql的查询,并且在MySql8.0中已经删除了查询缓存功能!

原因: 表中只要有一条数据发生变更,整张表的查询缓存就都失效了!

**注意:**本小节内容读者们只需要了解即可,因为上面说的很清楚,MySql缓存这一块已经逐步在削弱甚至舍弃,以下内容也是大概介绍一些缓存的配置参数,望大家理解!

1.全局缓存 --> 针对MySql整体而言
  • key_buffer_size

    只能在MyIASM中使用,读取索引文件存储

  • innodb_buffer_pool_size

​ 设置MySql分页信息的缓存的大小
在这里插入图片描述

  • innodb_additional_mem_pool_size

    用来设置存储MySql内部数据的结构缓存区域,如果表很对,建议调大一下!

  • innodb_log_buffer_size

​ 设置Innodb日志文件的缓冲区大小

在这里插入图片描述

2.局部缓存 --> 针对MySql的表
  • read_buffer_size

    对数据库表进行顺序扫描,读取的缓冲区,如果经常做顺序扫描建议调 大 一点!

  • sort_buffer_size

​ 排序的缓冲区,如果查询中 排序的需求很对,就把这个调大点

  • read_rnd_buffer_size

​ 随机读取的缓冲区,如果需要排序大量数据,可适当调高该值。

  • tmp_table_size

    临时表缓冲区,表连接查询的时候会出现临时表,如果频繁操作表链接查 询的时候,建议调大点!

  • record_buffer

    线程对表进行扫描的时候,为这个线程开辟的缓冲区,如果经常做顺序扫描,建议调大点!

3.其他缓存
  • table_cache

    表的高速缓存,当访问一个表的时候,MySql会为这个表开辟一个高速缓存, 如果缓存已满,MySql会 热点数据缓存机制,会将那些不经常用的表的缓存清除掉

  • thread_cache_size

    可以理解为Mysql 数据库的线程池当连接断开时,Mysql 会保留处理连接的线程信息,当再次有信的请求过来时,会复用 对数据库表进行顺序扫描,读取的缓冲区,如果经常做顺序扫描建议调 大 一点!

  • sort_buffer_size

​ 排序的缓冲区,如果查询中 排序的需求很对,就把这个调大点

  • read_rnd_buffer_size

​ 随机读取的缓冲区,如果需要排序大量数据,可适当调高该值。

  • tmp_table_size

    临时表缓冲区,表连接查询的时候会出现临时表,如果频繁操作表链接查 询的时候,建议调大点!

  • record_buffer

    线程对表进行扫描的时候,为这个线程开辟的缓冲区,如果经常做顺序扫描,建议调大点!

3.其他缓存
  • table_cache

    表的高速缓存,当访问一个表的时候,MySql会为这个表开辟一个高速缓存, 如果缓存已满,MySql会 热点数据缓存机制,会将那些不经常用的表的缓存清除掉

  • thread_cache_size

    可以理解为Mysql 数据库的线程池当连接断开时,Mysql 会保留处理连接的线程信息,当再次有信的请求过来时,会复用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值