#方法一:建立表时 CREATE TABLE 表名 ( 字段名1 数据类型 [完整性约束条件…], 字段名2 数据类型 [完整性约束条件…], [UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY [索引名] (字段名[(长度)] [ASC |DESC]) ); #方法二:CREATE在已存在的表上建立索引 CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 ON 表名 (字段名[(长度)] [ASC |DESC]) ; #方法三:ALTER TABLE在已存在的表上建立索引 ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 (字段名[(长度)] [ASC |DESC]) ; #删除索引:DROP INDEX 索引名 ON 表名字;
#方式一 create table t1( id int, name char, age int, sex enum('male','female'), unique key uni_id(id), index ix_name(name) #index没有key ); create table t1( id int, name char, age int, sex enum('male','female'), unique key uni_id(id), index(name) #index没有key ); #方式二 create index ix_age on t1(age); #方式三 alter table t1 add index ix_sex(sex); alter table t1 add index(sex); #查看 mysql> show create table t1; | t1 | CREATE TABLE `t1` ( `id` int(11) DEFAULT NULL, `name` char(1) DEFAULT NULL, `age` int(11) DEFAULT NULL, `sex` enum('male','female') DEFAULT NULL, UNIQUE KEY `uni_id` (`id`), KEY `ix_name` (`name`), KEY `ix_age` (`age`), KEY `ix_sex` (`sex`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
#1. 准备表 create table s1( id int, name varchar(20), gender char(6), email varchar(50) ); #2. 建立存储过程,实现批量插入记录 delimiter $$ #声明存储过程的结束符号为$$ create procedure auto_insert1() BEGIN declare i int default 1; while(i<3000000)do insert into s1 values(i,'eva','female',concat('eva',i,'@oldboy')); set i=i+1; end while; END$$ #$$结束 delimiter ; #从新声明分号为结束符号 #3. 查看存储过程 show create procedure auto_insert1\G #4. 调用存储过程 call auto_insert1();
#无索引:mysql根本就不知道究竟是否存在id等于333333333的记录,只能把数据表从头至尾扫描一遍,此时有多少个磁盘块就须要进行多少IO操做,因此查询速度很慢 mysql> select * from s1 where id=333333333; Empty set (0.33 sec)
PS:mysql
1. mysql先去索引表里根据b+树的搜索原理很快搜索到id等于333333333的记录不存在,IO大大下降,于是速度明显提高sql
2. 咱们能够去mysql的data目录下找到该表,能够看到占用的硬盘空间多了ide
3. 须要注意,以下图函数
#1. 必定是为搜索条件的字段建立索引,好比select * from s1 where id = 333;就须要为id加上索引 #2. 在表中已经有大量数据的状况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快 好比create index idx on s1(id);会扫描表中全部的数据,而后以id为数据项,建立索引结构,存放于硬盘的表中。 建完之后,再查询就会很快了。 #3. 须要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在innodb中,表数据文件自己就是按照B+Tree(BTree即Balance True)组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以innodb表数据文件自己就是主索引。 由于inndob的数据文件要按照主键汇集,因此innodb要求表必需要有主键(Myisam能够没有),若是没有显式定义,则mysql系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则mysql会自动为innodb表生成一个隐含字段做为主键,这字段的长度为6个字节,类型为长整型.
并非说咱们建立了索引就必定会加快查询速度,若想利用索引达到预想的提升查询速度的效果,咱们在添加索引时,必须遵循如下问题
测试
条件中出现这些符号或关键字:>、>=、<、<=、!= 、between...and...、like、大数据
大于号、小于号spa
尽可能选择区分度高的列做为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大咱们扫描的记录数越少,惟一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不一样,这个值也很难肯定,通常须要join的字段咱们都要求是0.1以上,即平均1条扫描10条记录3d
mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | MUL | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(5) | YES | | NULL | | | email | varchar(50) | YES | MUL | NULL | | +--------+-------------+------+-----+---------+-------+ rows in set (0.00 sec) mysql> drop index a on s1; Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> drop index d on s1; Query OK, 0 rows affected (0.18 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(5) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ rows in set (0.00 sec)
咱们编写存储过程为表s1批量添加记录,name字段的值均为egon,也就是说name这个字段的区分度很低(gender字段也是同样的,咱们稍后再搭理它) 回忆b+树的结构,查询的速度与树的高度成反比,要想将树的高低控制的很低,须要保证:在某一层内数据项均是按照从左到右,从小到大的顺序依次排开,即左1<左2<左3<... 而对于区分度低的字段,没法找到大小关系,由于值都是相等的,毫无疑问,还想要用b+树存放这些等值的数据,只能增长树的高度,字段的区分度越低,则树的高度越高。极端的状况,索引字段的值都同样,那么b+树几乎成了一根棍。本例中就是这种极端的状况,name字段全部的值均为'egon' #如今咱们得出一个结论:为区分度低的字段创建索引,索引树的高度会很高,然而这具体会带来什么影响呢??? #1:若是条件是name='xxxx',那么确定是能够第一时间判断出'xxxx'是不在索引树中的(由于树中全部的值均为'egon’),因此查询速度很快 #2:若是条件正好是name='egon',查询时,咱们永远无
索引列不能在条件中参与计算,保持列“干净”,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,缘由很简单,b+树中存的都是数据表中的字段值,但进行检索时,须要把全部元素都应用函数才能比较,显然成本太大。因此语句应该写成create_time = unix_timestamp(’2014-05-29’)unix
#一、and与or的逻辑 条件1 and 条件2:全部条件都成立才算成立,但凡要有一个条件不成立则最终结果不成立 条件1 or 条件2:只要有一个条件成立则最终结果就成立 #二、and的工做原理 条件: a = 10 and b = 'xxx' and c > 3 and d =4 索引: 制做联合索引(d,a,b,c) 工做原理: 对于连续多个and:mysql会按照联合索引,从左到右的顺序找一个区分度高的索引字段(这样即可以快速锁定很小的范围),加速查询,即按照d—>a->b->c的顺序 #三、or的工做原理 条件: a = 10 or b = 'xxx' or c > 3 or d =4 索引: 制做联合索引(d,a,b,c) 工做原理: 对于连续多个or:mysql会按照条件的顺序,从左到右依次判断,即a->b->c->d
在左边条件成立可是索引字段的区分度低的状况下(name与gender均属于这种状况),会依次往右找到一个区分度高的索引字段,加速查询code
通过分析,在条件为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的顺序能够任意调整。
使用函数 select * from tb1 where reverse(email) = 'egon'; - 类型不一致 若是列是字符串类型,传入条件是必须用引号引发来,否则... select * from tb1 where email = 999; #排序条件为索引,则select字段必须也是索引字段,不然没法命中 - order by select name from s1 order by email desc; 当根据索引排序时候,select查询的字段若是不是索引,则速度仍然很慢 select email from s1 order by email desc; 特别的:若是对主键排序,则仍是速度很快: select * from tb1 order by nid desc; - 组合索引最左前缀 若是组合索引为:(name,email) name and email -- 命中索引 name -- 命中索引 email -- 未命中索引 - count(1)或count(列)代替count(*)在mysql中没有差异了 - create index xxxx on tb(title(19)) #text类型,必须制定长度
- 避免使用select * - 使用count(*) - 建立表时尽可能使用 char 代替 varchar - 表的字段顺序固定长度的字段优先 - 组合索引代替多个单列索引(因为mysql中每次只能使用一个索引,因此常用多个条件查询时更适合使用组合索引) - 尽可能使用短索引 - 使用链接(JOIN)来代替子查询(Sub-Queries) - 连表时注意条件类型需一致 - 索引散列值(重复少)不适合建索引,例:性别不适合
联合索引是指对表上的多个列合起来作一个索引。联合索引的建立方法与单个索引的建立方法同样,不一样之处仅在于有多个索引列,如:
mysql> create table t( -> a int, -> b int, -> primary key(a), -> key idx_a_b(a,b) -> ); Query OK, 0 rows affected (0.11 sec)
InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就能够获得查询记录,而不须要查询汇集索引中的记录。
覆盖索引 查一个数据不须要回表 select name from 表 where age = 20 不是覆盖索引 select age from 表 where age =20 是覆盖索引 select count(age) from 表 where age =20 是覆盖索引
当咱们为单独的一列建立索引的时候 若是条件是这一列,且使用正确就能够命中索引 当咱们为两列分别建立单独的索引的时候 若是这两列都是条件,那么可能只能命中期中一个条件 若是这两列都是条件,那么可能会命中两个索引 - 合并索引 咱们为多列直接建立联合因此 条件命中联合索引
1.条件必定是创建了索引的字段,若是条件使用的字段根本就没有建立索引,那么索引不生效 2.若是条件是一个范围,随着范围的值逐渐增大,那么索引能发挥的做用也越小 3.若是使用like进行模糊查询,那么使用a%的形式能命中索引,%a形式不能命中索引 4.尽可能选择区分度高的字段做为索引列 5.索引列不能在条件中参与计算,也不能使用函数 6.在多个条件以and相连的时候,会优势选择区分度高的索引列来进行查询 在多个条件以or相连的时候,就是从左到右依次判断 7.制做联合索引 1.最左前缀原则 a,b,c,d 条件是a的能命中索引,条件是a,b能命中索引,a,b,c能命中,a,c.... 只要没有a就不能命中索引 若是在联合查询中,老是涉及到同一个字段,那么就在创建联合索引的时候将这个字段放在最左侧 2.联合索引 若是按照定义顺序,从左到右遇到的第一个在条件中以范围为条件的字段,索引失效 尽可能将带着范围查询的字段,定义在联合索引的最后面 drop index 若是咱们查询的条件老是多个列合在一块儿查,那么就创建联合索引 create index ind_mix on s1(id,email) select * from s1 where id = 1000000 命中索引 select * from s1 where email = 'eva1000000@oldboy' 未命中索引 但凡是建立了联合索引,那么在查询的时候,再建立顺序中从左到右的第一列必须出如今条件中 select count(*) from s1 where id = 1000000 and email = 'eva10%'; 命中索引 select count(*) from s1 where id = 1000000 and email like 'eva10%'; 能够命中索引 范围 : select * from s1 where id >3000 and email = 'eva300000@oldboy'; 不能命中索引 8.条件中涉及的字段的值必须和定义表中字段的数据类型一致,不然不能命中索引
执行计划 看看mysql准备怎么执行这条语句 能够看到是否命中索引,计划能命中哪些,实际命中了哪些,执行的顺序,是否发生了索引合并,覆盖索引 explain select * from s1;