1、MySQL源码分析 代码结构与基本流程,彭立勋,Alibaba DBA Team,Topics,MySQL基本架构 源码目录结构 核心类库与函数 主要模块 数据流,MySQL基本架构,MySQL目录结构(1),BUILD: 内含在各个平台、各种编译器下进行编译的脚本。如compile-pentium-debug表示在pentium架构上进行调试编译的脚本。 client: 客户端工具,如mysql,mysqladmin之类。 cmd-line-utils: readline,libedit工具。 config: 给aclocal使用的配置文件。 dbug: 提供一些调试用的宏定义。 Docs:
2、 MySQL在不同平台下的参考手册 extra: 提供innochecksum,resolveip等额外的小工具。 include: 包含的头文件 libmysql: 库文件,生产libmysqlclient.so。 libmysql_r: 线程安全的库文件,生成libmysqlclient_r.so。 libmysqld: 嵌入式MySQL Server库. libservices: 5.5.0中新加的目录,实现了打印功能。,MySQL目录结构(2),man: 适合man命令查看的帮助文件。 mysql-test: mysqld的测试工具套件。 mysys: 为实现跨平台,MySQL自己实现
3、了一套常用的数据结构和算法,如string, hash等。还包含一些底层函数的跨平台封装,一般以my_开头。 netware: 在netware平台上进行编译时需要的工具和库。 plugin: MySQL 5.1开始支持一个插件式API接口,不需要重启mysqld即可动态载入插件,FullText就是一个例子。 pstack: GNU异步栈追踪工具。 regex: 正则表达式实现(来自多伦多大学Henry Spencer大牛的源码)。 scripts: 提供脚本工具,如mysql_install_db/mysqld_safe等。 server-tools: 包含instance_manager
4、子目录,负责实例的本地和远程管理。,MySQL目录结构(3),sql: MySQL Server主要代码,将会生成mysqld文件。 sql-bench: 一些基准测试代码代码,主要是Perl程序(虽然后缀是sh)。 sql-common: 存放部分服务器端和客户端都会用到的代码,有些地方的同名文件是这里lin过去的。 storage: 存储引擎所在目录。 strings: string库,包含很多字符串处理的函数。 support-files: f示例配置文件及编译所需的一些工具。 tests: 测试文件所在目录。 unittest: 单元测试文件。 vio: 虚拟io系统,是对networ
5、k io的封装,把不同的协议封装成统一的IO函数。 win: 在windows平台编译所需的文件和一些说明。 zlib: zlib算法库(GNU),InnoDB目录结构(1),btr: B+树的实现 buf: 缓冲池的实现,包括LRU算法,Flush刷新算法等 dict: InnoDB内存数据字典的实现 dyn: InnoDB动态数组的实现 fil: InnoDB文件数据结构以及对于文件的一些操作 fsp: 对InnoDB物理文件的管理,如页/区/段等(即File Space) ha: 哈希算法的实现 handler: 继承与MySQL的handler,实现handler API与Server
6、交互 ibuf: 插入缓冲(Insert Buffer)的实现 include: InnoDB所有头文件都放在这个目录,是查找结构定义的最佳地点 lock: InnoDB的锁实现及三种锁算法实现 log: 日志缓冲(Log Buffer)和重做日志组(Redo Log)的实现,InnoDB目录结构(2),mem: 辅助缓冲池(Additional Memory Pool)的实现,用来申请一些内部数据结构的内存 mtr: 事务的底层实现(日志,缓冲) os: 封装一些对于操作系统的操作 page: 页的实现,研究InnoDB文件结构,这个目录至关重要 pars: 重载部分MySQL的SQL Pa
7、rser(有待商榷) que: Query graph,基本上没啥用 read: 读取游标的实现 rem: 行管理操作(比较操作,打印等) row: 对于各种类型行数据操作的实现 srv: InnoDB后台线程,启动服务,Master Thread,SQL队列等 sync: InnoDB互斥变量(Mutex)的实现,基本同步机制 thr: InnoDB封装的可移植线程库,InnoDB目录结构(3),trx: 事务的实现 usr: Session管理 ut: 各种通用小工具,核心类库,THD: 线程类 Item: Item类(查询条目,函数,WHERE,ORDER,GROUP,ON子句等) TA
8、BLE: 表描述符 TABEL_LIST: JOIN操作描述符 Field: 列数据类型及属性定义 LEX: 语法树 Protocol: 通讯协议 NET: 网络描述符 handler: 存储引擎接口,核心函数库(1),内存操作: init_alloc_root: 内存池初始化,生成内存池根(MEM_ROOT) alloc_root: 申请内存池内存,从mem_root制定的内存池申请内存块 free_root: 释放内存池,通过MyFlags指定哪种内存可以被释放 文件操作: my_open: 打开一个文件 my_close: 关闭一个文件 my_b_flush_io_cache: 讲数据从
9、内存缓冲写到物理磁盘 end_io_cache: 释放一个IO_CACHE对象 哈希操作: _hash_init: 初始化HASH描述符 hash_search: 搜索哈希表,调用hash_first hash_first: 返回哈希表中找到的第一个行指针,否则返回0,核心函数库(2),字符串操作: strappend:填充字符串 strmov: 移动字符串到新地址,核心算法,Bitmaps bitmap_init/bimap_free: 创建与释放一个位图 (8*n个位为单位) bitmap_set_bit/bitmap_fast_test_and_set: 设置位图的一个位 bitmap_
10、clear_all/bitmap_set_all: 清空或全部设置一个位图 bitmap_cmp: 对两个位图的特定位比较 Join Buffer 如果存在条件过滤,则第一次过滤完的记录将放入Join Buffer,避免第二次再判断 Sort Buffer 算法一: 将排序字段和主键放入Sort Buffer排序,按照结果用主键取出数据返回 算法二: 将整行数据放入Sort Buffer,排序完成后直接从Sort Buffer返回数据,MySQL数据流,MySQL启动流程,主要代码在sql/mysqld.cc中,精简后的代码如下: int main(int argc, char *argv)
11、/标准入口函数 MY_INIT(argv0);/调用mysys/My_init.c-my_init(),初始化mysql内部的系统库 logger.init_base(); /初始化日志功能 init_common_variables(MYSQL_CONFIG_NAME,argc, argv, load_default_groups) /调用load_defaults(conf_file_name, groups, /主要处理函数,处理新的连接并创建新的线程处理,MySQL监听连接,主要代码在sql/mysqld.cc中(handle_connections_sockets),精简后的代码如下
12、: pthread_handler_t handle_connections_sockets(void *arg _attribute_(unused) FD_SET(unix_sock, / 创建任务线程 ,MySQL创建连接(1),主要代码在sql/mysqld.cc中(create_new_thread/create_thread_to_handle_connection),精简后的代码如下: static void create_new_thread(THD *thd) NET *net= / 将新连接加入到thread_scheduler的连接队列中。 ,MySQL创建连接(2),而
13、thread_scheduler转而调用create_thread_to_handle_connection,精简后的代码如下: void create_thread_to_handle_connection(THD *thd) if (cached_thread_count wake_thread) /看当前工作线程缓存(thread_cache)中有否空余的线程thread_cache.append(thd);pthread_cond_signal( /没有可用空闲线程则创建一个新的线程 ,MySQL创建连接(3),创建连接使用handle_one_connection函数,精简代码如下:
14、 pthread_handler_t handle_one_connection(void *arg) thread_scheduler.init_new_connection_thread(); / 初始化线程预处理操作setup_connection_thread_globals(thd); /载入一些Session级变量for (;) lex_start(thd); /初始化LEX词法解析器login_connection(thd); / 进行连接身份验证prepare_new_connection_state(thd); / 初始化线程Status,即show status看到的do_
15、command(thd); / 处理命令end_connection(thd); /没事做了关闭连接,丢入线程池 ,MySQL执行Query(1),do_command函数在sql/sql_parse.cc定义,代码如下: bool do_command(THD *thd) NET *net= ,MySQL执行Query(2),再看dispatch_command函数在sql/sql_parse.cc定义,精简代码如下: bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint pa
16、cket_length) NET *net = /送去解析 ,MySQL执行Query(3),mysql_parse函数负责解析SQL(sql/sql_parse.cc),精简代码如下: void mysql_parse(THD *thd, const char *inBuf, uint length, const char * found_semicolon) lex_start(thd); /初始化线程解析描述符if (query_cache_send_result_to_client(thd, (char*) inBuf, length) = 0) / 看query cache中有否命中
17、,有就直接返回结果,否则进行查找Parser_state parser_state(thd, inBuf, length); parse_sql(thd, / 执行 ,MySQL执行Query(4),终于开始执行鸟mysql_execute_command接近3k行,非常精简代码如下: int mysql_execute_command(THD *thd) LEX *lex= thd-lex; / 解析过后的SQL语句的语法结构TABLE_LIST *all_tables = lex-query_tables; / 该语句要访问的表的列表switch (lex-sql_command) .ca
18、se SQLCOM_INSERT:insert_precheck(thd, all_tables);mysql_insert(thd, all_tables, lex-field_list, lex-many_values, lex-update_list, lex-value_list, lex-duplicates, lex-ignore);break; . case SQLCOM_SELECT:check_table_access(thd, lex-exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, all_tables, UINT_MAX, F
19、ALSE); / 检查用户对数据表的访问权限execute_sqlcom_select(thd, all_tables); / 执行select语句break; ,MySQL执行Query(5),我们看一个例子, mysql_insert (在sql/sql_insert.cc),精简代码如下: bool mysql_insert(THD *thd,TABLE_LIST *table_list, / 该INSERT要用到的表List /里面还有很多处理trigger,错误,view之类的杂七杂八的东西,我们都忽略。,MySQL执行Query(6),我们接着看真正写数据的函数write_reco
20、rd (在sql/sql_insert.cc),精简代码如下: int write_record(THD *thd, TABLE *table,COPY_INFO *info) / 写数据记录if (info-handle_duplicates = DUP_REPLACE | info-handle_duplicates = DUP_UPDATE) /如果是REPLACE或UPDATE则替换数据table-file-ha_write_row(table-record0);table-file-ha_update_row(table-record1, table-record0); else table-file-ha_write_row(table-record0); int handler:ha_write_row(uchar *buf) /这是啥? Handler API !write_row(buf); / 调用具体的实现binlog_log_row(table, 0, buf, log_func); / 写binlog ,MySQL执行Query(7),