一、索引:
1、索引存在的意义:为了提高数据查询的效率,对于数据库的表而言,索引其实就是它的“目录”。
2、索引的常见模型:哈希表、有序数组和搜索树。
1).哈希表是一种以键-值(key-value)存储数据的结构,我们只要输入待查找的值即key,就可以找到其对应的值即Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把key换算成一个确定的位置,然后把value放在数组的这个位置。哈希冲突的处理办法:链表。哈希表适用于只有等值查询的场景。(即:哈希表添加数据速度快,但查询的速度很慢)。
2).有序数组在等值查询和范围查询场景中的性能都非常优秀。有序数组索引只适用于静态存储引擎。查询时可以用二分法快速得到,这个时间复杂度是O(log(N))。(即:数组查询数据的速度很快,但更新数据的速度很慢)。
3).二叉搜索树的特点是:每个节点的左儿子小于父节点,父节点又小于右儿子。查询时的时间复杂度是O(log(N))。为了维持O(log(N))的查询复杂度,需要保持这棵树是平衡二叉树。为了做这个保证,更新的时间复杂度也是O(log(N))。实际上大多数的数据库存储却并不使用二叉树。其原因是,索引不止存在内存中,还要写到磁盘上。在机械硬盘时代,从磁盘随机读一个数据块需要10 ms左右的寻址时间。也就是说,对于一个100万行的表,如果使用二叉树来存储,单独访问一个行可能需要20个10 ms的时间,非常慢。为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,就不应该使用二叉树,而是要使用“N叉”树。这里,“N叉”树中的“N”取决于数据块的大小。多叉树就是每个节点有多个儿子,儿子之间的大小保证从左到右递增。
二、InnoDB 的索引模型:
1、在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。InnoDB使用的是B+树索引模型,所以数据都是存储在B+树中的。每个索引在InnoDB里面对应一棵B+树。也就是说:每一张表其实就是多棵B+树,树结点的key值就是某一行的主键,value是该行的其他数据。新建索引就是新增一个B+树,查询不走索引就是遍历主B+树。 (B/B+/B-/B*树相关:https://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html、https://www.jianshu.com/p/486a514b0ded)
2、根据叶子节点的内容,索引类型分为主键索引和非主键索引。主键索引的叶子节点存的是整行数据。在InnoDB里,主键索引也被称为聚簇索引(clustered index)。非主键索引的叶子节点内容是主键的值。在InnoDB里,非主键索引也被称为二级索引(secondary index)。
3、基于主键索引和普通索引的查询的区别:
主键查询方式,则只需要搜索ID这棵B+树;普通索引查询方式,则需要先搜索k索引树,得到主键的值,再到主键的索引树搜索一次。这个过程称为回表(回到主键索引树搜索的过程)。也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。注意:没有主键的表,InnoDB会给默认创建一个Rowid做主键
三、索引维护:
1、页分裂:B+树为了维护索引有序性,在插入新值的时候需要做必要的维护。当一个数据页满了,按照B+Tree算法,新增加一个数据页,然后挪动部分数据过去。这步叫做页分裂,会导致性能下降。空间利用率降低大概50%。当相邻的两个数据页由于删除了数据,导致利用率很低的时候会做数据页合并,合并的过程是分裂过程的逆过程。
2、自增主键:是指自增列上定义的主键,在建表语句中一般是这么定义的: NOT NULL PRIMARY KEY AUTO_INCREMENT。插入新记录的时候可以不指定主键的值,系统会获取当前主键最大值加1作为下一条记录的主键的值。
3、应该使用自增主键的场景:从性能的角度考虑,自增主键的插入数据模式,符合了“递增插入”的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。从存储空间的角度考虑,假设表中有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点(主键的值)占用约20个字节,而如果用整型做主键,则只要4个字节,如果是长整型(bigint)则是8个字节。显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。
4、不应该使用自增主键场景:只有一个索引且该索引必须是唯一索引。你一定看出来了,这就是典型的KV场景。由于没有其他索引,所以也就不用考虑其他索引的叶子节点大小的问题。此时应该优先考虑“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。
四、覆盖索引:
1、由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
2、最左前缀原则:先要看第一列的索引项,在第一列满足的条件下再看左边第二列的索引项,以此类推。索引项是按照索引定义里面出现的字段顺序排序的。只要满足最左前缀,就可以利用索引来加速检索。这个最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。
3、在建立联合索引的时候,安排索引内的字段顺序的原则:评估标准是,索引的复用能力。因为可以支持最左前缀,所以当已经有了(a,b)这个联合索引后,一般就不需要单独在a上建立索引了。所以, 第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。如果既有联合查询,又有基于a、b各自的查询呢?查询条件里面只有b的语句,是无法使用(a,b)这个联合索引的,此时不得不维护另外一个索引,也就是说需要同时维护(a,b)、(b) 这两个索引。这时候,我们要考虑的原则就是空间了。比如上面这个情况,a字段是比b字段大的 ,此时建议创建一个(a,b)的联合索引和一个(b)的单字段索引。
五、索引下推:
1、概念:可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。(MySQL 5.6 引入)。比如说,检索出表中“名字第一个字是张,而且年龄是10岁的所有男孩”。(mysql> select * from tuser where name like '张%' and age=10 and ismale=1;),无索引下推时,这个过程InnoDB并不会去看age的值,只是按顺序把“name第一个字是’张’”的记录一条条取出来回表。因此,需要回表所有姓张的人数。InnoDB在(name,age)索引内部就判断了age是否等于10,对于不等于10的记录,直接判断并跳过。只需要对符合条件的记录回表取数据判断,回表次数等于符合条件的人数,小于等于无索引下推时的回表次数。