- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
B+树是MySql数据结构的主流存储方式,包括InnoDB和MYISAM引擎,它们的默认存储结构都是B+树 。
了解B+树前,我们先要知道MySql 的实际存储位置在哪?
有人会说它存在我么的D盘或C盘的MySql文件夹的Data目录里,这个回答没错,我们在深入的了解一下呢?
不管是在个人电脑上使用本机MySql或者在互联网上把信息存在服务器上, 其实它们最终的存储地址都是被写在了物理磁盘上,只有存在物理磁盘上,才能保证数据长久不丢失 。
物理磁盘一般可以描述为:柱面,磁面,扇区,通过这三个参数可以精准定位到数据所在的地方 。
。
我们可以看到这个磁面上记录着磁道和扇区, 。
一般磁盘定位有固定头和移动头两种:
固定头每个磁道上都有一个读写头,造价高,但是读写速度快,定位时间短 。
移动头每个磁面上一个读写头,造价适中,读写速度主要取决于定位时间,从定位磁道,再到定位扇区所化的时间,现行的物理磁盘大多都是使用的移动头定位 。
我们在了解了我们的磁盘和移动头后,我们就需要了解一次物理磁盘的I/O,它指的是对于磁盘来说,一次磁盘的连续读或者连续写称为一次磁盘 I/O, 磁盘的 IOPS 就是每秒磁盘连续读次数和连续写次数之和.
我们这里就把他当作读写一次扇区,即一次I/O只读写一个扇区(实际上的I/O指的是根据查询的数据大小,可能会连续读写好几个或者几百个扇区,但是也只进行了一次I/O,因为I/O读写的时间开销最大的还是在移动头的定位时间上) 。
一个扇区的大小比较公认的是512字节 (后面慢慢发展的也有一个扇区2048字节的,我们这里就举例512字节) 。
这里我们定义了一张表,还有里面的数据,他有三个属性:id 8字节,name 40字节,no 16字节 。
由此我们可以推出:这张表的一条记录就是8+40+16=64字节,我们这会儿定义的这张表的一条记录就要占64字节 。
一个扇区512字节:512/64=8 。
所以一个扇区就只能装8条记录,我们这32条记录就需要32/8=4,就需要4个扇区去装入, 。
如果我们按照规定的一次I/O读写一个扇区,那我们要找到32这条记录的话需要4次I/O操作,4次还算一般性能,但是我们在大型的数据库存储一张表可不止32条记录哦 。
但我们简单的把记录加到800条的时候:800/8 = 100 ,也就是需要100个扇区来装入,那我们要查找第800条记录的时候就需要100次I/O操作,显然这样的I/O操作就太慢了,要是有1000个人需要查找,时间开销就很大了 。
这种情况我们就需要引入索引;B+树的根节点几乎全是索引 。
什么是索引呢?
索引是为了加速对表中数据行的检索而创建的一种分散的存储结构。索引是针对表而建立的,它是由数据页面以外的索引页面组成的,每个索引页面中的行都会含有逻辑指针,以便加速检索物理数据.
简单的说,索引就是建表或者后期加入的,它可以分为主键索引,复合索引,普通索引, 。
当我们建立索引以后,就会自动的把建立索引的属性或属性组单独的拿出来然后和这个属性所在的物理地址一起单独构成一个表来存放,虽然多了一个表但是因为属性较少,所占的空间少,查询效率高了 。
这里我们可以看到右边的表就是我们的数据表,它记录着800条记录,上面我们也看到了它要被查询到的话需要100次I/O,所以我们考虑为id建一个索引 。
当id被选为索引以后,id会和id所在的实际物理地址构成一个表,这个表id 8字节,地址 8字节,一共16字节, 它也是存在一个扇区的还有可能没有和数据原表在相邻的扇区 。
一个扇区的的大小512字节:512/16 = 32,所以这个索引表所在的扇区,可以装32条记录 。
第一条记录:记录id为1,它所在的物理地址, 。
第二条记录:记录id为33,它所在的物理地址, 。
第三十二条记录:记录id为1023,它所在的物理地址 。
首先 ,一次I/O操作会把这个索引表查出来(要用到索引才能命中,也就是再sql语句中的条件判断语句带有id),这里只进行了一次I/O就拿到了1~33,33~65,....~1023这些区间的信息 。
然后 ,我们查第800条记录的时候我们很幸运,因为索引表中有一个区间是800~833就可以通过地址直接去读800所在的扇区 这里就也只用了一次I/O操作 。
这是最好的情况:2次I/O就拿到了第800条件记录 。
。
我们看看最坏的情况,细心的朋友可以发现再每个区间都有一个最坏情况, 。
比如:
1~33区间上,当我们要查询32时只能通过找到id=1的扇区,然后依次读取直到读到id=32的扇区 。
33~65区间上,当我们要查询64时只能通过找到id=33的扇区,然后依次读取直到读到id=64的扇区 。
...... 。
800~833区间上,我们去查找832时:
只会给我们找到800的首地址,这是一次I/O读写了 。
从800读到832一共是32条记录:32/8=4(扇区),也就是我们要连续读取4个扇区才可以找到id = 832这条记录,这就需要4次I/O 。
我们可以看到,即使是最坏的情况:1+4=5,我们也只需要5次I/O就可以拿到任意的数据了 。
比起没有索引的查询:从第32条记录开始,就会成比例的不断增大读写次数 。
有索引的情况的查询:最坏的情况也就5次I/O操作,但是索引需要单独存储,总的来说还是利大于弊,不建议建立索引,因为索引的维护需要开销,一般只会给经常查询且不常更改的数据做索引 。
当数据量达到80000条的时候:我们的一级索引表(紧贴实际数据表的索引表被称为一级索引)就显得的很乏力了, 。
我们简单的计算一下,我们的一级索引表只能存放1~1025记录的区间地址,当数据为80000 。
80000/1024 = 78~79,我们需要79个扇区来存储 。
如果是最坏的情况:我们要查找第80000条记录,那么在一级索引表中我们就进行了79次I/O操作了,再加上一级索引表去查实际数据的地址又是4次I/O,所以在这种情况下就是79+4=83次I/O 。
对于80000条数据查询就需要83次I/O,很显然,这个I/O次数太多了,我们得想办法把I/O次数压下去 。
这个时候我们的开发者想到的就是加一层,二级索引来指向一级索引,一级索引指向数据,对于B+树结构来说就是多了一层 。
在二级索引上的adress字段就是记录的一级索引的地址,而不直接指向数据,在二级索引就是记录一个一级扇区的位置, 。
比如:
一级索引一个扇区的数据区间在1~1025 。
二级索引一条记录的就是一级索引区间的1~1025 。
所以一个二级索引扇区可以记录一级索引区间的范围是:
1024 * 32 = 32768 。
对于有80000条记录的表,二级索引扇区只需要3个就可以放下 。
1. 。
首先我们要找到80000这个区间,他在第三个扇区上,那么在二级索引上的I/O读写就需要 3次 。
然后去一级索引上找,由于是二级索引提供的物理地址,只需要一次I/O就可以读取到那个扇区,一级索引上有一个区间就是80000~81025, 。
最后去找实际的数据刚好80000就是头指针,也只需要一次I/O就可以完成 。
这是最好的一种情况:3+1+1=5,最好的状态5次I/O就可以拿到数据了 。
2. 。
最坏的情况也是出现在一级索引去找数据:就是80000~80033这个区间 。
我们要找80032的时候,只能通过首地址为80000的地址去找,要连续查找4个扇区,就是4次I/O操作才能拿到 。
3+1+4=8 。
所以在B+树上加了一层以后最坏,不管什么情况下,查找任意数据都只需要8次I/O操作, 。
综上:查找80000条数据(最坏的情况) 。
a.没有索引,一个扇区一个扇区的查找,80000/8 = 10000,没有索引需要10000次I/O操作 。
b.一级索引,由于第80000条记录在最后一个扇区上,80000/1024 = 79 ,一级索引需要79次I/O,利用一级索引去查找数据4次I/O,一级索引需要83次I/O操作 。
c.二级索引,由于第80000条记录在最后一个扇区上,80000 / 32768 = 3,二级索引需要3次I/O,利用二级索引的区间地址去找一级索引只需要1次I/O,然后一级索引去找数据需要4次I/O,二级索引查找数据就只需要8次I/O操作 。
到了这里我们就可以很明显的感受到索引的魅力了,其实讲到这里我们也可以很明显看到我们的B+树是怎么组织数据和索引的了 。
我们把上面的图片顺时针翻转90度,就可以很明显的看到我们的B+树就是这个模型 。
a.索引表的区间范围一定是32吗?
不是,要根据开发人员的默认设置,或者后期数据库管理员去调整,一般人是更改不了物理存储的,只能了解原理 。
b.索引的表每条记录都是16字节吗?
不是,要根据建立索引属性的大小和实际物理地址大小而定,不一定每个扇区都能存刚好存32条索引记录 。
c. 800条记录没有索引的情况真的要100次I/O操作吗?
不是,要根据一次读取的页面大小,MySql的默认读取是16KB:16kb / 64 =250 条记录,一般的查询一次I/O至少就是250条记录, 。
根据计算机局部性原理,当一个扇区的数据被使用后,它周围的数据极有可能再最近会被用到,所以mysql的缓存机制会支持一次查询返回4页,即从查询的当前的页开始往后磁头移动4页的大小,4*16=64kb 。
也就是64kb / 64 = 100条记录,在缓存中有一次I/O是有100条记录被返回,然后怎么查询就是InnoDB引擎所要了解的知识了 。
现在我简化一下那张图片,然后把他旋转90度,我们再来看看它是什么样的图形 。
。
这就是一颗较为完整的B+树了,我们在仔细的看看可以发现B+树的特征 。
a.索引区间由根节点不断的变小,描述数据所在的扇区也越来越细化 。
b.根节点存放的都是索引,只有最下面一层的子节点才是数据 。
我们看完了B+树的形成过程以及它的大致模型,我们就不得不提一下它和B树的区别了,这是更深了解B+树的关键, 。
现在图片上描述的B+树,其实B树有很大一部分都能做到,我们就来看看B树和B+树的区别吧 。
B树 的在根节点也存有数据,然后根节点存储的数据又是索引,又是数据 。
如图,在索引上出现的数据就不会在子节点出现了 。
可以说父节点可能既有索引又有数据,这就是B树的存储方式 。
B+树 根节点没有数据,只存放索引 。
如图,数据都在最下面一层的子节点上,除了这一层以外的父节点全是索引没有数据 。
B树,存在一种情况就是所查的数据在最上层,因为节点有数据可能第80000条记录在最上层区间上,所以一次I/O就可以拿到数据,最坏的情况就和B+树一样,要到最后一层才能拿到数据 。
B+树,每次拿数据都需要在最下一层去拿,即使我们去拿一个表的第一条记录,它也会从根结点一级一级的往下直到最后一层才有数据 。
感觉有时候B树比B+树快啊,为什么MySql要用B+树呢?
因为B+树的查询比B树更加稳定,有利于开发者,数据库管理员对其进行性能测试,运行时日志检测的数据也会很准确 。
举个简单的例子,就以上面两张图片:我们要查询第7条数据和第8条数据,这两条记录是紧挨着的 。
使用B树查询:
第七条记录:1次I/O 。
第八条记录:2次I/O 。
使用B+树查询:
第七条记录:2次I/O 。
第八条记录:2次I/O 。
我们可以看到使用B树的第一次查询需要1次I/O,第二个需要2次I/O,B+树则都需要两次 。
这就很明显阿可以看出B树的查询效率很不稳定,尤其是层数变多,数据区间变得的时候,就更加明显 。
这个区别是B+树特有的, 。
在B树上子节点和子节点是相互隔离开的,现在我们构造一种情况,B树中我们一条查询语句需要查询两条记录, 。
比如 id = 6 and id = 8 。
由于这是一条查询语句,所以要一起执行会发生什么,我们的磁头会先移动到根节点进行定位,然后就可以id = 6这条记录,然后从新去根节点查索引,磁头再定位到 id = 8 这条记录 。
很显然我们的B树查询进行了两次磁头定位,我们知道在读写磁盘时,最大的开销就是磁头的定位,所以我们要尽量少的进行磁头定位 。
所以,B+树在最后一层的数据结点上都加了指针(指向的是物理地址),让它指向下一个区间的开始位置,把尾部的数据又串起来了,就是专门对付这种相邻区间要重复磁头定位的情况 。
有了这个尾部指针,我们在用B+树去查找 id =6 和 id = 8的记录,当经过一次磁头定位以后找到了id = 6 的记录,它会根据尾指针直接读取 id = 8的记录,加快了相邻扇或几个相邻扇区的读写速度 。
上面所有的描述都只是B+树常规的数据存储方式,实际上MySql的运行存储比B+树要复杂的多,因为我们各自的设备或者后期对物理存储的默认参数不一样 。
都会导致B+树存储的不同 。
需要真正的就业或者更进一步学习,MySql的认识还有很长的一段路要走 。
。
最后此篇关于MySql的数据存储之B+树(浅谈)的文章就讲到这里了,如果你想了解更多关于MySql的数据存储之B+树(浅谈)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
目前我正在构建相当大的网络系统,我需要强大的 SQL 数据库解决方案。我选择 Mysql 而不是 Postgres,因为一些任务需要只读(MyISAM 引擎)而其他任务需要大量写入(InnoDB)。
我在 mysql 中使用如下命令。当它显示表格数据时,它被格式化为一个非常干净的表格,间距均匀且 |作为列分隔符。 SELECT * FROM TABLE_NAME; 当我从 CLI 运行命令时,如下
我知道这个问题之前已经被问过好几次了,我已经解决了很多问题,但到目前为止没有任何效果。 MySQL 试图将自身安装到的目录 (usr/local/mysql) 肯定有问题。关于我的错误的奇怪之处在于我
以下是我的 SQL 数据结构,我正在尝试如下两个查询: Select Wrk_ID, Wrk_LastName, Skill_Desc from Worker, Skill where
我们有一个本地 mysql 服务器(不在公共(public)域上),并希望将该服务器复制到我们拥有的 google 云 sql 实例。我的问题是:1.这可能吗?2.我们的本地服务器只能在本地网络上访问
我有一个表(test_table),其中一些字段值(例如字段 A、B 和 C)是从外部应用程序插入的,还有一个字段(字段 D),我想从现有表(store_table)插入其值,但在插入前者(A、B 和
我想创建一个 AWS RDS 实例,然后使用 terraform 管理数据库用户。因此,首先,我创建了一个 RDS 实例,然后使用创建的 RDS 实例初始化 mysql 提供程序,以进一步将其用于用户
当用户在我的网站上注册时,他们会在我的一个数据库中创建自己的表格。该表存储用户发布的所有帖子。我还想做的是也为他们生成自己的 MySql 用户——该用户仅有权从他们的表中读取、写入和删除。 创建它应该
我有一个关于 ColdFusion 和 Mysql 的问题。我有两个表:PRODUCT 和 PRODUCT_CAT。我想列出包含一些标记为:IS_EXTRANET=1 的特殊产品的类别。所以我写了这个
我想获取 recipes_id 列的值,以获取包含 ingredient_id 的 2,17 和 26 条目的值。 假设 ingredient_id 2 丢失则不获取记录。 我已经尝试过 IN 运算符
在 Ubuntu 中,我通常安装两者,但 MySQL 的客户端和服务器之间有什么区别。 作为奖励,当一个新语句提到它需要 MySQL 5.x 时,它是指客户端、服务器还是两者兼而有之。例如这个链接ht
我重新访问了我的数据库并注意到我有一些 INT 类型的主键。 这还不够独特,所以我想我会有一个指导。 我来自微软 sql 背景,在 ssms 中你可以 选择类型为“uniqeidentifier”并自
我的系统上有 MySQL,我正在尝试确定它是 Oracle MySQL 还是 MySQL。 Oracle MySQL 有区别吗: http://www.oracle.com/us/products/m
我是在生产 MySQL 中运行的应用程序的新维护者。之前的维护者已经离开,留下的文档很少,而且联系不上了。 我面临的问题是执行以下请求大约需要 10 秒: SELECT COUNT(*) FROM `
我有两个位于不同机器上的 MySQL 数据库。我想自动将数据从一台服务器传输到另一台服务器。比方说,我希望每天早上 4:00 进行数据传输。 可以吗?是否有任何 MySQL 内置功能可以让我们做到这一
有什么方法可以使用 jdbc 查询位于 mysql 根目录之外的目录中的 mysql 表,还是必须将它们移动到 mysql 根目录内的数据库文件夹中?我在 Google 上搜索时没有找到任何东西。 最
我在 mysql 数据库中有两个表。成员和 ClassNumbers。两个表都有一个付费年份字段,都有一个代码字段。我想用代码数字表中的值更新成员表中的付费年份,其中成员中的代码与 ClassNumb
情况:我有 2 台服务器,其中一台当前托管一个实时 WordPress 站点,我希望能够将该站点转移到另一台服务器,以防第一台服务器出现故障。传输源文件很容易;传输数据库是我需要弄清楚如何做的。两台服
Phpmyadmin 有一个功能是“复制数据库到”..有没有mysql查询来写这个函数?类似于将 db A 复制到新的 db B。 最佳答案 首先创建复制数据库: CREATE DATABASE du
我有一个使用 mySQL 作为后端的库存软件。我已经在我的计算机上对其进行了测试,并且运行良好。 当我在计算机上安装我的软件时,我必须执行以下步骤: 安装 mySQL 服务器 将用户名指定为“root
我是一名优秀的程序员,十分优秀!