5.1 版本开始MySQL开始支持plugin API,允许在mysqld运行时载入或者卸载组件,而不需要重启mysqld。

plugin API涵盖了UDF、full-text、advanced schema等功能,其中的daemon plugin个人认为是非常的有用。其功能是在plugin载入后可以创建额外的后台线程于mysqld主线程一同协同工作。

plugin API的具体实现在sql/sql_plugin.h 和sql/sql_plugin.cc两个文件中。载入plugin使用dl_open系的动态加载共享库的方法打开so文件,获得需要执行的加载函数和卸载函数的指针。daemon plugin 的启动和storage plugin 启动一样,由init_server_components(sql/mysqld.cc) 里的plugin_init函数来启动。

开发的plugin的时候,只需要关心API接口文件include/plugin.h(我把mysql安装在/usr/local/mysql,所以这个文件在/usr/local/mysql/include/mysql/plugin.h),里面可以看到一些API函数和宏。

plugin的几个相关命令

show plugins 可查询系统内所有的激活的plugin,也包括storage plugin。

mysql>show plugins;
+------------+--------+----------------+--------------+---------+
| Name       | Status | Type           | Library      | License |
+------------+--------+----------------+--------------+---------+
| binlog     | ACTIVE | STORAGE ENGINE | NULL         | GPL     |
| CSV        | ACTIVE | STORAGE ENGINE | NULL         | GPL     |
| MEMORY     | ACTIVE | STORAGE ENGINE | NULL         | GPL     |
| InnoDB     | ACTIVE | STORAGE ENGINE | NULL         | GPL     |
| MyISAM     | ACTIVE | STORAGE ENGINE | NULL         | GPL     |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL         | GPL     |
+------------+--------+----------------+--------------+---------+

mysql.plugin 表也可以获得目前的plugin,但不包括storage plugin,加载错误的plugin也会包含在内,例如so不存在。

plugin_dir参数用于告知系统plugin的目录,这个参数必须在mysqld启动前指定,如果不设置,默认目录为/usr/local/mysql/lib/mysql/plugin/(/usr/local/mysql是我的MySQL安装目录)。

mysql> show variables like 'plugin_dir';
+---------------+-------------------------+
| Variable_name | Value                   |
+---------------+-------------------------+
| plugin_dir    | /usr/local/mysql/plugin |
+---------------+------------------------

加载plugin

mysql>INSTALL PLUGIN plugin_name SONAME 'plugin_library'

这里的plugin_name后面会讲到,plugin_library即为要加载的共享库so文件的名字,目录必须是上面的plugin_dir。

加载插件后,通过前面的show plugins、mysql.plugin 可以看到你的plugin。

卸载plugin

mysql>UNINSTALL PLUGIN plugin_name

编写plugin

plugin模式的软件领域分明是oop继承、重载的用武之地,MySQL却使用了预编译宏和函数指针不是那么优雅的完成了这个任务(个人很喜欢这种方法,虽然代码丑陋了点)。另外说几句MySQL虽然看起来是c++写的,但实际上较少的使用c++的一些特性,只在内部几个模块用到模板类。跑题了,开始写plugin。

下面编写一个plugin,作用是加载后创建一个线程在后台持续的把MySQL内的一些信息打印到err.log日志。

首先介绍一下include/plugin.h里的st_mysql_plugin这个结构体,所有的plugin 都必须是基于这个结构体,填充必须的内容。

 st_mysql_plugin
     type                    
     info                 
      name          
      author       
      descr        
     license                
     init           
     deinit       
      version     
     st_mysql_show_var   
     st_mysql_sys_var     
      __reserved1

声明plugin需要使用2个预编译宏,你可以在include/plugin.h 查看mysql_declare_plugin,mysql_declare_plugin_end两个宏的具体内容。

 st_mysql_daemon monitor_plugin 
    MYSQL_DAEMON_INTERFACE_VERSION
 mysql_declare_pluginmonitor_plugin
    MYSQL_DAEMON_PLUGIN
    monitor_plugin
    
    
    
    PLUGIN_LICENSE_GPL
    monitor_plugin_init
    monitor_plugin_deinit
    
    NULL
    NULL
    NULLmysql_declare_plugin_end

结构体中的monitor_plugin_init和monitor_plugin_deinit就是接下来要编写的加载和卸载对应的函数。

下面是全部代码monitor.c

  ulong		thread_id uint		thread_count ulong		max_connections
  pthread_t	G_thread
 pthread_handler_t func p
     
         sleep
         fprintfstderr 
             thread_id thread_count max_connections
         
   monitor_plugin_init p
     pthread_createampG_thread NULL func NULL   
        fprintfstderr 
         
    
 
    fprintfstderr  
     
   monitor_plugin_deinit p
    pthread_cancelG_thread
    pthread_joinG_thread NULL
    fprintfstderr  
 
     
  st_mysql_daemon monitor_plugin 
    MYSQL_DAEMON_INTERFACE_VERSION
 mysql_declare_pluginmonitor_plugin
    MYSQL_DAEMON_PLUGIN
    monitor_plugin
    
    
    
    PLUGIN_LICENSE_GPL
    monitor_plugin_init
    monitor_plugin_deinit
    
    NULL
    NULL
    NULLmysql_declare_plugin_end

编译

gcc -g -Wall -I/usr/local/mysql/include/mysql -DMYSQL_DYNAMIC_PLUGIN   -c -o monitor.o monitor.c
gcc -shared -o libmonitor.so monitor.o
sudo cp libmonitor.so /usr/local/mysql/plugin/

加载

mysql>install plugin monitor soname 'libmonitor.so';
Query OK, 0 rows affected (0.01 sec)

观察日志可以看到具体的输出。

....
[New Thread 0xb4296b70 (LWP 3895)]
[New Thread 0xb1e34b70 (LWP 4042)]
Monitor plugin init
Thread id [2] Thread_count: 1 Max_connections:151
Thread id [2] Thread_count: 1 Max_connections:151
Thread id [2] Thread_count: 1 Max_connections:151
....
mysql>show plugins;
+------------+--------+----------------+---------------+---------+
| Name       | Status | Type           | Library       | License |
+------------+--------+----------------+---------------+---------+
| monitor    | ACTIVE | DAEMON         | libmonitor.so | GPL     |
+------------+--------+----------------+---------------+---------+

mysql> select * from mysql.plugin;
+------------+---------------+
| name       | dl            |
+------------+---------------+
....
| monitor    | libmonitor.so |
+------------+---------------+

好了,插件的编写工作就完成了,够简单吧。

在MySQL的附带demo里有个heartbeat的例子,MySQL的ref里面也有另外一个full-text的例子。

如何使用daemon plugin ,时还没想好,淘宝的同事 @ningoo有把zookeeper client 作为deamon plugin 想法。