MySQL使用总结以及MySQL性能优化,史上最详细的教程来啦~

Posted 緈諨の約錠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL使用总结以及MySQL性能优化,史上最详细的教程来啦~相关的知识,希望对你有一定的参考价值。

文章目录

1 前言

mysql到目前为止,算是互联网使用比较广泛的一种持久化数据库,笔者这里对该数据库的使用以及性能优化,做一些总结,方便查漏补缺。

2 MySQL安装

想操作MySQL数据库,那就要先安装数据库环境,不会安装的小伙伴,可以参考我的另外一篇博文:Linux下安装MySQL详细教程

3 JDBC编程

使用MySQL数据库,那么除了使用Hibernate、Mybatis等框架可以操作数据库外,还需要了解下如何使用原生JDBC的实现,因为这些框架底层,还是对JDBC编程进行了封装。

具体的可以参考我的另一篇博文:史上最全的Java JDBC编程详细教程

4 数据库流行度排行榜

可参考这里: https://db-engines.com/en/ranking
在这里插入图片描述

5 常用的关系型数据库

数据库名称描述
MySQL开源免费的关系型数据库,已经被Oracle收购了,MySQL 5.5版本之后都是由Oracle发布的版本
Oracle收费的大型数据库,Oracle公司的产品,Oracle收购SUN公司,SUN公司收购了MySQL
SQL ServerMicroSoft 公司收费的中型的数据库,C#、.net等语言常使用
PostgreSQL免费开源的功能完善的关系数据库管理系统(ORDBMS),支持多种平台Linux,FreeBSD,OS X,Solaris和Windows等
DB2IBM公司的数据库产品,收费的,常应用在银行系统中,在国内互联网公司,要求去IOE(IBM小型机、Oracle数据库、EMC存储设备)使用很少
SQLite嵌入式的小型数据库,应用在手机端、嵌入式设备中
SyBase已退出历史舞台,提供了一个非常专业的数据建模工具PowerDesigner

6 MySQL数据库版本变化

官网: https://www.mysql.com

MySQL常见版本:

版本发布年份备注
5.0.x2005MyISAM默认存储引擎,存储过程、服务端游标、触发器、查询优化
5.1.x2008崩溃恢复、Bug修复
5.5.x2010从该版本开始,是oracle公司发布的; 默认存储引擎更改为InnoDB
5.6.x2013一些优化
5.7.x2015查询性能提升
8.0.x2018性能大幅提升

所有版本的下载地址:https://downloads.mysql.com/archives/community/

7 MySQL体系结构

整体结构图如下:
在这里插入图片描述

  • Client Connectors
    接入方,支持多种协议
  • Serveices & Utilities
    系统服务和管理工具,例如:备份恢复,mysql复制集群等
  • Connection Pool
    连接池:管理缓冲用户连接、用户名、密码、权限校验、线程处理等
  • SQL Interface
    SQL接口:接受用户的SQL命令,并返回用户需要查询的结果,比如select from就是调用SQL Interface
  • Parser
    解析器,SQL命令传递到解析器时会被解析器验证和解析,解析器是由Lex和YACC实现的
  • Optimizer
    查询优化器,SQL语句在查询之前会使用查询优化器对查询进行优化
  • Caches
    查询缓存,如果查询缓存有命中的查询结果,查询语句就可以直接从查询缓存中获取数据
  • Pluggable Storage Engines
    插件式存储引擎,存储引擎是MySQL中具体与文件打交道的子系统,也是MySQL最具特色的一个组件,MySQL的存储引擎是插件式的,支持多种存储引擎
  • File System
    文件系统,数据、日志(redo,undo)、二进制日志、索引、错误日志、查询记录、慢查询等

8 MySQL执行机理图

在这里插入图片描述
每个步骤含义如下:

1、MySQL客户端/服务端通信
2、查询缓存
3、查询语句语法解析和预处理
4、查询优化处理
5、执行计划及查询执行引擎
6、返回结果给客户端

8.1 MySQL客户端/服务端通信

MySQL客户端与服务端的通信方式是“半双工

全双工: 双向通信,发送同时也可以接收
半双工: 双向通信,同时只能接收或者是发送,无法同时做既发送又接收的操作
单工: 只能单一方向传送

MySQL是半双工通信:

即在任何一个时刻,要么是客户端向服务端发送数据,要么是服务器向客户端发送数据,这两个动作不能同时发生

特点和限制:

  • 客户端一旦开始发送消息,另一端要接收完整个消息才能响应
  • 客户端一旦开始接收数据没法停下来发送指令
  • 无法将一个消息切成小块进行传输

8.2 查询缓存

实际开发中,基本不会用到MySQL的这个查询缓存,因为现在有好多好用的缓存数据库,比如Redis数据库等。

8.2.1 查询缓存基础介绍

MySQL数据库可以缓存SELECT操作的结果集和SQL语句。当执行新的SELECT语句时,先去查询缓存,判断是否存在可用的结果集,如果存在就使用缓存的结果集。

判断标准:
与缓存的SQL语句是否完全一样,区分大小写,key为查询的sql语句,value为sql语句执行的结果集
eg:
key: select * from xxl_job_log;
value: n条结果集数据

查看是否开启了MySQL缓存,执行以下命令:
show variables like 'query_cache%';
在这里插入图片描述

  • query_cache_type
    该配置显示 on 表示缓存打开了,默认mysql是OFF关闭的。

    如果需要开启查询缓存,可以修改my.cnf配置文件:

    #config mysql cache
    query_cache_type=1
    

    值:0 表示不启用查询缓存,默认值
    值:1 表示启用查询缓存,只要符合查询缓存的要求,客户端的查询语句和结果集都可以缓存起来,供其他客户端使用,不过,在查询时可以加上 SQL_NO_CACHE 将不使用查询缓存。
    eg: select SQL_NO_CACHE * from xxl_job_log;

    值:2 表示按需启用查询缓存,只要查询语句中添加了参数:SQL_CACHE,且符合查询缓存的要求,客户端的查询语句和记录集,则可以缓存起来,供其他客户端使用;
    select SQL_CACHE * from xxl_job_log;

  • query_cache_size
    允许设置query_cache_size的值最小为40K,默认1M,这是单次查询的结果集大小,超过此值,则不会放入缓存

  • query_cache_limit
    限制查询缓存区最大能缓存的总的查询记录集的大小,默认设置为1M,超过了此大小,则淘汰掉之前的缓存

  • 可查看缓存情况
    show status like 'Qcache%';
    在这里插入图片描述
    Qcache_hits: 缓存命中情况;
    Qcache_inserts: 缓存插入了多少次
    缓存失效: 当该表发生任何一点改变,则与该表的所有缓存都会失效

8.2.2 MySQL不会缓存的情况

  • 当查询语句中有一些不确定的数据时,则不会被缓存。如包含函数NOW(),
    CURRENT_DATE()等类似的函数,或者用户自定义的函数,存储函数,用户变
    量等都不会被缓存

  • 当查询的结果大于query_cache_size设置的值时,结果不会被缓存

  • 对于InnoDB引擎来说,当一个语句在事务中修改了某个表,那么在这个事务提交之前,所有与这个表相关的查询都无法缓存,因此长事务执行会降低缓存命中率

  • 查询的表是系统表

  • 查询语句不涉及到表

8.2.3 为什么mysql默认关闭了缓存?

  • 如果是一个多写的应用,需要经常让缓存失效,浪费计算资源

  • 查询需要将结果存入查询缓存,也会带来额外的系统消耗

8.2.4 MySQL查询缓存应用场景有哪些

以读为主的业务,数据生成之后就不常改变的业务场景下,比如字典表数据报表数据等。但是,通常情况下,这些数据会丢到Redis缓存中,使用MySQL查询缓存非常少。

8.3 查询语句语法解析和预处理

MySQL语法解析,将使用MySQL语法规则验证和解析查询,比如是否使用了错误的关键字或者关键字的顺序是否正确等。

通过lex词法分析yacc语法分析将SQL语句解析成解析树(编译原理),预处理则根据一些MySQL规则进一步检查解析树是否合法,比如检查数据表和数据列是否存在等,验证权限是否合法。

8.4 查询优化处理

优化器的主要作用就是找到最优的执行计划,那么,查询优化器如何找到最优的执行计划?

  • 使用等价变化规则
    1 = 1 and a > 1 ,会被MySQL优化改写成 a > 1
    a < b and a = 1 改写成 b > 1 and a = 1

  • 优化count、min、max等函数
    EXPLAIN select min(id) from xxl_job_log;
    min函数只需找索引最左边(基于索引的B+Tree结构)
    在这里插入图片描述
    EXPLAIN select max(id) from xxl_job_log;
    max函数只需找索引最右边(基于索引的B+Tree结构)
    在这里插入图片描述

  • myisam引擎count(*)优化,myisam引擎会单独存储数据的总数

  • 覆盖索引扫描
    在这里插入图片描述

  • 子查询优化
    EXPLAIN select * from (select * from xxl_job_log where id < 10) t;
    在这里插入图片描述

  • 提前终止查询
    用了limit关键字或者使用不存在的条件

  • in的优化
    先进行排序,再采用二分查找的方式

MySQL查询优化器主要是基于成本计算原则,它将尝试各种执行计划,采用数据抽样的方式进行试验,找到一个最优的方案去执行,表的数据量不同,可能执行计划也不同。

我们平时在工作中分析SQL语句执行效率,可以根据执行计划进行分析

8.5 查询执行引擎及执行计划

调用插件式存储引擎的API进行执行计划的执行
eg: EXPLAIN select * from xxl_job_log where id = 40;
在这里插入图片描述

8.5.1 执行计划ID

ID属性是 mysql 对select查询语句中提供的查询序列号,用于表示本次查询
过程中加载表的顺序或者查询子句执行顺序(标识执行的顺序 )。

  • id相同,执行顺序从上往下
  • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  • id有相同的又有不同的(混合式的),即两种情况都存在,id如果相同,可以认为是一组,从上往下顺序执行。并且,在所有组中,id值越大,优先级越高,越先执行
  • id为null的最后执行,比如union查询可能id为null

8.5.2 执行计划select_type

select_type表示当前查询语句中的查询类型,主要是用于区分普通查询联合查询子查询等,主要有以下几种类型:

类型描述
SIMPLE表示当前查询语句是一个简单查询语句,不包含子查询,不包含联合查询,不包含连接查询
PRIMARY如果执行的是一个包含子查询的查询,或者是一个联合查询,Primary指向外部查询语句或者联合查询中的第一个子查询语句
DEPENDENT SUBQUERY表示当前查询语句是一个子查询,并且执行条件依赖与外部查询提供的条件
SUBQUERY表示当前查询是一个子查询,并且这个子查询在执行时不需要得到外部查询的帮助
MATERIALIZED表示where后面有in条件的子查询
UNION若第二个select出现在union之后,则被标记为union
UNION RESULT从union表获取结果的select

示例1:

explain select id from xxl_job_log where id <3 union select id from xxl_job_log where id >= 3;

在这里插入图片描述
示例2:

EXPLAIN
select t2.job_id,t2.trigger_msg,(select max(executor_fail_retry_count) from xxl_job_log t1 where t1.id = t2.id)
from xxl_job_log t2

在这里插入图片描述

示例3:

EXPLAIN SELECT *  FROM xxl_job_log WHERE id = (SELECT id FROM xxl_job_log WHERE id = 40)

在这里插入图片描述

8.5.3 执行计划table

  • 查询涉及到的表,直接显示表名或者表的别名
  • <unionM,N> ID为M和N 查询结果进行union后产生的结果
  • ID为N进行子查询产生的结果
EXPLAIN
select id from xxl_job_log where id >= 100
UNION
select id from xxl_job_log where id < 100;

在这里插入图片描述

8.5.4 执行计划partitions

匹配的分区信息,如果查询基于分区表,将会显示访问的是哪个区。

表分区: 表分区是将一大表,根据条件分割成若干个小表,mysql 5.1开始支持数据表分区,比如:某用户表的记录超过了600万条,那么就可以根据入库日期将表分区,也可以根据所在地将表分区,当然也可根据其他的条件分区。
比如:

partition BY RANGE (uid) (
    partition p0 VALUES LESS THAN (10000),
    partition p1 VALUES LESS THAN (20000)
);

按照这种分区方案,uid小于10000的所有行被保存在分区P0中,uid在10000到20000的保存在P1中,依次类推。

目前的实际开发中,这个用的不是很多了,有成熟的框架,比如MyCat可以分库分表。

8.5.5 执行计划type

访问类型,sql查询优化中一个很重要的指标,结果值从好到坏依次是:

NULL > system > const > eq_ref > ref > range > index > ALL

类型描述
NULL无需访问表或者索引,比如获取一个索引列的最大值或最小值
systemsystem 是 const 的一种特殊情况,既表本身只有一行数据的情况,基本不会出现,可以忽略不计
const当查询最多匹配一行时,常出现于 where 条件是=的情况,表示通过索引一次就找到了,const用于比较primary key或者unique索引,可以说是性能最好的
eq_ref唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键 或 唯一索引扫描,性能次之
ref非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,性能次之
range在一个索引上进行范围查找,只检索给定范围的行,使用一个索引来选择行,性能次之,优化时最坏的情况要达到这个级别
indexFull Index Scan 索引全表扫描,把索引从头到尾扫描一遍。这种级别进行遍历索引树查询,通常发生在查询结果只包含索引字段时
ALLFull Table Scan,扫描全表以找到匹配的行,没有任何索引可以使用时,这是最差的情况,应该避免

8.5.6 执行计划possible_keys

表 示 当 前 查 询 语 句 执行时 可 能 用 到 的 索 引 有 哪 些 , 在
possible_keys 中可能出现多个索引,但是这些索引在本次查询
不一定会使用到。

8.5.7 执行计划key

表示当前查询语句真实使用的索引名称,如果这个字段为 null,则有两中可能:一个是当前表中没有索引二是当前表有索引但是失效了

8.5.8 执行计划key_len

表示当前索引字段存储内容最大长度。这个长度不是精准值,只是 MySQL 估计的值。这个值越大越精准。在能得到相同结果时,这个值越小那么查询速度越快。

如果本次查询使用了索引,则 key_len 内容不为空。

8.5.9 执行计划ref

当使用索引列等值查询时,与索引列进行等值匹配的对象信息

8.5.10 执行计划rows

根据表统计信息或索引选用情况,大致估算出找到所需记录所需要读取的行

8.5.11 执行计划filtered

表示返回结果的行数占需读取行数的百分比,filtered的值越大越好,100是最好的情况。

8.5.12 执行计划Extra

Extra记录了十分重要的额外信息,这些额外信息有:

  • Using filesort:
    mysql对数据使用一个外部的文件内容进行了排序,而不是按照表内的索引进行排序读取

  • Using temporary:
    使用临时表保存中间结果,也就是说mysql在对查询结果排序时使用了临时表,比如在order by 或 group by
    中间MySql处理过程需要多处理一个临时表,一般这种情况是需要优化处理的。
    优化处理一般使用索引优化

  • Using index:
    表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高

  • Using where:
    使用了where过滤条件

  • select tables optimized away:
    基于索引优化MIN/MAX操作或者MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即可完成优化
    注:执行计划在MySQL不同版本,所显示的字段可能不一样

8.6 返回客户端

  • 有需要做缓存的,执行缓存操作
  • 增量返回结果,开始得到第一条结果时就开始往客户端逐步返回数据,这样mysql服务器可以减少内存使用,客户端马上就拿到了数据

9 MySQL性能优化

9.1 影响数据库性能的因素

  • 服务器硬件
    cpu、内存、磁盘io、网卡流量

  • 服务器的操作系统
    Linux的配置参数不同性能不同

  • 数据库存储引擎的选择
    根据需要选择不同的存储引擎,MyISAM 不支持事务,表级锁,InnoDB 事务级存储引擎,完美支持行级锁和事务ACID特性

  • 数据库自身配置参数
    MySQL有上百项的数据库配置参数;(my.cnf)

  • 数据库表结构的设计和SQL语句的执行效率(影响最大的)
    慢查询是性能问题的罪魁祸首,不合理的数据库表结构设计和不合理的索引是影响数据库查询性能的重要因素

  • 数据库架构
    高并发下读写分离、分库分表、多级缓存、搜索引擎

9.2 MySQL如何定位慢查询

9.2.1 定位慢查询主要有如下几种方式

  • 业务驱动,主要是业务及运营人员或者用户反馈给我们的,他们在使用系统的过程中发现某些功能很慢,这种方式一般是项目上线后出现的,开发人员会比较被动,建议不要采用这种方式
  • 测试驱动,系统上线前通过测试人员的反馈,了解到哪些功能比较慢
  • 慢查询日志,通过开启MySQL慢查询日志监控慢查询sql并及时进行优化
  • 实时获取,实时获取存在性能问题的SQL

9.2.2 慢查询日志配置

  • 查看mysql是否开启慢查询日志
    show variables like 'slow_query_log';
    如果没有开启,需要开启,设置慢查询日志为on(开启):
    set global slow_query_log = on
    重启后mysql会失效

  • 设置慢查询日志文件的位置:
    set global slow_query_log_file = ‘/usr/local/mysql-5.7.24/data/3306/slow-sql.log’
    设置查询没有使用索引的情况(开启)

    set global log_queries_not_using_indexes = on
    设置查询时间大于多少秒的数据记录到慢日志中(默认是10秒),如果设置为0,则会有大量的信息记录到日志中,磁盘容易很快被占满;

    set long_query_time=5; (单位秒)
    以上条件是或者的关系,只要满足一个条件的都会把日志记录下来

  • 建议将以上配置写在mysql的配置文件中,直接用命令行配置时,在重启mysql后会失效,所以使用配置方式

    #config slow query
    slow_query_log = on
    slow_query_log_file = '/usr/local/mysql-5.7.24/data/3306/slow-sql.log'
    log_queries_not_using_indexes = on
    long_query_time=5
    

9.2.3 慢查询日志数据解读

Time:日志记录的时间

User@Host:执行的用户及主机

Query_time:查询耗费时间,Lock_time 锁表时间,Rows_sent 发送给请求方的记录条数,Rows_examined 语句扫描的记录条数

SET timestamp:语句执行的时间点

select …: 执行的具体语句

最主要看:# Query_time: 0.454651 Lock_time: 0.000095 Rows_sent: 5 Rows_examined: 620016

9.2.4 慢查询日志分析工具

查看帮助命令: mysqldumpslow --help

示例:
mysqldumpslow -t 10 -s at /usr/local/mysql-5.7.24/data/3306/slow-sql.log

9.3 B+Tree联合索引底层数据结构

  • 单列索引
    是指由一个字段(单个列)组成的索引
  • 联合索引
    联合索引又叫复合索引,是指由多个字段(字段是有顺序的)组成的索引,比如:
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `phone` varchar(25) NOT NULL,
  `password` varchar(64) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `account` varchar(15) DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_name_phone` (`name`,`phone`,`create_time`) USING BTREE --联合索引
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

单列索引是特殊的联合索引,是联合索引字段列个数为1的特例。联合索引叶子节点没有存储数据,节省磁盘空间,其数据结构如下:
在这里插入图片描述

  • 索引键index(col1, col2, col3)如何排序?
    先根据第一个字段col1排序,然后根据第二个字段col2排序,如果前两个一样,则根据第三个字段col3排序。
    B+Tree索引适用于全键值匹配查询,键值范围匹配查询,键值前缀匹配查询。
    其中键值前缀查询只适用于根据键值的最左前缀查询。

9.4 联合索引的最左前缀原理

9.4.1 准备测试表

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `phone` varchar(25) NOT NULL,
  `password` varchar(64) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_name_phone` (`name`,`phone`,`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

9.4.2 全值匹配(全列匹配、全键值匹配)

联合索引中的每一个索引字段都是where查询的条件;

EXPLAIN select * from users where name ='zhangsan' and phone = '18266668888';
-- 注意:理论上索引对顺序是敏感的,但是由于MySQL的查询优化器会自动调整where子句的条件顺序以使用适合的索引,例如我们将where中的条件顺序颠倒,也用到了索引,效果一样。
EXPLAIN select * from users where phone = '18266668888' and name ='zhangsan';

EXPLAIN select * from users where phone = '18266668888' and name in('zhangsan');
EXPLAIN select * from users where phone in ('18266668888') and name in ('zhangsan');

-- 上面4个都可以命中索引,最后这两个sql,or导致索引失效
EXPLAIN select * from users where name = 'zhangsan' or phone = '18266668888';
EXPLAIN select * from users where phone in ('18266668888') or name in ('zhangsan');

当按照索引中所有列进行精确匹配(这里精确匹配指“=”或“in”匹配,每一列都能精确匹配)时,索引可以被用到。

9.4.3 最左前缀匹配

这种情况指的是,只使用联合索引的第一列进行匹配,如果使用索引的第二列或者其他列,而不是用第一列查询,无法命中索引,例如:

EXPLAIN select * from users where name = 'zhangsan';
EXPLAIN select * from users where name = 'zhangsan' and date(create_time) = '2021-06-28';
-- 上面两条sql使用了最左列字段,可以命中索引,而下面这一条,只使用了第二列,无法命中索引
EXPLAIN select * from users where phone = '18266668888';

9.4.4 查询条件中没有索引第一列

EXPLAIN select * from users where phone = '18266668888' and date(create_time) = '2021-06-28';

由于不满足最左前缀匹配原则,这样的查询显然不能命中索引。

9.4.5 查询条件使用索引列的精确匹配,中间某个条件未提供

EXPLAIN select * from users where name = 'zhangsan' and date(create_time) = '2021-06-28';

这种也是可以命中索引,满足最左前缀匹配原则。

9.4.6 匹配列前缀字符串

这种情况指的是只匹配一个列值的开头部分,比如name=’张三210628’,采用like过滤匹配 zhangsan21%,下面这条sql命中了索引:

EXPLAIN select * from users where name like '张三21%'; 

在这里插入图片描述

注:
1、这里匹配越精确,效率也越高,如果只匹配 zhangsan%,表中有很多这样的数据,那么,mysql查询优化器可能会进行全表扫描。

2、索引一定不是越多越好,一定是创建合适的索引,一张表一般不超过6个。另外,匹配列前缀可用到索引,像like '9999%'可以用到索引,而like ‘%9999%’、like '%9999’用不到索引,比如下面这条sql,就无法命中索引:

EXPLAIN select * from users where name like '%张三'; 
如果查询中有某个列的范围查询,则右边所有列都无法使用索引查找,比如 where name = 'zhangsan' and phone like '1826666%' and date(create_time) = '2021-06-28',该查询只能使用索引的前两列,优化的话,如果like的结果较少,可以用in。
EXPLAIN select * from users where name = 'zhangsan' and phone like '1826666%' and date(create_time) = '2021-06-28'
假设 phone 只有几个甚至十几个,18266661111、18266662222等等,可优化为下面的写法:(全键值匹配的查询,更精准的定位,读取的数据更少,效率更好)
EXPLAIN select * from users where name = 'zhangsan' and phone in(18266661111,18266662222) and date(create_time) = '2021-06-28'

9.4.7 匹配范围值

匹配索引键的第一列范围值(必须是最左前缀),只能命中第一列的范围,范围列后面的列无法用到索引,范围包括 >、<、!=、in、is null、between … and 等。

EXPLAIN select * from users where `name` in ('张三', 'zhangsan21');
EXPLAIN select * from users where phone in ('18266668888', '18266661111');

9.4.8 精确匹配某一列并且范围匹配另一列

EXPLAIN select * from users where name = '张三' and phone in ('18266668888', '18266661111');

第一列精确匹配,第二列范围匹配,可以命中索引。

9.4.9 查询条件中有函数或表达式

如果查询条件中有函数或表达式,则MySQL不会为该列使用索引。

explain select * from users where id = id - 2;
explain select * from users where left(name, 5) = 'zhangsan';

9.4.10 只访问索引列的查询

只查询有索引的字段,这种查询叫“覆盖索引”查询,在索引键中就找到了数据,不需要获取数据行,也就是不需要回表中获取数据,性能得到提高,所以我们对于值需要查询一两个字段的业务,就不要采用select * from,直接使用具体字段查询:

select name, phone, create_time from users where phone = '18266668888';

9.4.11 排序分组查询

B+Tree索引是有序的数据结构,所以除了按索引键查询外,还可以按索引键进行order by排序。

9.4.12 分页limit查询

如果数据量比较大,可以使用以下sql进行查询优化:

SELECT a.* FROM users where name like 'zhangsan21%' LIMIT 1000000,15;
SELECT a.* FROM users a,(select id from users where name like 'zhangsan21%' LIMIT 3000000,15) b where a.id = b.id

9.5 索引离散性

9.5.1 面试题-索引越多越好?

既然索引可以加快查询速度,那么是不是只要是查询语句条件都建上索引?
答案是否定的,索引虽然加快了查询速度,但索引也有代价:

索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,MySQL在运行时也要消耗资源维护索引,因此索引并不是越多越好。

表记录比较少,例如一两千条甚至只有几百条记录的表,没必要建索引,让查询做全表扫描即可,一般记录在2000条以上可以酌情考虑建立索引。

9.5.2 列的离散性

公式: count(distinct col)/ count(col) 比值越大离散性越好,离散性越好则选择性越好。

索引的离散性较低不建议建索引,离散性较低也就是选择性较低(B+Tree要走很多个分叉去找数据),所谓索引的选择性(Selectivity),是指不重复的索引值(也叫基数,Cardinality)与表记录数(#T)的比值:Index Selectivity = Cardinality / #T

选择性的取值范围为(0, 1],选择性越高的索引价值越大,这是由B+Tree的性质决定的。
eg:

SELECT count(DISTINCT(name))/count(*) AS selectivity FROM users;

如果列的离散性很差,一般创建索引的价值也不大,可以采用联合索引提高列的离散性。

SELECT count(DISTINCT(concat(name, phone)))/count(*) AS selectivity FROM users;
SELECT count(DISTINCT(concat(name, right(phone,4))))/count(*) AS selectivity FROM users;

我们把这个前缀索引建上:

ALTER TABLE `users` ADD INDEX `idx_name_phone` (name, phone(4));

使用前缀索引兼顾了索引大小和查询速度,但其缺点是不能用于ORDER BY和GROUP BY操作,也不能用于覆盖索引Covering index(即当索引本身包含查询所需全部数据时,不再回表访问数据文件本身)。

10 MySQL存储引擎

10.1 基础介绍

MySQL有多种存储引擎,存储引擎特征:

  • 插拔式的存储引擎
  • 存储引擎指定在表之上,即一个库中的每一张表都可以指定专门的存储引擎,如果不指定,则MySQL会采用默认的存储引擎
  • 不管表采用何种存储引擎,都会在数据区产生一个对应的frm文件(表结构定义描述文件)

使用命令,可以查看存储引擎有哪些:show engines
在这里插入图片描述
常见的主要有以下两种。

10.2 MyISAM存储引擎

MyISAM是MySQL 5.5版本之前的默认存储引擎,MySQL中很多系统表也还是使用该存储引擎,系统临时表也会用到MyISAM存储引擎。

特点:
a)select count(*) from table 无需进行数据的扫描
b)数据(MYD)和索引(MYI)分开存储
eg:news.frm-定义表结构文件 news.MYD-数据文件、news.MYT-索引文件
c)表级锁
d)不支持事务

10.3 InnoDB存储引擎

InnoDB是MySQL 5.5及以后版本的默认存储引擎,主要有以下几个特性:

  • 支持事务ACID特性
  • 支持行级锁
  • 聚集索引(主键索引)方式进行数据存储
    底层使用的是b+tree索引, 数据文件和索引文件在一起存储
    eg: user.idb(索引和数据文件),users.frm-表的定义文件
  • 支持外键关系保证数据完整性(基本上不用)

10.4 如何设置表的存储引擎?

CREATE TABLE `customer` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `phone` varchar(25) NOT NULL,
  `password` varchar(64) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`谷歌开源史上最详《kotlin入门进阶实战》,学不动也要学

史上更全的MySQL高性能优化实战总结!

史上最详Android版kotlin协程入门进阶实战

Ubuntu零基础教学-史上最详Ubuntu20.04安装教程,超级详细,强烈建议收藏!

Ubuntu零基础教学-史上最详Ubuntu20.04安装教程,超级详细,强烈建议收藏!

(4000 字,38 图)也许可能是史上最详尽的 MySQL 分库分表文章!