MySQL创建索引

创建索引

1#方法一:创建表时 2   CREATE TABLE 表名 ( 3 字段名1 数据类型 [完整性约束条件…], 4 字段名2 数据类型 [完整性约束条件…], 5 [UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY 6 [索引名] (字段名[(长度)] [ASC |DESC]) 7 ); 8 9 10#方法二:CREATE在已存在的表上创建索引 11 CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 12 ON 表名 (字段名[(长度)] [ASC |DESC]) ; 13 14 15#方法三:ALTER TABLE在已存在的表上创建索引 16 ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX 17 索引名 (字段名[(长度)] [ASC |DESC]) ; 18 19#删除索引:DROP INDEX 索引名 ON 表名字;

1#方式一 2create table t1( 3 id int, 4 name char, 5 age int, 6 sex enum('male','female'), 7 unique key uni_id(id), 8 index ix_name(name) #index没有key 9); 10create table t1( 11 id int, 12 name char, 13 age int, 14 sex enum('male','female'), 15 unique key uni_id(id), 16 index(name) #index没有key 17); 18 19 20#方式二 21create index ix_age on t1(age); 22 23 24#方式三 25alter table t1 add index ix_sex(sex); 26alter table t1 add index(sex); 27 28#查看 29mysql> show create table t1; 30| t1 | CREATE TABLE `t1` ( 31 `id` int(11) DEFAULT NULL, 32 `name` char(1) DEFAULT NULL, 33 `age` int(11) DEFAULT NULL, 34 `sex` enum('male','female') DEFAULT NULL, 35 UNIQUE KEY `uni_id` (`id`), 36 KEY `ix_name` (`name`), 37 KEY `ix_age` (`age`), 38 KEY `ix_sex` (`sex`) 39) ENGINE=InnoDB DEFAULT CHARSET=latin1

例子

测试索引

数据准备

1#1. 准备表 2create table s1( 3id int, 4name varchar(20), 5gender char(6), 6email varchar(50) 7); 8 9#2. 创建存储过程,实现批量插入记录 10delimiter $$ #声明存储过程的结束符号为$$ 11create procedure auto_insert1() 12BEGIN 13 declare i int default 1; 14 while(i<3000000)do 15 insert into s1 values(i,'eva','female',concat('eva',i,'@oldboy')); 16 set i=i+1; 17 end while; 18END$$ #$$结束 19delimiter ; #重新声明分号为结束符号 20 21#3. 查看存储过程 22show create procedure auto_insert1\G 23 24#4. 调用存储过程 25call auto_insert1();

View Code

在没有索引的前提下测试查询速度

1#无索引:mysql根本就不知道到底是否存在id等于333333333的记录,只能把数据表从头到尾扫描一遍,此时有多少个磁盘块就需要进行多少IO操作,所以查询速度很慢 2mysql> select * from s1 where id=333333333; 3Empty set (0.33 sec)

在表中已经存在大量数据的前提下,为某个字段段建立索引,建立速度会很慢

在索引建立完毕后,以该字段为查询条件时,查询速度提升明显

PS:

1. mysql先去索引表里根据b+树的搜索原理很快搜索到id等于333333333的记录不存在,IO大大降低,因而速度明显提升

2. 我们可以去mysql的data目录下找到该表,可以看到占用的硬盘空间多了

3. 需要注意,如下图

总结

1#1. 一定是为搜索条件的字段创建索引,比如select * from s1 where id = 333;就需要为id加上索引 2 3#2. 在表中已经有大量数据的情况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快 4比如create index idx on s1(id);会扫描表中所有的数据,然后以id为数据项,创建索引结构,存放于硬盘的表中。 5建完以后,再查询就会很快了。 6 7#3. 需要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI 8 9MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在innodb中,表数据文件本身就是按照B+Tree(BTree即Balance True)组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此innodb表数据文件本身就是主索引。 10因为inndob的数据文件要按照主键聚集,所以innodb要求表必须要有主键(Myisam可以没有),如果没有显式定义,则mysql系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则mysql会自动为innodb表生成一个隐含字段作为主键,这字段的长度为6个字节,类型为长整型.

如果才能正确命中索引

索引未命中

**并不是说我们创建了索引就一定会加快查询速度,**若想利用索引达到预想的提高查询速度的效果,我们在添加索引时,必须遵循以下问题

范围问题,或者说条件不明确

条件中出现这些符号或关键字:****>、>=、<、<=、!= 、between...and...、like、

大于号、小于号

不等于!=

between ...and...

like

区分度问题

尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

1mysql> desc s1; 2+--------+-------------+------+-----+---------+-------+ 3| Field | Type | Null | Key | Default | Extra | 4+--------+-------------+------+-----+---------+-------+ 5| id | int(11) | YES | MUL | NULL | | 6| name | varchar(20) | YES | | NULL | | 7| gender | char(5) | YES | | NULL | | 8| email | varchar(50) | YES | MUL | NULL | | 9+--------+-------------+------+-----+---------+-------+ 10rows in set (0.00 sec) 11 12mysql> drop index a on s1; 13Query OK, 0 rows affected (0.20 sec) 14Records: 0 Duplicates: 0 Warnings: 0 15 16mysql> drop index d on s1; 17Query OK, 0 rows affected (0.18 sec) 18Records: 0 Duplicates: 0 Warnings: 0 19 20mysql> desc s1; 21+--------+-------------+------+-----+---------+-------+ 22| Field | Type | Null | Key | Default | Extra | 23+--------+-------------+------+-----+---------+-------+ 24| id | int(11) | YES | | NULL | | 25| name | varchar(20) | YES | | NULL | | 26| gender | char(5) | YES | | NULL | | 27| email | varchar(50) | YES | | NULL | | 28+--------+-------------+------+-----+---------+-------+ 29rows in set (0.00 sec)

先把表中的索引都删除,让我们专心研究区分度的问题

1我们编写存储过程为表s1批量添加记录,name字段的值均为egon,也就是说name这个字段的区分度很低(gender字段也是一样的,我们稍后再搭理它) 2 3回忆b+树的结构,查询的速度与树的高度成反比,要想将树的高低控制的很低,需要保证:在某一层内数据项均是按照从左到右,从小到大的顺序依次排开,即左1<2<3<... 4 5而对于区分度低的字段,无法找到大小关系,因为值都是相等的,毫无疑问,还想要用b+树存放这些等值的数据,只能增加树的高度,字段的区分度越低,则树的高度越高。极端的情况,索引字段的值都一样,那么b+树几乎成了一根棍。本例中就是这种极端的情况,name字段所有的值均为'egon' 6 7#现在我们得出一个结论:为区分度低的字段建立索引,索引树的高度会很高,然而这具体会带来什么影响呢??? 8 9#1:如果条件是name='xxxx',那么肯定是可以第一时间判断出'xxxx'是不在索引树中的(因为树中所有的值均为'egon’),所以查询速度很快 10 11#2:如果条件正好是name='egon',查询时,我们永远无

分析原因

索引列不能在条件中参与计算

索引列不能在条件中参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)

 

and/or

1#1、and与or的逻辑 2 条件1 and 条件2:所有条件都成立才算成立,但凡要有一个条件不成立则最终结果不成立 3 条件1 or 条件2:只要有一个条件成立则最终结果就成立 4 5#2、and的工作原理 6 条件: 7 a = 10 and b = 'xxx' and c > 3 and d =4 8 索引: 9 制作联合索引(d,a,b,c) 10 工作原理: 11 对于连续多个and:mysql会按照联合索引,从左到右的顺序找一个区分度高的索引字段(这样便可以快速锁定很小的范围),加速查询,即按照d—>a->b->c的顺序 12 13#3、or的工作原理 14 条件: 15 a = 10 or b = 'xxx' or c > 3 or d =4 16 索引: 17 制作联合索引(d,a,b,c) 18 19 工作原理: 20 对于连续多个or:mysql会按照条件的顺序,从左到右依次判断,即a->b->c->d

在左边条件成立但是索引字段的区分度低的情况下(name与gender均属于这种情况),会依次往右找到一个区分度高的索引字段,加速查询

经过分析,在条件为name='egon' and gender='male' and id>333 and email='xxx'的情况下,我们完全没必要为前三个条件的字段加索引,因为只能用上email字段的索引,前三个字段的索引反而会降低我们的查询效率

最左前缀匹配原则

非常重要的原则,对于组合索引mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配(指的是范围大了,有索引速度也慢),比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

其他情况

1使用函数 2 select * from tb1 where reverse(email) = 'egon'; 3 4- 类型不一致 5 如果列是字符串类型,传入条件是必须用引号引起来,不然... 6 select * from tb1 where email = 999; 7 8#排序条件为索引,则select字段必须也是索引字段,否则无法命中 9- order by 10 select name from s1 order by email desc; 11 当根据索引排序时候,select查询的字段如果不是索引,则速度仍然很慢 12 select email from s1 order by email desc; 13 特别的:如果对主键排序,则还是速度很快: 14 select * from tb1 order by nid desc; 15 16- 组合索引最左前缀 17 如果组合索引为:(name,email) 18 name and email -- 命中索引 19 name -- 命中索引 20 email -- 未命中索引 21 22 23- count(1)或count()代替count(*)在mysql中没有差别了 24 25- create index xxxx on tb(title(19)) #text类型,必须制定长度

其他注意事项

1- 避免使用select * 2- 使用count(*) 3- 创建表时尽量使用 char 代替 varchar 4- 表的字段顺序固定长度的字段优先 5- 组合索引代替多个单列索引(由于mysql中每次只能使用一个索引,所以经常使用多个条件查询时更适合使用组合索引) 6- 尽量使用短索引 7- 使用连接(JOIN)来代替子查询(Sub-Queries) 8- 连表时注意条件类型需一致 9- 索引散列值(重复少)不适合建索引,例:性别不适合

联合索引

联合索引

联合索引是指对表上的多个列合起来做一个索引。联合索引的创建方法与单个索引的创建方法一样,不同之处仅在于有多个索引列,如:

1mysql> create table t( 2 -> a int, 3 -> b int, 4 -> primary key(a), 5 -> key idx_a_b(a,b) 6 -> ); 7Query OK, 0 rows affected (0.11 sec)

覆盖索引

InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。

1覆盖索引 查一个数据不需要回表 2 select name from 表 where age = 20 不是覆盖索引 3 select age from 表 where age =20 是覆盖索引 4 select count(age) from 表 where age =20 是覆盖索引

合并索引

1当我们为单独的一列创建索引的时候 2 如果条件是这一列,且使用正确就可以命中索引 3当我们为两列分别创建单独的索引的时候 4 如果这两列都是条件,那么可能只能命中期中一个条件 5 如果这两列都是条件,那么可能会命中两个索引 - 合并索引 6我们为多列直接创建联合所以 7 条件命中联合索引

索引总结:

11.条件一定是建立了索引的字段,如果条件使用的字段根本就没有创建索引,那么索引不生效 22.如果条件是一个范围,随着范围的值逐渐增大,那么索引能发挥的作用也越小 33.如果使用like进行模糊查询,那么使用a%的形式能命中索引,%a形式不能命中索引 44.尽量选择区分度高的字段作为索引列 55.索引列不能在条件中参与计算,也不能使用函数 66.在多个条件以and相连的时候,会优点选择区分度高的索引列来进行查询 7 在多个条件以or相连的时候,就是从左到右依次判断 87.制作联合索引 9 1.最左前缀原则 a,b,c,d 条件是a的能命中索引,条件是a,b能命中索引,a,b,c能命中,a,c.... 只要没有a就不能命中索引 10 如果在联合查询中,总是涉及到同一个字段,那么就在建立联合索引的时候将这个字段放在最左侧 11 2.联合索引 如果按照定义顺序,从左到右遇到的第一个在条件中以范围为条件的字段,索引失效 12 尽量将带着范围查询的字段,定义在联合索引的最后面 13 drop index 14 如果我们查询的条件总是多个列合在一起查,那么就建立联合索引 15 create index ind_mix on s1(id,email) 16 17 select * from s1 where id = 1000000 命中索引 18 select * from s1 where email = 'eva1000000@oldboy' 未命中索引 19 但凡是创建了联合索引,那么在查询的时候,再创建顺序中从左到右的第一列必须出现在条件中 20 select count(*) from s1 where id = 1000000 and email = 'eva10%'; 命中索引 21 22 select count(*) from s1 where id = 1000000 and email like 'eva10%'; 可以命中索引 23 范围 : 24 select * from s1 where id >3000 and email = 'eva300000@oldboy'; 不能命中索引 258.条件中涉及的字段的值必须和定义表中字段的数据类型一致,否则不能命中索引

执行计划

1执行计划 2看看mysql准备怎么执行这条语句 可以看到是否命中索引,计划能命中哪些,实际命中了哪些,执行的顺序,是否发生了索引合并,覆盖索引 3explain select * from s1;
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )