周五的早高峰, 各地软件园地铁站里中出现了不少穿着长袖加绒格子衫, 背双肩电脑包的年轻码农, 现在节气正值 [ 小雪 ] , 11月的全国性突然降温 , 让经历过996摧残的猿们一出地铁站就冻的打了个激灵 , 很庆幸的告诉大家距离放年假还剩不到 37 个工作日, 要买火车票的赶紧预约抢, 要租男朋友的赶紧联系我.
前几天我, 张大胖 , Mason 下班约好一起去吃海X捞火锅, 大家去了都围到到一张小火锅桌子上, 服务员把菜单递给我们.
这时张大胖拿着菜单就开口了, “今天不要点绿色的菜, 我最近手里的股票全部绿油油的 太护眼了, 先来个 牛油麻辣锅底! 来3份 草原牛肉片, 来1份牛肉丸子, 来3份牛楠, 剩下的你们点吧”.
我吸了口气说 “你这点全是肉, 合着哥几个今天陪你来长膘了” .
Mason 看我俩要掐起来 赶紧接上: “要不来一份西兰花, 一份花菜, 这样看着火锅汤色也不错~”.
张大胖皱了下眉毛, 叹了口气 “我前段时间去面试, 被面试官连环追问, Mysql数据库优化 一路追问到 B+树索引底层 ,我恨不得当场GG, 今天你又点树, 想想就头痛 ~” .
Mason 笑了笑说 "这个Mysql B+索引, 你每天都在用但是不知道它原理, 面试官估计心里在犯嘀咕, 这个人其它方面都合格, 但是就怕 ‘新同事来了,数据库变慢了’ 正确的使用并理解数据库索引就是最好的优化反之"
张大胖听的一头雾水然后问 到底怎么表述数据库优化给面试官才能抱的Offer归呢 ?
Mason 喝了口水说道 "那今天我们就边打边炉边聊聊数据库优化, 我以前专门整理过数据库优化相关的知识"
我急忙打断说 "要不先上菜吧, 中午就吃了碗热干面, 我现在太饿了"
SQL执行计划
MySQL 对于千万级的大表要怎么优化?
先来看看 这两种树形数据结构模拟自增ID索引场景 Ps: 大家有空也可以玩玩 数据可视化
左 : 二叉查找树, 右 : 平衡二叉树
平衡二叉树概述
假设用来做索引:
综上所述: 这两种经典树形数据结构都不是最理想的数据库索引树, 当当当 !!!
大名鼎鼎的 B+树就横空出世了
B树和B+树的插入、删除图文详解 - nullzx - 博客园
B+ 树的特点
B+ 树在 MySQL 的应用
综上所述: 矮胖的B + 树 恰好弥补了 瘦高平衡二叉树的两点不足. 层级少, IO预加载数据多.
那在MySQL数据库中是如何使用 B+ Tree 构建自己的索引呢?
InnoDB是一种兼顾了高可靠性和高性能的通用存储引擎。在MySQL 5.7中,InnoDB是默认的MySQL存储引擎。除非您配置了其他默认存储引擎,否则发出CREATE TABLE不带ENGINE= 子句的语句将创建一个InnoDB表。
select * from test_innodb where name = 'to%'
InnoDB 的缺点
物理层面 (一张 test_innodb表默认有 2个存储文件组成)
使用InnoDB表时的最佳做法 。
创建的表占用的空间很小。表级锁定限制了读/写工作负载中的性能,因此它通常用于Web和数据仓库配置中的只读或只读工作负载中。
所有数据值均以低字节开头存储。这使数据机和操作系统独立。二进制可移植性的唯一要求是机器使用二进制补码带符号整数和IEEE浮点格式。这些要求已在主流机器中广泛使用。二进制兼容性可能不适用于有时具有特殊处理器的嵌入式系统。
先存储低字节数据没有明显的速度损失;表行中的字节通常是未对齐的,按顺序读取未对齐的字节所需的处理要多于反向的顺序。而且,与其他代码相比,服务器中获取列值的代码不是时间紧迫的。
所有数字键值都先存储高字节,以实现更好的索引压缩。
在支持大文件的文件系统和操作系统上,支持大文件。
MyISAM 的缺点
物理层面 (一张 test_myisam 表默认有 3个存储文件组成)
hash:虽然可以快速定位,但是没有顺序,IO复杂度高。
二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。
平衡二叉树:树的高度随着数据量增加而增加,IO代价高。
B-Tree: 常见的 B+树 为三层, 每个节点磁盘存储默认大小 16KB, 索引节点存有多个索引和多个指针(一个索引为 bigint类型约 8KB ,一个指针约 6 KB, 最多可存 1170个索引指针组合). 一次加载一个索引(16KB)节点有效利于IO资源, 读取当前索引任意叶子节点都只需 3次 IO操作.
select * from test_innodb where name = '%to%'
先问是不是再问为什么, 我们先建一张表测试一波, 全模糊匹配是否使用索引 !
-- auto-generated definition
create table test_fuzzytext_index
(
id int auto_increment primary key,
context text not null,
context_fulltext text not null,
context_index varchar(100) not null
);
给 test_fuzzytext_index 的 文本类型的一些字段 加上索引形成和不加索引的对照组 !
-- 0. context 字段不添加索引
-- 1. context_index 字段 添加INDEX(普通索引)
ALTER TABLE test_fuzzytext_index ADD INDEX index_name (context_index);
-- 2. context_fulltext 字段 添加FULLTEXT(全文索引)
ALTER TABLE test_fuzzytext_index ADD FULLTEXT (context_fulltext);
最终表结构
Explain - Type 指标
访问类型,SQL查询优化中一个重要指标,查询性能从好到坏依次是
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,好的SQL查询至少达到range级别,最好能达到ref。
index与ALL区别:index类型只遍历索引树,索引文件通常比数据文件小(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)。
Explain - Extra 指标
预先随机插入一些文本数据到表中. 使用查询语句.
explain select * from test_fuzzytext_index tfi where tfi.context like '%索引%'
explain select * from test_fuzzytext_index tfi where tfi.context like '索引%'
分析结果:
%索引% type = ALL Extra = Using where , 结果集 50+ 耗时平均 130ms
索引% type = ALL Extra = Using where , 结果集 50+ 耗时平均 130ms
综上所述: 没有索引的字段默认全表扫描
explain select * from test_fuzzytext_index tfi where tfi.context_index like '%索引%'
select * from test_fuzzytext_index tfi where tfi.context_index like 索引%';
分析结果:
%索引% type = ALL Extra = Using where , 结果集 50+ 耗时平均 130ms
索引% type = renge Extra = Using where , 结果集 50+ 耗时平均 125ms
综上所述: %匹配词% 全模糊 没有使用索引, 右模糊type 为 renge ,
select * from test_fuzzytext_index where MATCH(context_fulltext) AGAINST ('+索引' in boolean mode);
分析结果:
使用了全文索引后 type = fulltext Extra = Using where; Ft_hints: no_ranking, 结果集 50+ 耗时平均 59ms
综上所述: 全文检索type为 fulltext, 注意目前 MySQL 全文索引只支持根据空格分词, 意思是 中文分词要提前用空格分词存入, 仅适合数据量小的场景. 随着数据增加检索速度也会和普通索引拉开差距.
select * from test_fuzzytext_index where MATCH(context_fulltext) AGAINST ('+索引' in boolean mode);
总结一下 %代表一个或多个字符的通配符, %关键字% 场景不使用索引, 只会进行全表扫描. 如果有搜索检索文章内容的需求, 可以使用 fulltext 索引 满足大多数 搜索场景. MySQL 5.7 支持.
那为什么 %关键字% 不使用索引呢?
在where条件后对索引字段加了函数转换或者运算逻辑**(+、-、*、/、!、<>、%、like’%_’(%放在前面)、or、in (疑问、可能存在成本问题)、exist等)**的处理,比如对时间戳字段进行日期格式化函数都会引起索引失效。
被优化器分析后, 发现走索引还不如不走索引, 效率更高.
索引节点: 一个索引为 bigint类型约 8 KB ,一个指针约 6 KB, 最多可存 1170个索引指针组合
数据节点: 大约 16KB , 索引节点不存储数据 , Mysql 一页 为 16 KB.
MySQL B+ Tree 三层树的最大行数为 : 1170 * 1170 * 16 = 21,902,400
+ Mysql 数据库索引的常用种类有几种,每种场景是什么 ?
主要有两种, 一种是 InnoDB 索引, 一种是 替代索引方案.
InnoDB 是 Mysql 5.7 的默认索引, 支持现代数据库理念的一切操作, 比如 事务, 行级锁, 数据库可恢复性好,默认顺序索引等…
替代索引方案中, 在一定的特定场景可用, 比如 MyISAM 可以在大量查询场景使用. CSV 可以用来做 数据分析, 使用场景有限.
使用多列索引并根据最左匹配原则, 保证表结构设计阶段主表与关联表之间的关联字段的数据类型、数据长度、字段的编码格式以及字段的排序规则需要保持一致 .
在Mysql建立多列索引(联合索引)有最左前缀的原则,即最左优先。
如果我们建立了一个2列的联合索引(col1,col2),实际上已经建立了两个联合索引(col1)、(col1,col2);
如果有一个3列索引(col1,col2,col3),实际上已经建立了三个联合索引(col1)、(col1,col2)、(col1,col2,col3)。
1、b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+树是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道第一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。
2、比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。(这种情况无法用到联合索引)
当一张表的查询方式比较固定,这时候可以尝试创建多列索引,查询时应当遵从组合索引的规则,最左原则,查询时使用最频繁的一列放在最左边,
例:index(user_id,user_name,user_type)这是一个组合索引,当查询时如果想走索引则
sql:select * from userInfo where user_id='001' and user_name='小张' and user_type='1';
-- 这个时候是走了索引的,但是
select * from userInfo where user_name='小张' and user_type='1';
-- 这时user_id没有在where条件内将不走索引;
-- 此例,user_id字段必须出现在where后面,不然索引将不会生效。
看完不妨问问自己如何回答这些问题, 也可写上在评论区留言回答, 温故知新 …
深入浅出分享 Java 干货 , 找回对代码的 Passion , 助力月入 20K+