mysql数据库索引类型和原理

索引初识:

最普通的状况,是为出如今where子句的字段建一个索引。为方便讲述,咱们先创建一个以下的表。前端

CREATE TABLE mytable (
 id serial primary key,
 category_id int not null default 0,
 user_id int not null default 0,
 adddate int not null default 0
);

很简单吧,不过对于要说明这个问题,已经足够了。若是你在查询时经常使用相似如下的语句:mysql

SELECT * FROM mytable WHERE category_id=1; 

  最直接的应对之道,是为category_id创建一个简单的索引:sql

CREATE INDEX mytable_categoryid ON mytable (category_id);

  OK,搞定?先别高兴,若是你有不止一个选择条件呢?例如:数据库

SELECT * FROM mytable WHERE category_id=1 AND user_id=2;

  你的第一反应多是,再给user_id创建一个索引。很差,这不是一个最佳的方法。你能够创建多重的索引。数据库设计

CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);

  注意到我在命名时的习惯了吗?我使用"表名_字段1名_字段2名"的方式。你很快就会知道我为何这样作了。

  如今你已经为适当的字段创建了索引,不过,仍是有点不放心吧,你可能会问,数据库会真正用到这些索引吗?测试一下就OK,对于大多数的数据库来讲,这是很容易的,只要使用EXPLAIN命令:post

EXPLAIN

 SELECT * FROM mytable 
  WHERE category_id=1 AND user_id=2;

This is what Postgres 7.1 returns (exactly as I expected) 

 NOTICE: QUERY PLAN:

Index Scan using mytable_categoryid_userid on 
  mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

  以上是postgres的数据,能够看到该数据库在查询的时候使用了一个索引(一个好开始),并且它使用的是我建立的第二个索引。看到我上面命名的好处了吧,你立刻知道它使用适当的索引了。

  接着,来个稍微复杂一点的,若是有个ORDER BY字句呢?无论你信不信,大多数的数据库在使用order by的时候,都将会从索引中受益。性能

SELECT * FROM mytable WHERE category_id=1 AND user_id=2 ORDER BY adddate DESC;

  有点迷惑了吧?很简单,就象为where字句中的字段创建一个索引同样,也为ORDER BY的字句中的字段创建一个索引:测试

CREATE INDEX mytable_categoryid_userid_adddate ON mytable (category_id,user_id,adddate);

  

注意: "mytable_categoryid_userid_adddate" 将会被截短为
"mytable_categoryid_userid_addda"大数据

CREATE

  EXPLAIN SELECT * FROM mytable
  WHERE category_id=1 AND user_id=2
   ORDER BY adddate DESC;

 NOTICE: QUERY PLAN:

 Sort (cost=2.03..2.03 rows=1 width=16)
  -> Index Scan using mytable_categoryid_userid_addda 
    on mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

  看看EXPLAIN的输出,好象有点恐怖啊,数据库多作了一个咱们没有要求的排序,这下知道性能如何受损了吧,看来咱们对于数据库的自身运做是有点过于乐观了,那么,给数据库多一点提示吧。

  为了跳过排序这一步,咱们并不须要其它另外的索引,只要将查询语句稍微改一下。这里用的是postgres,咱们将给该数据库一个额外的提示--在ORDER BY语句中,加入where语句中的字段。这只是一个技术上的处理,并非必须的,由于实际上在另外两个字段上,并不会有任何的排序操做,不过若是加入,postgres将会知道哪些是它应该作的。优化

EXPLAIN SELECT * FROM mytable 
  WHERE category_id=1 AND user_id=2
  ORDER BY category_id DESC,user_id DESC,adddate DESC;

NOTICE: QUERY PLAN:

Index Scan Backward using 
 mytable_categoryid_userid_addda on mytable 
   (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

  如今使用咱们料想的索引了,并且它还挺聪明,知道能够从索引后面开始读,从而避免了任何的排序。

  以上说得细了一点,不过若是你的数据库很是巨大,而且每日的页面请求达上百万算,我想你会获益良多的。不过,若是你要作更为复杂的查询呢,例如将多张表结合起来查询,特别是where限制字句中的字段是来自不止一个表格时,应该怎样处理呢?我一般都尽可能避免这种作法,由于这样数据库要将各个表中的东西都结合起来,而后再排除那些不合适的行,搞很差开销会很大。

  若是不能避免,你应该查看每张要结合起来的表,而且使用以上的策略来创建索引,而后再用EXPLAIN命令验证一下是否使用了你料想中的索引。若是是的话,就OK。不是的话,你可能要创建临时的表来将他们结合在一块儿,而且使用适当的索引。

  要注意的是,创建太多的索引将会影响更新和插入的速度,由于它须要一样更新每一个索引文件。对于一个常常须要更新和插入的表格,就没有必要为一个不多使用的where字句单独创建索引了,对于比较小的表,排序的开销不会很大,也没有必要创建另外的索引。

  以上介绍的只是一些十分基本的东西,其实里面的学问也很多,单凭EXPLAIN咱们是不能断定该方法是否就是最优化的,每一个数据库都有本身的一些优化器,虽然可能还不太完善,可是它们都会在查询时对比过哪一种方式较快,在某些状况下,创建索引的话也未必会快,

例如索引放在一个不连续的存储空间时,这会增长读磁盘的负担,所以,哪一个是最优,应该经过实际的使用环境来检验。

  在刚开始的时候,若是表不大,没有必要做索引,个人意见是在须要的时候才做索引,也可用一些命令来优化表,例如MySQL可用"OPTIMIZE TABLE"。

  综上所述,在如何为数据库创建恰当的索引方面,你应该有一些基本的概念了。

 

---------------------------------------------------------------

 

关于MySQL索引的好处,若是正确合理设计而且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一我的力三轮车。对于没有索引的表,单表查询可能几十万数据就是瓶颈,而一般大型网站单日就可能会产生几十万甚至几百万的数据,没有索引查询会变的很是缓慢。仍是以WordPress来讲,其多个数据表都会对常常被查询的字段添加索引,好比wp_comments表中针对5个字段设计了BTREE索引。

一个简单的对比测试

以我去年测试的数据做为一个简单示例,20多条数据源随机生成200万条数据,平均每条数据源都重复大概10万次,表结构比较简单,仅包含一个自增ID,一个char类型,一个text类型和一个int类型,单表2G大小,使用MyIASM引擎。开始测试未添加任何索引。

执行下面的SQL语句:

 SELECT id,FROM_UNIXTIME(time) FROM article WHERE a.title='测试标题';

查询须要的时间很是恐怖的,若是加上联合查询和其余一些约束条件,数据库会疯狂的消耗内存,而且会影响前端程序的执行。这时给title字段添加一个BTREE索引:

ALTER TABLE article ADD INDEX index_article_title ON title(200);

再次执行上述查询语句,其对比很是明显:

MySQL索引的概念

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里全部记录的引用指针。更通俗的说,数据库索引比如是一本书前面的目录,能加快数据库的查询速度。上述SQL语句,在没有索引的状况下,数据库会遍历所有200条数据后选择符合条件的;而有了相应的索引以后,数据库会直接在索引中查找符合条件的选项。若是咱们把SQL语句换成“SELECT * FROM article WHERE id=2000000”,那么你是但愿数据库按照顺序读取完200万行数据之后给你结果仍是直接在索引中定位呢?上面的两个图片鲜明的用时对比已经给出了答案(注:通常数据库默认都会为主键生成索引)。

索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不同了;聚簇索引能提升多行检索的速度,而非聚簇索引对于单行的检索很快。

MySQL索引的类型

1. 普通索引

这是最基本的索引,它没有任何限制,好比上文中为title字段建立的索引就是一个普通索引,MyIASM中默认的BTREE类型的索引,也是咱们大多数状况下用到的索引。

–直接建立索引
CREATE INDEX index_name ON table(column(length))
–修改表结构的方式添加索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))
–建立表的时候同时建立索引
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
INDEX index_name (title(length))
)
–删除索引
DROP INDEX index_name ON table

2. 惟一索引

与普通索引相似,不一样的就是:索引列的值必须惟一,但容许有空值(注意和主键不一样)。若是是组合索引,则列值的组合必须惟一,建立方法和普通索引相似。

–建立惟一索引
CREATE UNIQUE INDEX indexName ON table(column(length))
–修改表结构
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))
–建立表的时候直接指定
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
UNIQUE indexName (title(length))
);

3. 全文索引(FULLTEXT)

MySQL从3.23.23版开始支持全文索引和全文检索,FULLTEXT索引仅可用于 MyISAM 表;他们能够从CHAR、VARCHAR或TEXT列中做为CREATE TABLE语句的一部分被建立,或是随后使用ALTER TABLE 或CREATE INDEX被添加。////对于较大的数据集,将你的资料输入一个没有FULLTEXT索引的表中,而后建立索引,其速度比把资料输入现有FULLTEXT索引的速度更为快。不过切记对于大容量的数据表,生成全文索引是一个很是消耗时间很是消耗硬盘空间的作法。

–建立表的适合添加全文索引
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
FULLTEXT (content)
);
–修改表结构添加全文索引
ALTER TABLE article ADD FULLTEXT index_content(content)
–直接建立索引
CREATE FULLTEXT INDEX index_content ON article(content)

4. 单列索引、多列索引

多个单列索引与单个多列索引的查询效果不一样,由于执行查询时,MySQL只能使用一个索引,会从多个索引中选择一个限制最为严格的索引。

5. 组合索引(最左前缀)

平时用的SQL查询语句通常都有比较多的限制条件,因此为了进一步榨取MySQL的效率,就要考虑创建组合索引。例如上表中针对title和time创建一个组合索引:ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))。创建这样的组合索引,实际上是至关于分别创建了下面两组组合索引:

–title,time

–title

为何没有time这样的组合索引呢?这是由于MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并非只要包含这两列的查询都会用到该组合索引,以下面的几个SQL所示:

–使用到上面的索引
SELECT * FROM article WHREE title='测试' AND time=1234567890;
SELECT * FROM article WHREE utitle='测试';
–不使用上面的索引
SELECT * FROM article WHREE time=1234567890;

MySQL索引的优化

上面都在说使用索引的好处,但过多的使用索引将会形成滥用。所以索引也会有它的缺点:虽然索引大大提升了查询速度,同时却会下降更新表的速度,如对表进行INSERT、UPDATE和DELETE。由于更新表时,MySQL不只要保存数据,还要保存一下索引文件。创建索引会占用磁盘空间的索引文件。通常状况这个问题不太严重,但若是你在一个大表上建立了多种组合索引,索引文件的会膨胀很快。索引只是提升效率的一个因素,若是你的MySQL有大数据量的表,就须要花时间研究创建最优秀的索引,或优化查询语句。下面是一些总结以及收藏的MySQL索引的注意事项和优化方法。

1. 什么时候使用汇集索引或非汇集索引?

动做描述 使用汇集索引 使用非汇集索引
列常常被分组排序 使用 使用
返回某范围内的数据 使用 不使用
一个或极少不一样值 不使用 不使用
小数目的不一样值 使用 不使用
大数目的不一样值 不使用 使用
频繁更新的列 不使用 使用
外键列 使用 使用
主键列 使用 使用
频繁修改索引列 不使用 使用

事实上,咱们能够经过前面汇集索引和非汇集索引的定义的例子来理解上表。如:返回某范围内的数据一项。好比您的某个表有一个时间列,刚好您把聚合索引创建在了该列,这时您查询2004年1月1日至2004年10月1日之间的所有数据时,这个速度就将是很快的,由于您的这本字典正文是按日期进行排序的,聚类索引只须要找到要检索的全部数据中的开头和结尾数据便可;而不像非汇集索引,必须先查到目录中查到每一项数据对应的页码,而后再根据页码查到具体内容。其实这个具体用法我还不是很理解,只能等待后期的项目开发中慢慢学学了。

2. 索引不会包含有NULL值的列

只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。因此咱们在数据库设计时不要让字段的默认值为NULL。

3. 使用短索引

对串列进行索引,若是可能应该指定一个前缀长度。例如,若是有一个CHAR(255)的列,若是在前10个或20个字符内,多数值是唯一的,那么就不要对整个列进行索引。短索引不只能够提升查询速度并且能够节省磁盘空间和I/O操做。

4. 索引列排序

MySQL查询只使用一个索引,所以若是where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。所以数据库默认排序能够符合要求的状况下不要使用排序操做;尽可能不要包含多个列的排序,若是须要最好给这些列建立复合索引。

5. like语句操做

通常状况下不鼓励使用like操做,若是非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可使用索引。

6. 不要在列上进行运算

例如:select * from users where YEAR(adddate)<2007,将在每一个行上进行运算,这将致使索引失效而进行全表扫描,所以咱们能够改为:select * from users where adddate<’2007-01-01′。关于这一点能够围观:一个单引号引起的MYSQL性能损失。

最后总结一下,MySQL只对一下操做符才使用索引:<,<=,=,>,>=,between,in,以及某些时候的like(不以通配符%或_开头的情形)。而理论上每张表里面最多可建立16个索引,不过除非是数据量真的不少,不然过多的使用索引也不是那么好玩的,好比我刚才针对text类型的字段建立索引的时候,系统差点就卡死了。

----------------------------------------------------------------------

 

创建索引的优缺点:

为何要建立索引呢?

        这是由于,建立索引能够大大提升系统的性能。 
        第1、经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。 
        第2、能够大大加快 数据的检索速度,这也是建立索引的最主要的缘由。 
        第3、能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别有意义。 
        第4、在使用分组和排序子句进行数据检索时,一样能够显著减小查询中分组和排序的时间。 
        第5、经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。

        也许会有人要问:增长索引有如此多的优势,为何不对表中的每个列建立一个索引呢?这种想法当然有其合理性,然而也有其片面性。虽然,索引有许多优势, 可是,为表中的每个列都增长索引,是很是不明智的。

       这是由于,增长索引也有许多不利的一个方面:

        第1、建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。 

        第2、索引须要占物理空间,除了数据表占数据空间以外,每个索引还要占必定的物理空间。若是要创建聚簇索引,那么须要的空间就会更大。 

        第3、当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这样就下降了数据的维护速度。

什么样的字段适合建立索引:

 索引是创建在数据库表中的某些列的上面。所以,在建立索引的时候,应该仔细考虑在哪些列上能够建立索引,在哪些列上不能建立索引。

       通常来讲,应该在这些列上建立索引,例如:

       第1、在常常须要搜索的列上,能够加快搜索的速度; 

       第2、在做为主键的列上,强制该列的惟一性和组织表中数据的排列结构; 

       第3、在常常用在链接的列上,这些列主要是一些外键,能够加快链接的速度; 

       第4、在常常须要根据范围进行搜索的列上建立索引,由于索引已经排序,其指定的范围是连续的; 

       第5、在常常须要排序的列上建立索引,由于索引已经排序,这样查询能够利用索引的排序,加快排序查询时间; 

       第6、在常用在WHERE子句中的列上面建立索引,加快条件的判断速度。

 

       创建索引,通常按照select的where条件来创建,好比: select的条件是where f1 and f2,那么若是咱们在字段f1或字段f2上简历索引是没有用的,只有在字段f1和f2上同时创建索引才有用等。

什么样的字段不适合建立索引:

一样,对于有些列不该该建立索引。通常来讲,不该该建立索引的的这些列具备下列特色:

  第一,对于那些在查询中不多使用或者参考的列不该该建立索引。这是由于,既然这些列不多使用到,所以有索引或者无索引,

并不能提升查询速度。相反,因为增长了索引,反而下降了系统的维护速度和增大了空间需求。 
       第二,对于那些只有不多数据值的列也不该该增长索引。这是由于,因为这些列的取值不多,例如人事表的性别列,

在查询的结果中,结果集的数据行占了表中数据行的很大比 例,即须要在表中搜索的数据行的比例很大。

增长索引,并不能明显加快检索速度。 
       第三,对于那些定义为text, image和bit数据类型的列不该该增长索引。这是由于,这些列的数据量要么至关大,要么取值不多。 
       第四,当修改性能远远大于检索性能时,不该该建立索 引。这是由于,修改性能和检索性能是互相矛盾的。

当增长索引时,会提升检索性能,可是会下降修改性能。当减小索引时,会提升修改性能,下降检索性能。

所以,当修改性能远远大于检索性能时,不该该建立索引。

建立索引的方法::

一、建立索引,例如 create index <索引的名字> on table_name (列的列表); 
      二、修改表,例如 alter table table_name add index[索引的名字] (列的列表); 
      三、建立表的时候指定索引,例如create table table_name ( [...], INDEX [索引的名字] (列的列表) );

查看表中索引的方法:

show index from table_name; 查看索引

索引的类型及建立例子::

1.PRIMARY KEY (主键索引)

MySQL> alter table table_name add primary key ( `column` )

2.UNIQUE 或 UNIQUE KEY (惟一索引)

mysql> alter table table_name add unique (`column`)

3.FULLTEXT (全文索引)

mysql> alter table table_name add fulltext (`column` )

4.INDEX (普通索引)

mysql> alter table table_name add index index_name ( `column` )

5.多列索引 (聚簇索引)

 mysql> alter table `table_name` add index index_name ( `column1`, `column2`, `column3` )

 

转载标明出处谢谢!

相关文章
相关标签/搜索