mysql源码入口

mysql源码入口

主要记录mysql5.7.28源码阅读入门体验,和入口函数解释记录。

获取源码

mysql是基于c++和c编写的,支持linux和windows。
通过官方网站获取源码
1.通过官方网址获取源码
github获取源码
2.github获取源码

本地环境搭建

参照这个链接

GDB命令

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层的即是少量的、符合条件的元组。
另外,图中的部件层次关系,不再进行解释。
索引下推

sub_select---->innodb

select count(*)底层究竟做了什么?

子查询转换

优化规则

优化规则

mysql各种连接

各种连接查询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值