Mysql基础课五:索引

索引的基础知识

  1. Mysql中,索引是在存储引擎层实现的,创建索引,通过语句:
	CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [USING index_type] 
		ON tbl_name (index_col_name,...)
 	其中 index_col_name 是 col_name [(length)] [ASC | DESC]
  1. 索引的数据结构,常用的有:哈希表,有序数组,b+树;
	哈希表,适合于等值查找,本身无序,不适合范围查找
	有序数组,使用二分法就很容易进行等值查询和范围查询,所以比较适合等值和范围查找,但更新,插入数据都会很慢
	b+树,多叉平衡树,最后一层会存放所有节点,其插入和搜索的时间复杂度都是O(LogN),考虑到磁盘 IO 尽量少,InnoDB 的索引实现是 b+

聚簇索引和非聚簇索引

  1. 主键索引,又称为聚簇索引,每张表只能由一个聚簇索引,联合索引,唯一索引,前缀索引都是非聚簇索引;
	如果没有主键,会使用一个唯一且不为空的索引作为聚簇索引,如果这样的索引也没有,InnoDB表就会隐式生成一个 rowid,作为聚簇索引
  1. 每个索引都是一个 B+ 树,聚簇索引的叶子节点,是每条行记录,非聚簇索引,其叶子节点内容是主键值和索引字段,每创建一个索引,就表示多维护一棵 B+ 树;

  2. 如果使用主键查找记录,只需在主键索引的 B+ 树进行查找,如果使用非主键索引查找,需要先在非主键索引的 B+ 树,查找得到主键值,然后再通过主键值在主键索引的 B+ 树查找,这个过程被称为回表;

  3. 为了避免回表,如果在非主键索引的叶子节点上,已经有了想要的结果,这种索引被称为覆盖索引;

	举例,一张用户表,高频请求,根据身份证号查询用户名,如果只在身份证号上,建立了非主键索引,那么需要一次回表,才能获取到用户名,如果建立身份证号和用户名的联合索引,就不需要回表,可以得到结果
  1. 注意联合索引,是索引树,首先通过最左字段组织,然后最左字段相同的记录,会按照次左字段依次有序,同样一直到联合索引的最后一个字段,所以有最左前缀原则,即只有按照最左顺序的字段查找,才可以用到索引;

唯一索引和非唯一索引

  1. 两类索引在查询性能上,几乎没差别,但更新操作,非唯一索引利用内存 change buffer,性能会优于唯一索引,所以建议业务允许的情况下,优先考虑非唯一索引;

  2. 唯一索引,在更新操作时,需要将数据页读入内存中,然后进行更改;

  3. 而普通索引,可以将更新操作,只记录到内存的 change buffer 中,等到之后的读操作,才将 change buffer 的内容,merge 回内存的数据页中,这种机制减少了磁盘访问,效率更高;

  4. 如果更新操作常伴随着查询,就应该关闭 change buffer,直接写到数据页,因为写到 change buffer 后,马上的读操作也要立即 merge 回数据页;

  5. 注意 change buffer 的修改,在事务提交时,也会记录到 redo log 中,这样即使 Mysql 宕机,change buffer 的内容也不会丢失;

字符串索引

  1. 对于字符串字段,可以直接创建完整索引,或者创建前缀索引,或者先倒序存储,然后创建前缀索引,或者先增加hash字段,然后在hash字段上创建索引,这些手段,都是为了使得索引字段的区分度更好;

  2. 前缀索引,即以某个字段的前几位字符,用来建立索引树,选择合适的字符位数,建立前缀索引,可以节省空间,还不会增加额外的查询成本,对于查询结果包含该索引字段时,也需要回表查询下完整值;

  3. 索引区分度,越高越好,通过 SQL 语句 select count(distinct email) from user 来判断 email 这个列的区分度,通过 SQL语句 select count(distinct left(email,4)) from user 来判断 email 字段的 前4 个字节的前缀索引的区分度;

  4. 如果前缀索引也无法提供更好的区分度,而应用中字符串都是等值查询,可以考虑使用创建 倒序索引 和 额外hash字段索引;

  5. 倒序索引,即先将该字段倒序存储,然后在该倒序字段创建前缀索引;额外hash字段索引,是新添加一个对该字段hash值的存储字段,这样其区分度变高了,但查询时还是要带上原字段,因为不同字段值,其hash结果可能一致,如:select id_card from user where id_card_crc = crc32(input) and id_card = iput;

主键选择

  1. 推荐使用自增主键,int 类型,占空间小,并且非主键索引的占用空间也会小一些,如果只能有一个索引的情况下,也推荐使用业务字段做主键;

  2. 自增主键和 uuid 作为主键,还有一个区别是,如果主键是自增,那么对应的数据一定也是相邻存放在磁盘上的,写入性能会比较高;如果是 uuid 的形式,频繁的插入会频繁的移动磁盘块,写入性能就比较低了;

索引选择

  1. 索引选择是 Mysql 的优化器来决定的,通过 explain 可以查看 sql 语句的执行情况和索引选择情况;

  2. 优化器对索引字段的选择,会考虑 扫描行数,是否使用临时表,是否排序 等因素,扫描行数,是根据索引的区分度和预期的扫描行数来决定的,索引的区分度,是采样统计的,非准确值,预期扫描行数,是根据…;

  3. 如果发现,执行计划中的 rows 与实际情况差距较大,采样值可能不准,可以使用 analyze table t 来重新统计采样值;

  4. 如果,想要改变 Mysql 的索引选择,可以通过 force index () 来强制指定一个索引,或者 通过修改 SQL 语句,来使 Mysql 使用我们期望的索引,如,order by 语句让 Mysql 错误的选择了索引,还可以通过 增加和删除索引来解决这个问题;

索引的匹配原则

  1. 联合索引,最左匹配原则,name + age + position 的联合索引,只有 name,name + age,name + age + position 会用到索引,其他组合无法使用;

  2. 联合索引,如果是等值查询,mysql 会自动优化,按索引顺序,如果是范围查找,范围右边的索引失效,范围当前索引有效;

  3. 对索引列,进行任何计算,函数,自动或手动类型转换,均会导致索引失效;

  4. 不等于操作 !=,<> 会使索引失效;

  5. 尽量使用覆盖索引,即通过索引树查找可以得到查询结果,不用回表;

  6. like 操作用通配符开头,且非覆盖索引时,会使索引失效,改为使用覆盖索引,??当覆盖索引指向的字段是 varchar(380) 及以上时,索引会失效;

  7. 字符串类型字段不加单引号,会导致索引失效;

  8. or 操作可能导致索引失效;

  9. in 和 exist,当 B 的数据集小于 A 时,in 优于 exist,反之,如果 B 数据集大,exist 更优,通常 exist 可以使用 join 替代;

	select * from A t where t.id in (select id from B); 
	select * from A t where EXISTS (select f.id from B f where t.id = f.id)

你可能感兴趣的:(Mysql)