MySQL目前提供了以下四种索引
InnoDB引擎 | MyISAM引擎 | Memory引擎 |
---|---|---|
BTREE索引 | 支持 | 支持 |
HASH索引 | 不支持 | 不支持 |
R-tree索引 | 不支持 | 支持 |
Full-text | 5.6版本之后支持 | 支持 |
通常所说的索引,若为特别指出,基本值B+树(多路搜索树,并不一定是二叉树)结构组织的索引。其中聚集索引、复合索引、前缀索引、唯一索引默认都是使用B+tree树索引,统称为索引
Btree又叫多路平衡树,一颗m叉的btree特性如下:
key的数量:[ceil(m/2) - 1]<=n<=m-1。所以2<=n<=4。当n>4时,中间节点分裂到父节点,两边节点分裂
以插入C N G A H E K Q M F W L T Z D P R X Y S数据为例
演变过程:
再插入H,n>4,空间不够,中间元素G字母向上分裂到新的节点(当H插入的时候ACGHN,其中中间节点为G,所以是G向上分裂)
插入Z,Z大于M,走最右边指针,指向NQTW的节点,在这个节点中NQTW插入Z,Z排序后是最大,中间元素为T,但是此时n最大为4,T需要向上分裂,同时NQ和WZ分裂
插入D,与上述插入Z同样的步骤,D是中间元素,向上分裂,AC和EF分裂,而D插入到父节点中还能保持平衡;然后继续插入P、R、X、Y都满足Btree特性,不需要分裂
最后插入S,S大于M小于T走第四个指针,向NPQR中插入,插入后事NPQRS,key个数超过4了,所以中间元素Q向上分裂,同时NP、RS分裂;Q插入到父节点中DGMT,变成DGMQT,超过4,中间元素M向上分裂,同时DG和QT分裂
B+Tree为Btree的变种,区别为:
由于
B+Tree只有叶子节点保存key信息
,查询任何key都要从root走到叶子。所以B+Tree的查询效率更加稳定
MySQL索引数据结构对经典的B+TREE进行了优化。在原有的B+Tree基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能
-- 创建索引的时候不指定索引的类型的时候默认使用的是B+TREE索引
-- 方式一:使用create的方式创建索引
create [UNIQUE|FULL TEXT|SPATIAL] INDEX index_name [USING index_type] on table_name(index_col_name,...);
-- 方式二:使用alter方式创建索引
-- 普通的索引
alter table table_name add index index_name(columnName_list);
-- 唯一索引(索引的列里面的值除了null可以多次出现,其余的值必须唯一)
alter table table_name add unique index index_name(columnName_list);
-- 全文索引
alter table table_name add fulltext index_name(columnName_list);
show index from table_name;
drop index index_name on table_name;
create index name_status_address_index on table_name(name,status,address)
,其中name、status、address都是varchar类型的
where name = 'aa' and status ='1' and address = '苏州市'
都走索引where name = 'aa' and status >'1' and address = '苏州市'
由于status使用了范围查询,右边的列不走索引,所以此条件中只有name和status走索引,address不走索引where name = 'aa' and address = '苏州市'
根据最左前缀法则,只有name走了索引where address = '苏州市'
根据最左前缀法则,没有索引可走where status = '1' and address = '苏州市' and name = 'aa'
都走索引where name = 'aa'
走name索引where name = 'aa' and status = 1
只有name走索引,status涉及了隐式转换,不走索引select name,status,address where name = 'aa' or remark = 'bb';
-- 使用explain分析的时候name也没有走索引,索引失效了
select name,status,address where name = 'aa' and remark = 'bb';
-- 使用explain分析的时候name是走索引的,与or相反
-- 还是上述的索引案例,这里id字段是表table_name的主键,所以是有id字段索引的;
-- 此处即使后面模糊匹配使用的是%开头了,但是explain执行下面的情形的时候我们发现还是走name_status_address_index名字索引
select id,name,status,address where name like '%aa%';
如果MySQL评估使用索引比全表更慢,则不使用索引,如上面情形四种in和not in案例的数据,里面sellerid为主键,name,status,address为上述描述的联合索引,现在再设置一个address的单独的索引create index idx_seller_address on seller(address);
当执行select * from seller where name = '北京市';
我们发现即使使用了定义了索引,但是使用expalin的时候却没有像我们预期的那样,而是走的全表索引,'北京市’的太多,估算不走索引,走全表扫描;
但是执行select * from seller where name = '西安市';
这个时候,我们发现依旧走idx_seller_address
索引。
也就是说索引中的某个字段的值占据的比例特别大,基本占据了全表的记录,那么就不走索引,直接全表扫描
就像上面所说的那样,字段占据全表的记录多为null的话执行is null就不走索引,走is not null就会走索引;相反索引字段中值为null少的话,执行字段is null的时候走索引,走is not null就不走索引
例如数据
name字段有索引
当执行elplain select * from t_user where name is null;的时候
当执行elplain select * from t_user where name is not null;的时候
避免回表
(因为索引只记录了索引的数据,而不记录一整行的记录数据)例如上述的复合情形
select * from table_name where name= 'aa';
-- 此sql语句使用explain分析的时候可以看到Extra信息为Using index Condition,就是说查询的结果还需回表查询所有信息
select name,status,address from table_name where name= 'aa';
-- 此sql语句使用explain分析的时候可以看到Extra信息为Using where;Using index,就是说,查询直接用到了索引中的数据,不用再回表查询
select name,status,remark from table_name where name = 'aa';
-- 此sql语句使用explain分析的时候可以看到Extra信息为Using index Condition,就是说查询的结果中remark字段不再符合索引中,还需回表查询信息,所以extra为Using index Condition
大批量导入数据使用load指令导入大批量数据的时候
-- load指令,其中filepath,table_name为具体文件路径,和表名,','表示字段值之间以逗号分隔,\n表示换行符
load data local infile 'filepath' into table `table_name` fields terminated ',' line terminated '\n';
-- 会话级关闭
set UNIQUE_CHECKS=0;
-- 导入完成后,再开启
set UNIQUE_CHECKS=1;
多数据插入的时候尽量将数据拼接在一起,开启手动提交事务,被插入的数据记录对应的主键有序
有两种情形在explain的extra中的信息,一种执行时使用到索引using index,一种执行时没有使用到索引Using filesort
例如有emp表,表中的age和salary是复合索引
执行下面的情形始终在explain的extra是filesort,即使order by字段建立了索引,但是没有使用到
但是在查询的使用覆盖索引的时候,即查询的字段只有索引内的字段,但是没有索引外的字段,下面是只有索引内的字段展示;一旦select后面的字段有除索引定义的字段外的字段,如name的时候就不走索引了;当然也会出现一种情形,即使使用了覆盖索引,而在排序的条件,一个字段升序,一个字段降序,又会出现Using filesort的情形了;两个字段同时升序或同时降序也会用到索引,但是字段必须按照索引定义的时候的字段先后顺序,不然即便是同样的排序方式,字段先后顺序变了,也会出现Using filesort的情形;一个字段增降序同样用到索引
通过创建合适的索引,能够减少Filesort 的出现,但是在某些情况下,条件限制不能让Filesort消失,那就需要加快Filesort的排序操作。对于Filesort,MySQL 有两种排序算法:
1)两次扫描算法: MySQL4.1之前,使用该方式排序。首先根据条件取出排序字段和行指针信息,然后在排序区sort buffer中排序,如果sort buffer不够,则在临时表temporary table中存储排序结果。完成排序之后,再根据行指针回表读取记录,该操作可能会导致大量随机I/O操作。
2.)一次扫描算法: 一次性取出满足条件的所有字段,然后在排序区sort buffer中排序后直接输出结果集。排序时内存开销较大,但是排序效率比两次扫描算法要高。
MySQL通过比较系统变量max_length_for_sort_data
的大小和Query语句取出的字段总大小,来判定是否那种排序算法,如果max_length_for_sort_data更大,那么使用第二种优化之后的算法;否则使用第一种。
可以适当提高sort_buffer_size
和max_length_for_sort_data
系统变量,来增大排序区的大小,提高排序的效率。
由于**GROUP BY实际上也同样会进行排序操作**,且与ORDER BY相比,GROUP BY主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY的实现过程中,与ORDER BY一样也可以利用到索引。
如果查询包含group by但是用户想要避免排序结果的消耗,则可以执行order by null禁止排序。如下:
MySQL4.1版本之后,开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果 ,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,查询是可以被更高效的连接(JOIN )替代。
上面说到or关键字的时候,若or前面的字段有索引,后面的字段没有索引,查询时整个是不走索引的;而若有复合索引的时候,例如有一个复合索引create index idx_age_salary on table_name (age,salary);
在使用查询的时候select * from table_name where age = 20 or salary = 3000;
这条语句即便使用到了age和salary,而age和salary是复合索引,但实际查询的时候不会走索引
建议使用union替换or
分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见又非常头疼的问题就是limit 2000000,10 , 此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大。
使用use index人为干预数据库走具体的索引,使用use index仅仅是提供参考,MySQL不一定使用
在执行select * from tb_seller where name = ‘aa’;的时候,可能走两个索引,虽然最终只走一个索引,看下explain具体情形,在使用use index(index_name)的时候就会指定了
例如某个字段有索引,但是执行的时候遇到了索引值的记录过多的情形,MySQL会认为不走索引更高效的情形,就不走索引,而是全表扫描了
使用force index后的情况