《MySQL索引》学习笔记
- MySQL的体系结构
- 存储引擎简介
- InnoDB简介
- MyISAM简介
- 索引
- 索引结构
- B+Tree
- Hash索引
- 思考
- 索引分类
- 索引语法
- SQL性能分析
- 索引使用
- 最左前缀法则
- 索引失效的情况
- 范围查询
- 索引列运算
- 字符串不加引号
- 模糊查询
- or连接的条件
- 数据分布影响
- SQL提示
- 覆盖索引
- 前缀索引
- 单列索引与联合索引
- 索引设计原则
- SQL优化
- 插入优化
- 主键优化
- oder by优化
- group by优化
- limit优化
- count优化
- update优化
MySQL的体系结构
- 连接层:
包含客户端的连接器。如java用于连接MySQL服务的JDBC - 服务层:
该层接收客户端的连接,完成连接的处理。如验证登录用户、密码;设置连接条件等控制MySQL连接的限制操作 - 引擎层:
MySQL提供了很多可选择的存储引擎,用于控制数据的存储和提取的方式。索引是在存储引擎层实现的,不同的存储引擎,索引的实现也是不一样的。 - 存储层:
数据的存放位置
存储引擎简介
存储引擎是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可以被称为表类型。
建表时默认的存储引擎是 I n n o D B InnoDB InnoDB
InnoDB简介
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎。
特点:
- DML操作遵循ACID模型,支持事务
ACID模型:
数据库管理系统中保证食物处理安全的一组特性:
- 原子性
- 一致性
- 隔离性
- 持久性
- 行级锁,提高并发访问性能
- 支持外键FOREIGN KEY约束,保证数据的完整性和正确性
xxx.ibd文件:表空间文件
innoDB引擎的每张表都会对应这样的一个表空间,存储该表的表结构(frm、sdi)、数据和索引
InnoDB的逻辑存储结构:
- 表空间
- 段
- 区
- 页
- 行
MyISAM简介
MyISAM是MySQL早期的默认存储引擎
特点:
- 不支持事务,不支持外键
- 支持表锁、不支持行锁
- 访问速度快
若应用是以读操作和插入操作为主,只有很少的更新和删除操作,并对事务的完整性、并发性要求不高,可以选择MyISAM
索引
索引(index)是帮助MySQL高效获取数据的数据结构(有序)。
索引结构
B+Tree
-
二叉树缺点:
顺序插入时,会形成一个链表,查询性能大大降低
【改进:红黑树】
-
红黑树:
本质也是一个二叉树,在大数据量的情况下,层级比较深,检索速度慢
【改进:B-Tree】 -
B-Tree(多路平衡查找树)
-
B+Tree
非叶子节点起到索引的作用,叶子节点存储数据。
叶子节点会存储成一个单向链表,指向下一个叶子节点。
MySQL索引数据结构对B+Tree进行了优化。在B+Tree的基础上,添加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能
Hash索引
哈希索引采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中
特点:
- 哈希索引只能用于对等比较(=,in),不支持范围查询(between、>、<)
- 无法利用索引完成排序操作
- 查询效率高,通常只需要一次检索
思考
为什么InnoDB存储引擎使用B+Tree索引结构?
- 相比二叉树,层级更少,搜索效率高
- 对于B-Tree,叶子节点和非叶子节点,都会保存数据,导致一页中存储的键值减少,指针跟着减少,要保存大量数据,只能增加树的高度,导致性能降低
- 相对Hash索引,B+Tree支持范围查询
索引分类
在InnoDB存储引擎中,根据索引的存储形式,可分为以下两种:
聚集索引选取规则:
- 若存在主键,主键索引就是聚集索引
- 如果不存在主键,将使用第一个唯一索引作为聚集索引
- 如果没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
回表查询:
先走二级索引找到对应的行id,再到聚焦索引中查询对应的行数据。
索引语法
-
创建索引
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(index_col_name,……);
默认创建的是常规索引,一个索引可以关联多个字段 -
查看索引
SHOW INDEX FROM table_name;
-
删除索引
DROP INDEX index_name ON table_name;
SQL性能分析
-
SQL执行频率
通过以下指令可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:
SHOW [GLOBAL|SESSION] STATUS LIKE 'Com_'
- gloabal:全居的信息
- session:当前会话
-
慢查询日志:
慢查询日志记录了所有执行时间超过指定参数(long_query_time,默认10s)的所有SQL语句的日志。
需要再MySQL的配置文件中配置以下信息:
-
profile详情
show profile
能够再做SQL优化时帮助我们了解事件都耗到哪里去了。
通过have profiling
参数,能够看到MySQL是否支持profile操作
相关操作:
-
explain执行计划
EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序
EXPLAIN执行计划各字段含义:
-
id:
-
select_type:
-
type:
- NULL:不查询任何表
- SYSTEM:查系统表
- REF:使用唯一性的索引进行查询
- ALL:全表扫描
-
Key:
实际使用的索引,如果为NULL,则没有使用索引
索引使用
最左前缀法则
如果索引了多列(联合索引),要遵循最左前缀法则。
最左前缀法则:查询从索引的最右列开始,且不跳过索引中的列。
如果跳跃某一列,索引将部分失效(后面的索引都失效)
索引失效的情况
范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效
业务查询允许的情况下,使用>=、<=
索引列运算
不要再索引列上进行运算操作,索引将失效
字符串不加引号
字符串类型字段使用时,不加引号,索引将失效
模糊查询
如果仅仅是尾部模糊查询,索引不会失效,如果是头部模糊查询,索引会失效
or连接的条件
用or分割开的条件,如果or前的条件中有列索引,而后面的列中没有索引,那么涉及的索引都不会被用到。只有前后都有才会有效
数据分布影响
如果MySQL评估使用索引比全表扫描还慢,则不使用索引
SQL提示
SQL提示是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的
如存在联合索引和单列索引时,可人为指定走哪个索引
覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经能够全部找到),减少select *
不满足覆盖索引会进行回表查询
。5
前缀索引
当字段类型为字符串(vchar、text)时,有时候需要索引很长的字符串,这会让索引变得很长,索引变得很大。查询时浪费大量得磁盘IO,影响查询效率。
此时可以只将字符串得一部分前缀,建立索引,这样可以大大节约索引空间,提高索引效率
CREATE INDEX id_xxx ON table_name(column(n));
- 前缀长度:
可根据索引得选择性来决定,选择性是指不重复得索引值(基数)和数据库表的记录总数的比值。索引选择性越高则查询效率越高。
单列索引与联合索引
多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询
索引设计原则
- 针对数据量较大、且查询比较频繁的表建立索引
- 针对常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
- 如果时字符串类型的字段,字段长度较长,可针对字段特点建立前缀索引
- 尽量使用联合索引,减少单列索引。查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引数量,不是越多越好。索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
- 如果索引列不能存储空值,在创建表时就使用NOT NULL约束它。当优化器指定每列是否包含空值时,它能更好地确认哪个索引更有效地用于查询
SQL优化
插入优化
-
批量插入
-
手动提交事务
避免单独提交,重复的开启事务操作 -
主键顺序插入
- 主键乱序插入会出现页分裂
如突然到来一个中建值50,这时候会开启一个new page,将第一页的一般数据搬到该new page上,并将50插入到new page上
- 主键乱序插入会出现页分裂
页合并:
主键优化
- 满足业务需求的情况下,尽量降低主键的长度
- 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键
- 尽量不要使用UUID做主键或者其他自然主键,如身份证号
- 业务操作时,避免对主键的修改
oder by优化
尽量优化成Using index
实例:
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
- 尽量使用覆盖索引
- 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)
- 如果不可避免的出现filesort,大量数据排序时,可以适当增大排序缓冲区大小sort_buffer_size
group by优化
limit优化
count优化
count()是一个聚合函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加,返回累计值
效率排序:
count(字段)<count(主键id)<count(1)≈count(*)
,尽量使用count(*)
update优化
当update student set no=‘2222’ where id = 1;
- 若id存在索引,那么更改后,会给id=1这一行上行锁,在事务结束之前,其他事务对其他行操作不会发生阻塞冲突
- 若id不存在索引,那么更改之后,会给整个表上表锁,在事务结束之前,其他事务不能对该表进行操作
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁