InnoDB储存引擎插件定义和初始化过程
在MySQL中,储存引擎也是作为一种插件实现的,其插件类型为
#define MYSQL_STORAGE_ENGINE_PLUGIN 1 /* Storage Engine */
InnoDB 储存引擎插件
而 InnoDB 作为 MySQL 的默认储存引擎,也是实现了功能最全面的储存引擎。
在 MySQL 中,每一个插件都是利用 mysql_declare_plugin 和 mysql_declare_plugin_end
(include/mysql/plugin.h
文件中)两个宏定义组合声明的,通过宏展开可以发现,其实是定义了一个 struct st_mysql_plugin
的结构体。
InnoDB 充分利用了插件的结构,除了储存引擎相关的,还将 INFORMATION_SCHEMA
下的一系列信息表给插件化了,从而定了一个struct st_mysql_plugin
结构体数组(通过 show plugins
可以查到所有与 InnoDB 有关的插件):
mysql> show plugins;
+----------------------------+----------+--------------------+---------------+---------+
| Name | Status | Type | Library | License |
+----------------------------+----------+--------------------+---------------+---------+
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
| INNODB_TRX | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_LOCKS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_LOCK_WAITS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP_RESET | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMPMEM | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMPMEM_RESET | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP_PER_INDEX | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP_PER_INDEX_RESET | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_BUFFER_PAGE | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_BUFFER_PAGE_LRU | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_BUFFER_POOL_STATS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_TEMP_TABLE_INFO | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_METRICS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_DEFAULT_STOPWORD | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_DELETED | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_BEING_DELETED | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_CONFIG | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_INDEX_CACHE | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_INDEX_TABLE | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_TABLES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_TABLESTATS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_INDEXES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_COLUMNS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_FIELDS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_FOREIGN | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_FOREIGN_COLS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_TABLESPACES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_DATAFILES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_VIRTUAL | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
+----------------------------+----------+--------------------+---------------+---------+
在 MySQL 源码文件 storage/innobase/handler/ha_innodb.cc
中,有 InnoDB 插件的定义:
mysql_declare_plugin(innobase)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&innobase_storage_engine,
innobase_hton_name,
plugin_author,
"Supports transactions, row-level locking, and foreign keys",
PLUGIN_LICENSE_GPL,
innobase_init, /* Plugin Init */
NULL, /* Plugin Deinit */
INNODB_VERSION_SHORT,
innodb_status_variables_export,/* status variables */
innobase_system_variables, /* system variables */
NULL, /* reserved */
0, /* flags */
},
i_s_innodb_trx,
i_s_innodb_locks,
......
i_s_innodb_sys_tables,
i_s_innodb_sys_tablestats,
i_s_innodb_sys_indexes,
i_s_innodb_sys_columns,
i_s_innodb_sys_fields,
......
i_s_innodb_sys_tablespaces,
i_s_innodb_sys_datafiles,
i_s_innodb_sys_virtual
mysql_declare_plugin_end;
因为所有的插件都是定义了一个struct st_mysql_plugin
结构体,首先看看结构体:
/* Plugin description structure. */
struct st_mysql_plugin
{
int type; /* the plugin type (a MYSQL_XXX_PLUGIN value) */
void *info; /* pointer to type-specific plugin descriptor */
const char *name; /* plugin name */
const char *author; /* plugin author (for I_S.PLUGINS) */
const char *descr; /* general descriptive text (for I_S.PLUGINS) */
int license; /* the plugin license (PLUGIN_LICENSE_XXX) */
int (*init)(MYSQL_PLUGIN); /* the function to invoke when plugin is loaded */
int (*deinit)(MYSQL_PLUGIN);/* the function to invoke when plugin is unloaded */
unsigned int version; /* plugin version (for I_S.PLUGINS) */
struct st_mysql_show_var *status_vars;
struct st_mysql_sys_var **system_vars;
void * __reserved1; /* reserved for dependency checking */
unsigned long flags; /* flags for plugin */
};
struct st_mysql_plugin
结构体中,需要每一个插件定义指定其各自的属性:
例如:插件类型,插件名称,作者,描述,初始化函数,版本,插件系统变量等信息;
这些信息最终会通过information_schema.plugins
中展示出来。
mysql> select * from information_schema.plugins where plugin_name='innodb';
+-------------+----------------+---------------+----------------+---------------------+----------------+------------------------+--------------------+------------------------------------------------------------+----------------+-------------+
| PLUGIN_NAME | PLUGIN_VERSION | PLUGIN_STATUS | PLUGIN_TYPE | PLUGIN_TYPE_VERSION | PLUGIN_LIBRARY | PLUGIN_LIBRARY_VERSION | PLUGIN_AUTHOR | PLUGIN_DESCRIPTION | PLUGIN_LICENSE | LOAD_OPTION |
+-------------+----------------+---------------+----------------+---------------------+----------------+------------------------+--------------------+------------------------------------------------------------+----------------+-------------+
| InnoDB | 5.7 | ACTIVE | STORAGE ENGINE | 50725.0 | NULL | NULL | Oracle Corporation | Supports transactions, row-level locking, and foreign keys | GPL | FORCE |
+-------------+----------------+---------------+----------------+---------------------+----------------+------------------------+--------------------+------------------------------------------------------------+----------------+-------------+
1 row in set (0.01 sec)
为了更清晰直观的看 InnoDB 插件的定义,将宏mysql_declare_plugin(innobase)
展开,其定义与information_schema.plugins
展示的是一一对应的:
struct st_mysql_plugin builtin_innobase_plugin[]= {//定义了一个数组
//第一个元素为 Innodb Storage engine
{
1, //PLUGIN_TYPE, 类型为储存引擎,MYSQL_STORAGE_ENGINE_PLUGIN
&innobase_storage_engine, //
"InnoDB",//PLUGIN_NAME
"Oracle Corporation", //PLUGIN_AUTHOR
"Supports transactions, row-level locking, and foreign keys",//PLUGIN_DESCRIPTION
1, //PLUGIN_LICENSE_GPL
innobase_init, //init
NULL,
MYSQL_VERSION_MAJOR<<8|MYSQL_VERSION_MINOR,//PLUGIN_VERSION
{ {"Innodb",
(char*) &show_innodb_vars,
SHOW_FUNC,
SHOW_SCOPE_GLOBAL
},
{ NullS,
NullS,
SHOW_LONG,
SHOW_SCOPE_GLOBAL
}
},//status_vars[]
innobase_system_variables[],//InnoDB引擎的一系列系统变量;
NULL,//reserved
0, //flags
},//第一个元素为 Innodb Storage engine结束
{},//第二个元素
{}...
}
再看看builtin_innobase_plugin[]
数组的二个元素,为 InnoDB 一个 INFORMATION_SCHEMA 插件
的定义:
i_s_innodb_trx
{
type = 4,//PLUGIN_TYPE,INFORMATION_SCHEMA 插件
info = &i_s_info,
name = "INNODB_TRX", //PLUGIN_NAME
author ="Oracle Corporation", //PLUGIN_AUTHOR
descr = "InnoDB transactions",//PLUGIN_DESCRIPTION
license = 1,
init = innodb_trx_init(void*),
deinit = i_s_common_deinit(void*),
version = INNODB_VERSION_SHORT,
status_vars = NULL,
system_vars = NULL,
__reserved1 = NULL,
flags = 0,
}
MySQL 插件初始化大致流程
MySQL 源码编译过程中,cmake 之后,将插件分为必备和可选的两大类插件,builtin_innobase_plugin
即为上述定义的 InnoDB 的引擎:
st_mysql_plugin *mysql_mandatory_plugins[] = {
builtin_binlog_plugin,
builtin_mysql_password_plugin,
builtin_csv_plugin,
builtin_heap_plugin,
builtin_innobase_plugin,
builtin_myisam_plugin,
builtin_myisammrg_plugin,
builtin_perfschema_plugin,
0x0
}
st_mysql_plugin *mysql_optional_plugins[] = {
builtin_archive_plugin,
builtin_blackhole_plugin,
builtin_federated_plugin,
builtin_partition_plugin,
builtin_ngram_parser_plugin,
0x0
}
MySQL 服务器启动后,初始化这两类的插件,下面为 InnoDB 的初始化简要流程:
>main
>mysqld_main(argc, argv)
>init_server_components
/*Load early plugins */
if (plugin_register_early_plugins(&remaining_argc, remaining_argv, opt_help ?
PLUGIN_INIT_SKIP_INITIALIZATION : 0)); THEN
fi
/* Load builtin plugins, initialize MyISAM, CSV and InnoDB */
if (plugin_register_builtin_and_init_core_se(&remaining_argc, remaining_argv)); THEN
for (struct st_mysql_plugin **builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)
do
for (struct st_mysql_plugin *plugin= *builtins; plugin->info; plugin++)
do
struct st_plugin_int tmp
memset(&tmp, 0, sizeof(tmp))
struct st_plugin_int *plugin_ptr
if (plugin_ptr->state != PLUGIN_IS_UNINITIALIZED || plugin_initialize(plugin_ptr)); THEN
>plugin_initialize()
if (plugin_type_initialize[plugin->plugin->type]); THEN//InnoDB为储存引擎,type=1(storage engine)
>ha_initialize_handlerton(st_plugin_int *plugin) //所以此处调用的是initialize_handerton
hton= (handlerton *)my_malloc(key_memory_handlerton, //申请了一个handlerton的内存块,为一个储存引擎的handlerton
sizeof(handlerton),
MYF(MY_WME | MY_ZEROFILL));
plugin->data= hton // plugin的data地址指向了储存引擎的handlerton的地址,用以通过plugin寻找到handlerton头地址
if (plugin->plugin->init && plugin->plugin->init(hton));THEN //调用InnoDB handlerton的init函数来初始化
>innobase_init(hton) //上文介绍到,builtin_innobase_plugin[0]为InnoDB储存引擎的定义,init函数赋值了innobase_init
handlerton* innobase_hton= (handlerton*) p //实例化了InnoDB的handlerton并进行初始化
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->commit = innobase_commit
innobase_hton->rollback = innobase_rollback
......
<innobase_init()
fi
<ha_initialize_handlerton
fi
<plugin_initialize()
fi
done
done
fi
if (plugin_register_dynamic_and_init_all(&remaining_argc, remaining_argv,
(opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) | (opt_help ?
(PLUGIN_INIT_SKIP_INITIALIZATION | PLUGIN_INIT_SKIP_PLUGIN_TABLE) : 0))); THEN
fi
<init_server_components
...
<mysqld_main(argc, argv)
<main
可以看到,InnoDB 插件初始化最终实例出了一个hanlderton
,从而进入到了储存引擎,关于handlerton
可以参考(MySQL如何创建自定义储存引擎(一))