源码浅析:InnoDB聚集索引如何定位到数据的物理位置,并从磁盘读取
索引结构概述:
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。这与Oracle的索引结构相似,比较好理解。那么,常用的Innodb聚集索引结构是怎样的呢?
InnoDB的数据文件本身(.ibd文件)就是索引文件。在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
索引数据结构:
可能上面的描述还不够直观,我又翻阅了《MySQL技术内幕:Innodb存储引擎》一书,在其第172页找到了如下图片。

从磁盘读取:
可以看出,聚集索引非叶子节点中,Pointer(索引键值)指向的是ibd文件的Page Offset(File Header中的FIL_PAGE_OFFSET),这样就定位到数据块在ibd文件中的偏移量了。
接下来,首先确定该块是否在Innodb buffer pool中,我在这里仅分析不在Innodb buffer pool的情况:
每个page的头部,有Fil Header,从Fil Header中的FIL_PAGE_SPACE可以知道space id,然后通过一系列的函数调用,找到数据块,将它读入Innodb buffer pool,然后通过Page Directory(页目录)进行二分查找,来定位到行记录,这个过程中需要使用Record Header中的next_record。
函数调用过程:
本案例(基于MySQL5.7.12)中重点的函数调用过程如下图:
mysqld.exe!SyncFileIO::execute(const IORequest & request) 行 4000 //之后,会通过SyncFileIO::execute去调用操作系统的ReadFile SYSTEM CALL,真正地从文件系统中读取数据块
mysqld.exe!os_file_io(const IORequest & in_type, void * file, void * buf, unsigned __int64 n, unsigned __int64 offset, dberr_t * err) 行 5388
mysqld.exe!os_file_pread(IORequest & type, void * file, void * buf, unsigned __int64 n, unsigned __int64 offset, dberr_t * err) 行 5566
mysqld.exe!os_file_read_page(IORequest & type, void * file, void * buf, unsigned __int64 offset, unsigned __int64 n, unsigned __int64 * o, bool exit_on_err) 行 5605
mysqld.exe!os_file_read_func(IORequest & type, void * file, void * buf, unsigned __int64 offset, unsigned __int64 n) 行 5999
mysqld.exe!fil_node_open_file(fil_node_t * node) 行 760
mysqld.exe!fil_node_prepare_for_io(fil_node_t * node, fil_system_t * system, fil_space_t * space) 行 5305
mysqld.exe!fil_space_get_space(unsigned __int64 id) 行 1497
mysqld.exe!fil_space_get_flags(unsigned __int64 id) 行 1587
mysqld.exe!fil_space_get_page_size(unsigned __int64 id, bool * found) 行 1686
mysqld.exe!buf_page_get_gen(const page_id_t & page_id, const page_size_t & page_size, unsigned __int64 rw_latch, buf_block_t * guess, unsigned __int64 mode, const char * file, unsigned __int64 line, mtr_t * mtr, bool dirty_with_no_latch) 行 4076
mysqld.exe!btr_cur_open_at_index_side_func(bool from_left, dict_index_t * index, unsigned __int64 latch_mode, btr_cur_t * cursor, unsigned __int64 level, const char * file, unsigned __int64 line, mtr_t * mtr) 行 2255
mysqld.exe!btr_pcur_open_at_index_side(bool from_left, dict_index_t * index, unsigned __int64 latch_mode, btr_pcur_t * pcur, bool init_pcur, unsigned __int64 level, mtr_t * mtr) 行 564
mysqld.exe!row_search_get_max_rec(dict_index_t * index, mtr_t * mtr) 行 6344
mysqld.exe!row_search_max_autoinc(dict_index_t * index, const char * col_name, unsigned __int64 * value) 行 6383
mysqld.exe!ha_innobase::innobase_initialize_autoinc() 行 5694
mysqld.exe!ha_innobase::open(const char * name, int mode, unsigned int test_if_locked) 行 6097
mysqld.exe!handler::ha_open(TABLE * table_arg, const char * name, int mode, int test_if_locked) 行 2680
mysqld.exe!open_table_from_share(THD * thd, TABLE_SHARE * share, const char * alias, unsigned int db_stat, unsigned int prgflag, unsigned int ha_open_flags, TABLE * outparam, bool is_create_table) 行 3319
mysqld.exe!open_table(THD * thd, TABLE_LIST * table_list, Open_table_context * ot_ctx) 行 3521
mysqld.exe!open_and_process_table(THD * thd, LEX * lex, TABLE_LIST * tables, unsigned int * counter, unsigned int flags, Prelocking_strategy * prelocking_strategy, bool has_prelocking_list, Open_table_context * ot_ctx) 行 5107
mysqld.exe!open_tables(THD * thd, TABLE_LIST * * start, unsigned int * counter, unsigned int flags, Prelocking_strategy * prelocking_strategy) 行 5718
mysqld.exe!open_tables_for_query(THD * thd, TABLE_LIST * tables, unsigned int flags) 行 6485
mysqld.exe!execute_sqlcom_select(THD * thd, TABLE_LIST * all_tables) 行 5080
mysqld.exe!mysql_execute_command(THD * thd, bool first_level) 行 2758
mysqld.exe!mysql_parse(THD * thd, Parser_state * parser_state) 行 5519
mysqld.exe!dispatch_command(THD * thd, const COM_DATA * com_data, enum_server_command command) 行 1432
mysqld.exe!do_command(THD * thd) 行 997
mysqld.exe!handle_connection(void * arg) 行 301
mysqld.exe!pfs_spawn_thread(void * arg) 行 2190
mysqld.exe!win_thread_start(void * p) 行 37
[外部代码]
其中几个函数对应的源代码文件位置如下:
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
buf_read_page(storage/innobase/buf/buf0rea.cc)
buf_read_page_low(storage/innobase/buf/buf0rea.cc)
buf_page_io_complete(storage/innobase/buf/buf0buf.cc)
ibuf_merge_or_delete_for_page(storage/innobase/buf/ibuf0ibuf.cc)
ibuf_bitmap_get_map_page_func(storage/innobase/buf/ibuf0ibuf.cc)
fil_io(storage/innobase/fil/fil0fil.cc)
fil_space_get_by_id(storage/innobase/fil/fil0fil.cc) -- 这是通过space_id查找对应ibd文件的函数
fil_node_prepare_for_io(storage/innobase/fil/fil0fil.cc)
fil_node_open_file(storage/innobase/fil/fil0fil.cc)
os_file_read(storage/innobase/os/os0file.cc)
参考链接:
http://blog.codinglabs.org/articles/theory-of-mysql-index.html
https://www.cnblogs.com/wade-luffy/p/6289917.html
源码浅析:InnoDB聚集索引如何定位到数据的物理位置,并从磁盘读取的更多相关文章
- 源码浅析:MySQL一条insert操作,会写哪些文件?包括UNDO相关的文件吗?
DML操作的大致流程 在解答上述疑惑之前,我们来梳理一下DML操作的大致流程: 1.语法解析.语义解析 2.生成执行计划 3.事务修改阶段 1) 激活事务,事务状态由not_active变为activ ...
- String 源码浅析————终结篇
写在前面 说说这几天看源码的感受吧,其实 jdk 中的源码设计是最值得进阶学习的地方.我们在对 api 较为熟悉之后,完全可以去尝试阅读一些 jdk 源码,打开 jdk 源码后,如果你英文能力稍微过得 ...
- MySQL多版本并发控制机制(MVCC)-源码浅析
MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<<事务处理-概念与技术>>诚然 ...
- HashMap的源码浅析
一.HashMap 的数据结构 Java7 及之前主要是"数组+链表",到了 Java8 之后,就变成了"数组+链表+红黑树". 二.Java7 源码浅析: 在 ...
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析2--奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- Struts2源码浅析-ConfigurationProvider
ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...
- (转)【深入浅出jQuery】源码浅析2--奇技淫巧
[深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html
- HashSet其实就那么一回事儿之源码浅析
上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap, 本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...
随机推荐
- typescript 第一弹
typescript官网: http://typescriptlang.org typescript 在线运行环境: http://www.typescriptlang.org/play/index. ...
- nodejs 执行 最近 发现 nodejs 执行的 是非等待的。
上一步结果 没有完成 下一步就执行了 结果就 不行
- 史上最详细的二叉树、B树,看不懂怨我
今天我们要说的红黑树就是就是一棵非严格均衡的二叉树,均衡二叉树又是在二叉搜索树的基础上增加了自动维持平衡的性质,插入.搜索.删除的效率都比较高.红黑树也是实现 TreeMap 存储结构的基石. 1.二 ...
- 谈谈模型融合之三 —— GBDT
前言 本来应该是年后就要写的一篇博客,因为考完试后忙了一段时间课设和实验,然后回家后又在摸鱼,就一直没开动.趁着这段时间只能呆在家里来把这些博客补上.在之前的文章中介绍了 Random Forest ...
- 清晰架构(Clean Architecture)的Go微服务: 编码风格
编码风格在编程中是一个相对乏味的主题,但是合适的编码风格对一个有效的程序员是至关重要的. 它有三个组成部分: 程序结构 ( application layout) 编码规则或风格 命名约定 我已经在清 ...
- Web自动化测试项目(七)日志
一.日志作用 调试程序 了解系统程序运行的情况,是否正常 系统程序运行故障分析与问题定位 用来做用户行为分析和数据统计 二.为项目添加日志 └── utils ├── log_utils.py └── ...
- SVN: 聚合工程下的子工程无法使用 svn:ignore
当想将聚合工程manager下子工程没用的一些文件使用svn:ignore,发现该功能不能使用 这是因为SVN 服务器上还没有这些子工程的文件夹,只有聚合工程的文件夹,所以SVN认为在服务器上这些代码 ...
- Flutter 入门 --- 内部分享
八月部门给分配的分享任务,由于项目太赶,推迟一个月. 选 Flutter 这个主题,是因为现在它慢慢流行起来了,而我却不了解,故而借此契机,上手试试. 简介 Flutter 是 Google 推出的跨 ...
- 谈下程序设计算法的准备心得与体会-nCov隔离也许帮你提升能力
最近武汉的n-Cov使得大家只能在家办公了. 在家里的感受是什么样的呢? 1.上班的时候一直在奔跑,现在总算可以有集中的时间来思考一些之前一直没能好好整理的内容 2.时间变得自己可以掌控,优先级有自己 ...
- SpringBoot学习(3) - jdbc
数据库使用MySQL 5.7.18版本. 装配DataSource的步骤:1.加入数据库驱动 pom.xml: <project xmlns="http://maven.apache. ...