Mysql源代码分析系列(2): 源代码结构
Mysql源代码主要包括客户端程序代码,服务器端代码,测试工具和一些库构成,下面我们对比较重要的目录做些介绍。
BUILD
这个目录在本系列的上篇文章中我们仔细看过,内含各种平台的编译脚本,这里就不仔细说了。
client
这个目录下有如下比较让人眼熟的文件: , mysqlcheck.c, , mysqlshow.c,等等,如果你编译一下就会发现那些眼熟的程序也出现了,比如mysql。明白了吧,这个目录就是那些客户端程序所在的目录。这个目录的内容也比较少,而且也不是我们阅读的重点。
Docs
这个目录包含了文档。
storage
这个目录包含了所谓的Mysql存储引擎 (storage engine)。存储引擎是数据库系统的核心,封装了数据库文件的操作,是数据库系统是否强大最重要的因素。Mysql实现了一个抽象接口层,叫做handler(sql/handler.h),其中定义了接口函数,比如:ha_open, ha_index_end, ha_create等等,存储引擎需要实现这些接口才能被系统使用。这个接口定义超级复杂,有900多行 :-(,不过我们暂时知道它是干什么的就好了,没必要深究每行代码。对于具体每种引擎的特点,我推荐大家去看mysql的在线文档: sql/doc/refman/5.1/en/storage-engines.html
应该能看到如下的目录:
* innobase, innodb的目录,当前最流行的存储引擎
* myisam, 最早的Mysql存储引擎,一直到innodb出现以前,使用最广的引擎。
* heap, 基于内存的存储引擎
* federated, 一个比较新的存储引擎
* example, csv,这几个大家可以作为自己写存储引擎时的参考实现,比较容易读懂
mysys
包含了对于系统调用的封装,用以方便实现跨平台。大家看看文件名就大概知道是什么情况了。
sql
这个目录是另外一个大块头,你应该会看到,没错,这里就是数据库主程序mysqld所在的地方。大部分的系统流程都发生在这里。你还能看到, , ,等等,分别实现了对应的SQL命令。后面我们还要经常提到这个目录下的文件。
大概有如下及部分:
SQL解析器代码: , , , 等,实现了对SQL语句
的解析操作。
"handler"代码: , handler.h,定义了存储引擎的接口。
"item"代码:, ,定义了SQL解析后的各个部分。
SQL语句执行代码: , , , ,执行SQL对应的语句。当你要看"SELECT ..."的执行的时候,直接到去看就OK了。
辅助代码: 实现网络操作
还有其他很多代码。
vio
封装了virtual IO接口,主要是封装了各种协议的网络操作。
plugin
插件的目录,目前有一个全文搜索插件(只能用在myisam存储引擎)。
libmysqld
Mysql连接库源代码。
开源函数库目录
和所有的开源项目一样,Mysql也使用了一些开源的库,在其代码库中我们能看到dbug、pstack、strings、 zlib等。
多说无益,主要是对于mysql的代码目录有个概念,要的时候也有个方向。万一要某个东西不到了就只能grep了...
Mysql源代码分析系列(3): 主要调用流程
引言
本文主要介绍Mysql主要的调用流程,将从代码的角度来看一个从用户发出的"select * from
test" SQL命令在服务器内部是如何被执行的。从我个人的经验来看,阅读理解大规模项目的代码最重要的两个方面,一是了解主要的数据结构,二是了解数据流,在这里主要是调用流程。把这两个主线把握住以后,大部分代码都是比较容易阅读的,Mysql的源代码属于比较好读的类型,因为函数的调用关系比较明确。难读的代码一般都充斥着大量的回调、异步调用,很可能你极难到某个函数在哪里或什么时候被调用了。当然,算法的实现代码也很难读。幸好Mysql不是那种难读的类型,所以我们也不要害怕,大步向前吧!
主要执行过程
从架构上来看,Mysql服务器对于一条SQL语句的执行过程可以分成如下几部分:
接受命令 包括用户验证,资源申请等
|
V
命令解析 解析SQL语句,生成语法树
|
V
寻执行计划 根据解析出来的语法树,到可能的执行计划。对于一条SQL语句,很可能会有多种执行方案,特别是在SQL语句比较复杂的时候。这里需要对于各种可能的方案进行代价评估,最快的到最有的执行方案。
|
V
优化执行计划 优化执行计划。这是SQL执行中最复杂的部分之一,据说全都是由数学博士们写出来的,而且比较难懂。我目前还处于不懂的状态。
|
V
执行 没啥可说的,只剩执行及返回结果了
系统启动
所有的程序都从main开始,mysqld也不例外,打开,稍加搜索,你就能看到熟悉的main函数,我们可以将其进行如下简写:
mysql存储文档int main(int argc, char* argv[]) {
logger.init_base();
init_common_variables(MYSQL_CONFIG_NAME, argc, argv, load_default_groups)); // 解析配置文件和命令行参数,将配置文件中的内容转行成命令行参数
init_signals();
user_info= check_user(mysqld_user);
set_user(mysqld_user, user_info);
init_server_components(); // 初始化服务器模块
network_init(); // 初始化网络模块,根据配置,打开IP socket/unix socket/windows named pipe来进行监听。
start_signal_handler(); // 开始接收信号
acl_init(...); // 初始化ACL (Access Control List)
servers_init(0); // 服务器初始化
init_status_vars(); // 状态变量初始化
create_shutdown_thread(); // 创建关闭线程
create_maintenance_thread(); // 创建维护线程
sql_print_information(...); // 打印一些信息
handle_connections_sockets(0); // 主要的服务处理函数,循环等待并接受命令,进行查询,返回结果,也是我们要详细关注的函数
wait for exit; // 服务要退出
cleanup;
exit(0);
}
可以仔细的看看这个简写的main函数,逻辑很清楚,就算没有我的这些注释大部分人也能容易的理解整个系统的执行流程。其实完整的main函数有接近300行,但是中心思想已经被包含在这里简短的十几行代码中了。
通过看这些代码,读者会发现mysqld是通过多线程来处理任务的,这点和Apache服务器是不一样的。
等待命令
mysqld等待和处理命令主要在handle_connections_sockets(0);来完成,这里我们仔细看看这个函数调用发生了什么。该函数也在中,也有大概300行,我们继续简写。
为了方便分析,这里我们假定配置服务器通过unix domain socket来监听接受命令,其他方式类同。
pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
{
FD_ZERO(&clientFDs);
FD_SET(unix_sock,&clientFDs); // unix_socket在network_init中被打开
socket_flags=fcntl(unix_sock, F_GETFL, 0);
while (!abort_loop) { // abort_loop是全局变量,在某些情况下被置为1表示要退出。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论