由于网络原因,图片都贴不上来

MySQL的main函数在哪?执行一个sql语句到底经历了哪些流程和函数?MySQL的事务一致性到底是如何保证的?本文通过分析一个insert语句在MySQL代码中的关键流程,来为大家解答这些问题,同时这个insert,也是”insert”进入MySQL源码学习的第一步。

注:下面MySQL的源代码,若无特别说明,将会以5.7.18版本的代码作为依据

从哪里开始看MySQL代码?

MySQL源码下载下来后,大家会看到一堆文件夹,不知从何入手。不用着急,一开始只需要关注两个文件夹:sql和storage。其他文件夹暂不进行说明,后面需要时再进行介绍。

众所周知,MySQL通过把存储引擎做成插件一样,可以随意替换,其中sql文件夹中的,就是存储层之上的核心代码,storage文件夹中的,就是存储引擎的代码。

进入storage文件夹,可以看到一些熟悉的名字,ndb, myisam, innobase等等。这些就是每个存储引擎的代码。我们就以最流行的innodb存储引擎为例,来进行说明。(注意innobase文件夹中,就是innodb存储引擎的代码)。

Main函数在哪里?

进入sql文件夹,可以看到一个http://main.ccMySQL的无限循环在哪里?

写代码的人都知道,我要让程序什么都不敢,还不结束,肯定得有个无限循环,俗称:主循环。MySQL的主循环在哪呢?继续看mysqld_main函数,在经历了几百行代码后,终于看到了: mysqld_socket_acceptor->connection_event_loop(); 这里就是主循环。文件,没错,这里就是main函数,也就是整个mysql程序的入口。但是打开一看,只有只有寥寥20几行,还基本都是注释,然后发现mysqld_main才是真正的main函数。mysqld_main就在sql/http://mysqld.cc文件中。可以看到,mysqld_main函数在4364行定义的,藏得够深。

如果有IDE的朋友,可以直接点进去看,如果是用notepad++不带插件,甚至使用文本编辑器打开的高手,可以移步sql\connection_acceptor.h,就可以看到connection_event_loop方法:

看到这个方法,应该就很快能理解这里的逻辑了。

一次Insert的调用栈

看到这里,大家一定不耐烦了,啥时候开始正儿八经的插入逻辑呢。那么现在展示一下插入一行数据调用了多少函数:

大家可以看到从180964行开始,到181593行,光堆栈信息就打印了600多行,就可以知道一次insert,究竟经历了多少东西。如果每一个函数都进行说明的话,可以出一本书了。所以,本文只讲几个关键的地方,尤其是说明事务的一致性是如何保证的。

SQL命令的分发

繁多,复杂的SQL命令一定有解析和分发的地方,就算是写代码的新手也一定会想到,MySQL中一定有个庞大的switch case语句,根据不同的命令,调用不同方法。没错,这个switch就在sql/http://sql_parse.cc文件中,顾名思义,这个文件中包含了sql的解析和命令分发。找到mysql_execute_command方法,有可能你会见到一个比之前都长的swith语句:

图:(左)从2740行开始 (右)到4998行

2258行的switch语句,涵盖了mysql几乎所有命令的分发。字符串形式的命令,在进入到mysql_execute_command之前,字符串形式的命令,会被解析为mysql支持的命令,并放到thd->lex->sql_commond中,这个命令的全集,可以在include/my_sqlcommand.h的enum enum_sql_command中找Insert的函数

Insert对应的sql command枚举是:SQLCOM_INSERT,在mysql_execute_command方法中,可以轻易的搜到这个关键字:

图:insert语句的入口

但是当使用一般的IDE点击进入函数是,会发现这个是virtual的函数,它的实现在别的地方。关于sql解析的部分这里不展开说明,后面的文章会对此进行深入说明。有兴趣的读者可以先通过parse_sql,以及http://sql_yacc.cc文件了解sql的解析原理。

这里直接写出mysql_insert的实现方法:

bool Sql_cmd_insert::mysql_insert(THD *thd,TABLE_LIST *table_list)

其中,几个关键的步骤:

open_tables_for_query(thd, table_list, 0)

打开所需的tables,用于后面prepare_insert做准备。为什么是table_list,因为这里要考虑insert select,以及触发器等复杂的语句,因此有时插入数据到一张表,并不意味这就只访问一张表。

mysql_prepare_insert_check_table(thd, table_list, insert_field_list,

select_insert)

做插入之前的检查工作,包括:

区分insert和insert select,因为insert select会与select共享select_lex

检查表是否可以被修改,包括了:表是否是insertable的,table_list中的第一张表是否有insert权限,第二张表是否有select的权限(table_list中的第一个表是被插入的表,其他表一定是SELECT使用的)等。

预先计算和保存NATURAL/USING join类型行类型

……

add_function_default_columns

这里是标记表中的哪些列是需要使用默认值插入的。在使用insert语句时,我们并不一定会每个列都需要给值,那么这里就会把这些列给进行标记。

lock_tables

获得所有表的锁,里面包含了对已有锁,以及与其他类型的锁(如事务锁)的判断,以避免死锁的产生。

ha_start_bulk_insert

这里是当我们insert中使用了多行插入时,允许存储引擎进行插入优化。当只有一个值是,我们从trace日志中也可以看到并没有执行其他的内容。这里可以看到这个函数是ha开头的,在我们sql层的代码中,绝大部分ha_开头的函数都是指要调用存储引擎的接口,ha代表handler。关于handler

write_record

把要插入的数据写到table的buffer中,同时调用对应的触发器;这里是在内存中真正写数据的地方。如果使用的是innodb,从trace日志中可以看到,write_record里面,会调用innobase的handler,最终写入到b+ tree中

图 write record的调用栈

事务提交

执行完mysql_insert方法,程序又回到了mysql_execute_command方法中,并结束巨大的switch语句,在继续向下,就到了关键步骤:事务的提交。

MySQL为了提高写性能,遵循WAL(Write ahead Log)机制,事务日志落盘即可完成事务,这个过程就在trans_commit_stmt(thd)调用中完成

图 trans_commit_stmt的调用栈

如果使用的是innodb,事务提交主要流程,就是从innobase_commit开始的。从上面的调用栈中可以看到,在提交事务的过程中,执行了日志的写入,其中ib_log就是我们熟知的redo log,而后面的write 45060255 to 45060385就是lsn的变化。

详细的来看整个事务的提交过程:

>Innobase_commit

| >innobase_commit_low(trx)

| | >trx_commit_for_mysql(trx)

| | | >trx_commit(trx)

| | | | >trx_commit_low(trx, mtr)

| | | | | >trx_commit_in_memory(rx, mtr, serialised)

| | | | | | >trx_flush_log_if_needed(lsn, trx)

| | | | | | | >trx_flush_log_if_needed_low(lsn)

| | | | | | | | >log_write_up_to

|

其中关注这三个函数trx_commit_in_memory ,log_write_up_to,trx_commit_complete_for_mysql。

trx_commit_in_memory中,生成了lsn,同时,通过innodb_flush_log_at_trx_commit参数来控制mysql是否严格遵循WAL机制。

图 redo log的刷盘机制

从这段代码可以看出,若innodb_flush_log_at_trx_commit=0时,并没有立刻执行刷盘,而是调用thd_requested_durability暂时把日志写到缓冲区中,通过其他线程定时的去把事务日志写入存储。若trx_commit_complete_for_mysql调用完成后,日志还没有写入存储,而mysql又发生了故障,则就会出现事务丢失。因此在mysql的使用过程中,要尽量保证trx_commit_complete_for_mysql=1

再来看log_write_up_to,这里把事务写入了日志缓存中

图 log_write_up_to调用了log_group_write_buf写入日志

注意,从函数名和传入参数可以看出,带有group字样,这就是大名鼎鼎的group commit,把mysql的性能提高了数倍。

最后来看trx_commit_complete_for_mysql,发现几乎和trx_commit_in_memory是同样的流程,单是唯一不同的是,传入参数flush_to_disk不同了,上一次是false,这一次是true,因此,执行了这个函数:log_write_flush_to_disk_low(),其中又调用了file_flush()

图 file flush

再看file flush的定义,传入的是一组日志文件的id,或者tablespace的id

图 file_flush的定义

Innodb就通过这个id,找到对应的datafile文件,最终调用os_file_flush(file) 中的fsync真正把事务写到了存储上。

图 使用fsync写入文件

看到这里,事务终于提交了,但是也有个疑问,为什么有两个几乎相同流程的trx_commit_in_memory和trx_commit_complete_for_mysql呢?

原来,这就是大名鼎鼎的两阶段事务,mysql先在内存中提交事务,然后再把日志真正刷到存储上。两阶段事务,给mysql带来很多的好处,包括:group commit,分布式事务,解决redo和binlog一致性问题等。

mysql insert 源码_MySQL insert源码走读相关推荐

  1. mysql 不完全插入_MySql insert插入操作不完全指北_MySQL

    bitsCN.com MySql insert插入操作不完全指北 1.插入的数据来源自其他表 表A有id, cola 字段 表B有id, cola, colb...等字段,其中id都为主键,cola为 ...

  2. 什么是mysql分发版_MySQL:使用源码分发版还是二进制分发版

    选择了MySQL的安装版本后,要做的第二项决策是你是使用源码分发版还是二进制分发版.大多数情况,如果你的平台上已经有了一个二进制分发版,你可能使用二进制分发版.大多数平台可以使用原格式二进制分发版,例 ...

  3. PHP+Mysql高仿百度知道签到源码演示与下载

    PHP+Mysql高仿百度知道签到源码演示与下载 演示下载地址:http://www.erdangjiade.com/js/512.html 效果图片: 签到表 --  -- 表的结构 `sign`  ...

  4. MySQL医疗信息管理系统数据库(源码)

    MySQL医疗信息管理系统数据库(源码) 友情连接 1.学生成绩管理系统数据库设计--MySQL 2.邮件管理数据库设计--MySQL 3.SQL Server医疗信息管理系统数据库[英文版-源码]- ...

  5. mysql 线程池源码模块_易语言Mysql线程池2.0模块源码

    易语言Mysql线程池2.0模块源码 易语言Mysql线程池2.0模块源码 系统结构:GetThis,初始化,关闭类线程,线程_测试,其他_附加文本,连接池初始化,取mysql句柄,释放mysql句柄 ...

  6. php广告任务网源码_THINKPHP仿我爱广告任务网|任务网站源码下载基于PHP+MYSQL的在线广告打码任务网站源码...

    THINKPHP仿我爱广告任务网|任务网站源码下载基于PHP+MYSQL的在线广告打码任务网站源码 THINKPHP仿我爱广告任务网|任务网站源码下载是一款基于PHP+MYSQL开发制作的在线广告打码 ...

  7. jsp人事管理系统_Jsp+Ssm+Mysql实现的医院人事管理系统源码附带视频运行教程

    项目地址: jsp+ssm+mysql实现的医院人事管理系统源码附带视频运行教程|猿来入此[beta]多用户版IT项目教程源码分享网站​www.yuanlrc.com 今天给大家演示的是一款由jsp+ ...

  8. 实时折线图php mysql 源码_超级漂亮网址导航源码,自助链源码(PHP+MYSQL完整版)...

    品牌: 其他 语言: PHP 数据库: Mysql 源文件: 完全开源(含全部源文件) 授权: 免授权 规格: 整站源码 移动端: 无移动端 安装服务: 收费安装(另补差价) 操作系统: Window ...

  9. 基于SSM框架+MySQL的超市订单管理系统【源码+文档+PPT】

    目录 1.系统需求分析 1.1 系统功能分析 1.2 系统功能需求 1.3 系统性能需求 2.数据库设计 2.1 数据库需求分析 3.数据库物理结构设计 4.各功能模块的设计与实现 4.1 开发框架 ...

最新文章

  1. 大佬教你Android如何实现时间线效果
  2. Github 入门1 (下载git , 连接本地库与github仓库)
  3. 某网SQL注入漏洞实战
  4. Maven对插件进行全局设置
  5. 计算机软件与电子出版物,电子出版物出版和互联网出版.pdf
  6. 《Java EE 7精粹》—— 第3章 JSF 3.1 Facelets
  7. Python实现恋爱AA公式
  8. 收集 - 实际开发中的技巧记录【页面】
  9. excel中提取中文拼音
  10. 原理图端口符号_电气的原理图和接线图的区别,今天终于弄明白了!
  11. dedecms index.php挂马,dedecms被挂马常见解决方法
  12. Linux 操作系统的体系结构
  13. 移动端隐藏手机虚拟键盘
  14. linux系统认证中级是什么,红帽中级RHCE证书有什么用
  15. 看看这篇ARM体系结构你就都明白了
  16. php生成word文件
  17. 区块链是什么意思?区块链的概念及特点
  18. Spring Boot学习案例开源项目
  19. L2-1 盲盒包装流水线 (25 分)
  20. HTML5之内联框架和音视频标签

热门文章

  1. Linux下 gdb调试打印数组元素说明
  2. plsql中两个不同数据库之间 数据表数据操作
  3. RabbitMQ(两天掌握之第一天)
  4. C. Feast Coins(背包求方案数)
  5. Bootstrap框架(一)
  6. solidworks中装配体如何旋转一个零件
  7. 运动员减压各有各招儿
  8. 蓝牙体重体脂秤解决方案
  9. android apk导出工具下载,APK导出神器
  10. Unbuntu 18.04 显卡驱动崩溃