索引
索引相关SQL
索引创建 : create , alter , 建表时创建
# create
# 普通索引
CREATE INDEX indexName ON tableName (columnName(length) [ASC|DESC]);# INDEX 关键字可以 改成 KEY
# 唯一索引的创建
CREATE UNIQUE INDEX indexName ON tableName (columnName(length));
# 主键索引
# alter
# 普通索引
ALTER TABLE tableName ADD INDEX indexName(columnName(length) [ASC|DESC]);
# 唯一索引
ALTER TABLE tableName ADD UNIQUE INDEX indexName(columnName);
# 主键索引
ALTER TABLE tableName ADD PRIMARY KEY indexName(columnName);
# 建表时创建
CREATE TABLE tableName(
columnName1 INT(8) NOT NULL,
columnName2 ....,
.....,
INDEX [indexName] (columnName(length)) # 普通索引
UNIQUE INDEX [indexName] (columnName(length)) # 唯一索引
PRIMARY KEY [indexName] (columnName(length)) # 主键索引
);
# 主键和唯一索引还可以这样定义
CREATE TABLE users (
id INT PRIMARY KEY, -- 主键约束
username VARCHAR(50) UNIQUE, -- 列级唯一约束
email VARCHAR(100), -- 普通列
);
查询索引 : show
SHOW INDEX FROM tableName;
删除索引 drop
DROP INDEX indexName ON tableName;
强制SELECT使用索引 FORCE INDEX(index_name)
SELECT * FROM table_name FORCE INDEX(index_name) WHERE .....;
索引类型
按照数据结构划分
- B+Tree 类型 : 最常用,innodb默认
- Hash 类型
- R-Tree 类型
- T-Tree 类型
字段数量
- 单列索引:唯一索引,主键索引,普通单列索引(通过key,index创建)
- 多列索引:通常称为组合索引
功能划分
- 唯一索引
- 主键索引
- 普通索引
- 全文索引:用于文章检索
- 空间索引
存储方式划分
-
聚簇索引:索引的叶子节点直接就是存储的具体数据,索引数据和表数据是存放在同一个文件(
.ibd)内。查询时,先获取所有索引,当沿着树结构查询到叶子节点就得到了对应的数据。查看索引时,key_name为PRIMARY说明就是聚簇索引
聚簇索引:的索引值最好是自增,避免页分裂 页分裂是指当一个数据页(Page)无法容纳新插入的数据时,数据库系统将该页拆分成两个页的过程。
InnoDB 只支持主键索引作为聚簇索引,一般默认主键索引作为聚簇索引,没有主键选择首个非空的唯一索引作为主键,都没有默认创建一个隐藏的字段作为主键索引,一个表只有一个聚簇索引。
-
非聚簇索引:索引节点存放的是聚簇索引字段的值,使用非聚簇索引,在需要其他字段的情况下,会通过聚簇索引查询具体数据。
如何选择索引?
索引的好处
- 数据越大,查询效果越明显
- 分组和排序,速度更快
- 连表查询速度快
- 范围查询速度快(B+)
- 速度快,数据库整体吞吐量更高
索引的缺点
- 额外的磁盘空间
- 影响增删改的性能,尤其是增加
选择何种索引?
聚簇索引
聚簇索引也就是主键索引
特点
- Inodb中和数据是存放在一起的
- 索引的叶子节点直接指向数据位置
聚簇索引建议使用自增id作为主键,非自增id容易引起页分裂降低性能
也容易导致B+树的频繁调整
联合索引
最左匹配原则: 在B+树的进行搜索时,会按照索引字段先后顺序进行匹配搜索
前缀索引
优点:只选n个字符作为索引,节省空间
缺点:无法通过前缀索引完成分组和排序
全文索引 : 规模比较大的项目用ES
基于分词实现:索引文件量很大
修改字段时需要通过自定义的存储过程更新索引
对中文支持不够友好
唯一索引
查询比普通索引效率高:因为找到需要的就停止扫描
插入需要检查完整性
哈希索引
速度比B+树更快:一次hash就能找到数据位置
缺点:执行排序,分组时效率低
永远不会进行排序分组的字段适合hash索引,查询性能会比B+高不少
回表机制
针对非聚簇索引,需要查询索引字段以外的其他字段时,会通过找到的聚簇索引(主键索引)再次进行搜索
- 为什么不能使用*? 网络io角度回答,回表问题回答
索引覆盖机制
查找的字段就在索引中,不回表了
索引下推机制
通过索引获取到数据,会经过索引的数据,再次进行筛选,减少回表次数
where user_name like "竹%" and user_sex="男";
#user_name和user_sex都是索引,user_name在前,所以不会user_sex用不到索引,会通过user_name的索引找到所有数据符合要求的数据,经过索引下推后,在回表时会对user_sex进行过滤,不符合要求的过滤掉,减少回表的记录数量
需要开启,5.6之后默认开启
MMR机制
通过普通索引查询到聚簇索引的id不会拿到一个id就去回表查询,而是放到缓存中,所有id都准备好,再一次性查询结果,从而减少io次数
5.6之后默认开启
索引跳跃式扫描
8.0后才有这个机制,当查询条件出现不符合最左前缀匹配规则时,根据情况补充上对应字段的数据
对那些字段建立索引?
什么时候考虑索引?
- 频繁用于查询到字段
- 频繁分组,排序,范围查询的字段
- 连表字段必须建立索引,最好是主键索引,性能更好
- 索引字段区分度要住够高,索引才有效果,如对性别建立索引没有意义
- 索引字段不应该过长,适当选用前缀索引
- 联合索引:按照优先级排列,避免在使用时过多造成索引失效问题
- 不分组,排序,范围查询的字段:尽量用哈希索引,当然唯一与否也是一个考量是否使用Hash的维度
- 充分利用索引覆盖的:普通索引建议考虑是否能使用联合索引
需要注意的地方
- 频繁增删改查的不适合建立索引
- 重复度过高的字段不适合索引
- 索引数量不是越多越好:3-5个最佳,多了增删改的维护成本会很大
- 联合索引在建立时一定要考虑好最左匹配的问题:也就是优先级
- 数据量小时,维护索引的成本更大
- 无序的字段不建议建立索引,尤其是主键索引
索引失效
- 没有遵循最左匹配原则
- 优化器判断走索引性能会更低
- 使用OR会导致索引失效:or的有一个条件不是索引
- 对范围查询取反,不等查询
- 模糊查询like以%开头:一定会失效 非要用%开头,非要用数据库如何解决呢?对字段进行取反冗余存储,%开头就用取反的字段
- 对索引字段进行运算 : 普通的字符串作为索引时,匹配条件写成和数值比较
where str=123,数据库会优化成where number(str)=123,本质上是对运算索引所以失效了 为什么不优化成where str=string(123)? 因为"00123"在逻辑上和123值是相等的,str=string(123)无法处理这个逻辑上的问题
索引原理
目的:减少io次数
为什么使用b+树?
不用顺序二叉树:极端(全表)情况查询性能差
不用平衡二叉树:调整频繁
不用红黑树:调整不那么频繁,但是一般来说存储红黑树节点采用数组的结构存储,红黑树的层数和io次数成正比,尽管有调整功能,但是数据量少的情况下树的高度还是有些高了
不用b树:b树单节点能存放很多数据,高度比红黑树低很多,但是数据库查询会有范围查询,b树不适合做范围查询
为什么用b+树:高度合适,叶子节点通过链表连接方便做范围查询
千万级别的表B+树层数多高?
innoDB一次IO的单页大小为16KB=>树的单节点最大16KB
树的节点指针大小:6B
以int类型为索引字段:4B
单页(单节点):能存放16KB/10B(约1600)个字段
最佳情况下:高度为3的节点叶子节点能有 1600 *1600*单页存放数据数量
- 假设行数据为1.6k,则
1600*1600*10=2560,0000
大部分情况下,B+树的层数不会超过5,也就是说io次数不会超过5
前缀索引为什么性能好? 单页能存放的索引数量更大
不同存储引擎索引实现方式
MyISAM:没有聚簇索引,查完索引必然有一次额外的io开销,插入数据时只要调整索引数据即可
- zz_myisam_index.frm:该⽂件中存储表的结构信息。
- zz_myisam_index.MYD:该⽂件中存储表的⾏数据。
- zz_myisam_index.MYI:该⽂件中存储表的索引数据。
InnoDB:支持聚簇索引,插入数据时,可能会额外触发聚簇索引的调整
- zz_innodb_index.frm:该⽂件中存储表的结构信息。
- zz_innodb_index.ibd:该⽂件中存储表的⾏数据和索引数据。