版本

CentOS release 6.7环境下mysql-5.7.16 社区版

概述

mysql支持插件的开发,以插件的方式实现mysql的附加功能,既可以减少对mysql服务器源码的入侵,也可以实现动态的插拔。可以较好的实现我们需要的功能。

mysql维护一套插件框架来保证所有插件可以有效的运行,源码分析如下。

源码分析

插件的种类

当前在使用的插件共11种,在plugin.h中以宏的方式定义。其中MYSQL_MAX_PLUGIN_TYPE_NUM为插件的数量。

#define MYSQL_UDF_PLUGIN 0 /* User-defined function */

#define MYSQL_STORAGE_ENGINE_PLUGIN 1 /* Storage Engine */

#define MYSQL_FTPARSER_PLUGIN 2 /* Full-text parser plugin */

#define MYSQL_DAEMON_PLUGIN 3 /* The daemon/raw plugin type */

#define MYSQL_INFORMATION_SCHEMA_PLUGIN 4 /* The I_S plugin type */

#define MYSQL_AUDIT_PLUGIN 5 /* The Audit plugin type */

#define MYSQL_REPLICATION_PLUGIN 6 /* The replication plugin type */

#define MYSQL_AUTHENTICATION_PLUGIN 7 /* The authentication plugin type */

#define MYSQL_VALIDATE_PASSWORD_PLUGIN 8 /* validate password plugin type */

#define MYSQL_GROUP_REPLICATION_PLUGIN 9 /* The Group Replication plugin */

#define MYSQL_KEYRING_PLUGIN 10 /* The Keyring plugin type */

#define MYSQL_MAX_PLUGIN_TYPE_NUM 11 /* The number of plugin types */

MySQL启动时插件初始化

...

init_server_components

// 插件初始化以配置文件、命令行输入的变量设置为输入参数

// 根据配置决定是否添加未装载的插件

| plugin_init(&remaining_argc, remaining_argv

// 初始化打开插件so的句柄数组

plugin_dl_array= new (std::nothrow) Prealloced_array...

// 初始化插件定义结构体的动态数组

plugin_array= new (std::nothrow) Prealloced_arrayfor (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)

if (my_hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0,

// 装载静态插件,见后续描述

for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)

......

// 装载动态插件,见后续描述

while (NULL != (item= iter++))

......

装载静态插件

for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)

{

tmp.plugin= plugin; //定义插件结构体

....

....

// 装载插件的系统变量

if (test_plugin_options(&tmp_root, &tmp, argc, argv))

| if (mysql_add_sys_var_chain(chain.first))

if (register_builtin(plugin, &tmp, &plugin_ptr))

| if (plugin_array->push_back(tmp)) // 将插件定义结构体,存入plugin_array中

| if (my_hash_insert(&plugin_hash[plugin->type],(uchar*) *ptr)) //根据插件类型,将差价定义结构体,放入hash桶中

if(...plugin_initialize(plugin_ptr))

// 调用插件自定义的初始化函数

| plugin->plugin->init(plugin)

// 如果插件有状态变量,将状态变量加入到全局链表中

| if (add_status_vars(plugin->plugin->status_vars))

}

mysql_mandatory_plugins为编译时生成,存放binlog、password、innodb等插件的插件定义结构体,定义如下

struct st_mysql_plugin *mysql_mandatory_plugins[]={

builtin_binlog_plugin, builtin_mysql_password_plugin, builtin_innobase_plugin,

builtin_csv_plugin, builtin_heap_plugin, builtin_myisammrg_plugin, builtin_myisam_plugin,

builtin_perfschema_plugin,

0

};

装载动态插件

在装载静态插件之前,会优先装在系统变量early-plugin-load 声明的插件

I_List_iteratoriter(opt_early_plugin_load_list);

while (NULL != (item= iter++))

plugin_load_list(&tmp_root, argc, argv, item->ptr);

装载静态插件之后,装载系统变量plugin-load和plugin-load-add声明的插件

I_List_iterator iter(opt_plugin_load_list); //系统变量

while (NULL != (item= iter++))

plugin_load_list(&tmp_root, argc, argv, item->ptr);

|while (list) // 装载插件链表

|plugin_dl_add(&dl, REPORT_TO_LOG)))

// 动态的加载插件的so文件

// dl为插件的so名字,dlpath为的so全路径

plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))

// plugin_interface_version_sym为字符串_mysql_plugin_interface_version_

// plugin_declarations_sym为字符串"_mysql_plugin_declarations_"

// sizeof_st_plugin_sym为字符串"_mysql_sizeof_struct_st_plugin_"

// 在dlopen插件的so后,读取固定的变量名来获取插件的相关信息

if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym))) // 读取版本变量

if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym))) // 读取

if ((sym= dlsym(plugin_dl.handle, sizeof_st_plugin_sym)))

// 将动态库的句柄保存到全局变量plugin_dl_array中

plugin_dl_insert_or_reuse(&plugin_dl)

|if (plugin_dl_array->push_back(plugin_dl))

// 根据so中读取信息,装载插件

|plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG))

// 装载流程同装载静态插件流程相同

for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)

tmp.plugin= plugin; //定义插件结构体

....

....

if (test_plugin_options(&tmp_root, &tmp, argc, argv))

if (register_builtin(plugin, &tmp, &plugin_ptr))

if(...plugin_initialize(plugin_ptr))

命令行安装和卸载插件

使用命令行命令安装插件,插件的具体装载流程同启动时装载大体相同

......

mysql_execute_command

| case SQLCOM_INSTALL_PLUGIN:

| Sql_cmd_install_plugin::execute

mysql_install_plugin

// 初始化mysql.plugin表,为后续加入记录做准备

| tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);

| if (! (table = open_ltable(thd, &tables, TL_WRITE, ......

// 这里调用相同的接口来装载插件

| plugin_add(thd->mem_root, name, dl, &argc, argv, REPORT_TO_USER);

| (plugin_initialize(tmp)

// 在mysql.plugin表中增加一条记录

| table->use_all_columns();

| restore_record(table, s->default_values);

......

| error= table->file->ha_write_row(table->record[0]);

命令行插件卸载

...

mysql_execute_command

| case SQLCOM_INSTALL_PLUGIN:

| Sql_cmd_uninstall_plugin::execute

mysql_uninstall_plugin

// 初始化mysql.plugin表,为后续删除记录做准备

| tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);

| if (! (table = open_ltable(thd, &tables, TL_WRITE, ......

// 卸载插件

| reap_plugins

| plugin_deinitialize

// 删除系统状态变量

remove_status_vars(plugin->plugin->status_vars);

// 如果存在系统定义的卸载函数,调用系统插件卸载函数

// 部分类型的插件存在,如audit、

if (plugin_type_deinitialize[plugin->plugin->type])

*plugin_type_deinitialize[plugin->plugin->type])(plugin)

// 插件自定义卸载函数

(plugin->plugin->deinit(plugin)

| plugin_del(plugin)

// 删除系统变量

mysql_del_sys_var_chain(plugin->system_vars);

...

plugin_vars_free_values(plugin->system_vars);

// 在对应类型的hash桶中删除该插件

my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);

// 在全局数组plugin_dl_array中清理dlopen的句柄

// 注意计数(存在多个插件使用一个so的情况)

plugin_dl_del(&plugin->plugin_dl->dl);

// 操作mysql.plugin表,将该插件对应的表记录删除

| table->use_all_columns();

| ......

| table->file->ha_delete_row(table->record[0])

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐