编写 MySQL 插件
条评论众所周知,MySQL是支持插件式存储引擎的,意思是MySQL源码中开放了存储引擎相关API,只要插件实现相关的API,就能安装到MySQL中,并作为存储引擎开始工作了。其实MySQL支持多种类型的插件,比如UDF,Daemon,认证,半同步,存储引擎。其中UDF和Daemon插件都非常简单,UDF只是实现了一个在MySQL的SQL接口里可以调用的函数;而Daemon插件只要按照MySQL Plugin的声明方式,声明plugin的描述以及入口函数,从这个plugin入口函数开始,你可以编写任意的代码,比如创建一个后台线程,甚至直接调用MySQL server的源码,访问数据表的数据。
本文主要描述在MySQL中编写Daemon plugin的方法,本文创建一个为monitor的后台plugin,后台每隔若干秒将thread_count等打印到日志中。具体代码如下:
声明MySQL插件的描述以及入口出口函数 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38struct st_mysql_daemon monitor_info = { MYSQL_DAEMON_INTERFACE_VERSION};
mysql_declare_plugin(monitor_plugin)
{
MYSQL_DAEMON_PLUGIN,
&monitor_info,
"monitoring",
"wylazy",
"monitoring mysql thread",
PLUGIN_LICENSE_BSD,
monitoring_plugin_init,
monitoring_plugin_deinit,
0x0100, //1.0
NULL,
vars_system_var,
NULL
}
mysql_declare_plugin_end;
/**
* 具体的含义如下:
*
* struct st_mysql_plugin {
* int type; // 插件类型, 比如MYSQL_DAEMON_PLUGIN,MYSQL_STORAGE_ENGINE_PLUGIN
* void * info;
* const char * name; // INSTALL PLUGIN用到的插件名字
* const char * author; // 插件的作者
* const char * descr; // 插件的描述
* int license; // PLUGIN_LICENSE_GPL, PLUGIN_LICENSE_BSD
* int (* init)(void *); // 插件的入口函数
* int (* deinit)(void *); // 插件的退出函数
* unsigned int version; // 低8个bit存minor version,其他的为major version
* struct st_mysql_show_var * status_vars; // 通过SHOW STATUS显示的状态信息
* struct st_mysql_sys_var ** system_vars; // 通过SHOW VARIABLES显示的变量信息
* void * __reserved; // 在MySQL 5.1里未用到
* }
*
*/1
install plugin monitoring so name xxxxx.so
因为我们的plugin想在后台执行,所以需要在init中创建一个后台执行的线程。具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70//后台线程
pthread_handler_t monitoring(void * p) {
char buffer[MONITOR_BUFFER];
char time_str[20];
while (1) {
struct timeval tv;
tv.tv_sec = waiting_seconds;
tv.tv_usec = 0;
//每隔若干秒,打印一次日志
if (select(1, NULL, NULL, NULL, &tv) == 0) {
get_date(time_str, GETDATE_DATE_TIME, 0);
sprintf(buffer, "%s: %u of %lu clients connected, %lu connections made\n",
time_str, thread_count, max_connections, thread_id);
write(monitoring_file, buffer, strlen(buffer));
} else {
fprintf(stderr, "select return error");
break;
}
}
}
/**
* plugin的入口
* 返回0:安装插件成功
* 返回非0:安装插件失败。mysqld内部会调用deinit()方法做清理
*/
static int monitoring_plugin_init(void *p) {
pthread_attr_t attr;
char monitoring_filename[FN_REFLEN];
char buffer[MONITOR_BUFFER];
char time_str[20];
//打开日志文件
fn_format(monitoring_filename, "monitor", "", ".log", MY_REPLACE_EXT | MY_UNPACK_FILENAME);
unlink(monitoring_filename);
if ((monitoring_file = open(monitoring_filename, O_CREAT|O_RDWR, 0644)) < 0) {
fprintf(stderr, "plugin 'monitoring' could not create file %s", monitoring_filename);
return 1;
}
get_date(time_str, GETDATE_DATE_TIME, 0);
sprintf(buffer, "Monitoring started at %s\n", time_str);
write(monitoring_file, buffer, strlen(buffer));
//创建后台线程
if (pthread_create(&monitoring_thread, NULL, monitoring, NULL) != 0) {
fprintf(stderr, "Plugin monitoring could not create monitoring thread\n");
return 1;
}
return 0;
}
//plugin的出口
static int monitoring_plugin_deinit(void * p) {
char buffer[MONITOR_BUFFER], time_str[20];
//结束后台线程
pthread_cancel(monitoring_thread);
pthread_join(monitoring_thread, NULL);
get_date(time_str, GETDATE_DATE_TIME, 0);
sprintf(buffer, "Monitoring stopped at %s\n", time_str);
write(monitoring_file, buffer, strlen(buffer));
//关闭日志文件
close(monitoring_file);
return 0;
}
为了可以动态的控制打印日志的时间间隔,我们还在plugin中添加了一个动态的变量waiting_seconds,控制后台线程打印日志的时间间隔。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16int waiting_seconds = 0;
//将变量与waiting_seconds 关联
static MYSQL_SYSVAR_INT(mo_waiting_seconds, waiting_seconds, PLUGIN_VAR_RQCMDARG,
"waiting time before print log", NULL, NULL
, 5 //默认值
, 0 //最小值
, 600 //最大值
, 0);
//将mo_waiting_seconds 添加到系统中
struct st_mysql_sys_var * vars_system_var[] =
{
MYSQL_SYSVAR(mo_waiting_seconds)
, NULL
};1
set global monitoring_mo_waiting_seconds = 8;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21CC = gcc
MYSQL_PATH = $(HOME)/programs/mysql
MYSQL_CONFIG = $(MYSQL_PATH)/bin/mysql_config
INCLUDES = ${shell $(MYSQL_CONFIG) --include}
# LIBS = ${shell $(MYSQL_CONFIG) --libs}
CPPFLAGS := -g $(CPPFLAGS) $(INCLUDES) -fPIC -DMYSQL_DYNAMIC_PLUGIN
SO_BASE=monitor
all: $(SO_BASE).so
$(SO_BASE).so: $(SO_BASE).o
$(CC) -o $@ -shared $<
install: all
cp $(SO_BASE).so $(MYSQL_PATH)/lib/mysql/plugin/
clean:
rm -f *.o *.gch *.so
相关说明:
- 同一个名称的plugin只能被install一次,如果install多次,只有第一次可以install成功,后面的都会失败,而且连plugin的init方法都不会调用
- 在install plugin以后,如果没有卸载plugin。如果将mysqld重启,plugin会在重启的时候自动install