mysql源代码分析.docx
- 文档编号:26646287
- 上传时间:2023-06-21
- 格式:DOCX
- 页数:18
- 大小:23.21KB
mysql源代码分析.docx
《mysql源代码分析.docx》由会员分享,可在线阅读,更多相关《mysql源代码分析.docx(18页珍藏版)》请在冰豆网上搜索。
mysql源代码分析
Mysql源代码分析系列
(2):
源代码结构
Mysql源代码主要包括客户端程序代码,服务器端代码,测试工具和一些库构成,下面我们对比较重要的目录做些介绍。
BUILD
这个目录在本系列的上篇文章中我们仔细看过,内含各种平台的编译脚本,这里就不仔细说了。
client
这个目录下有如下比较让人眼熟的文件:
,,,等等,如果你编译一下就会发现那些眼熟的程序也出现了,比如mysql。
明白了吧,这个目录就是那些客户端程序所在的目录。
这个目录的内容也比较少,而且也不是我们阅读的重点。
Docs
这个目录包含了文档。
storage
这个目录包含了所谓的Mysql存储引擎(storageengine)。
存储引擎是数据库系统的核心,封装了数据库文件的操作,是数据库系统是否强大最重要的因素。
Mysql实现了一个抽象接口层,叫做handler(sql/,其中定义了接口函数,比如:
ha_open,ha_index_end,ha_create等等,存储引擎需要实现这些接口才能被系统使用。
这个接口定义超级复杂,有900多行:
-(,不过我们暂时知道它是干什么的就好了,没必要深究每行代码。
对于具体每种引擎的特点,我推荐大家去看mysql的在线文档:
应该能看到如下的目录:
*innobase,innodb的目录,当前最流行的存储引擎
*myisam,最早的Mysql存储引擎,一直到innodb出现以前,使用最广的引擎。
*heap,基于内存的存储引擎
*federated,一个比较新的存储引擎
*example,csv,这几个大家可以作为自己写存储引擎时的参考实现,比较容易读懂
mysys
包含了对于系统调用的封装,用以方便实现跨平台。
大家看看文件名就大概知道是什么情况了。
sql
这个目录是另外一个大块头,你应该会看到,没错,这里就是数据库主程序mysqld所在的地方。
大部分的系统流程都发生在这里。
你还能看到,,,等等,分别实现了对应的SQL命令。
后面我们还要经常提到这个目录下的文件。
大概有如下及部分:
SQL解析器代码:
,,等,实现了对SQL语句的解析操作。
"handler"代码:
,定义了存储引擎的接口。
"item"代码:
,定义了SQL解析后的各个部分。
SQL语句执行代码:
,,,执行SQL对应的语句。
当你要看"SELECT..."的执行的时候,直接到去看就OK了。
辅助代码:
实现网络操作
还有其他很多代码。
vio
封装了virtualIO接口,主要是封装了各种协议的网络操作。
plugin
插件的目录,目前有一个全文搜索插件(只能用在myisam存储引擎)。
libmysqld
Mysql连接库源代码。
开源函数库目录
和所有的开源项目一样,Mysql也使用了一些开源的库,在其代码库中我们能看到dbug、pstack、strings、zlib等。
多说无益,主要是对于mysql的代码目录有个概念,要找的时候也有个方向。
万一要找某个东西找不到了就只能grep了...
Mysql源代码分析系列(3):
主要调用流程
引言
本文主要介绍Mysql主要的调用流程,将从代码的角度来看一个从用户发出的"select*fromtest"SQL命令在服务器内部是如何被执行的。
从我个人的经验来看,阅读理解大规模项目的代码最重要的两个方面,一是了解主要的数据结构,二是了解数据流,在这里主要是调用流程。
把这两个主线把握住以后,大部分代码都是比较容易阅读的,Mysql的源代码属于比较好读的类型,因为函数的调用关系比较明确。
难读的代码一般都充斥着大量的回调、异步调用,很可能你极难找到某个函数在哪里或什么时候被调用了。
当然,算法的实现代码也很难读。
幸好Mysql不是那种难读的类型,所以我们也不要害怕,大步向前吧!
主要执行过程
从架构上来看,Mysql服务器对于一条SQL语句的执行过程可以分成如下几部分:
接受命令 包括用户验证,资源申请等
|
V
命令解析 解析SQL语句,生成语法树
|
V
寻找执行计划 根据解析出来的语法树,找到可能的执行计划。
对于一条SQL语句,很可能会有多种执行方案,特别是在SQL语句比较复杂的时候。
这里需要对于各种可能的方案进行代价评估,最快的找到最有的执行方案。
|
V
优化执行计划 优化执行计划。
这是SQL执行中最复杂的部分之一,据说全都是由数学博士们写出来的,而且比较难懂。
我目前还处于不懂的状态。
|
V
执行 没啥可说的,只剩执行及返回结果了
系统启动
所有的程序都从main开始,mysqld也不例外,打开sql/,稍加搜索,你就能看到熟悉的main函数,我们可以将其进行如下简写:
intmain(intargc,char*argv[]){
();
init_common_variables(MYSQL_CONFIG_NAME,argc,argv,load_default_groups)); .); .); .;
caseCOM_TABLE_DUMP:
...;
caseCOM_CHANGE_USER:
...;
...
caseCOM_QUERY:
alloc_query(thd,packet,packet_length);
mysql_parse(thd,thd->query,thd->query_length,&end_of_stmt);
}
}
进行sql语句解析
voidmysql_parse(THD*thd,constchar*inBuf,uintlength,constchar**found_semicolon){
lex_start(thd);
if(query_cache_send_result_to_client(thd,(char*)inBuf,length)<=0){ .
caseSQLCOM_INSERT:
insert_precheck(thd,all_tables);
mysql_insert(thd,all_tables,lex->field_list,lex->many_values,lex->update_list,lex->value_list,lex->duplicates,lex->ignore);
break;
...
caseSQLCOM_SELECT:
check_table_access(thd,lex->exchangeSELECT_ACL|FILE_ACL:
SELECT_ACL, all_tables,UINT_MAX,FALSE); .);
foreachvalueinvalues_list{
write_record(...);
}
}
其实里面还有很多处理trigger,错误,view之类的,我们暂时都忽略。
734 */
735 if(table->next_number_field&&buf==table->record[0]) c-rni*
plugin/daemon_example/:
187:
mysql_declare_plugin(daemon_example)
sql/:
6269:
mysql_declare_plugin(partition)
sql/:
5528:
mysql_declare_plugin(binlog)
sql/:
10533:
mysql_declare_plugin(ndbcluster)
storage/csv/:
1603:
mysql_declare_plugin(csv)
storage/example/:
893:
mysql_declare_plugin(example)
storage/myisam/:
2057:
mysql_declare_plugin(myisam)
storage/heap/:
746:
mysql_declare_plugin(heap)
storage/innobase/handler/:
8231:
mysql_declare_plugin(innobase)
storage/myisammrg/:
1186:
mysql_declare_plugin(myisammrg)
storage/blackhole/:
356:
mysql_declare_plugin(blackhole)
storage/federated/:
3368:
mysql_declare_plugin(federated)
storage/archive/:
1627:
mysql_declare_plugin(archive)
呵呵,连binlog都是plugin哦,不过还是storageplugin占大多数。
Plugin初始化
在见面的介绍main函数的文章中我也提到了其中有个函数plugin_init()是初始化的一部分,这个东东就是所有静态链接初始化plugin的初始化入口。
该函数定义在"sql/"中。
intplugin_init(int*argc,char**argv,intflags){
.
.
}
这个函数执行结束以后,在plugin_array,plugin_dl_array,plugin_hash中保存了当前加载了的所有的plugin。
到此plugin初始化结束。
在plugin_initialize函数里面,调用了每个plugin自己的init函数(参见前面的内容)。
特别要提到的是对于各种不同类型的plugin,初始化函数的参数也不一样,这是通过一个全局的plugin_type_initialize间接层来实现的。
这个数组对于每种类型的plugin定义了一个函数,比如对于storageplugin对应的是ha_initialize_handlerton,对于informationscheme对应的是initialize_schema_table,然后在这些函数中再调用plugin的初始化函数。
暂时对于其他类型的plugin没有定义这个中间层初始化函数,所以就直接调用了plugin的初始化函数。
Mysql源代码分析(6):
Plugin架构介绍-续
上篇文章我们分析了Mysql的Plugin接口以及plugin的初始化过程,这里我们继续看plugin怎么被使用的。
基本还是通过例子看问题,主要分析myisam如何通过plugin接口被调用的。
myisam是mysql最早的和默认的storageengine,前面我们也看到在plugin初始化的时候是优先初始化myisam,然后才初始化其他的存储引擎。
这里我们假定用户要对一个myisam的表做操作,具体看看其中涉及的调用过程。
myisam的初始化
myisamplugin的定义可以在storage/myisam/中找到:
mysql_declare_plugin(myisam)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&myisam_storage_engine,
"MyISAM",
"MySQLAB",
"DefaultengineasofMySQLwithgreatperformance",
PLUGIN_LICENSE_GPL,
myisam_init,/*PluginInit*/
NULL,/*PluginDeinit*/
0x0100,/**/
NULL, /*statusvariables */
NULL, /*systemvariables */
NULL /*configoptions */
}
mysql_declare_plugin_end;
初始化函数是myisam_init。
在前面文章中提到,storageengine类型的plugin均是通过ha_initialize_handlerton初始化。
myisam_init的输入参数是void*p,实际上是handlerton*。
handlerton在mysql中封装了访问一个存储引擎需要的接口,每个存储引擎在全局空间有一个handlerton对象,保存在对应的内存中plugin结构的data域中。
该结构具体定义可以在sql/中找到。
myisam_init做的事情很简单,设置handlerton中的各个域,其中最重要的域是create,被指向了一个函数myisam_create_handler,这个函数用来创建handler,用来对于数据库文件进行操作。
打开一个表
数据库表是数据库中所有操作的基础,我们看看打开一个表需要做些什么。
当一个select命令进来的时候,中的execute_sqlcom_select被执行,并被传入parse出来的所有该命令要用的到表。
它会调用open_and_lock_tables来打开指定的表,然后调用open_and_lock_tables_derived,再调用open_tables,再调用open_table。
一大堆调用之后真正开始干实事儿的是open_unireg_entry,名字很奇怪,但是确实就是它开始打开表了,我们仔细将仔细看这个函数,以及它调用的函数。
这个函数很长,其实大部分都是在做错误处理,最重要的就以下几行:
staticintopen_unireg_entry(THD*thd,TABLE*entry,TABLE_LIST*table_list,constchar*alias,char*cache_key,uintcache_key_length,MEM_ROOT*mem_root,uintflags){
...
share=get_table_share_with_create(thd,table_list,cache_key,cache_key_length,OPEN_VIEW|table_list->i_s_requested_object,&error);
open_table_from_share(thd,share,alias,...);
...
}
get_table_share_with_create是创建一个table_share结构,包括了同一个类型的表公用的数据结构,open_table_from_share则通过这个公用结构打开对应的要操作的表。
TABLE_SHARE*get_table_share(THD*thd,TABLE_LIST*table_list,...){
share=alloc_table_share(table_list,key,key_length));rm文件,存储了表的定义,这个函数就是要打开表对应的frm文件,读入定义信息,填入TABLE_SHARE结构。
intopen_table_def(THD*thd,TABLE_SHARE*share,uintdb_flags){
file=my_open(path,O_RDONLY|O_SHARE,MYF(0))
open_binary_frm(thd,share,head,file);
}
open_binary_frm读入二进制的frm文件信息。
这个函数超长,但是我们暂时只是对与plugin相关的部分感兴趣。
因为每个表的storageengine信息就是从frm文件中读出来的,我们看相关的代码片段:
open_binary_frm(...){
...
plugin_reftmp_plugin=ha_resolve_by_name(thd,&name); .
}
好了,TABLE_SHARE设置好了,我们回到open_unireg_entry中,继续看open_table_from_share。
这才是真正打开表的地方。
这个函数还是在sql/中。
这个函数还是超长...,万幸的是我们还是只想关注plugin相关的内容。
TABLE中有一个file结构,类型是handler*,我们以前提到过,handler就是一个打开的表的引用,显然open_table_from_share的责任之一就是要设置这个域。
intopen_table_from_share(THD*thd,TABLE_SHARE*share,...TABLE*outparam,...){ .
outparam->file=get_new_handler(share,&outparam->mem_root,share->db_type()));
...
outparam->file->ha_open(outparam,...);.
}
get_new_handler负责根据TABLE_SHARE的内容构造一个handler对象。
这个函数在sql/中。
handler*get_new_handler(TABLE_SHARE*share,MEM_ROOT*alloc,handlerton*db_type){
file=db_type->create(db_type,share,alloc); rm文件。
这个文件是跨引擎的,描述了该表的元信息,其中最重要的是表定义和表的数据库引擎。
.MYD文件。
这是我们要看的重点文件,包含了数据库record信息,就是数据库中的每个行。
.MYI文件。
索引文件,用来加速查找。
而对于MYD中的每个record,可以是fixed,dynamic以及packed三种类型之一。
fixed表示record的大小是固定的,没有VARCHAR,blob之类的东东。
dynamic则刚好相反,有变长数据类型。
packed类型是通过myisampack处理过的record。
参见:
。
需要注意的是record类型是针对表的设置,而不是对每个column的设置。
record处理接口
record的类型是表级别的设置,所以在一个表被打开的时候,myisam会检查元数据的选项,看该表的record是什么类型,然后设置对应的处理函数,具体处理在storage/myisam/的mi_setup_functions中,我们看其中的一个片段:
746voidmi_setup_functions(registerMYISAM_SHARE*share)
747{
....
759 elseif(share->options&HA_OPTION_PACK_RECORD)
760 {
761 share->read_record=_mi_read_dynamic_record;
762 share->read_rnd=_mi_read_rnd_dynamic_record;
763 share->delete_record=_mi_delete_dynamic_record;
764 share->compare_record=_mi_cmp_dynamic_record;
765 share->compare_unique=_mi_cmp_dynamic_unique;
766 share->calc_checksum=mi_checksum;
767
768 /*addbitsusedtopackdatatopack_reclengthforfasterallocation*/
769 share->+=share->;
770 if(share->
771 {
772 share->update_record=_mi_update_blob_record;
773 share->write_record=_mi_write_blob_record;
774 }
775 else
776 {
777 share->write_record=_mi_write_dynamic_record;
778 share->update_record=_mi_update_dynamic_record;
779 }
780 }
...
这是针对pack类型的处理函数设置。
设置了share结构中的一堆函数接口。
顺便说一句,这种方式是C语言编程中常用的实现”多态“的办法:
申明函数接口,动态设置接口实现,思想上和C++的动态绑定是一致的。
这段代码对于dynamic类型的表的record处理函数做了设置。
比较有趣的是HA_OPTION_PACK_RECORD用来指定dynamic类型。
看到这些函数名大家可以猜想出他们都是干嘛的,下面主要看看fixed类型和dynamic类型的具体处理。
Fixed类型
顾名思义,fixed类型的表中的所有字段都是定长的,不能出现TEXT,VARCHAR之类的东东。
这种严格限制带来的好处就是更快更直接的数据record操作,想想也知道,每个数据都是定长的,在文件操作的时候多方便啊。
看看一个数据的函数_mi_write_static_record,它在中,所有对于fixedrecord的操作的实现都定义在这个文件中。
21int_mi_write_static_
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- mysql 源代码 分析