MySQL中如何编写Information Schema Plugin
MySQL中如何编写Information Schema Plugin,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
1. 什么是i_s plugin
在mysql里面,默认会有一个information schema(以下简写为i_s),用于记录一些与元数据或表的模式相关的信息,与其他数据库不一样,在data目录下,并没有为i_s建立文件夹,这说明,i_s并不是物理存在的,而是在需要的时候,才会临时创建。这就可以解释为什么i_s库中的表的记录总是无法删除或修改。
2.为什么使用i_s plugin
虽然i_s中定义了丰富的表,但通过i_s plugin,我们可以将其功能进行扩展,丰富其中的信息,比如,我们可以把关心信息以表的形式展现出来,可以通过引入MySQL的内核代码,来监控内核的运行状态,例如锁资源状态、线程状态、table cache状态等信息。客户端可以通过sql来过滤想要的内容,甚至,我们可以在plugin中通过cond来进行过滤,而无需在层处理。
3.如何编写i_s plugin
1)之前已经介绍过的,这里不在赘述,在plugin间通用的包括:
a. plugin的声明;
b.添加系统变量(show /setvariables)
c.添加状态变量(show status)
2)初始化I_S插件
函数原型:name_init(void *p)
函数用于初始化插件,包括指定表的模式、创建表、构造表的函数指针等信息,指针p会指向一个结构体st_schema_table,如下表:
初始化的目的是为了填充结构体st_schema_table,从而确定表的定义,在查询表的时候,调用相应的函数进行记录填充。由于该结构体与内建的i_s表是公用的,因此一些字段我们可以直接忽略掉。在编写plugin的时候,我们需要填充的内容包括:
Ø Fields_info
Ø Fill_table
2).初始化表结构fields_info
Fields_info结构体为st_field_info
通常我们会预定义数组,以NULL列结束:
ST_FIELD_INFO is_field[] = {
{……},
……
{0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
}
3)fill_table()
函数原型:int fill_table(THD *thd, TABLE_LIST *tables, COND *cond);
参数描述:
为了将记录保存到i_s表中,这里不的不提到两个函数:field类的成员函数store_系列函数和schema_table_store_record(),前者用来存储数据到表结构体追踪,后者用来将一行存储好的数据放入到临时表中。
store函数是Field类的方法,有5个:
其中my_declimal类型或者MYSQL_TIME等MySQL代码内特有的类型,我们都可以通过引入相应的代码来构建结构体。
注意当列被声明为MY_I_S_MAYBE_NULL时,需要做一些额外的处理,见之前关于st_field_info结构体的介绍。
当store数据到Field结构体后,我们还需要将其存储到表中,API函数如下:
boolschema_table_store_record(THD *thd, TABLE *table);
其中thd为当前线程,table为tables->table
为了包含这两个函数,我们需要引入如下头文件:
#include <mysql_priv.h>
4)使用COND进行优化
从fill_table的函数原型中,我们可以看到结构体COND,这在MySQL层用作对条件进行记录过滤,实际上在plugin里,我们可以直接进行过滤,只返回到MYSQL层需要的数据。如下图所示:
如果你接触过源代码,会发现COND是一个相当复杂的类型,如果由我们自己编写代码来操作显然要耗费大量的精力,我们可以依葫芦画瓢,找到源代码里是如何使用该结构体的,构造相应的参数,就可以直接调用了,这也是Plugin的诱人之处,我们可以根据需求引用已有的代码。
MySQL层代码大多定义在sql文件夹下,我们在编译时指定相应的目录即可。
当我们的操作对系统影响比较大时,需要尽快的得到结果,例如,内建的I_S表COLUMNS,在填充数据时需要打开所有的表,如果在Plugin层做过滤,那么当我们找到一个不符合条件的表时,尽快关闭,而不是等到MYSQL层来过滤后关闭。
例如函数:
bool calc_lookup_values_from_cond(THD *thd,COND *cond, TABLE_LIST *table, LOOKUP_FIELD_VALUES *lookups);
其中LOOPUP_FIEDL_VALUES结构体为:
sql/sql_show.cc:
typedef struct st_lookup_field_values
{
LEX_STRING value1, value2;
bool value1_is_wildcard, value2_is_wildcard;
} LOOKUP_FIELD_VALUES;
这个函数用于处理等值的情况,函数将寻找类似field1 = constant1 和field2 = constant2这样的条件, 如果找到了,将被存储在LOOKUP_FIELD_VALUES结构体的value1和value2中:
lookups.value1.str
lookups.value2.str
当我们找到了在COND中定义的条件后,就可以进行字符串匹配了。
该函数用于支持INFORMATION_SCHEMA.TABLES, INFORMATION_ SCHEMA.COLUMNS,和其他类型的内建I_S表,主要用来存储表名和数据库名,也就是说,value值为string类型,并且只支持两个等值操作,如果想实现更复杂的cond遍历,我们需要自己来实现。
示例如下(参考自《mysql plugin development》):
view plain
#include <mysql_priv.h>
typedef struct st_lookup_field_values
{
LEX_STRING value1, value2;
bool value1_is_wildcard,value2_is_wildcard;
} LOOKUP_FIELD_VALUES;
bool calc_lookup_values_from_cond(THD *thd,COND *cond,
TABLE_LIST *table, LOOKUP_FIELD_VALUES*lookups);
bool schema_table_store_record(THD *thd,TABLE *table);
ST_FIELD_INFO cond_push_fields[] =
{
{"NUMBER",10, MYSQL_TYPE_LONG, 0, 0, 0, 0},
{"TEXT",100, MYSQL_TYPE_STRING, 0, 0, 0, 0},
{0, 0,MYSQL_TYPE_NULL, 0, 0, 0, 0}
}
int fill_cond_push(THD *thd, TABLE_LIST*tables, COND *cond)
{
CHARSET_INFO *cs= system_charset_info;
TABLE *table =tables->table;
const char**ptr, *output[] = {"hello", "world", "this", "is","a", "test", 0};
int num;
LOOKUP_FIELD_VALUESlookups;
bzero((char*)&lookups, sizeof(lookups));
if (calc_lookup_values_from_cond(thd, cond, tables,&lookups))
return 0;
for (num = 0,ptr = output; *ptr; ptr++)
{
if (lookups.value1.str &&
my_strnncoll(cs, (const uchar*)*ptr, strlen(*ptr),
(const uchar*)lookups.value1.str,
lookups.value1.length))
continue;
table->field[0]->store(++num);
table->field[1]->store(*ptr, strlen(*ptr), cs);
if (schema_table_store_record(thd, table))
return 1;
}
return 0;
}
int cond_push_init(void *p)
{
ST_SCHEMA_TABLE*schema = (ST_SCHEMA_TABLE*) p;
schema->fields_info= cond_push_fields;
schema->fill_table= fill_cond_push;
schema->idx_field1= 1;
return 0;
}
struct st_mysql_information_schemacond_push=
{MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };
mysql_declare_plugin(cond_push)
{
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&cond_push,
"COND_PUSH",
"AndrewHutchings (Andrew.Hutchings@Sun.COM)",
"A simplecondition pushdown demo table",
PLUGIN_LICENSE_GPL,
cond_push_init,
NULL,
0x0010,
NULL,
NULL,
NULL
}
mysql_declare_plugin_end;
5)例子:获取当前query cache中的QUERY信息(摘自网络,略改)
Query_cache中的query 存储在query_cache->queries结构体中,这是一个hash表,我们可以遍历其中的记录还获得想要的数据,代码如下:
view plain
#include <stdlib.h>
#include <ctype.h>
#ifndef MYSQL_SERVER
#define MYSQL_SERVER
#endif
#include <sql_cache.cc>
#include <mysql_priv.h>
#include <mysql/plugin.h>
#include <my_global.h>
#include <mysql_version.h>
#include <my_dir.h>
ST_FIELD_INFOmysql_is_cached_queries_fields[]=
{
{"STATEMENT_ID", 21, MYSQL_TYPE_LONG, 0, 0, "Id"},
{"SCHEMA_NAME", 64, MYSQL_TYPE_STRING, 0, 0,"Schema"},
{"STATEMENT_TEXT", MAX_STATEMENT_TEXT_LENGTH,MYSQL_TYPE_STRING, 0, 0, "Statment text"},
{"RESULT_BLOCKS_COUNT", 21, MYSQL_TYPE_LONG, 0, 0, "CountResult Blocks"},
{"RESULT_BLOCKS_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, 0,"Size Result Blocks"},
{"RESULT_BLOCKS_SIZE_USED", 21, MYSQL_TYPE_LONGLONG, 0, 0,"Size Used Result Blocks"},
{0,0, MYSQL_TYPE_STRING, 0, 0, 0}
};
static intmysql_is_cached_queries_fill_table(THD *thd, TABLE_LIST *tables, COND *cond)
{
intstatus;
CHARSET_INFO *scs= system_charset_info;
TABLE *table= (TABLE *)tables->table;
Accessible_Query_Cache *qc;
HASH *queries;
const uchar *query_cache_block_raw;
Query_cache_block* query_cache_block;
Query_cache_query* query_cache_query;
uint result_blocks_count;
ulonglong result_blocks_size;
ulonglong result_blocks_size_used;
Query_cache_block *first_result_block;
Query_cache_block *result_block;
const char *statement_text;
size_t statement_text_length;
const char *key;
size_t key_length;
qc= (Accessible_Query_Cache *)&query_cache;
query_cache.lock();
queries = qc->get_queries();
query_cache_block_raw = hash_element(queries, i);
query_cache_block = (Query_cache_block*)query_cache_block_raw;
query_cache_query= query_cache_block->query();
table->field[COLUMN_STATEMENT_ID]->store(i+1, 0);
statement_text = (const char*)query_cache_query->query();
statement_text_length = strlen(statement_text);
table->field[COLUMN_STATEMENT_TEXT]->store( (char*)statement_text
,statement_text_length > MAX_STATEMENT_TEXT_LENGTH?
MAX_STATEMENT_TEXT_LENGTH
:statement_text_length
, scs
);
key = (const char*)query_cache_query_get_key( query_cache_block_raw
,&key_length , 0 );
key_length =strlen(key+statement_text_length+1)-1;
table->field[COLUMN_SCHEMA_NAME]->store((char*)key+statement_text_length+1
, key_length
,scs );
first_result_block= query_cache_query->result();
if(first_result_block)
{
result_block= first_result_block;
result_blocks_count = 1;
result_blocks_size = result_block->length;
result_blocks_size_used = result_block->used;
while((result_block= result_block->next)!=first_result_block)
{
result_blocks_count++;
result_blocks_size += result_block->length;
result_blocks_size_used += result_block->used;
}
}
else
{
result_blocks_count = 0;
result_blocks_size = 0;
result_blocks_size_used = 0;
}
table->field[COLUMN_RESULT_BLOCKS_COUNT]->store( result_blocks_count ,0);
table->field[COLUMN_RESULT_BLOCKS_SIZE]->store( result_blocks_size , 0);
table->field[COLUMN_RESULT_BLOCKS_SIZE_USED]->store(result_blocks_size_used , 0 );
status = schema_table_store_record(thd, table);
if (status) {
status= 1;
goto cleanup;
}
}
status = 0;
cleanup:
query_cache.unlock();
return status;
}
static intmysql_is_cached_queries_plugin_init(void *p)
{
ST_SCHEMA_TABLE *schema= (ST_SCHEMA_TABLE *)p;
schema->fields_info= mysql_is_cached_queries_fields;
schema->fill_table= mysql_is_cached_queries_fill_table;
return 0;
}
static int mysql_is_cached_queries_plugin_deinit(void*p)
{
return0;
}
struct st_mysql_information_schemamysql_is_cached_queries_plugin=
{MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };
mysql_declare_plugin(mysql_is_cached_queries)
{
MYSQL_INFORMATION_SCHEMA_PLUGIN,
&mysql_is_cached_queries_plugin,
"MYSQL_CACHED_QUERIES",
"Roland Bouman",
"Lists all queries in the query cache.",
PLUGIN_LICENSE_GPL,
mysql_is_cached_queries_plugin_init,
mysql_is_cached_queries_plugin_deinit,
0x0010 ,
NULL,
NULL,
NULL
}
mysql_declare_plugin_end;
view plain
</pre><pre name="code" class="cpp">
view plain
看完上述内容,你们掌握MySQL中如何编写Information Schema Plugin的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341