索引是对数据库表中一列或多列的值进行排序的一种数据结构,使用索引能够快速访问数据库表中的特定信息。mysql
咱们建立索引的时候是这样的:算法
create index index_name on table_name(column)
能够这样想:索引是取出了一列或者几个特殊的列,而后把他们重命名为index_name,而后存起来,存的方式是用特殊的数据结构,好比二叉树。sql
索引中包含了一个表中列的值和它的地址的值,而且这些值存储在一个数据结构中。索引的组织形式有二叉树、哈希、B-树、B+树。若是索引存的是一列就叫作单索引,多列就是复合索引。数据库
索引通常以文件形式存在磁盘中(也能够存于内存),存储索引的原理大体归纳为以空间换时间。数据结构
执行这样一条语句:函数
select name from student where stuno = "003"
在一张学生表里面找学号是003的学生,假如一共到100号学生。那么DBMS会生成一个相似指针的东西,从stuno ="001"一直到最后,进行全表扫描。那么为何到stuno="003"不中止呢?由于数据库在未添加索引的时候默认执行的是全量搜索,select执行就就是全表扫描,即便扫描主键也同样。性能
若是咱们在stuno字段上建立了一个索引,假如索引的底层存储结构是二叉树,那么只须要log2(100)次就能查到所须要的数据。所以,创建索引的目的就是加快对表中记录的查找或排序。mysql索引
固然索引不是万能的,它的缺点有:建立和维护索引须要时间和空间成本,每次增删改索引都须要进行动态维护。优化
索引类型有:主键索引,普通索引,复合索引,全文索引,惟一索引搜索引擎
主键索引
--建立1 crete table student(id int primary key auto_increment, name varchar(20) not null default) --建立2:先建立表,后建立index alter table student add primary key(id) --在建立主键的时候,就会为咱们建立好主键索引。
惟一索引
惟一索引和普通索引相似,主要区别在于,惟一索引限制列的值必须惟一,但容许存在空值(只能有一个)。主键索引不容许有空值。
全文索引
在执行模糊查询的时候,如like "value%"
,这种状况下,须要考虑使用全文搜索的方式进行优化。全文搜索在MySQL中是一个FULLTEXT类型索引。全文索引主要用来查找文本中的关键字,而不是直接与索引中的值进行比较,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。目前只有char/vachar/text列上能够建立全文索引,默认Mysql不支持中文全文搜索。Mysql全文搜索只是一个临时方案,对于全文搜索场景,更专业的作法是使用全文搜索引擎,如ElasticSearch。
select * from user where id=7;
哈希算法首先计算出id=7的无力地址addr=hash(7)=4213,而4213映射的物理地址是0x77,而后就能够找到咱们想要的数据了。
从时间复杂度来看,哈希算法时间复杂度为o(1),速度很是快,可是Mysql并无采起哈希做为其底层算法。由于考虑到数据检索有一个经常使用的手段就是范围查找,使用哈希就没有办法作到高效的范围查找。好比:
select * from user where id>3
二叉查找树的时间复杂度是O(logn),从检索效率上看是能作到高效检索,而二叉树全部左子树比根节点小,右子树比根节点大的特性使得它也支持范围查找。
可是二叉树有个致命缺点:极端状况下会退化为线性表。在数据库中,数据的自增是一个很常见的形式,好比主键id通常默认都是自增的,若是采起二叉树做为索引的底层数据结构,那上面介绍的不平衡状态致使的线形查找问题必然出现!!!所以,简单的二叉树不能直接用于实现Mysql底层索引的。
学过数据结构的都知道,解决二叉树不平衡致使的性能问题,就是二叉平衡树。经过调整树的结构,来达到高度的平衡状态,可是频繁的调整树的结构也是一种开销,而红黑树就是舍去一部分的平衡性来得到低的调整开销。可是红黑树也存在“右倾现象”,查找性能又会下降。所以在查找性能和调整树的开销之间很难找到一种平衡,也不适合做为Mysql底层索引结构。
有人会问:数据库查询数据的瓶颈在于磁盘IO,调整树的开销存在于增删,而查找不须要调整,为何AVL树不适合呢?
答:AVL不是不适合,而是有更好的数据结构。咱们知道磁盘IO有一个特色,就是从磁盘读取1B的数据和1KB的数据所消耗的时间基本是同样的,那咱们的优化思路就能够改成:尽量在一次磁盘 IO 中多读一点数据到内存。这个直接反映到树的结构就是,每一个节点能存储的 key 能够适当增长,这就是B树、B+树的设计原理了。
B树和B+树又被称为多路查找,一个节点存储了多个key来减小磁盘IO,从而提升检索速度。
B树和B+树有什么区别呢?
B树一个节点里面存的是(key&value),而B+树存储的是(key),因此B树里一个节点存不了不少(key&value),可是B+树一个节点能存储不少(key),B+树叶子节点存全部的数据。
B+树的叶子节点是(key&value,并用一个链表串联起来。
经过 B 树和 B+树的对比咱们看出,B+树节点存储的是索引,在单个节点存储容量有限的状况下,单节点也能存储大量索引,使得整个 B+树高度下降,减小了磁盘 IO。其次,B+树的叶子节点是真正数据存储的地方,叶子节点用了链表链接起来,这个链表自己就是有序的,在数据范围查找时,更具有效率。所以 Mysql 的索引用的就是 B+树,B+树在查找效率、范围查找中都有着很是不错的性能。
Mysql底层数据引擎以插件形式设计,最多见的就是Innodb引擎和Myisam引擎,用户能够根据我的需求选择不一样的引擎做为Mysql数据表的底层引擎。咱们刚分析了,B+树做为 Mysql 的索引的数据结构很是合适,可是数据和索引到底怎么组织起来也是须要一番设计,设计理念的不一样也致使了 Innodb 和 Myisam 的出现,各自呈现独特的性能。
MyISAM 虽然数据查找性能极佳,可是不支持事务处理。Innodb 最大的特点就是支持了 ACID 兼容的事务功能,并且他支持行级锁。Mysql 创建表的时候就能够指定引擎,好比下面的例子,就是分别指定了 Myisam 和 Innodb 做为 user 表和 user2 表的数据引擎。
create table 'user' ( 'id' int not null default '0' ) engine=myisam default charset=utf8
Innodb建立表后生成的文件有:
Myisam建立表后生成的文件有:
从生成的文件看来,这两个引擎底层数据和索引的组织方式并不同,MyISAM 引擎把数据和索引分开了,一人一个文件,这叫作非汇集索引方式;Innodb 引擎把数据和索引放在同一个文件里了,这叫作汇集索引方式。下面将从底层实现角度分析这两个引擎是怎么依靠 B+树这个数据结构来组织引擎实现的。
MyISAM 用的是非汇集索引方式,即数据和索引落在不一样的两个文件上。MyISAM 在建表时以主键做为 KEY 来创建主索引 B+树,树的叶子节点存的是对应数据的物理地址。咱们拿到这个物理地址后,就能够到 MyISAM 数据文件中直接定位到具体的数据记录了。
当咱们为某个字段添加索引时,咱们一样会生成对应字段的索引树,该字段的索引树的叶子节点一样是记录了对应数据的物理地址,而后也是拿着这个物理地址去数据文件里定位到具体的数据记录。
InnoDB 是汇集索引方式,所以数据和索引都存储在同一个文件里。首先 InnoDB 会根据主键 ID 做为 KEY 创建索引 B+树,如左下图所示,而 B+树的叶子节点存储的是主键 ID 对应的数据,好比在执行 select * from user_info where id=15 这个语句时,InnoDB 就会查询这颗主键 ID 索引 B+树,找到对应的 user_name='Bob'。
这是建表的时候 InnoDB 就会自动创建好主键 ID 索引树,这也是为何 Mysql 在建表时要求必须指定主键的缘由。当咱们为表里某个字段加索引时 InnoDB 会怎么创建索引树呢?好比咱们要给 user_name 这个字段加索引,那么 InnoDB 就会创建 user_name 索引 B+树,节点里存的是 user_name 这个 KEY,叶子节点存储的数据的是主键 KEY。注意,叶子存储的是主键 KEY!拿到主键 KEY 后,InnoDB 才会去主键索引树里根据刚在 user_name 索引树找到的主键 KEY 查找到对应的数据。
问题来了,为何 InnoDB 只在主键索引树的叶子节点存储了具体数据,可是其余索引树却不存具体数据呢,而要画蛇添足先找到主键,再在主键索引树找到对应的数据呢?
其实很简单,由于 InnoDB 须要节省存储空间。一个表里可能有不少个索引,InnoDB 都会给每一个加了索引的字段生成索引树,若是每一个字段的索引树都存储了具体数据,那么这个表的索引数据文件就变得很是巨大(数据极度冗余了)。从节约磁盘空间的角度来讲,真的没有必要每一个字段索引树都存具体数据,经过这种看似“画蛇添足”的步骤,在牺牲较少查询的性能下节省了巨大的磁盘空间,这是很是有值得的。
在进行 InnoDB 和 MyISAM 特色对比时谈到,MyISAM 查询性能更好,从上面索引文件数据文件的设计来看也能够看出缘由:MyISAM 直接找到物理地址后就能够直接定位到数据记录,可是 InnoDB 查询到叶子节点后,还须要再查询一次主键索引树,才能够定位到具体数据。等于 MyISAM 一步就查到了数据,可是 InnoDB 要两步,那固然 MyISAM 查询性能更高。
复合索引:选择索引列的顺序
1)尽可能把字段长度小的列放在联合索引的最左侧(由于字段长度越小,一页能存储的数据量越大,IO性能也就越好) 2)区分度最高的放在联合索引的最左侧(区分度=列中不一样值的数量/列的总行数) 3)使用最频繁的列放到联合索引的左侧(这样能够比较少的创建一些索引)
表关联查询
1)类型和大小要相同,可使用索引。 VARCHAR(10)和 CHAR(10)大小相同,但 VARCHAR(10)与 CHAR(15)不相同。 2)字符串列之间比较,两列应使用相同的字符集。例如,将utf8列与 latin1列进行比较会不使用索引。 3)将字符串列与时间或数字列进行比较时,在没有转换状况下,不使用索引。