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
TABLE\* handler_tables
TABLE\* temporary_tables
TABLE\* derived_tables
•NET 网络连接描述(sql/mysql_com.h)
该类在HandlerSocket中没有用到
<pre
Vio *vio; //底层的网络I/O socket描述符 ch\ar *bu\ff,*buff_e\nd,*write_p\* s,*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:特例,表示缓存不能装下当前这么大的包 \*/
</code> </pre>
•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</span> ha_index_init(uint idx)
//为下面的操作准备索引初始化,在find操作中使用了,但是在insert中没有使用
int</span> 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,请阅读上文中提到的两本书,并且务必请阅读源码。
BIGDATA
handlerSocket MySQL源码,关键结构