Mysql源码学习笔记 偷窥线程_Mysql

感觉代码有些凌乱,注释代码都写的比较随意,好像没有什么统一的规范,不同的文件中代码风格也有差异,可能Mysql经过了很多牛人的手之后,集众牛人之长吧。也可能是我见识比较浅薄,适应了自己的代码风格,井底之蛙了,总之还是怀着敬畏的心情开始咱的源码之旅吧。本人菜鸟,大神轻拍。

  Mysql可以启动起来了,应该怎么学习呢?总不能从main开始一步一步的看吧,Mysql作为比较底层的大型软件,涉及到数据库实现的方方面面,没有厚实的数据库理论基础和对Mysql各个模块相当的熟悉,从main开始势必会把自己引入某个死胡同啊,什么都看,最后啥也不会,咱伤不起。

  经过思考后,我想还是通过客户端来调试服务器,从而学习服务器代码比较现实。也就是通过客户端的动作,看服务器的反应。比如从客户端的登录动作来看SERVER如何进行通信、用户识别、鉴定以及任务分配的,通过CREATE TABLE,来看SERVER如何解析DDL语句以及针对不同的存储引擎采取的不同的物理存储方式,通过INSERT语句,来看SERVER如何进行Btree的操作。通过SELECT语句来看如何进行SQL语句语法树的创建和优化的,通过ROLL BACK,来看SERVER事务是如何实现的。这里主要是通过跟踪代码学习Mysql数据库实现的思想,对于具体的代码不去做过多的追究(主要是我对C++不是很熟悉),好读书,不求甚解,呵呵。
  由此,暂时准备了以下几条SQL语句,来有针对的进行SERVER的分析

复制代码 代码如下:

1、 LOGIN(登录)
mysql.exe –uroot –p
2、 DDL(建表语句)
create table tb_myisam(c1 int, c2 varchar(256)) engine = myisam;
create table tb_innodb(c1 int, c2 varchar(256)) engine = innodb;
3、 INSERT
Insert into tb_myisam values(1 , '寂寞的肥肉');
Insert into tb_innodb values(1 , '寂寞的肥肉');
4、 SELECT
Select c1 from tb_myisam;
Select * from tb_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的,先粗略看下源码。

复制代码 代码如下:

int pthread_create(pthread_t *thread_id, pthread_attr_t *attr,
pthread_handler func, void *param)
{
HANDLE hThread;
struct pthread_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)
{
int error=errno;
DBUG_PRINT("error",
("Can't create thread to handle request (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,断点停下来,看下系统的线程状况:


我们第一次进入pthread_create,任何线程都没开始创建呢,按理说系统线程应该就只有一个主线程,可现在多了这么多,这些应该是innodb存储引擎创建的线程了(具体是在plugin_init)。根据线程的名称,结合注释,猜测了下这些线程的作用。
   Io_handler_thread:从名称可以知道这些是I/O线程,负责进行磁盘I/O。
Svr_error_monitor_thread:应该是服务器出错监控线程。
Svr_lock_timeout_thread:应该是和上锁相关的线程。
Svr_master_thread:
/*************************************************************************
The master thread controlling the server. */
服务器控制线程,应该是具体进行作业的线程。
Svr_monitor_thread:
/*************************************************************************
A thread prints the info output by various InnoDB monitors. */
监控线程,负责打印信息。

  淡然飘过吧,不去细究了,我们只关心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需要的线程了,下次针对这个线程,分析下如何进行登录的,以及登录后为用户分配哪些资源。时间不早啦,洗洗睡了
作者 : cnblogs 心中无码

时间: 2024-09-20 05:44:05

Mysql源码学习笔记 偷窥线程_Mysql的相关文章

Linux下mysql源码安装笔记_Mysql

1.假设已经有mysql-5.5.10.tar.gz以及cmake-2.8.4.tar.gz两个源文件 (1)先安装cmake(mysql5.5以后是通过cmake来编译的) [root@ rhel5 local]#tar -zxv -f cmake-2.8.4.tar.gz [root@ rhel5 local]#cd cmake-2.8.4 [root@ rhel5 cmake-2.8.4]#./configure [root@ rhel5 cmake-2.8.4]#make [root@

MySQL源码学习: concat + outfile的bug 原因分析

项目中碰到一个bug,需要将MySQL表中的数据导出,字段中间用逗号隔开. 1.复现 步骤: 版本 5.1.48 a) 准备数据 CREATE TABLE `test` ( `id` int(11) DEFAULT NULL, `data` char(10) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into tad2 values (1,'丁\\奇'); b) select concat(id, data) from te

MySQL源码学习:ib_logfile、bin-log与主从同步

今天研究MySQL主从同步的同事问了一个问题,如果InnoDB写完ib_logfile后,服务异常关闭.会不会由于主库能够根据ib_logfile恢复数据,而由于bin-log没写导致从库同步时少了这个事务?或者反之,bin-log写成功,而ib_logfile没有写完,导致从库执行事务,而主库不执行? 这会导致主从不一致. 本文简要说明下这个问题. 1. 写入流程 源码sql/handler.cc: ha_commit_trans { - if ((err= ht->prepare(ht, t

BufferedInputStream 源码学习笔记

BufferedInputStream是一个带有内存缓冲的InputStream. BufferedInputStream是继承自FilterInputStream. FilterInputStream继承自InputStream属于输入流中的链接流,同时引用了InputStream,将InputStream封装成一个内部变量,同时构造方法上需要传入一个InputStream.这是一个典型的装饰器模式,他的任何子类都可以对一个继承自InputStream的原始流或其他链接流进行装饰,如我们常用的

MySQL源码学习:关于慢查询日志中的Rows_examined=0

最近在一个项目中DBA同学问了一个问题:为什么很多慢查询日志中显示 Rows_examined : 0? 需要说明的是, 这类慢查询语句都是类似 select count(*) from (-)t; 在说明这个问题之前,我们先指出两个相关背景: 1.MySQL的临时表,都是MyISAM的. 2.MyISAM表中的记录总数是额外存储的,count(*)的时候不需要遍历数据. 3.把count(*)转换为取一个const值这件事情,是在优化(optimize)阶段作的. 问题分析: 这个值对应于代码

MySQL源码学习:InnoDB的ib_logfile写入策略

ib_logfile是InnoDB的事务日志文件.本文简要说明其写入时机.写入策略及如何保证数据安全. 1. 基本概念 a) ib_logfile文件个数由innodb_log_files_in_group配置决定,若为2,则在datadir目录下有两个文件,命令从0开始,分别为ib_logfile0和ib_logfile. b) 文件为顺序写入,当达到最后一个文件末尾时,会从第一个文件开始顺序复用. c) lsn: Log Sequence Number,是一个递增的整数. Ib_logfil

MySQL源码学习:InnoDB关于group commit的简单QA

    前天同事问了个问题,今天又再翻了下group commit.关于这个话题Kristian Nielsen有一个很详尽的系列文章(http://kristiannielsen.livejournal.com/12254.html), 有四个页面,文中有链接.这里列出一些细节,主要是对上面文章补充一下. Q:什么是group commit. A:1) 简单说就是:好几个线程写文件,然后一个线程fsync: 2) 只有事务日志(ib_logfile)用到: 3) 注意是多个线程(多用户).一个

MySQL源码学习:索引使用统计功能

今天刚刚知道Oracle有个索引统计的功能,可以统计每个索引的使用次数.作为一个Oracle的门外汉,我还是再次感叹人家做的是真细致.第二个想法就是给MySQL也加上. Percona版本的information_schema.innodb_index_stats 已经有索引的统计信息,我们就在巨人的肩膀上踩一脚了. 先来看下原来的表结构. CREATE TEMPORARY TABLE `INNODB_INDEX_STATS` ( `table_schema` varchar(192) NOT

MySQL源码学习:MySQL中禁止跨库访问的实现

 先说一下这里"跨库"的意思:当前use的是db1, 仍可以使用select * from db2.table1来访问table1表. 这样使得我们需要访问同一个MySQL下的其他表时不需要多一次use,也使得多个库间的表join这样的操作成为可能. 1. 问题背景 但有些使用场景下是有禁掉这种功能的需求.比如一些开放应用托管服务,一般给一个应用指定使用一种类型的db, 多个用户使用相同的应用,但每个用户访问自己的db.由于有复用连接的需求,使得不能给连接的mysqluser作库权限限