2019独角兽企业重金招聘Python工程师标准>>>
问题:SQL查询慢怎么办?
优化手段,加索引。
索引是帮助MYSQL高效的获取数据的排好序的数据结构。
问题:索引结构为什么使用Btree而不使用二叉树,红黑树或者HASH结构?
二叉树的特性,右边子节点的值大于父节点,且左边子节点的值小于父节点
二叉树:使用二叉树来做索引的数据结构,当索引值递增的时候,二叉树也会不断地增加右子节点 ,那么在查找索引的时候,所做的IO操作,等同于没有索引逐行查找数据的IO操作。
红黑树:平衡二叉树。随着数据量增大,树的高度也会变高。2的N次方=数量量(假设100万),那么N至少也是50,那么树的高度也是50,如果数据在子叶节点上,那么至少要经过50次的IO操作,才能找到数据。
HASH:散列表结构,key不适合排序,而且数据是散列的分布的,不利于IO读写。
BTree:一个节点上,横向扩展。即一个节点上,可以存储多个元素(Degree),且每个元素,都可以有子节点,叫做多叉树。
- 度(Degree)-节点的数据存储个数
- 叶节点具有相同深度
- 叶节点的指针为空
- 节点中的数据KEY从左至右递增排列
以5阶B树举例
B+Tree(Btree变种)
- 非叶子节点不存储data,只存储key,可以增大度(Degree)-节点的数据存储个数
- 叶子节点不存储指针
- 叶子节点增加了顺序访问指针,提高区间访问的性能
以5阶B+树举例
问题:Btree和B+Tree的区别?
- Btree每个节点,都包含了key和data,而B+tree只有叶子节点保存了key和data,其他节点,只保存了key,方便增大度的个数
- B+tree的叶子节点之间, 增加了指针,提高了查询区间的性能,不需要像Btree一样每次都从根节点开始查找
- Btree中,父节点和叶子节点,关键字的字段值不会冗余,而B+tree会冗余的存储关键的索引值,即父节点和叶子结点都存在相同的关键值
MyISAM索引实现(非聚集)
MyISAM索引文件和数据文件是分离的。它是针对建表的,而不是针对数据库的,既同一个数据库可以多种类型的表,myISAM和innoDB。
在mysql的文件件中,会发现一个表有三个文件:
- test_table_myisam.frm // 存储表的结构,表的定义
- test_table_myisam.MYD // 存储表的数据
- test_table_myisam.MYI // 存储表的索引文件
myisam存储引擎是非聚集索引,所以在MYI的文件中,以B+tree的数据结构存储了表的索引信息,而索引内容中的data,是指向数据的在MYD文件中的地址。
在myisam存储引擎中,主键索引使用了B+tree,而辅助索引,非主键索引,也同样根据字段的值,使用了B+tree结构来存储。
innoDB索引实现(聚集)
- 表数据文件本身就是按B+Tree组织的一个索引结构文件
- 聚集索引 定义:叶节点包含了完整的数据记录
- 为什么innoDB表必须有主键,并且推荐使用整形的自增主键?
- 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
在mysql的文件件中,会发现一个表有两个文件:
- test_table_myisam.frm // 存储表的结构,表的定义
- test_table_myisam.ibd // 存储表的索引+数据
在innoDB存储引擎中,辅助索引、非主键索引都不是聚集索引。
问题:为什么B+tree比Btree快?
树的高度h,影响了查找效率,因为从根节点开始,每次查找下一个节点,都可能需要一次IO读写。所以增加非叶子节点的度,可以有效提高查询效率。而CPU从磁盘上读取数据到内存中,最小单元是页,一页读取的最小数据是4KB,而CPU一次IO,读取的数据是一页的整数倍,且最大值也有限制。而mysql在底层,设置的大小是16KB。所以B+TREE要把数据放在叶子节点,所以非叶子几点,就可以增加更多的节点数。而Btree节点和数据是保存在一起的,所以非叶节点的节点数,要比B+tree少,树的高度就比B+tree高。
问题:为什么MYSQL页文件默认的配置是16KB?
我们假设一行的数据是1K,那我们一页的数据,就能存储16行数据,也就是一个叶节点可以存储16条记录;再来看非叶节点,假设ID是bigint类型,那么长度为8B,指针大小在InnoDB源码中为64(6B),一共就是14B,那么一页里面就可以存储16K/14=1170个(主键+指针)
那么一颗高度为2的B+树能存储的数据为:1170*16=18720条,一颗高度为3的B+树可以存储1170*1170*16=21902400(千万条)
原因就是16KB就足够应对千万条表记录了。
问题:为什么innoDB表必须有主键,并且推荐使用整形的自增主键?
使用innoBD存储引擎时,要建立主键索引,如果我们没有主动创建索引,那么innoDB会自动帮我们建立主键索引。innoDB会在我们创建的表里找一列唯一的,可以代表主键的字段来创建主键索引,如果没有这样的字段,innoDB会默认加一列字段,来创建主键索引。有点类似oracle中的rowId。为什么要推荐整形而且自增的字段当做主键?为什么建议使用整形类型,因为如果使用uuid作为主键索引,uuid类型比整数类型大,为了方便IO操作一次读取4个页的数据(mysql默认16KB,一页是4KB),意味索引的非叶子节点上的关键字要比使用整数类型的关键字数量小,那么B+tree的高度就可能增加,那么读取叶子节点上的数据的IO操作次数就增加了。那么查找的效率就低了。为什么要自增,因为当新纪录插入时,主键自增的话,在B+tree中插入节点比较方便,而使用uuid为主键,新纪录的uuid不一定比前面的uuid大,可能会将新纪录插在靠前面的叶子节点中,叶子节点满了,会取中间的关键字向上存放到父节点中,可能会造成树的分裂,微观上性能就比自增的差。