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 */
...
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_iterator iter(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<i_string> 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])