如何通过源码来实现mysql中的线程实现
线程一般在MySQL中可以反映出来,有一种方法可以通过客户端的动作,通过从客户端的登录一系列的工作来看,线程在mysql中的行踪,本文主要采用了跟踪代码来学习mysql数据库实现线程。
经过思考后,我想还是通过客户端来调试服务器,从而学习服务器代码比较现实。也就是通过客户端的动作,看服务器的反应。比如从客户端的登录动作来看SERVER如何进行通信、用户识别、鉴定以及任务分配的,通过CREATETABLE,来看SERVER如何解析DDL语句以及针对不同的存储引擎采取的不同的物理存储方式,通过INSERT语句,来看SERVER如何进行Btree的操作。通过SELECT语句来看如何进行SQL语句语法树的创建和优化的,通过ROLLBACK,来看SERVER事务是如何实现的。这里主要是通过跟踪代码学习Mysql数据库实现的思想。
由此,暂时准备了以下几条SQL语句,来有针对的进行SERVER的分析
1、LOGIN(登录)
mysql.exe–uroot–p
2、DDL(建表语句)
createtabletb_myisam(c1int,c2varchar(256))engine=myisam;
createtabletb_innodb(c1int,c2varchar(256))engine=innodb;
3、INSERT
Insertintotb_myisamvalues(1,’寂寞的肥肉’);
Insertintotb_innodbvalues(1,’寂寞的肥肉’);
4、SELECT
Selectc1fromtb_myisam;
Select*fromtb_innodb;
5、ROLLBACK
大家都知道,mysql可以通过多个客户端,进行并发操作,当然也包括登录了。在别人登录的时候,其他的用户可能正在进行一些其它的操作,因此对于登录我们猜测应该有专门的线程负责客户端和服务器的连接的创建,以保证登录的及时性,对于每个连接的用户,应该用一个独立的线程进行任务的执行。
首先介绍下mysql中创建线程的函数,创建线程的函数貌似就是_begin_thread,CreateThread,我们通过VS在整个解决方案中进行查找,bingo!在my_winthread.c中找到了调用_begin_thread的函数pthread_create,在os0thread.c中找到了调用CreateThread的函数os_thread_create,一个系统怎么封装两个系统函数呢??再仔细看下,发现my_winthread.c是在项目mysys下,而os0thread.c是在项目innobase下。innobase!!这不就是innodb的插件式存储引擎么,原来这是存储引擎自己的封装的底层函数,哥心中豁然开朗了。我想Mysql应用范围如此之广,除了开源之外,插件式的存储引擎功不可没啊,用户可以根据自己的实际应用采取不同的存储引擎,对于大公司,估计会开发自己的存储引擎。
下面分析下pthread_create是如何调用_begin_thread的,先粗略看下源码。
intpthread_create(pthread_t*thread_id,pthread_attr_t*attr,
pthread_handlerfunc,void*param)
{
HANDLEhThread;
structpthread_map*map;
DBUG_ENTER(“pthread_create”);
if(!(map=malloc(sizeof(*map))))
DBUG_RETURN(-1);
map->func=func;
map->param=param;
pthread_mutex_lock(&THR_LOCK_thread);
#ifdef__BORLANDC__
hThread=(HANDLE)_beginthread((void(_USERENTRY*)(void*))pthread_start,
attr->dwStackSize?attr->dwStackSize:
65535,(void*)map);
#else
hThread=(HANDLE)_beginthread((void(__cdecl*)(void*))pthread_start,
attr->dwStackSize?attr->dwStackSize:
65535,(void*)map);
#endif
DBUG_PRINT(“info”,(“hThread=%lu”,(long)hThread));
*thread_id=map->pthreadself=hThread;
pthread_mutex_unlock(&THR_LOCK_thread);
if(hThread==(HANDLE)-1)
{
interror=errno;
DBUG_PRINT(“error”,
(“Can'tcreatethreadtohandlerequest(error%d)”,error));
DBUG_RETURN(error?error:-1);
}
VOID(SetThreadPriority(hThread,attr->priority));
DBUG_RETURN(0);
}
关键的代码是下面三句:
map->func=func;
map->param=param;
_beginthread((void(__cdecl*)(void*))pthread_start,
attr->dwStackSize?attr->dwStackSize:
65535,(void*)map);
从这可以看出,创建的新线程的名字是个固定的函数——pthread_start,而我们传进来的想创建的函数func是挂载在了map上了,函数的参数同样的挂载在map上了,这样我们就可以推理出在pthread_start函数中,肯定会出现这样的代码:
map->func(map->param);
mysql没有选择直接_beginthread(func,stack_size,param)的形式,而是进行了一次封装,不知道这样的好处是什么,可能牛人的思想不是我这样小菜鸟能顿悟的,跑题了~~
至此,我们只在pthread_create函数上设置断点,调试启动mysqld,断点停下来,看下系统的线程状况:
#p#分页标题#e#
我们第一次进入pthread_create,任何线程都没开始创建呢,按理说系统线程应该就只有一个主线程,可现在多了这么多,这些应该是innodb存储引擎创建的线程了(具体是在plugin_init)。根据线程的名称,结合注释,猜测了下这些线程的作用。
Io_handler_thread:从名称可以知道这些是I/O线程,负责进行磁盘I/O。
Svr_error_monitor_thread:应该是服务器出错监控线程。
Svr_lock_timeout_thread:应该是和上锁相关的线程。
Svr_master_thread:
/*************************************************************************
Themasterthreadcontrollingtheserver.*/
服务器控制线程,应该是具体进行作业的线程。
Svr_monitor_thread:
/*************************************************************************
AthreadprintstheinfooutputbyvariousInnoDBmonitors.*/
监控线程,负责打印信息。
淡然飘过吧,不去细究了,我们只关心pthread_create创建的线程。根据调试,发现多了几个线程同名的线程_threadstart,如下所示:
调试时看堆栈可以知道这三个线程的创建者和作用,如下所示
创建者 处理函数
create_shutdown_thread handle_shutdown
start_handle_manager handle_manager
handle_connections_methods handle_connections_sockets
创建者:调用pthread_create进行创建线程的函数。
处理函数:调用pthread_create所创建的线程的具体的线程函数。
由名称我们就可以看出,handle_connections_sockets应该是处理连接的线程了,从顺序上看,也应该是这样,只有系统中所有的其他必须的线程创建完毕后,才能创建监听线程(连接线程),即监听线程应该是系统最后创建的。
找到了我们LOGIN需要的线程了,下次针对这个线程,分析下如何进行登录的,以及登录后为用户分配哪些资源。时间不早啦,洗洗睡了