您的位置:

MySQL源码分析

一、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 
    
#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