您好,欢迎访问一九零五行业门户网

MySQL:如何编写Information Schema Plugin_MySQL

bitscn.com
转载请署名:印风
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来进行过滤,而无需在mysql层处理。
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,如下表:
字段
类型
描述
table_name
const char*
mysql会自动对表赋予插件名,因此我们无需直接赋值
fields_info
st_field_info *
st_field_info类型的结构体数组,用于存储表的每一列的信息,如列名及类型等
create_table
table *(*create_table)  (thd *thd, table_list *table_list);
函数指针,用来创建table结构体,所有的i_s表基本一致,mysql会自动赋值
fill_table
int (*fill_table) (thd *thd, table_list *tables, cond *cond);
函数指针,用于向表中填充记录
old_format
int (*old_format) (thd *thd, struct st_schema_table *schema_table);
用于支持内建i_s表的show功能,无需关注
process_table
int (*process_table) (
thd *thd, table_list *tables, table *table,                        bool res,  lex_string *db_name,
lex_string *table_name);
仅用于内建i_s表
idx_field1, idx_field2
int
仅用于内建i_s表
hidden
bool
如果为true,则其中的数据只能通过show展现,由于i_s plugin不支持show,无需关心此变量
i_s_requested_object
uint
仅用于内建i_s表
初始化的目的是为了填充结构体st_schema_table,从而确定表的定义,在查询表的时候,调用相应的函数进行记录填充。由于该结构体与内建的i_s表是公用的,因此一些字段我们可以直接忽略掉。在编写plugin的时候,我们需要填充的内容包括:
ø  fields_info
ø  fill_table
2).初始化表结构fields_info
fields_info结构体为st_field_info
字段
类型
描述
field_name
const char*
列名,通常用大写表示
field_length
uint
当列的类型为varchar、text时表示字符数,列类型为blob时表示字节数,类型为float 或double时表示数字数,对于decima类型,值为precision*100+scale
field_type
enum enum_field_types
枚举类型,用于指定行类型,包括如下:
mysql_type_tiny、
mysql_type_short、
mysql_type_int24、
mysql_type_long、
mysql_type_longlong、
mysql_type_time、
mysql_type_date、
mysql_type_datetime、mysql_type_timestamp、mysql_type_float、
mysql_type_double、mysql_type_decimal、mysql_type_newdecimal、mysql_type_tiny_blob、mysql_type_medium_blob、mysql_type_blob、mysql_type_long_blob、mysql_type_string,虽然类型很多,但在内置的i_s表中,只用到了mysql_type_string, mysql_type_longlong, mysql_type_long, mysql_type_decimal,  mysql_type_datetime这几种类型,为了避免意外的分享,我们也尽量使用这几种。
value
int
未使用
field_flags
uint
用于表示列的属性,为my_i_s_unsigned表示列为unsigned类型,为my_i_s_maybe_null 表示该列的值可能为null
这里需要注意,如果定义为my_i_s_maybe_null类型,那么在填充表字段信息时,我们总需要首先调用:
(非空)tables->table->field[0]->set_notnull();
(为空)tables->table->field[0]->set_null();
old_name
const char*

open_method
uint
仅用于内建的i_s表
通常我们会预定义数组,以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);
参数描述:
参数名
类型
描述
thd
thd*
当前执行query的线程
tables
table_list
当前query所指定的表(在i_s中的表,都是查询时才自动创建的临时表),我们需要把数据放如到tables中
cond
cond*
where条件,我们可以在函数中通过cond来过滤数据,只把需要的记录加入到临时表中。也可以不搭理,让mysql层来处理
为了将记录保存到i_s表中,这里不的不提到两个函数:field类的成员函数store_系列函数和schema_table_store_record(),前者用来存储数据到表结构体追踪,后者用来将一行存储好的数据放入到临时表中。
store函数是field类的方法,有5个:
函数
描述
field::store(const char *to, uint length, charset_info *cs)
to:字符串指针;length:字符串长度
cs:字符串的字符集,默认的字符集为system_charset_info
bin类型为my_charset_bin、
latin1类型为 my_charset_latin1,此外,我们还可以通过get_charset()、get_charset_by_name()或者get_charset_by_csname()来获得字符集信息
field::store(longlong nr, bool unsigned_val)
nr:longlong整数值
unsigned_val:是否为unsigned类型
field::store(double nr)
存储double类型
field::store_decimal(const my_decimal *d)
decimal类型
field::store_time(mysql_time *ltime, timestamp_type t_type)
时间类型
其中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
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
/*声明相关的结构体和函数*/ 
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) 

         /*系统默认字符集:utf-8*/ 
charset_info *cs= system_charset_info; 
table *table =tables->table;
/*字符串数组output,用于测试只返回符合条件的字符串*/ 
const char**ptr, *output[] = {hello, world, this, is,a, test, 0}; 
int num;
/*声明变量*/ 
lookup_field_valueslookups; 
bzero((char*)&lookups, sizeof(lookups)); 
/*调用函数获得cond中定义的条件*/ 
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中*/ 
table->field[0]->store(++num); 
table->field[1]->store(*ptr, strlen(*ptr), cs); 
if (schema_table_store_record(thd, table)) 
return 1; 

return 0; 

/*初始化i_s plugin*/ 
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  
#include  
/*内核中一些代码定义在mysql_server宏中*/ 
#ifndef mysql_server 
#define mysql_server 
#endif
/*sql_cache.cc中包含了全部跟querycache相关的代码*/ 
#include  
#include  
#include  
#include  
#include  
#include
/*创建一个子类,query_cache的成员queries为私有变量
class accessible_query_cache : privatequery_cache {
public:
 hash *get_queries()
  {
   return &this->queries; //&query_cache.queries;
  }
};
bool schema_table_store_record(thd *thd,table *table);
#define max_statement_text_length 32767
#define column_statement_id 0
#define column_schema_name 1
#define column_statement_text 2
#define column_result_blocks_count 3
#define column_result_blocks_size 4
#define column_result_blocks_size_used 5
/* 定义表结构*/ 
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;  /* need this to store field into table */ 
 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;
/*引用query_cache全局变量*/ 
  qc= (accessible_query_cache *)&query_cache;
/*对query_cache加锁*/ 
  query_cache.lock(); 
/*获取hash*/ 
 queries = qc->get_queries();
/* 遍历hash中的所有记录/
  for(uint i= 0; i records; i++)
  {
         /*根据索引号获取记录*/ 
   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);
/* 获取sql语句*/ 
   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*/ 
   key = (const char*)query_cache_query_get_key(  query_cache_block_raw                          
                                                           ,&key_length  , 0 );
key_length =strlen(key+statement_text_length+1)-1;
/*数据库名是key的一部分,适当的偏移key指针可以得到数据库名*/ 
   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) 
    { 
     /* initialize so we can loop over the result blocks*/ 
     result_block= first_result_block; 
     result_blocks_count = 1;     
     result_blocks_size = result_block->length; 
     result_blocks_size_used = result_block->used;
/* loop over the result blocks*/ 
     while((result_block= result_block->next)!=first_result_block) 
     { 
       /* calculate total number of result blocks */ 
             result_blocks_count++;               
       /* calculate total size of result blocks */ 
       result_blocks_size += result_block->length; 
       /* calculate total of used size of result blocks */ 
       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 };
/*
 plugin library descriptor
*/
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, /* plugin init */ 
 mysql_is_cached_queries_plugin_deinit, /* plugin deinit */ 
 0x0010 /* 1.0 */, 
 null,                       /*status variables                */ 
 null,                       /*system variables                */ 
 null                        /*config options                  */ 

mysql_declare_plugin_end; 
view plain
view plain
参考  
view plain
1.《mysql plugin development》 
view plain
2. mysql5.1.48源代码 
view plain
bitscn.com
其它类似信息

推荐信息