专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

源码浅析:InnoDB聚集索引如何定位到数据的物理位置,并从磁盘读取

索引结构概述:

MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。这与Oracle的索引结构相似,比较好理解。那么,常用的Innodb聚集索引结构是怎样的呢?

InnoDB的数据文件本身(.ibd文件)就是索引文件。在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

104_1.png

上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

索引数据结构:

可能上面的描述还不够直观,我又翻阅了《MySQL技术内幕:Innodb存储引擎》一书,在其第172页找到了如下图片。

104_2.png

从磁盘读取:

可以看出,聚集索引非叶子节点中,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://tech.souyunku.com/wade-luffy/p/6289917.html

文章永久链接:https://tech.souyunku.com/48047

未经允许不得转载:搜云库技术团队 » 源码浅析:InnoDB聚集索引如何定位到数据的物理位置,并从磁盘读取

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们