[沫忘录]MySQL索引
索引概述
- 优点
- 提高数据检索效率,降低数据库IO成本
- 通过索引对数据进行排序,降低数据排序成本,降低CPU消耗
- 缺点
- 索引会占用一定空间
- 当更新数据时,也需更新索引数据,这会降低数据的更新效率
索引结构
B+Tree索引
- 最常见的索引类型,大部分引擎都支持B+树索引。只在叶子节点储存数据,且相邻叶子节点间有双向指针能够有效支持范围查询。
Hash索引
- 底层数据结构用hash表实现的,只有精确匹配索引列的查询才有效(即对等比较(=,in)),不支持范围查询(between, >, <, …)和利用索引完成排序操作。
除此之外还有R-tree(空间索引), **Full-text(全文索引)**这些不常见的索引结构。
大多数索引结构默认由B+树组织。
索引分类
根据索引的功能索引可分为以下种类:
而根据索引的储存形式,也可分为以下类别:
聚集索引的选取规则(顺序):
- 聚集索引
- 唯一索引
- InnoDB自动生成rowid作为隐藏的聚集索引
回表:通过二级索引查找到主键,再通过聚集索引找到对应数据。
索引语法
#显示指定表的索引
SHOW INDEX FROM 表名
#为字段创造索引
CREATE INDEX 索引名 ON 表名(字段名)
#为字段创造唯一索引
CREATE UNIQUE INDEX 索引名 ON 表名(字段名)
#为字段创造联合
CREATE INDEX 索引名 ON 表名(字段1, 字段2, ...)
tips:
- 在业务场景中,如果存在多个查询条件,考虑对于查询字段建立索引时,建议建立联合索引而非单列索引。
- 多条件联查时,MySQL优化器会评估哪个字段的索引效率更高,选择该索引完成本次查询。
高效使用索引
SQL执行频率
#通过 show [session | global] status 命令可以查询mysql服务器的状态信息。
#而通过以下指令可以看到数据库INSERT, UPDATE, DELETE等语句的执行频次
SHOW GLOBAL STATUS LIKE 'Com_______'#七个占位符
慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time, 单位:sec, 默认10 sec)的所有SQL语句的日志。
#查询慢查询日志的是否开启
SHOW VARIABLES LIKE 'slow_query_log';
mysql的慢查询日志默认没有开启, 需要在MySQL的配置文件(/etc/my.cnf)中配置信息
#开启MySQL慢查询日志开关
show_query_log = 1
#设置慢查询日志的时间(SQL语句执行时间超过2秒就会被视为慢查询,自动记录慢查询日志)
long_query_time = 2
profile优化
show profiles
能够在做SQL优化时帮助我们了解耗费时间的分配
通过以下指令能够看到当前MySQL是否支持profile操作(默认关闭)。
SELECT @@have_profiling#设置开启
SET [GLOBAL | SESSION] profiling = 1;#查看指定query_id(show profiles查看)的SQL语句各个阶段的耗时情况
show profile for query query_id;#查看指定query_id的SQL语句的CPU使用情况
show profile cpu for query query_id;
explain执行计划
EXPLAIN或者DESC命令获取如何执行SELECT语句的信息,包括SELECT语句执行过程中如何连接和连接顺序。
#语法:直接SQL语句加EXPLAIN或DESC
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件
索引的使用规则
最左前缀法则
在通过联合索引进行查询时,如果不通过联合索引的执行顺序进行查询(即按创建联合索引时,字段的书写顺序)进行查询,则未按照执行顺序查询的字段则会失效。
#字段1,字段2, 字段3创建的联合索引EXPLAIN SELECT * FROM 表1 WHERE 字段1 = value, 字段3 = value;
#只有字段1有效EXPLAIN SELECT * FROM 表1 WHERE 字段3 = value...
#联合索引无效,进行全文扫描
索引失效情况
- 在索引列上进行运算操作(使用函数等操作),导致索引失效。
- 字符串不加单引号,引起隐式类型转换,导致索引失效。
- 使用头部模糊匹配(如
%abc
), 导致索引失效。 - 使用or并列的条件,如果前者条件的字段没有索引,则之后条件字段的索引都失效。
- 使用索引比全表扫描还慢(索引扫描的数据占数据的大部分),则索引失效。
索引提示(指定索引)
- use index(建议使用该索引)
- ingnore index(忽略使用该索引)
- force index(强制使用该索引)
SELECT * FROM 表1 [USE INDEX | IGNORE INDEX | FORCE INDEX](索引名) WHERE ...
索引的使用
-
尽量使用覆盖索引(避免回表查询)
查询使用了某索引,需要返回的字段列在该索引中全部能找到。
- use index: 该索引(多为主键或唯一)所携带的字段覆盖了所需的查询的字段(高效)且由于字段唯一,不需要做筛选。
- use where: 加载了数据到内存(使用二级索引后,进行了回表),此时数据在server层,需要进行where条件的筛选。
- use where; use index: 由于索引携带的字段覆盖了索引,不需要回表。但仍需要在server层做where条件的筛选。
- use index condition: 对use where的优化,在回表之前使用二级索引对where条件筛选。减少了回表时加载到内存的数据量。
前缀索引
当建立索引的字段是字符串时,我们可能会遇到很长的字符串。如果使用大量很长的字符串做索引,在查询时会浪费大量磁盘I/O。这很影响查询效率。此时,我们们不妨取字符串的部分前缀来建立索引,这样的做法能够大幅节约索引空间,从而提高索引效率。
CREATE INDEX 索引名 ON 表名(字段名(前缀长度n));
当前缀越短,索引值越容易重复。所以我们应尽量保证前缀短的同时减少建立索引的前缀重复。
我们可以通过以下SQL语句查看索引的选择性(不重复的索引数与索引总数之比)
#查看一般索引的选择性
SELECT COUNT(DISTINCT 字段名)/COUNT(*) FROM 表名;
#查看前缀索引的选择性
SELECT COUNT(DISTINCT substring(字段, 1, n)) / COUNT(*) FROM 表名;