一、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语句。
#include
int 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
#includestatic 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原创文章,作者:VZWTL,如若转载,请注明出处:https://www.506064.com/n/361268.html
微信扫一扫
支付宝扫一扫