笔者实习小白,公司让我优化一下数据库,这几天温习了一下数据库的相关知识并且查阅了一些资料,准备过几天把高性能MySQL看一下。
MYSQL优化的主要方式之一:增加索引
一、索引:
1.定义:
是一种数据结构。(完整定义:数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据的基础上实现高级查找算法,这种数据结构就是索引)
2.分类:
BTREE索引(B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接。)在B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。因此,B+树索引被广泛应用于数据库、文件系统等场景。
哈希索引:只需要做等值比较查询,而不包含排序或范围查询的需求,适合使用哈希索引。比如对于身份证号的匹配。哈希索引定义:哈希索引基于哈希表实现,只有精确匹配索引的所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码,哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
3.索引优势:不必检索全表,提高检索效率,(使用类似二分查找的方式)降低数据库的IO成本;降低数据排序的成本,降低了CPU的消耗。
4.索引劣势:索引也是占空间的;虽然提高了查询速度,但是会降低表的更新速度,在表更新时,也会对索引更新;
5.需要索引的情况:
主键自动建立唯一索引;
频繁查找的字段;
查询中与其他表关联的字段,通过外键关系建立索引;
频繁更新的字段不要建立索引;
where条件里用不到的不要建立索引;
二、MYSQL优化查询语句EXPLAIN:
MySQL 提供了一个 EXPLAIN 命令, 它可以对 SELECT 语句进行分析, 并输出 SELECT 执行的详细信息, 以供开发人员针对性优化.
EXPLAIN 命令用法为:在 SELECT 语句前加上 EXPLAIN,例如:
EXPLAIN出来的信息有10列,分别是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra,下面对这些字段出现的可能进行解释:
1.id
它是SQL执行的顺序的标识,从大到小的执行
2.select_type
表示查询中每个select子句的类型
(1) SIMPLE(简单SELECT,不使用UNION或子查询等)
(2) PRIMARY(查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)
(3) UNION(UNION中的第二个或后面的SELECT语句)
(4) DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
(5) UNION RESULT(UNION的结果)
(6) SUBQUERY(子查询中的第一个SELECT)
(7) DEPENDENT SUBQUERY(子查询中的第一个SELECT,取决于外面的查询)
(8) DERIVED(派生表的SELECT, FROM子句的子查询)
(9) UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
3.table
显示这一行的数据是关于哪张表的,有时不是真实表名字,subquery表示子查询的表。
4.type
表示MySQL在表中找到所需行的方式,又称“访问类型”。
常用的类型有: ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)
ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
index: Full Index Scan,index与ALL区别为index类型只遍历索引树
range:只检索给定范围的行,使用一个索引来选择行
ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system。
NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
5.possible_keys
指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。
6.Key
key列显示MySQL实际决定使用的键(索引)
如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在在FROM 表名后面添加语句FORCE INDEX (索引名) 或者IGNORE INDEX (索引名)。
7.key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)不损失精确性的情况下,长度越短越好
8.ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
9.rows
表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
10.Extra
该列包含MySQL解决查询的详细信息,有以下几种情况:
Using where:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤。
Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询
Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”
Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
Impossible where:这个值强调了where语句会导致没有符合条件的行。
away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行。
三、实例:
mapper文件夹中xml文件部分sql语句为例:
SELECT
student.REAL_NAME stuName,
teacher.REAL_NAME AS realName,
r.EXPERIENCE_ID AS experienceId,
r.USER_ID AS userId,
r.EXPERIENCE_CONTENT AS experienceContent,
r.ASSESSOR_TYPE AS assessorType,
r.REASION AS reasion,
r.DEPART_COMMENT AS departComment,
r.DEPART_COMMENT_ID AS departCommentId,
r.DEPART_COMMENT_DATE AS departCommentDate,
r.UPDATE_TIME AS updateTime
FROM
tb_stu_teachexperience r
LEFT JOIN t_rygl_user student
ON r.USER_ID = student.USER_ID
AND student.IS_DEL = '0'
LEFT JOIN t_rygl_user teacher
ON r.DEPART_COMMENT_ID = teacher.USER_ID
AND teacher.IS_DEL = '0'
WHERE
r.IS_DEL = '0'
AND r.ASSESSOR_TYPE IN (4,8)
/*(此处可以针对tb_stu_teachexperience表的IS_DEL字段
和ASSESSOR_TYPE字段增加复合索引,提高检索速度)*/
AND r.USER_ID IN (
SELECT
rc.STUDENT_ID
FROM
t_rygl_choicestuandteach rc
WHERE
rc.TEACH_ID = #{userId}
AND rc. STATUS = 2
AND rc.IS_DEL = 0
/*(此处可以针对tb_stu_teachexperience表的
STATUS字段和IS_DEL字段增加复合索引,提高检索速度)*/
)
AND r.ASSESSOR_TYPE = #{assessorType}
AND student.REAL_NAME LIKE CONCAT('%',#{stuName,jdbcType=VARCHAR},'%')
/*(此处为全模糊搜索,如果需要保留按照姓氏和按照名字都能搜索的功能,可以考虑
将t_rygl_user表中的REAL_NAME字段的长度减少,原长度为50,可以减少到15)*/
ORDER BY r.EXPERIENCE_ID DESC
/*(order by之后的字段也可以建立索引,但是此处字段为主键,所以无需优化)*/
未优化查询信息:
增加索引界面:
优化后查询信息:
可以看到,结果信息中的type由原来的存在两个all类型(全表扫描),变成了现在的Ref类型;最后一行中rows(需要检索的行数)由原来的405变为389;