mysql学习笔记四

先说个题外话,docker可以非常方便的管理一些常用工具,但是server端如果服务器是windows的话只能安装在64位机器上面。
这一节主要学习了索引,一说到索引,在我们定位sql查询慢的时候第一时间就会想到索引的命中以及优化问题。。。
用一句话总结下就是:索引的出现就是为了提高查询的效率,索引直白的理解就像是一本书的目录一样。

索引模型:

有三种常见的数据结构,分别是hash表,有序数组和搜索树。(其他的结构后续也许会补充)
hash索引:
hash表是key-value的数据结构,通过key查找value的思想在这里也是一种体现。但是有个问题,多个key经过hash计算可能出现相同值的情况,对这种问题的处理办法就是,拉出来一个链表,具体解释下:
mysql学习笔记四_第1张图片
根据上图,根据索引查找User2,但是User4和User2的key计算出的hash值相等,这里的处理步骤就是,先计算hash表里面key=N的位置,缩小了查找空间,然后遍历链表,找到User2。
上图中,可以看到,这四条记录并不是连续的,非常符合hash的散列排布方式,这样带来的的优点是:增加一条记录的速度非常快。但是如果是查找一个区间之内的值,速度就很慢了,所以用hash索引的话,对于区间查找来说,效率是比较低的。
总结下,哈希表这种结构使用于只有等值查询的场景
有序数组:
有序数组在等值查询和范围查询的性能都很优秀。
mysql学习笔记四_第2张图片
如图所示,等值查询通过二分法快速查询,时间复杂度是O(log(N)),范围查询就是找到区间左侧边界值之后,想数组右侧遍历,直到遍历完区间,每个遍历的值都是查询的结果。
但是缺点也很明显,每当查询一条新记录时,如果是往中间插入记录的话,后面的所有记录又必须向后移位,成本过高。
所以,有序数组只适用于静态存储引擎。比如说要保存2018年某个城市的人口数据,这种数据不会再修改,使用有序数组引擎是非常合适的。
搜索树:
之前我们学习过二叉树的概念,其中中序遍历的树,从左儿子节点出发,父节点大于左儿子,小于右儿子,查找效率是O(log(N)),为了保持这个效率,这棵树得是平衡二叉树。平衡二叉树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
上面说到了效率,二叉树的查询效率似乎很可观,插入新数据也不是问题(插入新数据该怎么插?。。。因为要满足二叉树顺序排列规则,所以插入的时候相关节点还是得移位把,,我这里没想明白,后面复习的时候来补充),那么实际使用的时候,是否直接采用二叉树的结构作为索引数据结构呢?
答案是否定的,因为索引的话不止存在于内存中,还要写到磁盘上。打个比方,因为二叉树单层存储数据量比较小,一张比较大的表比如说有100W的数据量,作为平衡二叉树他的深度是20,这就代表命中一行可能需要访问20次数据块的时间,在机械硬盘时代,平均一个数据块耗时10ms,那么整体下来整个时间就是200ms了,这对于性能来说是不可接受的。
所以为了让执行器尽可能少的去搜索磁盘,我们有了N叉树的概念,比如说数据库引擎InnoDB它采用了N叉树的数据结构存储索引,这个N大约为1200,想象一下,深度为4的时候,数据量已经达到17亿了,这个数据量的时候,查询索引仅仅最多只需要访问三次磁盘(因为树的根节点总是保存在内存中的)。
结论是:N叉树由于读写上性能的优点,以及适配磁盘访问性能的模式,已经被广泛的应用在数据库引擎中了
InnoDB的索引:
为了更清楚的理解树结构的索引,我们以InnoDB为例子说明树结构索引的读写原理。
InnoDB使用了B+树索引模型,索引是存放在B+树中的,每个索引在InnoDB对应以可B+树。
我们新建一张表:id是主键,设置了k有索引

mysql> create table T(
id int primary key, 
k int not null, 
name varchar(16),
index (k))engine=InnoDB;

表中(id,k)的值分别为(100,1),(200,2),(300,3),(400,4),(500,5)和(600,6)
mysql学习笔记四_第3张图片
id的索引是InnoDB自带的主键索引(聚簇索引),存放的是整行的数据,k的索引存放内容是主键的值,也叫二级索引。那么两种索引有何区别呢?打个比方:

select * from T where ID=500

会直接在主键索引表里面命中索引,返回整行的数据

select * from T where k=5

会先在二级索引查找到主键索引,然后在聚簇索引查找到对应的行。
就是说二级索引会多一次查询索引表,所以我们应该尽量使用主键索引。

索引维护:

回到上面的问题,B+树为了维护索引表的有序性,在插入索引数据的时候会做必要的维护。
以上图为例,如果插入新行id为700,只需要在R5后面插入一个新记录,但是如果插入的是400,就相对麻烦,需要逻辑上移动后面的数据,空出位置。
还以一种更糟糕的情况,如果R5所在的数据页已经满了,根据B+树的算法,需要申请一个新的数据页,然后挪动部分数据过去,这个过程称为页分裂,这种情况,性能必然会受到影响。除了性能,页分裂操作还影响数据页的利用率,原本存放在一个页的数据,现在需要两页保存,整体空间利用率降低大约50%,相对的,如果有相邻两页有数据删除,会有一个数据页合并的操作,这个具体操作原文没说,后面学习
我们日常操作建表的时候,一般会带一个自增主键,这种操作是否必要呢?
NOT NULL PRIMARY KEY AUTO_INCREMENT,这样建的表,在插入新数据的时候不需要带入这个主键,mysql自动取当前记录的主键最大值加一操作入库,对于InnoDB引擎来说,自增主键是非常适合的,因为这种插入是一个追加的插入,不涉及挪动其他数据,也不会触发叶子节点的分裂,同样道理,如果我们使用业务数据做主键,往往不是顺序追加的操作,这样写数据的成本会比较高。
一种情景,表里面有一个身份证号码,把这个号码用作主键,那么每个非主键索引保存主键索引的情况下,每个非主键都保存身份证号码,所占用的空间自然比我们默认自增主键的空间消耗得多。这种行为显然不可取。
所以,从性能和空间占用来看,自增主键是比较好的选择。
那么,有没有什么场景适合业务数据字段做主键呢?
这要看业务需求,1.只有一个索引2.这个索引必须是唯一索引—>这就是典型的KV场景,有点不明白需求1,2有什么区别

你可能感兴趣的:(mysql)