mysql源码入口
mysql源码入口
主要记录mysql5.7.28源码阅读入门体验,和入口函数解释记录。
获取源码
mysql是基于c++和c编写的,支持linux和windows。
1.通过官方网址获取源码
2.github获取源码
本地环境搭建
GDB命令
源码入口
我们都知道,mysql服务端启动是执行mysqld文件,那么服务端入口在mysqld的main函数 [mysqld_main],初一看这个方法有700行,整个文件甚至接近1w行代码,兴致而来,懵逼想走。但是转念一想,管它多复杂,其实我也就是想知道一个sql的执行过程,对于其它的初始化,监控和日志,还有一堆系统库重写管它多复杂,暂时不care,毕竟罗马非一日建成,先抓重点。往下翻到方法快结束的地方,找到了这个调用
mysqld_socket_acceptor->connection_event_loop();
瞬间就感觉找到重点了,服务端的入口肯定就是在这无限循环中接收client请求,然后创建线程处理请求。
/**
Connection acceptor loop to accept connections from clients.
*/
void connection_event_loop()
{
Connection_handler_manager *mgr= Connection_handler_manager::get_instance();
while (!abort_loop)
{
Channel_info *channel_info= m_listener->listen_for_connection_event();
if (channel_info != NULL)
mgr->process_new_connection(channel_info);
}
}
然后处理函数显而易见就是 [process_new_connection] 继续点进去
void Connection_handler_manager::process_new_connection(Channel_info* channel_info)
{
if (abort_loop || !check_and_incr_conn_count())
{
channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true);
delete channel_info;
return;
}
if (m_connection_handler->add_connection(channel_info))
{
inc_aborted_connects();
delete channel_info;
}
}
处理请求函数 [add_connection] 这个函数用到了多态,所以需要找初始化位置,看子类是哪一个,我为你找好了默认是 [Per_thread_connection_handler::add_connection]
从这个文件 [connection_handler_per_thread.cc]继续找,发现使用mysql_thread_create创建线程,这个是pthread封装的创建线程工具,执行体如下
extern "C" void *handle_connection(void *arg)
{
Global_THD_manager *thd_manager= Global_THD_manager::get_instance();
Connection_handler_manager *handler_manager=
Connection_handler_manager::get_instance();
Channel_info* channel_info= static_cast<Channel_info*>(arg);
bool pthread_reused MY_ATTRIBUTE((unused))= false;
...
可以看到用c语言写的,性能一节更比6节强,(ps难怪源码这么难阅读,毕竟一切为了性能)。
解释:THD是MySQL server层最核心的类,没有之一,基本上所有的信息都是和THD 实例进行绑定的。对于每一个客户端链接进来后,系统会实例化一个THD对象,而后这个对象伴随这个链接的一生。
if (thd_prepare_connection(thd))
handler_manager->inc_aborted_connects();
else
{
while (thd_connection_alive(thd))
{
if (do_command(thd))
break;
}
end_connection(thd);
}
找到do_command的实现在 [sql_parse.cc] 最后一句分发请求 [dispatch_command]
bool do_command(THD *thd)
{
bool return_value;
int rc;
const bool classic=
(thd->get_protocol()->type() == Protocol::PROTOCOL_TEXT ||
thd->get_protocol()->type() == Protocol::PROTOCOL_BINARY);
...
return_value= dispatch_command(thd, &com_data, command);
server_command(DDL,DCL)
这里主要是数据定义语句和数据控制语句。
enum enum_server_command
{
COM_SLEEP,
COM_QUIT,
COM_INIT_DB,
COM_QUERY,
COM_FIELD_LIST,
COM_CREATE_DB,
COM_DROP_DB,
COM_REFRESH,
COM_SHUTDOWN,
COM_STATISTICS,
COM_PROCESS_INFO,
COM_CONNECT,
COM_PROCESS_KILL,
COM_DEBUG,
COM_PING,
COM_TIME,
COM_DELAYED_INSERT,
COM_CHANGE_USER,
COM_BINLOG_DUMP,
COM_TABLE_DUMP,
COM_CONNECT_OUT,
COM_REGISTER_SLAVE,
COM_STMT_PREPARE,
COM_STMT_EXECUTE,
COM_STMT_SEND_LONG_DATA,
COM_STMT_CLOSE,
COM_STMT_RESET,
COM_SET_OPTION,
COM_STMT_FETCH,
COM_DAEMON,
COM_BINLOG_DUMP_GTID,
COM_RESET_CONNECTION,
/* don't forget to update const char *command_name[] in sql_parse.cc */
/* Must be last */
COM_END
};
其中 case COM_QUERY: 代码段是增删改查的入口,其中关键语句是:
mysql_parse(thd, &parser_state);
然后点进去找到:
error= mysql_execute_command(thd, true);
SQL Commands(DML)
这里有我们熟悉的对表数据操作的语句。
enum enum_sql_command {
SQLCOM_SELECT,
SQLCOM_CREATE_TABLE,
SQLCOM_CREATE_INDEX,
SQLCOM_ALTER_TABLE,
SQLCOM_UPDATE,
SQLCOM_INSERT,
SQLCOM_INSERT_SELECT,
SQLCOM_DELETE,
SQLCOM_TRUNCATE,
SQLCOM_DROP_TABLE,
SQLCOM_DROP_INDEX,
SQLCOM_SHOW_DATABASES,
SQLCOM_SHOW_TABLES,
SQLCOM_SHOW_FIELDS,
SQLCOM_SHOW_KEYS,
SQLCOM_SHOW_VARIABLES,
SQLCOM_SHOW_STATUS,
SQLCOM_SHOW_ENGINE_LOGS,
SQLCOM_SHOW_ENGINE_STATUS,
SQLCOM_SHOW_ENGINE_MUTEX,
SQLCOM_SHOW_PROCESSLIST,
SQLCOM_SHOW_MASTER_STAT,
SQLCOM_SHOW_SLAVE_STAT,
SQLCOM_SHOW_GRANTS,
SQLCOM_SHOW_CREATE,
SQLCOM_SHOW_CHARSETS,
SQLCOM_SHOW_COLLATIONS,
SQLCOM_SHOW_CREATE_DB,
SQLCOM_SHOW_TABLE_STATUS,
SQLCOM_SHOW_TRIGGERS,
SQLCOM_LOAD,
SQLCOM_SET_OPTION,
SQLCOM_LOCK_TABLES,
SQLCOM_UNLOCK_TABLES,
SQLCOM_GRANT,
SQLCOM_CHANGE_DB,
SQLCOM_CREATE_DB,
SQLCOM_DROP_DB,
SQLCOM_ALTER_DB,
SQLCOM_REPAIR,
SQLCOM_REPLACE,
SQLCOM_REPLACE_SELECT,
SQLCOM_CREATE_FUNCTION,
SQLCOM_DROP_FUNCTION,
SQLCOM_REVOKE,
SQLCOM_OPTIMIZE,
SQLCOM_CHECK,
SQLCOM_ASSIGN_TO_KEYCACHE,
SQLCOM_PRELOAD_KEYS,
SQLCOM_FLUSH,
SQLCOM_KILL,
SQLCOM_ANALYZE,
SQLCOM_ROLLBACK,
SQLCOM_ROLLBACK_TO_SAVEPOINT,
SQLCOM_COMMIT,
SQLCOM_SAVEPOINT,
SQLCOM_RELEASE_SAVEPOINT,
SQLCOM_SLAVE_START,
SQLCOM_SLAVE_STOP,
SQLCOM_START_GROUP_REPLICATION,
SQLCOM_STOP_GROUP_REPLICATION,
SQLCOM_BEGIN,
SQLCOM_CHANGE_MASTER,
SQLCOM_CHANGE_REPLICATION_FILTER,
SQLCOM_RENAME_TABLE,
SQLCOM_RESET,
SQLCOM_PURGE,
SQLCOM_PURGE_BEFORE,
SQLCOM_SHOW_BINLOGS,
SQLCOM_SHOW_OPEN_TABLES,
SQLCOM_HA_OPEN,
SQLCOM_HA_CLOSE,
SQLCOM_HA_READ,
SQLCOM_SHOW_SLAVE_HOSTS,
SQLCOM_DELETE_MULTI,
SQLCOM_UPDATE_MULTI,
SQLCOM_SHOW_BINLOG_EVENTS,
SQLCOM_DO,
SQLCOM_SHOW_WARNS,
SQLCOM_EMPTY_QUERY,
SQLCOM_SHOW_ERRORS,
SQLCOM_SHOW_STORAGE_ENGINES,
SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP,
SQLCOM_CREATE_USER,
SQLCOM_DROP_USER,
SQLCOM_RENAME_USER,
SQLCOM_REVOKE_ALL,
SQLCOM_CHECKSUM,
SQLCOM_CREATE_PROCEDURE,
SQLCOM_CREATE_SPFUNCTION,
SQLCOM_CALL,
SQLCOM_DROP_PROCEDURE,
SQLCOM_ALTER_PROCEDURE,
SQLCOM_ALTER_FUNCTION,
SQLCOM_SHOW_CREATE_PROC,
SQLCOM_SHOW_CREATE_FUNC,
SQLCOM_SHOW_STATUS_PROC,
SQLCOM_SHOW_STATUS_FUNC,
SQLCOM_PREPARE,
SQLCOM_EXECUTE,
SQLCOM_DEALLOCATE_PREPARE,
SQLCOM_CREATE_VIEW,
SQLCOM_DROP_VIEW,
SQLCOM_CREATE_TRIGGER,
SQLCOM_DROP_TRIGGER,
SQLCOM_XA_START,
SQLCOM_XA_END,
SQLCOM_XA_PREPARE,
SQLCOM_XA_COMMIT,
SQLCOM_XA_ROLLBACK,
SQLCOM_XA_RECOVER,
SQLCOM_SHOW_PROC_CODE,
SQLCOM_SHOW_FUNC_CODE,
SQLCOM_ALTER_TABLESPACE,
SQLCOM_INSTALL_PLUGIN,
SQLCOM_UNINSTALL_PLUGIN,
SQLCOM_BINLOG_BASE64_EVENT,
SQLCOM_SHOW_PLUGINS,
SQLCOM_CREATE_SERVER,
SQLCOM_DROP_SERVER,
SQLCOM_ALTER_SERVER,
SQLCOM_CREATE_EVENT,
SQLCOM_ALTER_EVENT,
SQLCOM_DROP_EVENT,
SQLCOM_SHOW_CREATE_EVENT,
SQLCOM_SHOW_EVENTS,
SQLCOM_SHOW_CREATE_TRIGGER,
SQLCOM_ALTER_DB_UPGRADE,
SQLCOM_SHOW_PROFILE,
SQLCOM_SHOW_PROFILES,
SQLCOM_SIGNAL,
SQLCOM_RESIGNAL,
SQLCOM_SHOW_RELAYLOG_EVENTS,
SQLCOM_GET_DIAGNOSTICS,
SQLCOM_ALTER_USER,
SQLCOM_EXPLAIN_OTHER,
SQLCOM_SHOW_CREATE_USER,
SQLCOM_SHUTDOWN,
SQLCOM_ALTER_INSTANCE,
/* This should be the last !!! */
SQLCOM_END
};
分析SQLCOM_SELECT;
线上代码,再来仔细分析:
case SQLCOM_SELECT:
{
DBUG_EXECUTE_IF("use_attachable_trx",
thd->begin_attachable_ro_transaction(););
//初始化当前查询语句的花费时间为0
thd->clear_current_query_costs();
// 为 SELECT statement 执行第一阶段的权限检查
res= select_precheck(thd, lex, all_tables, first_table);
if (!res)
//执行查询
res= execute_sqlcom_select(thd, all_tables);
thd->save_current_query_costs();
DBUG_EXECUTE_IF("use_attachable_trx",
thd->end_attachable_transaction(););
break;
}
简化execute_sqlcom_select代码就是:
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
...
if (!(res= open_tables_for_query(thd, all_tables, 0)))
{
...
if (lex->is_explain()){
...
res= handle_query(thd, lex, result, 0, 0);
...
}else{
...
res= handle_query(thd, lex, result, 0, 0);
...
}
...
}
蛰里有这个方法详细的解释:
/**
Handle a data manipulation query, from preparation through cleanup
@param thd thread handler
@param lex query to be processed
@param result sink of result of query execution.
may be protocol object (for passing result to a client),
insert object, update object, delete object, etc.
@param added_options additional options for detailed control over execution
@param removed_options options that are not applicable for this command
@returns false if success, true if error
@details
Processing a query goes through 5 phases (parsing is already done)
- Preparation
- Locking of tables
- Optimization
- Execution or explain
- Cleanup
The queries handled by this function are:
SELECT
INSERT ... SELECT
REPLACE ... SELECT
UPDATE (multi-table)
DELETE (multi-table)
@todo make this function also handle INSERT ... VALUES, single-table
UPDATE and DELETE, SET and DO.
The function processes simple query expressions without UNION and
without multi-level ORDER BY/LIMIT separately.
Such queries are executed with a more direct code path.
*/
bool handle_query(THD *thd, LEX *lex, Query_result *result,
ulonglong added_options, ulonglong removed_options)
...
join::exec()解析
存储引擎的流程
索引条件下推优化 Index Condition Pushdown (ICP)
图一:不使用ICP技术(过程使用数字符号标示,如①②③等)
过程解释:
①:MySQL Server发出读取数据的命令,这是在执行器中执行如下代码段,通过函数指针和handle接口调用存储引擎的索引读或全表表读。此处进行的是索引读。
if (in_first_read)
{
in_first_read= false;
error= (*qep_tab->read_first_record)(qep_tab); //设定合适的读取函数,如设定索引读函数/全表扫描函数
}
else
error= info->read_record(info);
②、③:进入存储引擎,读取索引树,在索引树上查找,把满足条件的(经过查找,红色的满足)从表记录中读出(步骤④,通常有IO),从存储引擎返回⑤标识的结果。此处,不仅要在索引行进行索引读取(通常是内存中,速度快。步骤③),还要进行进行步骤④,通常有IO。
⑥:从存储引擎返回查找到的多条元组给MySQL Server,MySQL Server在⑦得到较多的元组。
⑦–⑧:⑦到⑧依据WHERE子句条件进行过滤,得到满足条件的元组。注意在MySQL Server层得到较多元组,然后才过滤,最终得到的是少量的、符合条件的元组。
图二:使用ICP技术(过程使用数字符号标示,如①②③等)
过程解释:
①:MySQL Server发出读取数据的命令,过程同图一。
②、③:进入存储引擎,读取索引树,在索引树上查找,把满足已经下推的条件的(经过查找,红色的满足)从表记录中读出(步骤④,通常有IO),从存储引擎返回⑤标识的结果。此处,不仅要在索引行进行索引读取(通常是内存中,速度快。步骤③),还要在③这个阶段依据下推的条件进行进行判断,不满足条件的,不去读取表中的数据,直接在索引树上进行下一个索引项的判断,直到有满足条件的,才进行步骤④,这样,较没有ICP的方式,IO量减少。
⑥:从存储引擎返回查找到的少量元组给MySQL Server,MySQL Server在⑦得到少量的元组。因此比较图一无ICP的方式,返回给MySQL Server层的即是少量的、符合条件的元组。
另外,图中的部件层次关系,不再进行解释。
索引下推