溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MySQL中如何編寫Information Schema Plugin

發(fā)布時(shí)間:2021-10-13 10:04:29 來源:億速云 閱讀:246 作者:柒染 欄目:數(shù)據(jù)庫

MySQL中如何編寫Information Schema Plugin,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

1. 什么是i_s plugin

在mysql里面,默認(rèn)會(huì)有一個(gè)information schema(以下簡寫為i_s),用于記錄一些與元數(shù)據(jù)或表的模式相關(guān)的信息,與其他數(shù)據(jù)庫不一樣,在data目錄下,并沒有為i_s建立文件夾,這說明,i_s并不是物理存在的,而是在需要的時(shí)候,才會(huì)臨時(shí)創(chuàng)建。這就可以解釋為什么i_s庫中的表的記錄總是無法刪除或修改。

2.為什么使用i_s plugin

雖然i_s中定義了豐富的表,但通過i_s plugin,我們可以將其功能進(jìn)行擴(kuò)展,豐富其中的信息,比如,我們可以把關(guān)心信息以表的形式展現(xiàn)出來,可以通過引入MySQL的內(nèi)核代碼,來監(jiān)控內(nèi)核的運(yùn)行狀態(tài),例如鎖資源狀態(tài)、線程狀態(tài)、table cache狀態(tài)等信息??蛻舳丝梢酝ㄟ^sql來過濾想要的內(nèi)容,甚至,我們可以在plugin中通過cond來進(jìn)行過濾,而無需在層處理。

3.如何編寫i_s plugin

1)之前已經(jīng)介紹過的,這里不在贅述,在plugin間通用的包括:

a. plugin的聲明;

b.添加系統(tǒng)變量(show /setvariables)

c.添加狀態(tài)變量(show status)

2)初始化I_S插件

函數(shù)原型:name_init(void *p)

函數(shù)用于初始化插件,包括指定表的模式、創(chuàng)建表、構(gòu)造表的函數(shù)指針等信息,指針p會(huì)指向一個(gè)結(jié)構(gòu)體st_schema_table,如下表:

初始化的目的是為了填充結(jié)構(gòu)體st_schema_table,從而確定表的定義,在查詢表的時(shí)候,調(diào)用相應(yīng)的函數(shù)進(jìn)行記錄填充。由于該結(jié)構(gòu)體與內(nèi)建的i_s表是公用的,因此一些字段我們可以直接忽略掉。在編寫plugin的時(shí)候,我們需要填充的內(nèi)容包括:

Ø  Fields_info

Ø  Fill_table

2).初始化表結(jié)構(gòu)fields_info

Fields_info結(jié)構(gòu)體為st_field_info

通常我們會(huì)預(yù)定義數(shù)組,以NULL列結(jié)束:

ST_FIELD_INFO  is_field[] = {

         {……},

         ……

{0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}

}

3)fill_table()

函數(shù)原型:int fill_table(THD *thd, TABLE_LIST *tables, COND *cond);

參數(shù)描述:

為了將記錄保存到i_s表中,這里不的不提到兩個(gè)函數(shù):field類的成員函數(shù)store_系列函數(shù)和schema_table_store_record(),前者用來存儲(chǔ)數(shù)據(jù)到表結(jié)構(gòu)體追蹤,后者用來將一行存儲(chǔ)好的數(shù)據(jù)放入到臨時(shí)表中。

store函數(shù)是Field類的方法,有5個(gè):

其中my_declimal類型或者M(jìn)YSQL_TIME等MySQL代碼內(nèi)特有的類型,我們都可以通過引入相應(yīng)的代碼來構(gòu)建結(jié)構(gòu)體。

注意當(dāng)列被聲明為MY_I_S_MAYBE_NULL時(shí),需要做一些額外的處理,見之前關(guān)于st_field_info結(jié)構(gòu)體的介紹。

當(dāng)store數(shù)據(jù)到Field結(jié)構(gòu)體后,我們還需要將其存儲(chǔ)到表中,API函數(shù)如下:

boolschema_table_store_record(THD *thd, TABLE *table);

其中thd為當(dāng)前線程,table為tables->table

為了包含這兩個(gè)函數(shù),我們需要引入如下頭文件:

#include <mysql_priv.h>

4)使用COND進(jìn)行優(yōu)化

從fill_table的函數(shù)原型中,我們可以看到結(jié)構(gòu)體COND,這在MySQL層用作對條件進(jìn)行記錄過濾,實(shí)際上在plugin里,我們可以直接進(jìn)行過濾,只返回到MYSQL層需要的數(shù)據(jù)。如下圖所示:

如果你接觸過源代碼,會(huì)發(fā)現(xiàn)COND是一個(gè)相當(dāng)復(fù)雜的類型,如果由我們自己編寫代碼來操作顯然要耗費(fèi)大量的精力,我們可以依葫蘆畫瓢,找到源代碼里是如何使用該結(jié)構(gòu)體的,構(gòu)造相應(yīng)的參數(shù),就可以直接調(diào)用了,這也是Plugin的誘人之處,我們可以根據(jù)需求引用已有的代碼。

MySQL層代碼大多定義在sql文件夾下,我們在編譯時(shí)指定相應(yīng)的目錄即可。

當(dāng)我們的操作對系統(tǒng)影響比較大時(shí),需要盡快的得到結(jié)果,例如,內(nèi)建的I_S表COLUMNS,在填充數(shù)據(jù)時(shí)需要打開所有的表,如果在Plugin層做過濾,那么當(dāng)我們找到一個(gè)不符合條件的表時(shí),盡快關(guān)閉,而不是等到MYSQL層來過濾后關(guān)閉。

例如函數(shù):

bool calc_lookup_values_from_cond(THD *thd,COND *cond, TABLE_LIST *table, LOOKUP_FIELD_VALUES *lookups);

其中LOOPUP_FIEDL_VALUES結(jié)構(gòu)體為:

sql/sql_show.cc:

typedef struct st_lookup_field_values

{

LEX_STRING value1, value2;

bool value1_is_wildcard, value2_is_wildcard;

} LOOKUP_FIELD_VALUES;

這個(gè)函數(shù)用于處理等值的情況,函數(shù)將尋找類似field1 = constant1 和field2 = constant2這樣的條件, 如果找到了,將被存儲(chǔ)在LOOKUP_FIELD_VALUES結(jié)構(gòu)體的value1和value2中:

lookups.value1.str

lookups.value2.str

當(dāng)我們找到了在COND中定義的條件后,就可以進(jìn)行字符串匹配了。

該函數(shù)用于支持INFORMATION_SCHEMA.TABLES, INFORMATION_ SCHEMA.COLUMNS,和其他類型的內(nèi)建I_S表,主要用來存儲(chǔ)表名和數(shù)據(jù)庫名,也就是說,value值為string類型,并且只支持兩個(gè)等值操作,如果想實(shí)現(xiàn)更復(fù)雜的cond遍歷,我們需要自己來實(shí)現(xiàn)。

示例如下(參考自《mysql plugin development》):

view plain

#include <mysql_priv.h> 

/*聲明相關(guān)的結(jié)構(gòu)體和函數(shù)*/ 

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); 

/*定義列類型

*包括一個(gè)整型和一個(gè)字符串型

*/ 

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) 

         /*系統(tǒng)默認(rèn)字符集:utf-8*/ 

CHARSET_INFO *cs= system_charset_info; 

TABLE *table =tables->table; 

/*字符串?dāng)?shù)組output,用于測試只返回符合條件的字符串*/ 

const char**ptr, *output[] = {"hello", "world", "this", "is","a", "test", 0}; 

int num; 

/*聲明變量*/ 

LOOKUP_FIELD_VALUESlookups; 

bzero((char*)&lookups, sizeof(lookups)); 

/*調(diào)用函數(shù)獲得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; 

                   /*只有滿足條件的字符串才會(huì)被存儲(chǔ)到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; 

/*指定記錄填充函數(shù)*/ 

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)例子:獲取當(dāng)前query cache中的QUERY信息(摘自網(wǎng)絡(luò),略改)

Query_cache中的query 存儲(chǔ)在query_cache->queries結(jié)構(gòu)體中,這是一個(gè)hash表,我們可以遍歷其中的記錄還獲得想要的數(shù)據(jù),代碼如下:

view plain

#include <stdlib.h> 

#include <ctype.h> 

/*內(nèi)核中一些代碼定義在MYSQL_SERVER宏中*/ 

#ifndef MYSQL_SERVER 

#define MYSQL_SERVER 

#endif 

/*sql_cache.cc中包含了全部跟querycache相關(guān)的代碼*/ 

#include <sql_cache.cc> 

#include <mysql_priv.h> 

#include <mysql/plugin.h> 

#include <my_global.h> 

#include <mysql_version.h> 

#include <my_dir.h> 

/*創(chuàng)建一個(gè)子類,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

/* 定義表結(jié)構(gòu)*/ 

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} 

}; 

/*填充數(shù)據(jù)函數(shù)*/ 

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 < queries->records; i++)

  {

         /*根據(jù)索引號獲取記錄*/ 

   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); 

/*當(dāng)超出長度時(shí)需要截?cái)?amp;hellip;*/ 

   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; 

         /*數(shù)據(jù)庫名是key的一部分,適當(dāng)?shù)钠苉ey指針可以得到數(shù)據(jù)庫名*/ 

   table->field[COLUMN_SCHEMA_NAME]->store((char*)key+statement_text_length+1 

                                             , key_length 

                                             ,scs  ); 

   /*獲得結(jié)果集所占塊的個(gè)數(shù)*/ 

   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; 

    } 

   /* 存儲(chǔ)塊的個(gè)數(shù)*/ 

   table->field[COLUMN_RESULT_BLOCKS_COUNT]->store( result_blocks_count ,0); 

   /* 存儲(chǔ)總的所占有塊的大小*/ 

   table->field[COLUMN_RESULT_BLOCKS_SIZE]->store( result_blocks_size  , 0); 

   /*存儲(chǔ)總的已使用塊的大小*/ 

   table->field[COLUMN_RESULT_BLOCKS_SIZE_USED]->store(result_blocks_size_used , 0 ); 

   /* 將記錄存儲(chǔ)到表中*/ 

   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

</pre><pre name="code" class="cpp"> 

view plain

看完上述內(nèi)容,你們掌握MySQL中如何編寫Information Schema Plugin的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI