一、MySQL源码结构
MySQL源码结构十分清晰,主要分为server、client、embedded、libmysql、mysys、regex、sql、vio、zlib几个模块。
其中,server是MySQL server的主体代码;client是MySQL的命令行客户端代码;embedded是嵌入式库代码;libmysql是MySQL C函数库代码;mysys是MySQL底层系统函数库代码;regex是正则表达式支持代码;sql是SQL语句解析器代码;vio是MySQL I/O模块代码;zlib是数据压缩库代码。
除了以上主要模块外,MySQL源码中还涉及到一些额外的库,如iconv、openssl、pcre等。
MySQL源码结构清晰,功能分明,模块耦合度低,可以很方便地进行针对性的调试和扩展。
二、MySQL Server源码分析
1. 数据结构
MySQL Server中的数据结构十分复杂,需要深入了解MySQL的内部机制才能理解其设计。
其中,MySQL通过表定义、表数据、索引文件三部分组成了数据存储层。表定义使用数据字典维护,包含表结构、列定义、约束条件等信息;表数据分为数据行和NULL值标示符两部分,数据行按照列字段顺序存储,而NULL值标示符则由位图维护;索引文件则由哈希表、B+tree等数据结构维护。
/* 字段的描述结构体 */ struct st_field_info { uint name_length; char *name; char *table_name; uint table_length; char *database_name; uint db_length; char *org_name; uint org_name_length; char *def; uint def_length; ulonglong length; uint flags; uint decimals; uint charsetnr; enum_field_types type; // 列类型 }; /* 指向存储引擎的handler结构体 */ struct st_handler { int (*write_row)(THD *thd, uchar **row); int (*update_row)(THD *thd, uchar *old_row, uchar **new_row); int (*delete_row)(THD *thd, uchar *row); }; /* 表定义结构体 */ struct TABLE { TABLE_SHARE *s; // 表结构共享体 dynamic_array *field; // 数据库表中的每一列 HA_CREATE_INFO *file; // 创建并处理存储文件的信息 create_field **create_field; File file_sort; // 用于排序的文件 key_map *key_map; TABLE_LIST *reginfo; MY_BITMAP null_bytes; // NULL值标示符位图 uint field_count; uint db_type; DYNAMIC_ARRAY checksums; ulonglong options; key_map *key_info; uint max_rows, min_rows; mark_table_pos *pos; handler *file_handle; // 存储引擎handler st_table_share *table_share; MY_BITMAP bit_fields; // 位字段标志位图 char *db, *alias, *table_name, *real_name; // 真实表名 char *update_low_priority; char *select_lex_start; Bool ex_update, no_replicate; ulonglong stats_auto_recalc; };
2. SQL解析过程
MySQL的SQL解析器是MySQL Server的核心部分之一,其主要功能是将SQL语句解析为内部数据结构,并根据数据结构执行相应的操作。
SQL解析的过程主要包括以下几个步骤。
- 语法解析:将SQL语句解析为语法树。
- 语义解析:在语法树上添加语义信息,如查询子句所使用的表、列等。
- 查询重写:对SQL语句进行优化、变形,生成可执行的查询计划。
- 执行查询计划:基于查询计划执行SQL语句,获取查询结果。
/* SQL解析器主处理函数 */ int mysql_execute_command(THD *thd) { switch (thd->lex->sql_command) { case SQLCOM_SELECT: // SELECT语句处理 case SQLCOM_DELETE: // DELETE语句处理 case SQLCOM_UPDATE: // UPDATE语句处理 case SQLCOM_INSERT: // INSERT语句处理 if (table->file->ha_misc_flags & HA_CAN_INSERT_DELAYED && thd->is_delayed_insert()) { if (table->file->state == handler::STATE_CLOSED) { if (open_delayed_table(thd)) // 检查是否已打开延迟插入表 goto end; } if (!ha_write_row(table->file, buffer)) { // 写入行 if (ha_write_error(table->file)) // 写入行出错 mysql_print_error(thd, MYF(0), ER_CHECKREAD); } } else if (require_prelocking || thd->options & OPTION_FOUND_ROWS) { error = execute_prepared_stmt(thd); // 执行准备语句 } else { error = execute_ha_data(node, buffer); // 执行SQL语句 if (error == HA_ERR_WRONG_COMMAND) { if (is_select(thd, *thd->query_string, thd->lex->current_pos)) { // 如果非法SQL是SELECT语句则将max_error_count减1 thd->variables.max_error_count-= error_count_adjustment; error_count_adjustment= 0; } goto err; } } break; ...... }
3. 存储引擎接口
MySQL的存储引擎接口是MySQL Server与存储引擎之间的桥梁,用于处理MySQL Server与具体存储引擎的交互操作。
MySQL通过handler结构体对存储引擎进行封装,存储引擎需要实现该结构体中的操作函数。
/* MYSQL存储引擎handler结构体 */ struct handler { const char *name; // 引擎名字 ulonglong flags; // 引擎标志位 handler *next,*prev; // 指向下一个、上一个handler uint (*create)(THD *thd); // 创建表 int (*write_row)(THD *thd, uchar **row); // 插入一行 bool (*update_row)(THD *thd, uchar *old_row, uchar **new_row); // 更新一行 void (*delete_row)(THD *thd, uchar *row); // 删除一行 int (*index_first)(uchar *buf); // 哈希表索引,通过key找到第一行 int (*index_last)(uchar *buf); // 哈希表索引,通过key找到最后一行 int (*index_next)(uchar *buf); // 哈希表索引,通过key找到下一行 int (*index_prev)(uchar *buf); // 哈希表索引,通过key找到上一行 int (*index_read_map)(uchar *buf, key_map *keyinfo, uchar *param); // 哈希表索引 THR_LOCK_DATA **mdl_request(uint no_of_tables, TABLE **tables, enum thr_lock_type lock_type); // 获得MDL锁 };
三、MySQL Client源码分析
1. SQL语句执行过程
MySQL Client的主要功能是与MySQL Server进行通信,并根据用户输入的SQL语句生成相应的协议命令并发送给MySQL Server。
SQL语句执行过程主要包括以下几个步骤。
- 设置连接参数:包括数据库名称、服务器地址、用户名、密码等。
- 连接MySQL Server,并进行认证。
- 构造SQL语句并发送给MySQL Server。
- 接收MySQL Server返回的结果,并进行结果处理。
- 关闭连接。
2. C API接口
MySQL Client的C API接口提供了丰富的操作MySQL的函数,具有高度的灵活性和可扩展性。
以下代码展示如何使用C API连接MySQL Server,并执行一条简单的SELECT语句。
#includeint main() { MYSQL mysql; MYSQL_RES *res; MYSQL_ROW row; int error = -1; /* 初始化mysql结构体 */ mysql_init(&mysql); /* 连接MySQL Server */ if (!mysql_real_connect(&mysql, "localhost", "root", NULL, "test", 0, NULL, 0)) { printf("Failed to connect to MySQL Server: %s\n", mysql_error(&mysql)); goto end; } /* 执行SELECT语句 */ if (mysql_real_query(&mysql, "SELECT * FROM db", strlen("SELECT * FROM db")) != 0) { printf("Failed to execute SQL: %s\n", mysql_error(&mysql)); goto end; } /* 获取查询结果 */ res = mysql_store_result(&mysql); while ((row = mysql_fetch_row(res))) { printf("%s, %s, %s\n", row[0], row[1], row[2]); } error = 0; end: /* 关闭连接并释放资源 */ mysql_free_result(res); mysql_close(&mysql); return error; }
四、MySQL存储引擎开发
1. 存储引擎框架
MySQL存储引擎开发需要进行基于handler结构体的封装,以便能够与MySQL Server进行交互。
以下代码展示了如何实现一个最基本的存储引擎,并在MySQL Server中进行使用。
/* MyISAM存储引擎类 */ #include#include #include #include static struct st_handler myisam_handler = { "myisam", // 引擎名字 HA_BINLOG_STMT_CAPABLE // 引擎标志位 | HA_CAN_RECREATE // 引擎标志位 | HA_SUPPORT_CLUST_INDEX // 引擎标志位 | HA_FILE_BASED, // 引擎标志位 NULL, NULL, NULL, NULL, NULL, // 几个操作函数都置为NULL,无需重载 NULL, NULL, NULL, NULL, NULL }; class MyISAM: public handler { public: MyISAM(handlerton *hton, TABLE_SHARE *table_arg); protected: virtual ~MyISAM(); public: virtual uint index_flags(uint inx, uint part, bool all_parts); virtual const char **bas_ext(); virtual int rnd_init(bool scan); virtual int rnd_next(uchar *buf); virtual int rnd_pos(uchar *buf, uchar *pos); virtual void position(const uchar *record); virtual int info(uint); virtual void print_error(int); private: MI_INFO m_info; char m_path[FN_REFLEN]; DBUG_ENTER("MyISAM::MyISAM"); memset(&m_info, 0, sizeof(m_info)); memcpy(&m_handler, &myisam_handler, sizeof(handler)); if (table_arg == NULL) { DBUG_VOID_RETURN; } fopen_system tmp_file(table_arg->db_type == DB_TYPE_COMMENT ? NULL: table_arg->path.str); char buff[FN_REFLEN + FN_EX_REFLEN + 2]; DBUG_PRINT("info", ("path=%s, share.rol_type() = %d", table_arg->path.str, table_arg->file_type == MYSQL_OPEN_FRM)); make_frm_name(buff, table_arg->path.str, table_arg->alias); DBUG_PRINT("info", ("frm_name=%s", buff)); if (mi_open((char*) table_arg->alias, &m_info