MySQL概览–系统架构和关键数据结构
近期在研究HandlerSocket的相关源码,其命名也正是因为handler这一重要的数据结构,handlerSocket的简单点说就是越过mysql的SQL层,直接通过handler操作底层的存储引擎,由于减少了SQL Parse,Opentable等消耗,并且直接利用handler通过索引index进行查询和更新操作,因此有较高的性能,近期在公司组内做了一次关于handlerSocket调研的分享,主要包括handler协议解析,handlerSocket关键代码和原理,innoDB关键特性等主题内容,当然mysql的系统架构和关键数据结构也是该次分享的基础内容之一,也因为这次分享,本人很荣幸的拿到starbucks的消费券以资奖励.本文主要讲下理解HandlerSocket原理以及mysql的必须知晓的相关系统架构和关键数据结构。
MySQL关键模块和流程
上述步骤不多做详述,图片来源于《Understanding MySQL Internals》,详细的各模块功能和描述,请详细查看书籍,这里给出仅仅说明一下mysql大致的系统架构和流程.
Mysql关键数据结构
•THD 线程描述符(sql/sql_class.h)
MySQL Server层 和用户连接的线程对象,包含处理用户请求时需要的相关数据。
NET net; // 客户连接描述符 TABLE* open_tables Protocol *protocol; // 当前的协议 Protocol_text protocol_text; // 普通协议 Protocol_binary protocol_binary; // 二进制协议 HASH user_vars; //用户变量的hash值 String packet; // 网络IO时所用的缓存 String convert_buffer; // 字符集转换所用的缓存 struct sockaddr_in remote; //客户端socket地址 THR_LOCK_INFO lock_info; // 当前线程的锁信息 pthread_mutex_t LOCK_thd_data; //thd的mutex锁,保护THD数据(thd->query, thd->query_length)不会被其余线程访问到。 Statement_map stmt_map; //prepared statements和stored routines 会被重复利用 LEX *lex; //语法树描述符
其中特别的注意到:
TABLE* open_tables 被查询引用的非临时和非Handler Open表
TABLE* handler_tables TABLE* temporary_tables TABLE* derived_tables
•NET 网络连接描述(sql/mysql_com.h)
该类在HandlerSocket中没有用到 Vio *vio; //底层的网络I/O socket描述符 unsigned char *buff,*buff_end,*write_pos,*read_pos; //缓存相关 unsigned long remain_in_buf,length, buf_length, where_b; unsigned long max_packet,max_packet_size; //当前值;最大值 unsigned int pkt_nr,compress_pkt_nr; //当前(未)压缩包的顺序值 my_bool compress; //是否压缩 unsigned int write_timeout, read_timeout, retry_count; //最大等待时间 unsigned int *return_status; //thd中的服务器状态 unsigned char reading_or_writing;/*1 代表读, 2 代表写, 0代表无状态 */ unsigned int last_errno; //返回给客户端的错误号 unsigned char error; /*0:执行成功 1:在协议层有逻辑错误 2:系统调用或标准库出错 3:特例,表示缓存不能装下当前这么大的包 */
•TABLE 数据库表描述符(sql/table.h)
/*每一个table的共享结构St_table_share*/ Field **field; /* 指向数据域的指针*/ KEY *key_info; /* 数据库中key的信息*/ TYPELIB keynames;/*通过keyname查找keynum(OPENINDEX)*/ TYPELIB fieldnames /*通过fieldname找fieldnumber*/
handler *file; //指向这张表在storage engine中的handler的指针 THD *in_use; /* 使用这张表的thread号 */ byte *record[2] ; /*每次找到的记录会先写入record[0],如需要修改则将要修改的原记录在record[1]中,利用field.store()会默认将更新的field写入record[0]中,逐一修改,具体见handler分析*/
•Field 字段描述符(sql/field.h)
/*域描述符,是各种字段的抽象基类*/ uchar *ptr; // 记录中数据域的位置 uchar *null_ptr; // 记录 null_bit 位置的byte TABLE *table; // 指向表的指针 TABLE *orig_table; // 指向原表的指针 const char **table_name, *field_name; /* 数据域是下列key的一部分 */ key_map key_start, part_of_key, part_of_key_not_clustered; key_map part_of_sortkey; /*以下操作将要insert\Update的值先设置到field中,这些field会默认填充到table.record[0]中*/ int store(const char *from, uintlength, CHARSET_INFO *cs) void store_time(TIME*ltime,timestamp_type t_type)
•handler SQL层与Storage的接口
/*可通过table->file得到,innoDB等存储引擎将会实现handler的子类,以提供具体的write、update操作实现,但是handler中的ha_write_row等已经实现整体的逻辑,如先write_row,再binlog_log_row()*/ int ha_open(const char *name, int mode, int test_if_locked) /*tbname.frm文件*/ int ha_index_init(uint idx) /*为下面的操作准备索引初始化,在find操作中使用了,但是在insert中没有使用*/ int ha_rnd_init(bool scan) /*初始化为随机位置访问,scan决定是否全表扫描*/ int ha_write_row(uchar *buf); /*先write_row,再binlog_log_row(),binlog写入是在handler中完成的*/
该类也是实现自己的StorageEngine必须实现的,具体的StorageEngine的写法本文不深入研究,有兴趣请查看《Understanding.Mysql.Internals》 和《Expert MySQL》,下面简单列一下InnoDB需要实现的关键接口
virtual int write_row(byte * buf) /*buf通常是table->record[0]*/ virtual int update_row (const byte *old_data, byte * new_data) /*record[1],record[0]*/ virtual int delete_row(const byte * buf) /*record[0]*/ virtual int index_read(byte * buf, const byte * key,uint key_len, enum ha_rkey_ function find_flag); /*根据findflag找到第一匹配记录*/ virtual int index_prev(byte * buf); /*根据当前索引的顺序,写入上一个record到buffer中*/ virtual int index_next(byte * buf); /*根据当前索引的顺序,写入下一个record到buffer中*/ virtual int index_next_same (byte *buf, const byte *key, uint keylen);/*从当前位置再找一个满足等于key的*/
本文只是有个粗略的介绍,分享下自己阅读代码和书籍的经验,只能对mysql的系统结构有个大致的描述,并且指出mysql源码研究必要的一些基础数据结构。如果读者想要深入研究mysql,请阅读上文中提到的两本书,并且务必请阅读源码。