MySQL 存储引擎是用插件方式实现的,所以在源码里分为两层:server 层、存储引擎层。
server 层负责解析 SQL、选择执行计划、条件过滤、排序、分组等各种逻辑。
存储引擎层做的事情比较单一,负责写数据、读数据。写数据就是把 MySQL 传给存储引擎的数据存到磁盘文件或者内存中(对于 Memory 引擎是存储到内存),读数据就是把数据从磁盘或者内存读出来返回给 server 层。
server 层和引擎层是相对独立的两个模块,它们之间要配合完成工作,就会存在数据交互的过程,今天我们就以 server 层从存储引擎
层读取数据来讲讲这个起着关键作用的数据交互过程。
在源码里,数据库中的每个表都会对应 TABLE
类的一个实例,实例中有个 record
属性,record 属性是一个有着 2 个元素的数组,server 层每次调用引擎层的方法读取数据时,都会用 table->record[0]
的形式把第 1 个元素的地址传给引擎层。引擎层从磁盘或者内存中读取数据之后,把引擎层的数据格式转换为 server 层的数据格式,然后写入到这个地址对应的内存空间里,server 层就可以拿这个数据来干各种事情了(比如:WHERE 条件筛选、分组、排序等)。
整个交互过程就是这么简单,既然这么简单,那还值得单独写篇文章来叨叨这个吗?
当然是值得的,台上一分钟,台下十年功
这句话大家应该都耳熟能详了,这个交互过程之所以这么简单,是因为 server 层前期做了足够的准备工作,才让这个过程看起来像百度的搜索框那么简单。
为了一探究竟,接下来就是我们往前追溯准备工作
(也就是前戏阶段)的时间了。
创建表时,会计算出来每个字段在记录
(也就是我们常说的行
)中的 Offset
,以及一条记录的最大长度(包含存储变长字段的长度需要占用的字节数)。
当我们第一次查询某个表的时候,MySQL 会从 frm 文件中读取字段、索引等信息,以及刚刚提到的字段 Offset
、一条记录的最大长度。
接下来会根据记录的最大长度
,为第 1 小节中提到的 TABLE
类实例的 record
属性申请内存,record 数组的两个元素 record[0]、record[1] 占用的字节数都等于记录的最大长度
。
在源码里,每个字段都对应 Field
子类的一个实例,实例中有个 ptr
属性,指向每个字段在 record[0]
中对应的内存地址。对于变长字段,Field 子类实例中还会存储内容长度
占用的字节数。
存储引擎从磁盘或者内存中读取一条记录的某个字段后,会判断字段的类型,如果是定长字段,把字段内容经过相应的格式转换后写入 ptr 指向的内存空间。
如果是变长字段,先把内容长度写入 ptr 指向的内存空间,然后紧挨着把字段内容经过相应的格式转换后写入内容长度之后的内存空间。
抽象的东西就写到这里为止了,接下来会用一个实际的表为例子,并且通过一张图来展示 record[0] 的内存布局,以便有个直观的了解。
这是示例表:
CREATE TABLE `t_recbuf` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`i1` int(10) unsigned DEFAULT '0',
`str1` varchar(32) DEFAULT '',
`str2` varchar(255) DEFAULT '',
`c1` char(11) DEFAULT '',
`e1` enum('北京','上海','广州','深圳') DEFAULT '北京',
`s1` set('吃','喝','玩','乐') DEFAULT '',
`bit1` bit(8) DEFAULT b'0',
`bit2` bit(17) DEFAULT b'0',
`blob1` blob,
`d1` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
这是 record[0] 的内存布局:
示例表和内存布局图都有了,接下来我们对照着图来分析一下各个字段对应的内存空间的情况:
字段 NULL 值标记区域
这个区域是标记一条具体的记录中,定义表结构时没有指定 NOT NULL
的字段,实际的内容是不是 NULL,如果是 NULL,在这个区域中对应的位置会设置为 1
,如果不是 NULL,则在这个区域中对应的位置会设置为 0
,每个字段的 NULL 标记占用 1 bit
。
这个字段在 record[0] 的开头处,所以它的 Offset = 0,由于示例表中,有 10 个字段都没有指定 NOT NULL
,所以总共需要 10 bit 来存储 NULL 标记,共占用 2 字节
。
存储引擎读取每个字段时,如果该字段在字段 NULL 值标记区域
有一席之地,就会把它对应的位置设置个值(0 或者 1)。
id
id 字段的类型是 int,定长字段,占用 4 字节,Offset = 字段 NULL 值标记区域占用字节数 = 2,ptr 属性指向 Offset 2。
存储引擎读取到 id 字段内容,经过大小端存储模式转换之后,把内容写入到 ptr 属性指向的内存空间。
由于 InnoDB 中,内容是按大端模式存储的(内容高位在前,低位在后),而 server 层是按照小端模式读取的,所以在写入整数字段内容到 record[0] 之前会进行大小端存储模式的转换。
i1
i1 字段的类型是 int,定长字段,占用 4 字节,Offset = id Offset(2) + id 长度(4) = 6,ptr 属性指向 Offset 6。
存储引擎读取到 i1 字段内容,经过大小端存储模式转换之后,把内容写入到 ptr 属性指向的内存空间。
str1
str1 字段的类型是 varchar,变长字段,Offset = i1 Offset(6) + i1 长度(4) = 10,ptr 属性指向 Offset 10。
str1 字段定义时指定要存储 32
个字符,表的字符集是 utf8,每个字符最多会占用 3 字节,32 个字符最多会占用 96 字节,96 < 255,只需要 1 字节
就够存储 str1 内容的长度了,所以 str1 len
区域占用 1 字节
。
str1 字段内容紧挨着 str1 len
之后,由于 str1 len
占用 1 字节,所以 str1 内容的 Offset = 10 + 1 = 11。
存储引擎读取 str1 字段的内容时,也会读取到 str1 的内容长度,会先把内容长度
写入 ptr 属性指向的内存空间,然后紧挨着写入 str1 的内容。
str2
str2 字段的类型也是 varchar,变长字段,Offset = str1 Offset(10) + str1 内容长度占用字节数(1) + 内容最大占用字节数(96) = 107,ptr 属性指向 Offset 107。
str2 字段定义时指定要存储 255
个字符,最多会占用 255 * 3 = 765 字节,需要 2 字节
才能存储 str2 的内容长度,所以 str2 len
区域占用 2 字节
。
str2 字段内容紧挨着 str2 len
之后存储,由于 str2 len
占用 2 字节,所以 str2 内容的 Offset = 107 + 2 = 109。
存储引擎读取 str2 字段内容后,会先把内容长度
写入 ptr 属性指向的内存空间,然后紧挨着写入 str2 的内容。
c1
c1 字段的类型是 char,定长字段,Offset = str2 Offset(107) + str2 内容长度占用字节数(2) + 内容最大占用字节数(765) = 874,ptr 属性指向 Offset 874。
c1 字段定义时指定要存储 11
个字符,最多会占用 11 * 3 = 33 字节。
存储引擎读取 c1 字段内容后,会把内容写入 ptr 属性指向的内存空间。如果 c1 字段的实际内容长度比字段内容最大字节数小,会挨着刚刚写入的内容,再写入一定数量的空格。
比如:实际内容长度为 11 字节,而字段内容最大字节数为 33,则会在实际内容之后再写入 22 个空格。
e1
e1 字段类型是 enum,定长字段,只有 4 个选项,占用 1 字节,Offset = c1 Offset(874) + 内容最大长度占用字节数(33) = 907。
enum 类型在存储引擎中是用整数存储的,存储引擎读取 e1 字段内容后,会对内容进行大小端转换,把转换后的内容写入 ptr 属性指向的内在空间。
s1
s1 字段类型是 set,定长字段,只有 4 个选项,占用 1 字节,Offset = e1 Offset(907) + e1 长度(1) = 908。
set 类型在存储引擎中也是按照整数存储的,存储引擎读取 s1 字段内容后,也需要对内容进行大小端转换,把转换后的内容写入 ptr 属性指向的内存空间。
set 字段是用 enum 来实现的,最多占用 8 字节,共 64 bit,每个选项用 1 bit 表示,所以 1 个 set 字段总共可以有 64 个选项。
enum、set 字段的需要长度说明一下,如果创建表时定义的选项数量不一样,字段的长度也可能会不一样(1 ~ 8 字节),但是字段长度在创建表时就已经是确定的了,所以它们也是定长字段。
bit1
bit1 字段的类型是 bit,定长字段,创建表时定义的长度表示的是 bit,不是字节数,Offset = s1 Offset(908) + s1 长度(1) = 909。
bit1 字段定义时指定的是 bit(8),表示该字段长度为 8 bit,也就是
1 字节
。
bit 类型的字段在存储引擎中是按 char 存储的,存储引擎读取 bit1 字段的内容后,把内容写入到 ptr 属性指向的内存空间。
这里的 char 是指的 C/C++ 里的 char,不是指的 MySQL 的 char 类型。
bit2
bit2 字段的类型也是 bit,定长字段,创建表时定义的是 bit(17),占用 3 字节,Offset = bit1 Offset(909) + bit1 长度(1) = 910。
bit 类型的字段,如果创建表时指定的 bit 数不是 8 的整数倍,存储引擎在插入数据到磁盘或者内存时,就会在前面补充 0,比如 bit(17),占用 3 字节,内容为 00010000010010011 时,会在前面再补充 7 个 0 变成 0000000
00010000010010011,读出来的时候也还是这样的内容。
之所以定义 2 个 bit 字段,是为了测试 bit 类型的字段,定义的 bit 位数不是 8 的整数倍时,是不是会把多出来的那些 bit 存储到
字段值 NULL 标记区域
中,后来发现,只有 MyISAM、NDB 存储引擎才会这样处理,InnoDB 中 bit 字段是按 char 存储的,bit 位数不是 8 的整数倍时,多出来的 bit 还需要占用 1 字节,比如:bit(17) 需要占用 3 字节。
blob1 len
blob1 字段的类型是 blob,变长字段,Offset = bit2 Offset(910) + bit2 长度(3) = 913。
blob 类型的字段,最多可以存储 2 ^ 16 = 65536 字节 = 64K。
存储引擎读取 blob1 字段内容之后,会分配一块能够容纳 blob1 字段内容的内存空间,把读取出来的内容写入该内存空间中。然后把 blob1 字段的内容长度
写入 ptr 属性指向的内存空间处,占用 2 字节,然后紧挨着写入刚刚分配的那块内存空间的首地址
,占用 8 字节。
注意:只是把 blob1 字段的内容首地址,而不是 blob1 字段的完整内容写入 record[0]。
示例中只使用了 blob 类型的字段,实际 blob 类型分为 4 种:tinyblob、blob、mediumblob、longblob,这 4 种类型的内容长度分别占用 1 ~ 4 字节。
另外,还需要说明的一点是:tinytext、text、mediumtext、longtext 也是用上面相应的 blob 类型实现的,json 类型是用 longblob 类型实现的。
d1
d1 字段的类型是 decimial,定长字段,Offset = blob1 Offset(913) + blob1 长度占用字节数(2) + blob1 内容首地址占用字节数(8) = 923。
decimal 类型的字段,在存储引擎中是用二进制
存储的,在创建表的时候,就计算出来了需要用几字节来存储。
存储引擎读取 d1 字段的内容之后,把内容写入 ptr 属性指向的内存空间。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/BMt2ll4cB-_BLZNfg8T5Kg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。