mysql源码分析——handlerton

一、数据引擎的应用接口

Mysql中为了扩展方便,基本上很多模块都是通过插件的形式(Plugin)的方式加载到Mysql主程序上的,这其中不仅有一些日志、状态等插件,还有数据引擎等核心的插件。在Mysql中访问接口的方式主要有两类,一类是通过注册使用观察者模式来调用,另外一类就是数据库引擎通过这个handlerton的方式来实现。在数据存储引擎中,对表及事务的相关操作都是通过这种方式来访问相关的引擎插件的。
在帮助文档中是如此描述它的:
“handlerton is a singleton structure - one instance per storage engine - to provide access to storage engine functionality that works on the “global” level (unlike handler class that works on a per-table basis).
usually handlerton instance is defined statically in ha_xxx.cc as
static handlerton { … } xxx_hton;
savepoint_*, prepare, recover, and * _by_xid pointers can be 0 . ”
它的意思是说“handlerton中一种单结构(这个学过程序的都明白),一个数据引擎在全局状态上只提供一个补全,通常定义为一个xxx_hton的形式。

二、数据结构定义和分析

老样子先看一下数据定义(sql/handler.h):

//sql/handler.h
struct handlerton {
  /**
    Historical marker for if the engine is available or not.
  */
  SHOW_COMP_OPTION state;

  /**
    Historical number used for frm file to determine the correct storage engine.
    This is going away and new engines will just use "name" for this.
  */
  enum legacy_db_type db_type;
  /**
    Each storage engine has it's own memory area (actually a pointer)
    in the thd, for storing per-connection information.
    It is accessed as

      thd->ha_data[xxx_hton.slot]

     slot number is initialized by MySQL after xxx_init() is called.
   */
  uint slot;
  /**
    To store per-savepoint data storage engine is provided with an area
    of a requested size (0 is ok here).
    savepoint_offset must be initialized statically to the size of
    the needed memory to store per-savepoint information.
    After xxx_init it is changed to be an offset to savepoint storage
    area and need not be used by storage engine.
    see binlog_hton and binlog_savepoint_set/rollback for an example.
   */
  uint savepoint_offset;

  /* handlerton methods */

  close_connection_t close_connection;
  kill_connection_t kill_connection;
  pre_dd_shutdown_t pre_dd_shutdown;
  savepoint_set_t savepoint_set;
  savepoint_rollback_t savepoint_rollback;
  savepoint_rollback_can_release_mdl_t savepoint_rollback_can_release_mdl;
  savepoint_release_t savepoint_release;
  commit_t commit;
  rollback_t rollback;
  prepare_t prepare;
  recover_t recover;
  commit_by_xid_t commit_by_xid;
  rollback_by_xid_t rollback_by_xid;
  create_t create;
  drop_database_t drop_database;
  panic_t panic;
  start_consistent_snapshot_t start_consistent_snapshot;
  flush_logs_t flush_logs;
  show_status_t show_status;
  partition_flags_t partition_flags;
  is_valid_tablespace_name_t is_valid_tablespace_name;
  get_tablespace_t get_tablespace;
  alter_tablespace_t alter_tablespace;
  get_tablespace_filename_ext_t get_tablespace_filename_ext;
  upgrade_tablespace_t upgrade_tablespace;
  upgrade_space_version_t upgrade_space_version;
  get_tablespace_type_t get_tablespace_type;
  get_tablespace_type_by_name_t get_tablespace_type_by_name;
  upgrade_logs_t upgrade_logs;
  finish_upgrade_t finish_upgrade;
  fill_is_table_t fill_is_table;
  dict_init_t dict_init;
  ddse_dict_init_t ddse_dict_init;
  dict_register_dd_table_id_t dict_register_dd_table_id;
  dict_cache_reset_t dict_cache_reset;
  dict_cache_reset_tables_and_tablespaces_t
      dict_cache_reset_tables_and_tablespaces;
  dict_recover_t dict_recover;
  dict_get_server_version_t dict_get_server_version;
  dict_set_server_version_t dict_set_server_version;
  is_reserved_db_name_t is_reserved_db_name;

  /** Global handler flags. */
  uint32 flags{0};

  /*
    Those handlerton functions below are properly initialized at handler
    init.
  */

  binlog_func_t binlog_func;
  binlog_log_query_t binlog_log_query;
  acl_notify_t acl_notify;
  discover_t discover;
  find_files_t find_files;
  table_exists_in_engine_t table_exists_in_engine;
  is_supported_system_table_t is_supported_system_table;

  /*
    APIs for retrieving Serialized Dictionary Information by tablespace id
  */

  sdi_create_t sdi_create;
  sdi_drop_t sdi_drop;
  sdi_get_keys_t sdi_get_keys;
  sdi_get_t sdi_get;
  sdi_set_t sdi_set;
  sdi_delete_t sdi_delete;

  /**
    Null-ended array of file extentions that exist for the storage engine.
    Used by frm_error() and the default handler::rename_table and delete_table
    methods in handler.cc.

    For engines that have two file name extentions (separate meta/index file
    and data file), the order of elements is relevant. First element of engine
    file name extentions array should be meta/index file extention. Second
    element - data file extention. This order is assumed by
    prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.

    For engines that don't have files, file_extensions is NULL.

    Currently, the following alternatives are used:
      - file_extensions == NULL;
      - file_extensions[0] != NULL, file_extensions[1] == NULL;
      - file_extensions[0] != NULL, file_extensions[1] != NULL,
        file_extensions[2] == NULL;
  */
  const char **file_extensions;

  is_dict_readonly_t is_dict_readonly;
  rm_tmp_tables_t rm_tmp_tables;
  get_cost_constants_t get_cost_constants;
  replace_native_transaction_in_thd_t replace_native_transaction_in_thd;
  notify_exclusive_mdl_t notify_exclusive_mdl;
  notify_alter_table_t notify_alter_table;
  rotate_encryption_master_key_t rotate_encryption_master_key;
  redo_log_set_state_t redo_log_set_state;

  get_table_statistics_t get_table_statistics;
  get_index_column_cardinality_t get_index_column_cardinality;
  get_tablespace_statistics_t get_tablespace_statistics;

  post_ddl_t post_ddl;
  post_recover_t post_recover;

  /** Clone data transfer interfaces */
  Clone_interface_t clone_interface;

  /** Flag for Engine License. */
  uint32 license;
  /** Location for engines to keep personal structures. */
  void *data;

  /*
    Log_resource functions that must be supported by storage engines
    with relevant log information to be collected.
  */
  lock_hton_log_t lock_hton_log;
  unlock_hton_log_t unlock_hton_log;
  collect_hton_log_info_t collect_hton_log_info;

  /** Flags describing details of foreign key support by storage engine. */
  uint32 foreign_keys_flags;

  check_fk_column_compat_t check_fk_column_compat;

  /**
    Suffix for auto-generated foreign key names for tables using this storage
    engine. If such suffix is specified by SE then its generated foreign key
    names follow (table name)(SE-specific FK name suffix)(FK number) pattern.
    Length of such suffix should not exceed MAX_FK_NAME_SUFFIX_LENGTH bytes.
    If no suffix is specified then FK_NAME_DEFAULT_SUFFIX is used as
    default.
  */
  LEX_CSTRING fk_name_suffix;

  /**
    Pointer to a function that prepares a secondary engine for executing a
    statement.

    @see prepare_secondary_engine_t for function signature.
  */
  prepare_secondary_engine_t prepare_secondary_engine;

  /**
    Pointer to a function that optimizes the current statement for
    execution on the secondary storage engine represented by this
    handlerton.

    @see optimize_secondary_engine_t for function signature.
  */
  optimize_secondary_engine_t optimize_secondary_engine;

  /**
    Pointer to a function that estimates the cost of executing a join in a
    secondary storage engine.

    @see compare_secondary_engine_cost_t for function signature.
  * /
  compare_secondary_engine_cost_t compare_secondary_engine_cost;

  /// Bitmap which contains the supported access path types for a
  /// secondary storage engine when used with the hypergraph join
  /// optimizer. If it is empty, it means that the secondary engine
  /// does not support the hypergraph join optimizer.
  ///
  /// It is currently only used to limit which join types the join
  /// optimizer can choose from. Bits that represent access path types
  /// that are not joins, are currently ignored.
  uint64_t secondary_engine_supported_access_paths;

  /// Pointer to a function that evaluates the cost of executing an access path
  /// in a secondary storage engine.
  ///
  /// @see secondary_engine_modify_access_path_cost_t for function signature.
  secondary_engine_modify_access_path_cost_t
      secondary_engine_modify_access_path_cost;

  se_before_commit_t se_before_commit;
  se_after_commit_t se_after_commit;
  se_before_rollback_t se_before_rollback;

  /** Page tracking interface * /
  Page_track_t page_track;
};

这个数据结构体挺长,但没有啥特别的。基本分成两大块,上面是一系列的相关的变量定义,比如state、type、slot等等;下面是一系列的函数指针,诸如binlog_func等,作为一个接口插件没有函数指针,这确实是说不过去的。看一下几个函数指针的定义(sql/handler.h):

typedef void (*acl_notify_t)(THD *thd,
                             const class Acl_change_notification *notice);

typedef int (*discover_t)(handlerton *hton, THD *thd, const char *db,
                          const char *name, uchar **frmblob, size_t *frmlen);

typedef int (*find_files_t)(handlerton *hton, THD *thd, const char *db,
                            const char *path, const char *wild, bool dir,
                            List<LEX_STRING> *files);

typedef int (*table_exists_in_engine_t)(handlerton * hton, THD *thd,
                                        const char * db, const char \* name);

如果去除对一些数据结构的细节了解,其实从形式上看,就是一个普通的函数指针,如果看不明白函数指针,就请回去翻翻相关的书籍,这个没有什么难度。正如一般函数指针都是用来做回调函数一样,这里的接口插件封装函数,也是做这个用的。
这其中真正关心的只需要db_type、comment、state、create()等不多的几个即可,特别是如果想到自定义一个存储引擎,就需要实现这些必须关心的成员。
同样,再想一想前面定义数据引擎时,都继承了一个handler的类,大致如下:

class ha_innobase : public handler {
 public:
  ha_innobase(handlerton *hton, TABLE_SHARE *table_arg);
  ~ha_innobase() override;
  ......
};

在这个handler中是有一个变量定义:handlerton ht; / storage engine of this handler * /,也就是说,这两个是一个具体的操作,一个是全局的管理,要分清它们之间的关系。

详细的说明看一下这个网址:
https://dev.mysql.com/doc/dev/mysql-server/latest/structhandlerton.html#details

三、源码中的应用

1、整体的管理
在Mysql中,模块的命名一般都是有一些特征的,比如引擎啥的一般都叫ha_xxx。同样,这个handlertor的命名都是xxx_hton,比如innobase_hton,myisam_hton等等,这个在下面初始化的代码中都有体现。通常,在Mysql中是通过全局变量来管理这个插件的,它其实是一个插件相关的哈希数组,它可以通过plugin_find_internal来发现插件,具体的内容(插件安装、加载等)会在后面讲整体插件使用时,进行分析介绍,这里只简单明白即可。

2、初始化
初始化其实就是调用相关的函数plugin_initialize来实现,看一下相关代码:

//sql/sql_plugin.cc
static int plugin_initialize(st_plugin_int *plugin) {
  int ret = 1;
  DBUG_TRACE;

  mysql_mutex_assert_owner(&LOCK_plugin);
  uint state = plugin->state;
  assert(state == PLUGIN_IS_UNINITIALIZED);

  mysql_mutex_unlock(&LOCK_plugin);

  DEBUG_SYNC(current_thd, "in_plugin_initialize");

  //根据不同的类型来调用不同的初始化函数
  if (plugin_type_initialize[plugin->plugin->type]) {
    if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) {
      LogErr(ERROR_LEVEL, ER_PLUGIN_REGISTRATION_FAILED, plugin->name.str,
             plugin_type_names[plugin->plugin->type].str);
      goto err;
    }

    /* FIXME: Need better solution to transfer the callback function
    array to memcached */
    if (strcmp(plugin->name.str, "InnoDB") == 0) {
      innodb_callback_data = ((handlerton *)plugin->data)->data;
    }
    //如果不存在,则直接调用默认初始化函数
  } else if (plugin->plugin->init) {
    if (strcmp(plugin->name.str, "daemon_memcached") == 0) {
      plugin->data = innodb_callback_data;
    }

    if (plugin->plugin->init(plugin)) {
      LogErr(ERROR_LEVEL, ER_PLUGIN_INIT_FAILED, plugin->name.str);
      goto err;
    }
  }
  state = PLUGIN_IS_READY;  // plugin->init() succeeded
  //添加状态变量
  if (plugin->plugin->status_vars) {
    if (add_status_vars(plugin->plugin->status_vars)) goto err;
  }

  /*
    set the plugin attribute of plugin's sys vars so they are pointing
    to the active plugin 添加系统变量
  */
  if (plugin->system_vars) {
    sys_var_pluginvar *var = plugin->system_vars->cast_pluginvar();
    for (;;) {
      var->plugin = plugin;
      if (!var->next) break;
      var = var->next->cast_pluginvar();
    }
  }

  ret = 0;

err:
  mysql_mutex_lock(&LOCK_plugin);
  plugin->state = state;

  return ret;
}

再看一下初始化时判断的数组定义:

plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM] = {
    nullptr,
    ha_initialize_handlerton,//引擎初始化函数
    nullptr,
    nullptr,
    initialize_schema_table,//模式-表的初始化
    initialize_audit_plugin,//授权插件初始化
    nullptr,
    nullptr,
    nullptr};

int ha_initialize_handlerton(st_plugin_int *plugin) {
      handlerton *hton;
      DBUG_TRACE;
      DBUG_PRINT("plugin", ("initialize plugin: '%s'", plugin->name.str));

      hton = static_cast<handlerton *>(my_malloc(key_memory_handlerton_objects,
                                                 sizeof(handlerton),
                                                 MYF(MY_WME | MY_ZEROFILL)));

      if (hton == nullptr) {
        LogErr(ERROR_LEVEL, ER_HANDLERTON_OOM, plugin->name.str);
        goto err_no_hton_memory;
      }
      //这个初始化没用0就是为了区别是不是使用了0槽
      hton->slot = HA_SLOT_UNDEF;
      /* Historical Requirement 开始一些基础的初始化,但更多的初始化在相关的引擎的init函数中 */
      plugin->data = hton;  // shortcut for the future 这是前面提到的handler 和handlerton的互操作
      //调用具体的init来初始化相关参数
      if (plugin->plugin->init && plugin->plugin->init(hton)) {
        LogErr(ERROR_LEVEL, ER_PLUGIN_INIT_FAILED, plugin->name.str);
        goto err;
      }

      /*
        the switch below and hton->state should be removed when
        command-line options for plugins will be implemented
      */
      DBUG_PRINT("info", ("hton->state=%d", hton->state));
      switch (hton->state) {
        case SHOW_OPTION_NO:
          break;
        case SHOW_OPTION_YES: {
          uint tmp;
          ulong fslot;
          /* now check the db_type for conflict */
          if (hton->db_type <= DB_TYPE_UNKNOWN ||
              hton->db_type >= DB_TYPE_DEFAULT || installed_htons[hton->db_type]) {
            int idx = (int)DB_TYPE_FIRST_DYNAMIC;

            while (idx < (int)DB_TYPE_DEFAULT && installed_htons[idx]) idx++;

            if (idx == (int)DB_TYPE_DEFAULT) {
              LogErr(WARNING_LEVEL, ER_TOO_MANY_STORAGE_ENGINES);
              goto err_deinit;
            }
            if (hton->db_type != DB_TYPE_UNKNOWN)
              LogErr(WARNING_LEVEL, ER_SE_TYPECODE_CONFLICT, plugin->plugin->name,
                     idx);
            hton->db_type = (enum legacy_db_type)idx;
          }

          /*
            In case a plugin is uninstalled and re-installed later, it should
            reuse an array slot. Otherwise the number of uninstall/install
            cycles would be limited. So look for a free slot.
          */
          DBUG_PRINT("plugin",
                     ("total_ha: %lu", static_cast<ulong>(se_plugin_array.size())));
          for (fslot = 0; fslot < se_plugin_array.size(); fslot++) {
            if (!se_plugin_array[fslot]) break;
          }
          if (fslot < se_plugin_array.size())
            hton->slot = fslot;
          else {
            hton->slot = se_plugin_array.size();
          }
          if (se_plugin_array.assign_at(hton->slot, plugin) ||
              builtin_htons.assign_at(hton->slot, (plugin->plugin_dl == nullptr)))
            goto err_deinit;

          installed_htons[hton->db_type] = hton;
          tmp = hton->savepoint_offset;
          hton->savepoint_offset = savepoint_alloc_size;
          savepoint_alloc_size += tmp;
          if (hton->prepare) total_ha_2pc++;
          break;
        }
          /* fall through */
        default:
          hton->state = SHOW_OPTION_DISABLED;
          break;
      }

      /*
        This is entirely for legacy. We will create a new "disk based" hton and a
        "memory" hton which will be configurable longterm. We should be able to
        remove partition and myisammrg.
      */
      switch (hton->db_type) {
        case DB_TYPE_HEAP:
          heap_hton = hton;
          break;
        case DB_TYPE_TEMPTABLE:
          temptable_hton = hton;
          break;
        case DB_TYPE_MYISAM:
          myisam_hton = hton;
          break;
        case DB_TYPE_INNODB:
          innodb_hton = hton;
          break;
        default:
          break;
      };

      /*
        Re-load the optimizer cost constants since this storage engine can
        have non-default cost constants.
      */
      reload_optimizer_cost_constants();

      return 0;

    err_deinit:
      /*
        Let plugin do its inner deinitialization as plugin->init()
        was successfully called before.
      */
      if (plugin->plugin->deinit) (void)plugin->plugin->deinit(nullptr);

    err:
      my_free(hton);
    err_no_hton_memory:
      plugin->data = nullptr;
      return 1;
    }
int initialize_schema_table(st_plugin_int *plugin) {
      ST_SCHEMA_TABLE *schema_table;
      DBUG_TRACE;

      if (!(schema_table = (ST_SCHEMA_TABLE *)my_malloc(key_memory_ST_SCHEMA_TABLE,
                                                        sizeof(ST_SCHEMA_TABLE),
                                                        MYF(MY_WME | MY_ZEROFILL))))
        return 1;
      /* Historical Requirement */
      plugin->data = schema_table;  // shortcut for the future
      if (plugin->plugin->init) {
        schema_table->old_format = make_old_format;

        /* Make the name available to the init() function. */
        schema_table->table_name = plugin->name.str;

        if (plugin->plugin->init(schema_table)) {
          LogErr(ERROR_LEVEL, ER_PLUGIN_INIT_FAILED, plugin->name.str);
          plugin->data = nullptr;
          my_free(schema_table);
          return 1;
        }

        /* Make sure the plugin name is not set inside the init() function. */
        schema_table->table_name = plugin->name.str;
      }
      return 0;
    }

再看一下innodb的init:

/** Initialize the InnoDB storage engine plugin.
@param[in,out]	p	InnoDB handlerton
@return error code
@retval 0 on success */
static int innodb_init(void *p) {
  DBUG_TRACE;

  acquire_plugin_services();

  handlerton *innobase_hton = (handlerton *)p;
  innodb_hton_ptr = innobase_hton;

  innobase_hton->state = SHOW_OPTION_YES;
  innobase_hton->db_type = DB_TYPE_INNODB;
  innobase_hton->savepoint_offset = sizeof(trx_named_savept_t);
  innobase_hton->close_connection = innobase_close_connection;
  innobase_hton->kill_connection = innobase_kill_connection;
  innobase_hton->savepoint_set = innobase_savepoint;
  innobase_hton->savepoint_rollback = innobase_rollback_to_savepoint;

  innobase_hton->savepoint_rollback_can_release_mdl =
      innobase_rollback_to_savepoint_can_release_mdl;

  innobase_hton->savepoint_release = innobase_release_savepoint;
  innobase_hton->commit = innobase_commit;
  innobase_hton->rollback = innobase_rollback;
  innobase_hton->prepare = innobase_xa_prepare;
  innobase_hton->recover = innobase_xa_recover;
  innobase_hton->commit_by_xid = innobase_commit_by_xid;
  innobase_hton->rollback_by_xid = innobase_rollback_by_xid;
  innobase_hton->create = innobase_create_handler;
  innobase_hton->is_valid_tablespace_name = innobase_is_valid_tablespace_name;
  innobase_hton->alter_tablespace = innobase_alter_tablespace;
  innobase_hton->get_tablespace_filename_ext =
      innobase_get_tablespace_filename_ext;
  innobase_hton->upgrade_tablespace = dd_upgrade_tablespace;
  innobase_hton->upgrade_space_version = upgrade_space_version;
  innobase_hton->upgrade_logs = dd_upgrade_logs;
  innobase_hton->finish_upgrade = dd_upgrade_finish;
  innobase_hton->pre_dd_shutdown = innodb_pre_dd_shutdown;
  innobase_hton->panic = innodb_shutdown;
  innobase_hton->partition_flags = innobase_partition_flags;

  innobase_hton->start_consistent_snapshot =
      innobase_start_trx_and_assign_read_view;

  innobase_hton->flush_logs = innobase_flush_logs;
  innobase_hton->show_status = innobase_show_status;
  innobase_hton->lock_hton_log = innobase_lock_hton_log;
  innobase_hton->unlock_hton_log = innobase_unlock_hton_log;
  innobase_hton->collect_hton_log_info = innobase_collect_hton_log_info;
  innobase_hton->fill_is_table = innobase_fill_i_s_table;
  innobase_hton->flags = HTON_SUPPORTS_EXTENDED_KEYS |
                         HTON_SUPPORTS_FOREIGN_KEYS | HTON_SUPPORTS_ATOMIC_DDL |
                         HTON_CAN_RECREATE | HTON_SUPPORTS_SECONDARY_ENGINE |
                         HTON_SUPPORTS_TABLE_ENCRYPTION;

  innobase_hton->replace_native_transaction_in_thd = innodb_replace_trx_in_thd;
  innobase_hton->file_extensions = ha_innobase_exts;
  innobase_hton->data = &innodb_api_cb;

  innobase_hton->ddse_dict_init = innobase_ddse_dict_init;

  innobase_hton->dict_register_dd_table_id = innobase_dict_register_dd_table_id;

  innobase_hton->dict_cache_reset = innobase_dict_cache_reset;
  innobase_hton->dict_cache_reset_tables_and_tablespaces =
      innobase_dict_cache_reset_tables_and_tablespaces;

  innobase_hton->dict_recover = innobase_dict_recover;
  innobase_hton->dict_get_server_version = innobase_dict_get_server_version;
  innobase_hton->dict_set_server_version = innobase_dict_set_server_version;

  innobase_hton->post_recover = innobase_post_recover;

  innobase_hton->is_supported_system_table = innobase_is_supported_system_table;

  innobase_hton->get_table_statistics = innobase_get_table_statistics;

  innobase_hton->get_index_column_cardinality =
      innobase_get_index_column_cardinality;

  innobase_hton->get_tablespace_statistics = innobase_get_tablespace_statistics;
  innobase_hton->get_tablespace_type = innobase_get_tablespace_type;
  innobase_hton->get_tablespace_type_by_name =
      innobase_get_tablespace_type_by_name;

  innobase_hton->is_dict_readonly = innobase_is_dict_readonly;

  innobase_hton->sdi_create = dict_sdi_create;
  innobase_hton->sdi_drop = dict_sdi_drop;
  innobase_hton->sdi_get_keys = dict_sdi_get_keys;
  innobase_hton->sdi_get = dict_sdi_get;
  innobase_hton->sdi_set = dict_sdi_set;
  innobase_hton->sdi_delete = dict_sdi_delete;

  innobase_hton->rotate_encryption_master_key =
      innobase_encryption_key_rotation;

  innobase_hton->redo_log_set_state = innobase_redo_set_state;

  innobase_hton->post_ddl = innobase_post_ddl;

  /* Initialize handler clone interfaces for. */

  innobase_hton->clone_interface.clone_capability = innodb_clone_get_capability;
  innobase_hton->clone_interface.clone_begin = innodb_clone_begin;
  innobase_hton->clone_interface.clone_copy = innodb_clone_copy;
  innobase_hton->clone_interface.clone_ack = innodb_clone_ack;
  innobase_hton->clone_interface.clone_end = innodb_clone_end;

  innobase_hton->clone_interface.clone_apply_begin = innodb_clone_apply_begin;
  innobase_hton->clone_interface.clone_apply = innodb_clone_apply;
  innobase_hton->clone_interface.clone_apply_end = innodb_clone_apply_end;

  innobase_hton->foreign_keys_flags =
      HTON_FKS_WITH_PREFIX_PARENT_KEYS |
      HTON_FKS_NEED_DIFFERENT_PARENT_AND_SUPPORTING_KEYS |
      HTON_FKS_WITH_EXTENDED_PARENT_KEYS;

  innobase_hton->check_fk_column_compat = innodb_check_fk_column_compat;
  innobase_hton->fk_name_suffix = {STRING_WITH_LEN("_ibfk_")};

  innobase_hton->is_reserved_db_name = innobase_check_reserved_file_name;

  innobase_hton->page_track.start = innobase_page_track_start;
  innobase_hton->page_track.stop = innobase_page_track_stop;
  innobase_hton->page_track.purge = innobase_page_track_purge;
  innobase_hton->page_track.get_page_ids = innobase_page_track_get_page_ids;
  innobase_hton->page_track.get_num_page_ids =
      innobase_page_track_get_num_page_ids;
  innobase_hton->page_track.get_status = innobase_page_track_get_status;

  ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);

  os_file_set_umask(my_umask);

  /* Setup the memory alloc/free tracing mechanisms before calling
  any functions that could possibly allocate memory. */
  ut_new_boot();

#ifdef HAVE_PSI_INTERFACE
  /* Register keys with MySQL performance schema */
  int count;

#ifdef UNIV_DEBUG
  /** Count of Performance Schema keys that have been registered. */
  int global_count = 0;
#endif /* UNIV_DEBUG */

  count = static_cast<int>(array_elements(all_pthread_mutexes));
  mysql_mutex_register("innodb", all_pthread_mutexes, count);

#ifdef UNIV_DEBUG
  global_count += count;
#endif /* UNIV_DEBUG */

#ifdef UNIV_PFS_MUTEX
  count = static_cast<int>(array_elements(all_innodb_mutexes));
  mysql_mutex_register("innodb", all_innodb_mutexes, count);

#ifdef UNIV_DEBUG
  global_count += count;
#endif /* UNIV_DEBUG */

#endif /* UNIV_PFS_MUTEX */

#ifdef UNIV_PFS_RWLOCK
  count = static_cast<int>(array_elements(all_innodb_rwlocks));
  mysql_rwlock_register("innodb", all_innodb_rwlocks, count);

#ifdef UNIV_DEBUG
  global_count += count;
#endif /* UNIV_DEBUG */

#endif /* UNIV_PFS_MUTEX */

#ifdef UNIV_PFS_THREAD
  count = static_cast<int>(array_elements(all_innodb_threads));
  mysql_thread_register("innodb", all_innodb_threads, count);

#ifdef UNIV_DEBUG
  global_count += count;
#endif /* UNIV_DEBUG */

#endif /* UNIV_PFS_THREAD */

#ifdef UNIV_PFS_IO
  count = static_cast<int>(array_elements(all_innodb_files));
  mysql_file_register("innodb", all_innodb_files, count);

#ifdef UNIV_DEBUG
  global_count += count;
#endif /* UNIV_DEBUG */

#endif /* UNIV_PFS_IO */

  count = static_cast<int>(array_elements(all_innodb_conds));
  mysql_cond_register("innodb", all_innodb_conds, count);

#ifdef UNIV_DEBUG
  global_count += count;
#endif /* UNIV_DEBUG */

  mysql_data_lock_register(&innodb_data_lock_inspector);

#ifdef UNIV_DEBUG
  if (mysql_pfs_key_t::get_count() != global_count) {
    ib::error(ER_IB_MSG_544) << "You have created new InnoDB PFS key(s) but "
                             << mysql_pfs_key_t::get_count() - global_count
                             << " key(s) is/are not registered with PFS. Please"
                             << " register the keys in PFS arrays in"
                             << " ha_innodb.cc.";

    return HA_ERR_INITIALIZATION;
  }
#endif /* UNIV_DEBUG */

#endif /* HAVE_PSI_INTERFACE */

  os_event_global_init();

  if (int error = innodb_init_params()) {
    return error;
  }

  /* After this point, error handling has to use
  innodb_init_abort(). */

  /* Initialize component service handles */
  if (innobase::component_services::intitialize_service_handles() == false) {
    return innodb_init_abort();
  }

  if (!srv_sys_space.parse_params(innobase_data_file_path, true)) {
    ib::error(ER_IB_MSG_545)
        << "Unable to parse innodb_data_file_path=" << innobase_data_file_path;
    return innodb_init_abort();
  }

  if (!srv_tmp_space.parse_params(innobase_temp_data_file_path, false)) {
    ib::error(ER_IB_MSG_546) << "Unable to parse innodb_temp_data_file_path="
                             << innobase_temp_data_file_path;
    return innodb_init_abort();
  }

  /* Perform all sanity check before we take action of deleting files*/
  if (srv_sys_space.intersection(&srv_tmp_space)) {
    log_errlog(ERROR_LEVEL, ER_INNODB_FILES_SAME, srv_tmp_space.name(),
               srv_sys_space.name());
    return innodb_init_abort();
  }

  /* Check for keyring plugin if UNDO/REDO logs are intended to be encrypted */
  if ((srv_undo_log_encrypt || srv_redo_log_encrypt) &&
      Encryption::check_keyring() == false) {
    return innodb_init_abort();
  }

  return 0;
}

函数从开始就对handlerton的相关成员进行了不断的赋值,这样,基本上初始化就完成了。同样,在storage/ha_myisam.cc中也有一个myisam_init类似的函数,包括以后自定义类似引擎都可以模仿着来操作。

3、调用流程
调用的流程是从plugin_foreach开始的,比如日志函数:

void ha_kill_connection(THD *thd) {
  plugin_foreach(thd, kill_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, nullptr);
}

它会调用:

static bool kill_handlerton(THD *thd, plugin_ref plugin, void *) {
  handlerton *hton = plugin_data<handlerton *>(plugin);

  if (hton->state == SHOW_OPTION_YES && hton->kill_connection) {
    if (thd_get_ha_data(thd, hton)) hton->kill_connection(hton, thd);
  }

  return false;
}

在前面的初始化中可以看到:

 innobase_hton->kill_connection = innobase_kill_connection;

 /** Cancel any pending lock request associated with the current THD. */
static void innobase_kill_connection(
    handlerton *hton, /*!< in:  innobase handlerton */
    THD *thd)         /*!< in: handle to the MySQL thread being
                      killed */
{
  DBUG_TRACE;
  assert(hton == innodb_hton_ptr);

  trx_t *trx = thd_to_trx(thd);

  if (trx != nullptr) {
    /* Cancel a pending lock request if there are any */
    lock_trx_handle_wait(trx);
  }
}

这样,一个基本的流程调用就清晰了。

四、总结

老生常谈,看别人代码,看什么?学技巧,是的;学新技术,也是;学各种设计模式,更是。可这里更需要提出的,是学习一个大型软件的架构和设计方式,从更高更宏观的角度来看待一个软件如何运作的,如何动态的扩展和伸缩,这才是真正能明白一个大型软件代码的意义。既要重视细节的代码编写和接口设计,又要掌握宏观的整体运行的流程,一静一动,一点一线一面,层次分明,步调有序。哪些可以为我所用,哪些可以为我所借鉴,有哪些优势,有哪些不足,等等如是。这才是一个真正学习源码的态度和学习观。
努力吧,归来的少年!
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值