索引是排好序的数据结构,索引包括一个或者多个列的值,合适的索引可以快速的查找所需要的数据。就比如字典的目录。但是不恰当的索引,在数据量逐渐增大时,性能会急剧下降。
索引对多个值进行排序的依据是按照CREATE TABLE语句中定义索引时列的顺序。存储引擎先在索引中在找到对应的值,然后根据匹配的索引记录找到对应的数据行(回表),聚簇索引无需回表。
mysql主要的索引类型分为BTree索引和哈希索引。对于不同的存储引擎,选择的数据结构不同。myism和innodb默认为b树索引,而memory引擎用的是哈希索引。
和Java中的HashMap类似。底层是哈希表实现,在等值查询中可以快速定位,时间复杂度接近O(1)。优点快。
使用hash表必须拥有好的hash算法,否则会产生大量的哈希冲突或则hash碰撞,会导致数据散列不均匀,可能会退化成一个链表。
使用hash表也不支持范围顺序和范围查询,也是它最大的缺点。当需要范围或者顺序匹配的时候,就需要挨个比对,效率太低。不能用order by和group by也无法模糊匹配。
Innodb引擎支持自适应哈希,当某些索引值被频繁使用时,它会在内存中基于b树索引之上在创建一个哈希索引,使b树也具有哈希索引的一些优点。
B树也称B-树,它们都为多路平衡查找树,B+树是B树的一种变体。
在 MySQL 中,MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是,两者的实现方式不太一样。(下面的内容整理自《Java 工程师修炼之道》)
MyISAM 引擎中,B+Tree 叶节点的 data 域存放的是数据记录的地址。在索引检索的时候,首先按照 B+Tree 搜索算法搜索索引,如果指定的 Key 存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。
InnoDB 引擎中,其数据文件本身就是索引文件。相比 MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。这个索引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”,而其余的索引都作为辅助索引,辅助索引的 data 域存储相应记录主键的值而不是地址,这也是和 MyISAM 不同的地方。在根据主索引搜索时,直接找到 key 所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,在走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
索引不可能全部存储在内存中,往往以索引文件的形式存储在磁盘上。这样索引在查找过程中就会产生磁盘的io消耗,相对于内存读取io存取消耗很大。所以说一个索引的好坏就成了再查找过程中磁盘io的操作次数好好坏。一般来说,一个io操作读取一页内容。操作系统中,页得大小通常为4k。B+树在新创建节点时候,直接申请一个页或几个页的空间,也保证了再物理上也存储在一个页里,计算机存储分配也是按页对齐的,读的时候很方便。mysql的一页数据大小为16k。
由于节点也存放数据导致每个页能存储的数据变小。如每条数据1k,则一页只能存16条数据,3层的b树为4096条数据(16 * 16 * 16)条数据。如果在要扩大数据量,只能扩充数据页的大小,或者增加层数,但增加层数也就增加IO数。
由于节点中没有存放数据,使每一个数据页可以存放更多的索引,千万级别的数据B+树3到4层足以。叶子节点中存放了全量数据,且叶节点间指针相连形成链表。这种方式使范围查询和全表扫描都更快。
当进行数据读取时,已经将16k加载到内存了,所以没必要了。
二叉树、红黑树、avl树:只有两个分支,当数据太多时,树的深度过深,可能造成io次数过多,从而效率变低。
B+树出度更大,树更低,查询次数少,且IO操作少,顺序读取方便。
数据表的主键列使用的就是主键索引。一张数据表有只能有一个主键,并且主键不能为 null,不能重复。在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。
主键是int类型好还是varchar类型好:越短越好。过长的索引会增加树的深度而且由于辅助索引都引用的是主键索引,过长的索引也会使辅助索引变的过大。 因为会产生页分列的情况,频繁的页分列导致性能下降,所以主键id最好自增。
二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。
唯一索引,普通索引,前缀索引等索引属于二级索引。
聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。
在 Mysql 中,InnoDB 引擎的表的 .ibd
文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。所以一个表只能有一个聚簇索引。
聚集索引的查询速度非常的快,因为整个 B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。避免了回表。
非聚集索引即索引结构和数据分开存放的索引。二级索引属于非聚集索引。
MYISAM 引擎的表的.MYI 文件包含了表的索引, 该表的索引(B+树)的每个叶子非叶子节点存储索引, 叶子节点存储索引和索引对应数据的指针,指向.MYD 文件的数据。
非聚集索引的叶子节点并不一定存放数据的指针, 因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
更新代价比聚集索引要小 。非聚集索引的更新代价就没有聚集索引那么大了,非聚集索引的叶子节点是不存放数据的
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了, 而无需回表查询。
如主键索引,如果一条 SQL 需要查询主键,那么正好根据主键索引就可以查到主键。
再如普通索引,如果一条 SQL 需要查询 name,name 字段正好有索引, 那么直接根据这个索引就可以查到数据,也无需回表。
覆盖索引:
从某一个索引的叶子节点中获取聚簇索引的id值,根据id再去聚簇索引中获取全量记录;要尽量减少回表查询。
select * from table where name = wangzixuan and age = 10(其中name和age为组合索引)
在没有索引下坠之前,sql执行的过程:先根据name去存储引擎拿到全量数据,将数据读取到server层,然后在server层按age做数据过滤
有了索引下坠后,根据name和age两列去存储引擎筛选数据,将最终的结果返回给客户端
索引的选择性值: 不重复的索引值/表记录数的比值
SELECT count(DISTINCT(字段名))/count(*)
范围为(0,1],越大越有辨识度
当索引的长度过长时,用索引的前几个字符来组成索引。但是不能用于order by 和 group by操作,也不能用于覆盖索引。
从左往右匹配,直到遇到范围查询。
建立联合索引(a,b,c):索引是先根据a排序,a相同时b有序,a不同无序,以此类推。总之遇到范围查询就停。
索引对多个值进行排序的依据是按照CREATE TABLE语句中定义索引时列的顺序。
(a,b)联合索引 [(2,4),(),()] \|/ \|/ [(1,1),(1,2),(2,1)] [(2,4),(3,1),(3,2)] 规律:a有顺序(1,1,2,2,2,3,3)b无顺序,a相同时b又有顺序,不同a之间b没有顺序,所以a=1,b>2走联合索引;a>1,b>2不走索引。 select * from table_name where a = '1' and b = '2' and c = '3' //全值匹配查询,用到索引,与顺序无关,查询优化器,会自动优化查询顺序 select * from table_name where a = '1' select * from table_name where a = '1' and b = '2' select * from table_name where a = '1' and b = '2' and c = '3' //匹配左边的列时,用到了索引 select * from table_name where b = '2' select * from table_name where c = '3' select * from table_name where b = '1' and c = '3' //没有用到索引 select * from table_name where a = '1' and c = '3' //a用到了索引,b、c没有到 select * from table_name where a > 1 and a < 3 and b > 1; //只有a用到索引,在1<a<3的范围内b是无序的,不能用索引,找到1<a<3的记录后,只能根据条件 b > 1继续逐条过滤 select * from table_name where a = 1 and b > 3; // a=1的情况下b是有序的,进行范围查找走的是联合索引 走 a b索引(a相同时b有序)
1.选择合适的字段创建索引:
2.被频繁更新的字段应该慎重建立索引。
虽然索引能带来查询上的效率,但是维护索引的成本也是不小的。 如果一个字段不被经常查询,反而被经常修改,那么就更不应该在这种字段上建立索引了。
3.尽可能的考虑建立联合索引而不是单列索引。
因为索引是需要占用磁盘空间的,可以简单理解为每个索引都对应着一颗 B+树。如果一个表的字段过多,索引过多,那么当这个表的数据达到一个体量后,索引占用的空间也是很多的,且修改索引时,耗费的时间也是较多的。如果是联合索引,多个字段在一个索引上,那么将会节约很大磁盘空间,且修改数据的操作效率也会提升。
4.注意避免冗余索引 。
冗余索引指的是索引的功能相同,能够命中索引(a, b)就肯定能命中索引(a) ,那么索引(a)就是冗余索引。如(name,city )和(name )这两个索引就是冗余索引,能够命中前者的查询肯定是能够命中后者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
5.考虑在字符串类型的字段上使用前缀索引代替普通索引。
前缀索引仅限于字符串类型,较普通索引会占用更小的空间,所以可以考虑使用前缀索引带替普通索引。
添加 PRIMARY KEY 主键索引
ALTER TABLE `table_name` ADD PRIMARY KEY (`column`)
添加UNIQUE 唯一索引
ALTER TABLE `table_name` ADD UNIQUE (`column`)
添加INDEX 普通索引
ALTER TABLE `table_name` ADD INDEX index_name(`column`)
添加全文索引
ALTER TABLE `table_name` ADD FULLTEXT(`column`)
添加多列索引(全文索引)
ALTER TABLE `table_name` ADD INDEX index_name(`colum1`,`colum2`,`colum3`)