Inception是对MySQL数据库的线上运维操作进行辅助的一个工具,它能提供SQL的审核、执行、备份的操作,保证运维的可靠性。
现在为了在公司中使用这个工具来减轻我们的运维工作负担,开始着手使用它。
问题
使用之前,我们需要了解这个工具的原理,包括它对一些我们关注的方面的解决方式,以及在出现一些问题时我们应该怎样应对解决。
- 首先,我们需要用到它的审核SELECT类型SQL功能,那么它的审核功能是怎么实现的?一些审核配置项是怎么发生作用的?
- 其次,我们还需要用到它的DML操作功能,Inception是怎样完成在审核SQL的同时还能执行?执行完成后是怎么生成反向恢复语句?如果在执行多条语句的时候中途发生了错误,后续的语句还会执行吗?如果在执行过程中网络发生问题,执行结果又是怎样的?
- 等等。。。
带着这些问题,我们开始一点点探索它内部的一些细节流程。
代码分析
我们先从执行函数的创建阶段开始查找,找到执行流程的具体入口在什么位置,以及这个具体入口是怎样被创建的。
-
执行流程创建
通过GDB的定位以及代码的分析,我们可以得到Inception的具体工作是在新创建的工作线程中完成,而这个工作线程的入口钩子函数就是实际的处理函数:
而钩子函数的挂钩的过程是在进程刚启动时的初始化函数中完成的:
根据上面两个流程图,我们得到的结论是Inception在启动成功后,一旦接收到SQL语句的请求,就会创建新的线程,而这个创建新线程的钩子函数是指向create_thread_to_handle_connection(), 在这个函数会创建线程来处理对SQL语句块的分析、检查、执行和备份流程。
-
执行SQL流程分析
在create_thread_to_handle_connection()函数中,新创建的线程入口函数为handle_one_connection(),从这个函数中,我们可以继续查找到SQL语句块的分析、检查、执行和备份流程以及回滚语句的具体执行位置,执行流程图如下所示:
上图从左往右的顺序就是函数栈的从上往下的顺序,一直到最底层实现具体逻辑的地方。
以下内容会对这个图的各个部分进行拆解分析。
-
SQL检查流程
当Inception监听的端口收到连接请求后,就会调用create_new_thread()函数,其中创建的新线程的入口函数钩子已经在Inception的main函数开始阶段确定下来,它是指向handle_one_connection()函数。
在handle_one_connection()函数中,会将输入的SQL语句进行处理(语法词法解析、检查规范、执行、备份语句等操作),处理完成后将结果给传回到客户端。
在一个Inception语句块中,一定会包含多条语句,因为它需要满足以下格式的语句块:
1 /*--user=inception_user;--password=123456;--host=192.168.1.200;--enable-execute;--port=3363;*/ 2 inception_magic_start; 3 use test_2; 4 update test set name="aaaaab" where id=20; 5 inception_magic_commit;
第1行是Inception语句块的头信息,放在SQL的注释块中
第2行是Inception语句块的起始标记
第3~4行是Inception语句块,客户端需要处理的SQL语句
第5行是Inception语句块的结束标记
头信息、开始标记、结束标记必然是需要的,语句块信息也至少是1句SQL,每条语句经过语法解析后,会得到一棵语法树,交由mysql_process_command()进行处理。
此函数对每个语句生成的语法树都会进行处理,在处理到Inception_magic_commit对应的语法树之前,都是检查的过程,只有在处理完它之后才会进行执行(如果在头信息中指定的是--enable-check而不是--enable-execute后续就不会有执行的操作发生了,只是将得到的检查结果发回给客户端)。
在mysql_parse()中生成了一条语句的语法树后,会继续执行对这条语句的处理流程,其中检查过程是在mysql_check_command()函数中完成的,包括检查dml语句是否有where条件(inception_check_dml_where)、inception_check_dml_limit、inception_check_dml_orderby、inception_enable_select_star等等检查项。
-
执行流程
在对所有语句的检查完成后,会进入到mysql_execute_commit()函数,开始进行所有语句的执行,每条语句按照顺序执行,并且每条语句都是独立的事务,如果中途有一条语句发生错误,后续的语句就不会执行了。
如果在执行过程中发生网络问题,那么这条语句对应到这个事务是否完成是未知的,但是对于Inception而言,发生网络错误了就相当于这条语句执行失败,后续的语句就不会执行了,当前的这条语句会标记为失败,也就不会有回滚语句备份。
-
生成回滚语句流程
在执行过程完成后,就开始对每条语句逐个进行回滚语句解析并备份:
从下图中可以看出备份是在执行完成之后,在mysql_backup_sql()中逐条解析语句的binlog,对于每个语句在执行的过程中记录了该语句执行之前的binlog pos和之后的binlog pos,以及该连接的thread_id, 使用这三个值可以对每条语句的binlog进行精确定位,从而可以得到这个语句的所有binlog。
每条语句的binlog中可能包含多个event,每个event可以解析出一条回滚语句。
如果在binlog的传输过程中发生网络错误,Inception给每条语句的备份流程中进行重试的机会有三次,如果重试次数超过三次,这条语句的回滚语句就会备份失败。
上面这张图是Inception的回滚语句备份流程,会对不同的语句类型执行不同的event解析过程。具体的解析过程后续会有文章再分析。
结论
这篇文章从Inception的DML语句的检查、执行、备份的实现流程的角度去分析了它的源码执行流程:
检查过程是在mysql_check_command()过程中完成的,这个过程发生在每条语句的语法树生成之后,在执行所有语句之前。
执行过程是在mysql_execute_commit()函数中完成,在这个函数中会在线上服务器上逐条执行每个sql语句,执行结果中会包含执行前的binlog位置和执行后的binlog位置以及thread_id
备份过程是在执行过程完成之后,逐条语句分析对应的binlog(流式下载到Inception端),然后根据在执行时记录的执行前binlog位置和执行后的binlog位置以及thread_id来得到属于这条语句的所有binlog,解析为回滚语句并备份到备份服务器上。
通过源码的分析,在后面的使用中如果碰到了一些问题可以很容易定位,如果有需要的话直接上手改也是一个不错的选择,即解决了自己的问题也为开源工具的发展做出一点贡献。