<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Database on bystander&#39;s blog</title>
    <link>http://blog.leaver.me/tags/database/</link>
    <description>Recent content in Database on bystander&#39;s blog</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Fri, 10 Jan 2020 07:55:18 +0800</lastBuildDate>
    <atom:link href="http://blog.leaver.me/tags/database/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Mysql中的B&#43;树介绍</title>
      <link>http://blog.leaver.me/2020/01/10/mysql%E4%B8%AD%E7%9A%84b-%E6%A0%91%E4%BB%8B%E7%BB%8D/</link>
      <pubDate>Fri, 10 Jan 2020 07:55:18 +0800</pubDate>
      <guid>http://blog.leaver.me/2020/01/10/mysql%E4%B8%AD%E7%9A%84b-%E6%A0%91%E4%BB%8B%E7%BB%8D/</guid>
      <description>&lt;p&gt;最近工作中遇到了一些索引的问题，发现自己其实并不了解，因此稍微了解下。在介绍B+树之前，需要先了解下B树，部分信息来源自参考文档。&lt;/p&gt;
&lt;h2 id=&#34;什么是-b-树&#34;&gt;什么是 B 树&lt;/h2&gt;
&lt;h3 id=&#34;b树概念&#34;&gt;B树概念&lt;/h3&gt;
&lt;p&gt;B树也称B-树,它是一棵多路平衡查找树（和二路对应）。二叉树我想大家都不陌生，其实，B树和后面讲到的B+树也是从最简单的二叉树变换而来，下面我们来看看B树的定义。我们定义m表示树的阶。阶数表示了一个节点最多有多少个子节点，那么一棵B需要满足以下几个条件&lt;/p&gt;
&lt;p&gt;1.每个节点最多有m-1个关键key（可以存有的键值）。
2.根节点最少可以只有1个关键字。意思是也可以有多个。
3.非根节点至少有m/2个关键字。如果少了，那么就要进行树的调整
4.为了平衡查找，每个节点中的关键字都按照从小到大的顺序排列，每个关键字的左子树中的所有关键字都小于它，而右子树中的所有关键字都大于它。这个很简单了。没有这个保证的话，平衡查找无从谈起。
5.所有叶子节点都位于同一层，或者说根节点到每个叶子节点的长度都相同。
6.包括非叶节点在内，每个节点都存有key和数据，也就是对应的key和value。&lt;/p&gt;
&lt;p&gt;也就是说，根节点的关键字数量k的范围：1 &amp;lt;= k &amp;lt;= m-1，非根节点的关键字数量范围：m/2 &amp;lt;= k &amp;lt;= m-1。&lt;/p&gt;
&lt;h2 id=&#34;什么是b树&#34;&gt;什么是B+树&lt;/h2&gt;
&lt;p&gt;B+树其实和B树是很相似的，特点是能够保持数据稳定有序，其插入与修改拥有较稳定的对数时间复杂度。B+ 树元素自底向上插入，这与二叉树恰好相反。&lt;/p&gt;
&lt;h3 id=&#34;相同点&#34;&gt;相同点&lt;/h3&gt;
&lt;p&gt;1.根节点至少一个元素
2.非根节点元素范围：m/2 &amp;lt;= k &amp;lt;= m-1&lt;/p&gt;
&lt;h3 id=&#34;不同点&#34;&gt;不同点&lt;/h3&gt;
&lt;p&gt;1.B+树有两种类型的节点：内部结点（也称索引结点）和叶子结点。内部节点就是非叶子节点，内部节点不存储数据，只存储索引，数据都存储在叶子节点。而B树都存储数据，这会导致查询性能不稳定，因为查找次数不确定。&lt;/p&gt;
&lt;p&gt;2.内部结点中的key都按照从小到大的顺序排列，对于内部结点中的一个key，左树中的所有key都小于它，右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
3.每个叶子结点都存有相邻叶子结点的指针，叶子结点本身依关键字的大小自小而大顺序链接。
4.父节点存有右孩子的第一个元素的索引。&lt;/p&gt;
&lt;h2 id=&#34;mysql中的选择&#34;&gt;mysql中的选择&lt;/h2&gt;
&lt;h3 id=&#34;索引的数据结构&#34;&gt;索引的数据结构&lt;/h3&gt;
&lt;p&gt;数据库中，数据都存在磁盘中，索引也多到大部分存在磁盘中，这样对于索引，每次查找数据时把磁盘IO次数控制在一个很小的数量级，最好是常数数量级。就这样，b+树应运而生。&lt;/p&gt;
&lt;p&gt;MySQL 默认的存储引擎选择 B+ 树而不是哈希或者 B 树的原因：&lt;/p&gt;
&lt;p&gt;1.哈希虽然能够提供 O(1) 的单数据行操作性能，但是对于范围查询和排序却无法很好地支持，最终导致全表扫描；
2.B 树能够在非叶节点中存储数据，但是这也导致在查询连续数据时可能会带来更多的随机 I/O，而 B+ 树的所有叶节点可以通过指针相互连接，能够减少顺序遍历时产生的额外随机 I/O；&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;b+树&#34; loading=&#34;lazy&#34; src=&#34;http://blog.leaver.me/images/2020-01-10-17-02-18.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;如上图，是一颗b+树，关于b+树的定义可以参见B+树，这里只说一些重点，浅蓝色的块我们称之为一个磁盘块，可以看到每个磁盘块包含几个数据项（深蓝色所示）和指针（黄色所示），如磁盘块1包含数据项17和35，包含指针P1、P2、P3，P1表示小于17的磁盘块，P2表示在17和35之间的磁盘块，P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据，只存储指引搜索方向的数据项，如17、35并不真实存在于数据表中。&lt;/p&gt;
&lt;h3 id=&#34;b树的查找过程&#34;&gt;b+树的查找过程&lt;/h3&gt;
&lt;p&gt;如图所示，如果要查找数据项29，那么首先会把磁盘块1由磁盘加载到内存，此时发生一次IO，在内存中用二分查找确定29在17和35之间，锁定磁盘块1的P2指针，内存时间因为非常短（相比磁盘的IO）可以忽略不计，通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存，发生第二次IO，29在26和30之间，锁定磁盘块3的P2指针，通过指针加载磁盘块8到内存，发生第三次IO，同时内存中做二分查找找到29，结束查询，总计三次IO。真实的情况是，3层的b+树可以表示上百万的数据，如果上百万的数据查找只需要三次IO，性能提高将是巨大的，如果没有索引，每个数据项都要发生一次IO，那么总共需要百万次的IO，显然成本非常非常高。&lt;/p&gt;
&lt;h3 id=&#34;b树和索引的关系&#34;&gt;b+树和索引的关系&lt;/h3&gt;
&lt;p&gt;联合索引，如果有一个3列索引(name,age,sex)，则已经对(name)、(name,age)、(name,age,sex)上建立了索引；&lt;/p&gt;
&lt;p&gt;1.我们知道IO次数取决于b+数的高度h，假设当前数据表的数据为N，每个磁盘块的数据项的数量是m，则有h=㏒(m+1)N，当数据量N一定的情况下，m越大，h越小；而m = 磁盘块的大小 / 数据项的大小，磁盘块的大小也就是一个数据页的大小，是固定的，如果数据项占的空间越小，数据项的数量越多，树的高度越低。这就是为什么每个数据项，即索引字段要尽量的小，比如int占4字节，要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点，一旦放到内层节点，磁盘块的数据项会大幅度下降，导致树增高。当数据项等于1时将会退化成线性表。&lt;/p&gt;
&lt;p&gt;2.当b+树的数据项是复合的数据结构，比如(name,age,sex)的时候，b+数是按照从左到右的顺序来建立搜索树的，比如当(张三,20,F)这样的数据来检索的时候，b+树会优先比较name来确定下一步的所搜方向，如果name相同再依次比较age和sex，最后得到检索的数据；但当(20,F)这样的没有name的数据来的时候，b+树就不知道下一步该查哪个节点，因为建立搜索树的时候name就是第一个比较因子，必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时，b+树可以用name来指定搜索方向，但下一个字段age的缺失，所以只能把名字等于张三的数据都找到，然后再匹配性别是F的数据了， 这个是非常重要的性质，即索引的最左匹配特性。&lt;/p&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/B%2B%E6%A0%91&#34;&gt;https://zh.wikipedia.org/wiki/B%2B%E6%A0%91&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://segmentfault.com/a/1190000020416577&#34;&gt;https://segmentfault.com/a/1190000020416577&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://tech.meituan.com/2014/06/30/mysql-index.html&#34;&gt;https://tech.meituan.com/2014/06/30/mysql-index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://draveness.me/whys-the-design-mysql-b-plus-tree&#34;&gt;https://draveness.me/whys-the-design-mysql-b-plus-tree&lt;/a&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
