MySql教程

MySQL索引

本文主要是介绍MySQL索引,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

索引

什么是索引

  • 索引是排好序的数据结构,索引包括一个或者多个列的值,合适的索引可以快速的查找所需要的数据。就比如字典的目录。但是不恰当的索引,在数据量逐渐增大时,性能会急剧下降。

  • 索引对多个值进行排序的依据是按照CREATE TABLE语句中定义索引时列的顺序。存储引擎先在索引中在找到对应的值,然后根据匹配的索引记录找到对应的数据行(回表),聚簇索引无需回表。

优缺点

  • 优点:索引可以提高查询速度(主要原因)。通过创建唯一性的索引,可以保证数据库表中每一行的唯一性。
  • 缺点:索引需要物理文件存储,会占据一定的空间。创建索引和维护索引都需要时间,当对数据进行增产改查时候,索引也需要动态的修改,降低SQL执行效率。

索引底层的数据结构

mysql主要的索引类型分为BTree索引和哈希索引。对于不同的存储引擎,选择的数据结构不同。myism和innodb默认为b树索引,而memory引擎用的是哈希索引。

Hash索引

和Java中的HashMap类似。底层是哈希表实现,在等值查询中可以快速定位,时间复杂度接近O(1)。优点快。img

使用hash表必须拥有好的hash算法,否则会产生大量的哈希冲突或则hash碰撞,会导致数据散列不均匀,可能会退化成一个链表。

使用hash表也不支持范围顺序和范围查询,也是它最大的缺点。当需要范围或者顺序匹配的时候,就需要挨个比对,效率太低。不能用order by和group by也无法模糊匹配。

Innodb引擎支持自适应哈希,当某些索引值被频繁使用时,它会在内存中基于b树索引之上在创建一个哈希索引,使b树也具有哈希索引的一些优点。

B树和B+树

B树也称B-树,它们都为多路平衡查找树,B+树是B树的一种变体。

B树和B+树的差异

  • B树的所有节点即存放(key)也存放数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
  • B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
  • B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。img

在 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。

B树的缺点

由于节点也存放数据导致每个页能存储的数据变小。如每条数据1k,则一页只能存16条数据,3层的b树为4096条数据(16 * 16 * 16)条数据。如果在要扩大数据量,只能扩充数据页的大小,或者增加层数,但增加层数也就增加IO数。

B+树的优点

由于节点中没有存放数据,使每一个数据页可以存放更多的索引,千万级别的数据B+树3到4层足以。叶子节点中存放了全量数据,且叶节点间指针相连形成链表。这种方式使范围查询和全表扫描都更快。

为什么叶节点内部不采用2分查找?

当进行数据读取时,已经将16k加载到内存了,所以没必要了。

为什么选用B+树而不用二叉树(红黑树等)

二叉树、红黑树、avl树:只有两个分支,当数据太多时,树的深度过深,可能造成io次数过多,从而效率变低。

B+树出度更大,树更低,查询次数少,且IO操作少,顺序读取方便。

索引的类型

主键索引

数据表的主键列使用的就是主键索引。一张数据表有只能有一个主键,并且主键不能为 null,不能重复。在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。

主键是int类型好还是varchar类型好:越短越好。过长的索引会增加树的深度而且由于辅助索引都引用的是主键索引,过长的索引也会使辅助索引变的过大。 因为会产生页分列的情况,频繁的页分列导致性能下降,所以主键id最好自增。

二级索引(辅助索引)

二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。img

唯一索引,普通索引,前缀索引等索引属于二级索引。

  1. 唯一索引(Unique Key) :唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
  2. 普通索引(Index)普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。
  3. 前缀索引(Prefix) :前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小, 因为只取前几个字符。
  4. 全文索引(Full Text) :全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。

聚集索引与非聚集索引

聚簇索引

聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。

在 Mysql 中,InnoDB 引擎的表的 .ibd文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。所以一个表只能有一个聚簇索引。

聚簇索引具体选择策略

  • 如果你的表中定义了 PRIMARY KEY,那么,这就是此表的 聚簇索引;
  • 如果你的表中没有定义 PRIMARY KEY,但是定义了 非空唯一索引,那么,InnoDB 会选择第一个 非空唯一索引 作为此表的 聚簇索引;
  • 如果不满足上述两个条件,即表中没有 PRIMARY KEY 也没有合适的 UNIQUE,则 InnoDB 会自动生成一个隐藏的聚簇索引,此索引包含一个单点递增的 ID 列。

聚集索引的优点

聚集索引的查询速度非常的快,因为整个 B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。避免了回表。

聚集索引的缺点

  1. 依赖于有序的数据 :因为 B+树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
  2. 更新代价大 : 如果对索引列的数据被修改时,那么对应的索引也将会被修改, 而且况聚集索引的叶子节点还存放着数据,修改代价肯定是较大的, 所以对于主键索引来说,主键一般都是不可被修改的。

非聚集索引

非聚集索引即索引结构和数据分开存放的索引。二级索引属于非聚集索引。

MYISAM 引擎的表的.MYI 文件包含了表的索引, 该表的索引(B+树)的每个叶子非叶子节点存储索引, 叶子节点存储索引和索引对应数据的指针,指向.MYD 文件的数据。

非聚集索引的叶子节点并不一定存放数据的指针, 因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。

非聚集索引的优点

更新代价比聚集索引要小 。非聚集索引的更新代价就没有聚集索引那么大了,非聚集索引的叶子节点是不存放数据的

非聚集索引的缺点

  1. 跟聚集索引一样,非聚集索引也依赖于有序的数据
  2. 可能会二次查询(回表) :这应该是非聚集索引最大的缺点了。 当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。

使用索引的原则

覆盖索引

如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!

覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了, 而无需回表查询。

如主键索引,如果一条 SQL 需要查询主键,那么正好根据主键索引就可以查到主键。

再如普通索引,如果一条 SQL 需要查询 name,name 字段正好有索引, 那么直接根据这个索引就可以查到数据,也无需回表。

覆盖索引: img

回表

从某一个索引的叶子节点中获取聚簇索引的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.选择合适的字段创建索引:

  • 不为 NULL 的字段 :索引字段的数据应该尽量不为 NULL,因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL,建议使用 0,1,true,false 这样语义较为清晰的短值或短字符作为替代。
  • 被频繁查询的字段 :我们创建索引的字段应该是查询操作非常频繁的字段。
  • 被作为条件查询的字段:被作为 WHERE 条件查询的字段,应该被考虑建立索引。
  • 频繁需要排序的字段 :索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
  • 被经常频繁用于连接的字段 :经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段,可以考虑建立索引,提高多表连接查询的效率。

2.被频繁更新的字段应该慎重建立索引。

虽然索引能带来查询上的效率,但是维护索引的成本也是不小的。 如果一个字段不被经常查询,反而被经常修改,那么就更不应该在这种字段上建立索引了。

3.尽可能的考虑建立联合索引而不是单列索引。

因为索引是需要占用磁盘空间的,可以简单理解为每个索引都对应着一颗 B+树。如果一个表的字段过多,索引过多,那么当这个表的数据达到一个体量后,索引占用的空间也是很多的,且修改索引时,耗费的时间也是较多的。如果是联合索引,多个字段在一个索引上,那么将会节约很大磁盘空间,且修改数据的操作效率也会提升。

4.注意避免冗余索引

冗余索引指的是索引的功能相同,能够命中索引(a, b)就肯定能命中索引(a) ,那么索引(a)就是冗余索引。如(name,city )和(name )这两个索引就是冗余索引,能够命中前者的查询肯定是能够命中后者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。

5.考虑在字符串类型的字段上使用前缀索引代替普通索引。

前缀索引仅限于字符串类型,较普通索引会占用更小的空间,所以可以考虑使用前缀索引带替普通索引。

使用索引的一些建议

  • 对于中到大型表索引都是非常有效的,但是特大型表的话维护开销会很大,不适合建索引
  • 避免 where 子句中对字段施加函数,这会造成无法命中索引。
  • 在使用 InnoDB 时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
  • 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 schema_unused_indexes 视图来查询哪些索引从未被使用
  • 在使用 limit offset 查询缓慢时,可以借助索引来提高性能

MySQL 如何为表字段添加索引?

  1. 添加 PRIMARY KEY 主键索引

    ALTER TABLE `table_name` ADD PRIMARY KEY (`column`)
    
  2. 添加UNIQUE 唯一索引

    ALTER TABLE `table_name` ADD UNIQUE (`column`)
    
  3. 添加INDEX 普通索引

    ALTER TABLE `table_name` ADD INDEX index_name(`column`)
    
  4. 添加全文索引

    ALTER TABLE `table_name` ADD FULLTEXT(`column`)
    
  5. 添加多列索引(全文索引)

    ALTER TABLE `table_name` ADD INDEX index_name(`colum1`,`colum2`,`colum3`)
    
这篇关于MySQL索引的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!