一、数据引擎的应用接口
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);
}
}
这样,一个基本的流程调用就清晰了。
四、总结
老生常谈,看别人代码,看什么?学技巧,是的;学新技术,也是;学各种设计模式,更是。可这里更需要提出的,是学习一个大型软件的架构和设计方式,从更高更宏观的角度来看待一个软件如何运作的,如何动态的扩展和伸缩,这才是真正能明白一个大型软件代码的意义。既要重视细节的代码编写和接口设计,又要掌握宏观的整体运行的流程,一静一动,一点一线一面,层次分明,步调有序。哪些可以为我所用,哪些可以为我所借鉴,有哪些优势,有哪些不足,等等如是。这才是一个真正学习源码的态度和学习观。
努力吧,归来的少年!