mysql plugin 调用_MySQL插件接口的调用方式
author:sufei
版本:8.0.16
一、簡介
首先簡單說明一下插件的實現原理
在程序的合適位置(掛鉤處)安插相應的函數指針,相應的結構類似:if (fun_ptr != null) fun_ptr();
在沒有插件安裝時,這些函數指針為空,程序運行到掛鉤處沒有任何操作,不會進入插件相關代碼;
在安裝了插件時,安裝過程會為相應的函數指針賦值,fun_ptr不在為null,從而當程序運行到掛鉤處,則進入相應的插件處理邏輯。
雖然插件的基本實現原理大致如上述所說,但對于mysql的插件實現來說,存在多種調用方式。下面具體講解各種調用方式的初始化以及調用過程,以便在閱讀mysql源碼時更好的理解插件調用過程。
二、插件訪問方式
2.1 觀察者方式
這是服務層調用插件是非常常見的方式。MySQL服務器將觀察者的掛鉤按照用途分為五類,并且定義了五類觀察者的全局變量,用于慣例不同種類的掛鉤。分別為:
//觀察者代表類
Trans_delegate *transaction_delegate;
Binlog_storage_delegate *binlog_storage_delegate;
Server_state_delegate *server_state_delegate;
Binlog_transmit_delegate *binlog_transmit_delegate;
Binlog_relay_IO_delegate *binlog_relay_io_delegate;
下面對這5類觀察者進行簡單說明:
Trans_delegate 事務觀察者:其主要用于事務掛鉤,包含:
before_dml,before_commit,before_rollback,after_commit,after_rollback,trans_begin。
顧名思義,這些就是事務觀察者的掛鉤位置。如果插件想在這些位置進入插件處理邏輯,就需要向Trans_delegate注冊。比如MGR就注冊了事務觀察者,其提交前(before_commit位置處)調用注冊時的相應函數進入沖突檢測階段。
Binlog_storage_delegate binlog存儲觀察者:其主要用于binlog同步位置掛鉤,包含:
after_flush,after_sync。
即在相應的binlog同步點處的掛鉤。如果插件向Binlog_storage_delegate進行了注冊,則會在相應位置點調用相應的注冊函數。比如半同步插件,就是注冊了binlog存儲觀察者,從而實現不同的ack等待點。
Server_state_delegate 服務器狀態觀察者:主要是在服務器狀態啟停處設置的掛鉤,包含:before_handle_connection,before_recovery,after_engine_recovery,after_recovery,before_server_shutdown,after_server_shutdown。
Binlog_transmit_delegate binlog傳輸觀察者:主要在binlog傳輸各節點設置的掛鉤,包含:
transmit_start,transmit_stop,reserve_header,before_send_event,after_send_event,after_reset_master
在發送binlog各節點處的掛鉤,可以實現對binlog傳輸控制,如在半同步插件中,通過注冊binlog傳輸觀察者,當從庫開啟復制時,主庫start dump線程,會調用transmit_start函數,進而半同步插件通過該調用添加半同步從庫。
Binlog_relay_IO_delegate relay IO 觀察者:主要在是在從庫復制線程上的掛鉤,包含:
thread_start,thread_stop,applier_start,applier_stop,before_request_transmit,after_read_event,after_queue_event,after_reset_slave,applier_log_event
在從庫回放個各位置設置的掛鉤,從而使得插件得以控制回放過程。
上面介紹了各類觀察者,既然是觀察者,就應該分為觀察者的注冊過程,以及調用過程。
2.1.1 注冊
要明白注冊的過程,首先需要知道上面5類觀察者都是Delegate的子類,而Delegate類主要維護著observer_info_list列表(也就是該類被成員鎖lock保護注冊列表)。列表的節點為Observer_info類型,其中就保存著插件相關信息。
class Observer_info {
public:
void *observer;
st_plugin_int *plugin_int;
plugin_ref plugin;
Observer_info(void *ob, st_plugin_int *p);
};
Observer_info::Observer_info(void *ob, st_plugin_int *p)
: observer(ob), plugin_int(p) {
plugin = plugin_int_to_ref(plugin_int);
}
class Delegate {
public:
typedef List Observer_info_list;
typedef List_iterator Observer_info_iterator;
//向注冊列表中添加相應注冊
int add_observer(void *observer, st_plugin_int *plugin) {
int ret = false;
if (!inited) return true;
write_lock();
Observer_info_iterator iter(observer_info_list);
Observer_info *info = iter++;
while (info && info->observer != observer) info = iter++;
if (!info) {
info = new Observer_info(observer, plugin); //構建新的Observer_info
if (!info || observer_info_list.push_back(info, &memroot)) ret = true;
} else
ret = true;
unlock();
return ret;
}
//向注冊列表中注銷相應注冊
int remove_observer(void *observer) {
int ret = false;
if (!inited) return true;
write_lock();
Observer_info_iterator iter(observer_info_list);
Observer_info *info = iter++;
while (info && info->observer != observer) info = iter++;
if (info) {
iter.remove();
delete info;
} else
ret = true;
unlock();
return ret;
}
inline Observer_info_iterator observer_info_iter() {
return Observer_info_iterator(observer_info_list);
}
//注冊列表是否為空,空著說明沒有相關注冊,即在該類掛鉤處直接返回
inline bool is_empty() {
DBUG_PRINT("debug", ("is_empty: %d", observer_info_list.is_empty()));
return observer_info_list.is_empty();
}
inline int read_lock() {
if (!inited) return true;
return mysql_rwlock_rdlock(&lock);
}
inline int write_lock() {
if (!inited) return true;
return mysql_rwlock_wrlock(&lock);
}
inline int unlock() {
if (!inited) return true;
return mysql_rwlock_unlock(&lock);
}
inline bool is_inited() { return inited; }
explicit Delegate(
#ifdef HAVE_PSI_RWLOCK_INTERFACE
PSI_rwlock_key key
#endif
);
~Delegate() {
inited = false;
mysql_rwlock_destroy(&lock);
free_root(&memroot, MYF(0));
}
private:
Observer_info_list observer_info_list; //維護的注冊列表
mysql_rwlock_t lock;
MEM_ROOT memroot;
bool inited;
};
注冊的過程就是向成員observer_info_list列表中添加Observer_info的過程。下面我們通過半同步插件的binlog存儲觀察者(即Binlog_storage_delegate)注冊過程,來詳解分析注冊機制。其他觀察者的注冊過程類似。
Binlog_storage_delegate觀察者的注冊過程
5個全局的觀察者變量,每類觀察者都有一個相應的注冊函數以及相應的注冊函數結構體,對于binlog存儲觀察者,其注冊函數如下:
/*
binlog存儲觀察注冊函數
參數: observer :binlog存儲觀察者注冊函數結構體
p :插件結構體指針
*/
int register_binlog_storage_observer(Binlog_storage_observer *observer,
void *p) {
DBUG_ENTER("register_binlog_storage_observer");
int result =
binlog_storage_delegate->add_observer(observer, (st_plugin_int *)p);
DBUG_RETURN(result);
}
// binlog存儲觀察者注冊函數結構體,最終會被關聯在Observer_info->observer成員中
typedef struct Binlog_storage_observer {
uint32 len;
after_flush_t after_flush;
after_sync_t after_sync;
} Binlog_storage_observer;
相對來說,注冊函數非常簡單,僅僅調用基類的add_observer函數即可。而對于半同步插件在插件初始化函數semi_sync_master_plugin_init中調用相關注冊函數。
if (register_binlog_storage_observer(&storage_observer, p)) { //注冊binlog存儲觀察
deinit_logging_service_for_plugin(®_srv, &log_bi, &log_bs);
return 1;
}
而相應的注冊函數結構體storage_observer,定義如下
Binlog_storage_observer storage_observer = {
sizeof(Binlog_storage_observer), // len
repl_semi_report_binlog_update, // report_update
repl_semi_report_binlog_sync, // after_sync
};
通過上述分析,注冊的過程大致過程如下:
相關注冊函數構建成注冊結構體,如上述將repl_semi_report_binlog_update和repl_semi_report_binlog_sync函數作為成員構建Binlog_storage_observer結構體;
通過相應的注冊函數,將相應observer結構體添加到相應觀察者注冊鏈表中。
通過上述兩步,將相應的注冊函數被放入觀察者的注冊鏈表中,在調用過程通過遍歷注冊鏈表,調用相應函數,完成注冊函數的調用。下面會具體分析。
2.1.2 調用過程
注冊了相應觀察者后,通常是通過定義的參數宏來實現調用的。同樣以binlog存儲觀察者注冊結構體中的注冊函數after_sync為例,來說明完整的調用過程。
after_sync掛鉤安放位置
在組提交sync階段之后,引擎層commit階段前,會調用如下函數:
sync_error = call_after_sync_hook(commit_queue);
而該函數的內部有調用了如下宏
RUN_HOOK(binlog_storage, after_sync, (queue_head, log_file, pos))
宏的擴展為:
#define RUN_HOOK(group, hook, args) \
(group##_delegate->is_empty() ? 0 : group##_delegate->hook args)
實際調用為:
(binlog_storage_delegate->is_empty()? 0 : binlog_storage_delegate->after_sync(queue_head, log_file, pos))
從上面可以看到:首先判斷binlog存儲觀察者注冊列表是否為空,為空則直接返回;不為空則調用
binlog_storage_delegate->after_sync成員函數。下面我們具體看一下其實現。
binlog存儲觀察者after_sync成員函數實現
首先看一下Binlog_storage_delegate類的實現.
class Binlog_storage_delegate : public Delegate {
public:
Binlog_storage_delegate()
: Delegate(
#ifdef HAVE_PSI_RWLOCK_INTERFACE
key_rwlock_Binlog_storage_delegate_lock
#endif
) {
}
//重定義Observer為Binlog_storage_observer注冊結構體類,以便后續成員函數查找相應的注冊函數
typedef Binlog_storage_observer Observer;
/*
對于該類管理的所有hook掛鉤,都以一個相應的成員函數。如after_sync掛鉤處調用after_sync函數
*/
int after_flush(THD *thd, const char *log_file, my_off_t log_pos);
int after_sync(THD *thd, const char *log_file, my_off_t log_pos);
};
針對after_sync函數,其實現如下:
int Binlog_storage_delegate::after_sync(THD *thd, const char *log_file,
my_off_t log_pos) {
DBUG_ENTER("Binlog_storage_delegate::after_sync");
DBUG_PRINT("enter",
("log_file: %s, log_pos: %llu", log_file, (ulonglong)log_pos));
Binlog_storage_param param;
param.server_id = thd->server_id;
DBUG_ASSERT(log_pos != 0);
int ret = 0;
FOREACH_OBSERVER(ret, after_sync, (¶m, log_file, log_pos)); //調用注冊列表中相應函數
DEBUG_SYNC(thd, "after_call_after_sync_observer");
DBUG_RETURN(ret);
}
從上面可以看到:after_sync成員函數實際調用FOREACH_OBSERVER參數宏
#define FOREACH_OBSERVER(r, f, args) \
Prealloced_array plugins(PSI_NOT_INSTRUMENTED); \
read_lock(); \
Observer_info_iterator iter = observer_info_iter(); \
Observer_info *info = iter++; \
for (; info; info = iter++) { //遍歷注冊列表 \
plugin_ref plugin = my_plugin_lock(0, &info->plugin); \
if (!plugin) { \
/* plugin is not intialized or deleted, this is not an error */ \
r = 0; \
break; \
} \
plugins.push_back(plugin); \
//如果注冊結構體observer中相應注冊函數不為空,則調用相應注冊函數指針 \
if (((Observer *)info->observer)->f && \
((Observer *)info->observer)->f args) { \
r = 1; \
LogEvent() \
.prio(ERROR_LEVEL) \
.errcode(ER_RPL_PLUGIN_FUNCTION_FAILED) \
.subsys(LOG_SUBSYSTEM_TAG) \
.function(#f) \
.message("Run function '" #f "' in plugin '%s' failed", \
info->plugin_int->name.str); \
break; \
} \
} \
unlock(); \
/* \
Unlock plugins should be done after we released the Delegate lock \
to avoid possible deadlock when this is the last user of the \
plugin, and when we unlock the plugin, it will try to \
deinitialize the plugin, which will try to lock the Delegate in \
order to remove the observers. \
*/ \
if (!plugins.empty()) plugin_unlock_list(0, &plugins[0], plugins.size());
下面對上述宏做一個簡單說明:
遍歷binlog存儲觀察類的Observer_info列表;
取出Observer_info的observer指針,將其轉換為Binlog_storage_observer類型(宏中是轉換為Observer類型,但在該類中存在typedef Binlog_storage_observer Observer,故最終轉換為binlog存儲類的注冊結構體Binlog_storage_observer類型);
查找注冊結構體中的相應注冊函數after_sync是否為空,不為空則調用,從而進入插件邏輯。
通過上面,我們也看到了觀察點的調用過程。下面是是對插件在觀察者方式下調用總結。
2.1.3 總結
觀察者模式
注冊:
首先構建相應的注冊結構體,對于binlog 存儲觀察類,也就是Binlog_storage_observer結構體,其中包含了相應注冊函數指針;
構建Observer_info類型,其中observer指針指向注冊結構體,如Binlog_storage_observer;
調用各類觀察者相應的注冊函數,如register_binlog_storage_observer,將上述Observer_info注冊到Binlog_storage_delegate的注冊列表中
調用
mysql在合適的調用位置,都設置相應的RUN_HOOK宏;
如RUN_HOOK(binlog_storage, after_sync, (queue_head, log_file, pos))則是在組提交sync之后設置的掛鉤宏
通過參數宏替換,調用觀察者的相應成員函數,最終會遍歷注冊鏈表中所有相應的設置函數。
對于上述例子,調用binlog_storage_delegate成員函數after_sync,最終也就是遍歷binlog_storage_delegate注冊鏈表中observer結構體內的after_sync函數,參數由RUN_HOOK中的(queue_head, log_file, pos)指定。
2.2 handlerton方式
該方式主要操作存儲引擎的主要方式。
在存儲引擎中表的操作以及事務相關的操作都是通過這種方式來訪問插件引擎層的。如create創建表,commit提交事務等操作。
下面我們來具體講解此種方式訪問的過程。
2.2.1 函數掛載
對于插件存儲引擎,mysql都會有一個handlerton類型的結構體與之對應,通常命名為xxx_hton。對于innodb存儲引擎,即為innobase_hton。handlerton類型結構體的成員包含:
一些類型狀態說明變量;
SHOW_COMP_OPTION state; //標記引擎狀態
enum legacy_db_type db_type; //存儲引擎類型
uint slot;
數據表相應操作的函數指針。
//以下都是表相應操作的函數指針成員
close_connection_t close_connection;
kill_connection_t kill_connection;
pre_dd_shutdown_t pre_dd_shutdown;
savepoint_set_t savepoint_set;
savepoint_rollback_t savepoint_rollback;
savepoint_rollback_can_release_mdl_t savepoint_rollback_can_release_mdl;
savepoint_release_t savepoint_release;
commit_t commit;
rollback_t rollback;
prepare_t prepare;
……
上面介紹了handlerton結構體的構成,下面我們來看一下對于一個特定引擎的handlerton(如innobase_hton )是如何掛載到mysql服務器上,以便后續服務層當需要操作該引擎的表時,通過該結構體內的函數指針,調用內部相應的函數真正引擎層操作物理表。
要理解引擎的handlerton的掛載過程,實際就是理解存儲引擎install過程,以及mysql插件管理過程。
1. 插件管理
mysql服務器通過如下全局變量來管理所有安裝的插件。
static collation_unordered_map<:string st_plugin_int>
*plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM] = {nullptr};
該變量是一個map指針數組。對于每一類插件,都存放在一個map結構體中,所有map結構體指針都存放在plugin_hash數組中。
一個安裝的插件,我們可以通過插件類型以及插件名找到插件handle句柄(即st_plugin_int結構體),過程都在函數plugin_find_internal中
/*
從安裝的插件中,獲取特定插件的操作handle句柄
參數:
name : 插件名
type : 插件類型
*/
static st_plugin_int *plugin_find_internal(const LEX_CSTRING &name, int type) {
uint i;
DBUG_ENTER("plugin_find_internal");
if (!initialized) DBUG_RETURN(NULL);
mysql_mutex_assert_owner(&LOCK_plugin);
if (type == MYSQL_ANY_PLUGIN) { //沒有指定插件類型,則遍歷所有插件類型,查找名為name的插件
for (i = 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) {
const auto it = plugin_hash[i]->find(to_string(name));
if (it != plugin_hash[i]->end()) DBUG_RETURN(it->second);
}
} else //如果指定了插件類型,直接到相應map中查找即可
DBUG_RETURN(find_or_nullptr(*plugin_hash[type], to_string(name)));
DBUG_RETURN(NULL);
}
明白了mysql內部是如何管理插件,以及如何查找相應插件。下面我們需要講解插件的安裝以及初始化(handlerton掛載)。
2.插件的安裝以及初始化(handlerton掛載)
在講解插件安裝過程之前,我們先看看插件相關結構體以及之間的關系。
插件相關結構體
在執行install指令安裝插件時,內部會調用mysql_install_plugin函數,下面我們來具體看一下該函數的執行過程。
打開mysql.plugins表,以便后續進行寫表
/* need to open before acquiring LOCK_plugin or it will deadlock */
if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
DBUG_RETURN(true);
構建st_plugin_int結構體(參考plugin_add函數),即插件句柄,并插入到管理變量plugin_hash中,其中包含以下幾步
st_plugin_dl動態庫結構體體構建,主要是調用plugin_dl_add函數,加載動態庫,版本檢測,最后將動態庫中插件描述結構體指針賦值給st_plugin_dl->plugins
從動態庫結構體中的plugins中,尋找指定插件名的插件,找到了,則加載該插件描述結構到st_plugin_int->plugin,并將state狀態設置為PLUGIN_IS_UNINITIALIZED
將st_plugin_int插入到管理變量plugin_hash中
/* 循環查找,插件名一致的插件描述結構體 */
for (plugin = tmp.plugin_dl->plugins; plugin->info; plugin++) {
size_t name_len = strlen(plugin->name);
//版本檢測以及插件名檢測是否都滿足
if (plugin->type >= 0 && plugin->type < MYSQL_MAX_PLUGIN_TYPE_NUM &&
!my_strnncoll(system_charset_info,
pointer_cast(name->str), name->length,
pointer_cast(plugin->name), name_len)) {
……
}
// 找到相應插件描述結構體,進行插件句柄賦值
tmp.plugin = plugin;
tmp.name.str = (char *)plugin->name;
tmp.name.length = name_len;
tmp.ref_count = 0;
tmp.state = PLUGIN_IS_UNINITIALIZED; //標記插件狀態
tmp.load_option = PLUGIN_ON; //標記加載狀態
if (test_plugin_options(tmp_root, &tmp, argc, argv))
tmp.state = PLUGIN_IS_DISABLED;
if ((tmp_plugin_ptr = plugin_insert_or_reuse(&tmp))) {
plugin_array_version++;
//將插件句柄插入到插件管理變量plugin_hash中
if (plugin_hash[plugin->type]
->emplace(to_string(tmp_plugin_ptr->name), tmp_plugin_ptr)
.second) {
init_alloc_root(key_memory_plugin_int_mem_root,
&tmp_plugin_ptr->mem_root, 4096, 4096);
DBUG_RETURN(false);
}
tmp_plugin_ptr->state = PLUGIN_IS_FREED;
}
mysql_del_sys_var_chain(tmp.system_vars);
restore_pluginvar_names(tmp.system_vars);
plugin_dl_del(dl);
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(true);
}
}
檢測相應結構體是否添加到plugin_hash中,并將相關插件信息寫入mysql.plugins表
// 找到相應插件句柄
if (!(tmp = plugin_find_internal(name_cstr, MYSQL_ANY_PLUGIN))) {
mysql_mutex_unlock(&LOCK_plugin);
goto err;
}
Disable_binlog_guard binlog_guard(thd); //寫入plugins表時關閉binlog,避免主從同步
table->use_all_columns();
restore_record(table, s->default_values);
table->field[0]->store(name->str, name->length, system_charset_info);
table->field[1]->store(dl->str, dl->length, files_charset_info);
error = table->file->ha_write_row(table->record[0]); //將插件信息寫入表中
調用plugin_initialize函數,對插件進行初始化,初始化過程有三步
調用插件初始化函數
添加插件狀態變量
將插件系統變量添加到服務器系統變量管理結構
//插件初始化函數,會調用插件聲明時初始化函數指針
static int plugin_initialize(st_plugin_int *plugin) {
....
/*
根據插件類型不同,初始化過程也不同,其過程被plugin_type_initialize數組指定
*/
if (plugin_type_initialize[plugin->plugin->type]) {
//如果plugin_type_initialize數組相應插件位置不為空,則調用數組指定函數進行插件初始化,參數為plugin
if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) {
LogErr(ERROR_LEVEL, ER_PLUGIN_REGISTRATION_FAILED, plugin->name.str,
plugin_type_names[plugin->plugin->type].str);
goto err;
}
....
} else if (plugin->plugin->init) {//為空,直接調用聲明的插件初始化函數,參數為st_plugin_int類型
....
if (plugin->plugin->init(plugin)) {
LogErr(ERROR_LEVEL, ER_PLUGIN_INIT_FAILED, plugin->name.str);
goto err;
}
}
state = PLUGIN_IS_READY; // plugin->init() succeeded
//添加插件狀態變量
if (plugin->plugin->status_vars) {
if (add_status_vars(plugin->plugin->status_vars)) goto err;
}
//添加插件系統變量
if (plugin->system_vars) {
sys_var_pluginvar *var = plugin->system_vars->cast_pluginvar();
for (;;) {
var->plugin = plugin;
if (!var->next) break;
var = var->next->cast_pluginvar();
}
}
ret = 0;
err:
mysql_mutex_lock(&LOCK_plugin);
plugin->state = state;
DBUG_RETURN(ret);
}
通過上面可知,不同插件初始化方式不一樣,與plugin_type_initialize數組有關,對此我們看一下該數組
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM] = {
0,
ha_initialize_handlerton, //存儲引擎插件類型
0,
0,
initialize_schema_table, //I_S插件類型
initialize_audit_plugin, //audit審計插件類型
0,
0,
0};
進入ha_initialize_handlerton存儲引擎插件的初始化函數,我們發現調用插件描述結構體中初始化函數的參數不在為插件句柄st_plugin_int,而是handlerton類型,如下:
//創建handlerton結構體指針,并分配空間
hton = (handlerton *)my_malloc(key_memory_handlerton, sizeof(handlerton),
MYF(MY_WME | MY_ZEROFILL));
if (hton == NULL) {
LogErr(ERROR_LEVEL, ER_HANDLERTON_OOM, plugin->name.str);
goto err_no_hton_memory;
}
plugin->data = hton; //將handlerton綁定到該插件句柄的data成員
/*
調用插件描述符結構體中的初始化函數,傳參為handlerton指針,這樣在初始化函數中
即可完成對handlerton中函數指針進行賦值。
*/
if (plugin->plugin->init && plugin->plugin->init(hton)) {
LogErr(ERROR_LEVEL, ER_PLUGIN_INIT_FAILED, plugin->name.str);
goto err;
}
如innodb插件初始化函數中handlerton結構體的賦值過程。
static int innodb_init(void *p) {
DBUG_ENTER("innodb_init");
handlerton *innobase_hton = (handlerton *)p;
innodb_hton_ptr = innobase_hton;
innobase_hton->state = SHOW_OPTION_YES;
innobase_hton->db_type = DB_TYPE_INNODB;
innobase_hton->savepoint_offset = sizeof(trx_named_savept_t);
innobase_hton->close_connection = innobase_close_connection;
...
}
通過上述分析,已經明白了插件初始化過程以及引擎插件handlerton結構體綁定過程。下面的調用過程就相對簡單一些。
2.2.2 調用過程
調用過程相對簡單,handlerton的調用方式從plugin_foreach參數宏開始,這里以innodb flush redolog調用為例,進行調用過程說明。
存儲引擎flush日志調用函數為ha_flush_logs,其核心為
/*
plugin_foreach宏操作:
1、找出指定類型的所有插件(這里為MYSQL_STORAGE_ENGINE_PLUGIN),找到其插件句柄
2、分別調用指定函數(這里為flush_handlerton),函數參數為宏的第一個參數(NULL,通常為thd),和最后一個參數(static_cast(&binlog_group_flush))
*/
if (plugin_foreach(NULL, flush_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN,
static_cast(&binlog_group_flush))) {
return true;
}
調用指定函數flush_handlerton
static bool flush_handlerton(THD *, plugin_ref plugin, void *arg) {
//獲得對于插件的handlerton結構體
handlerton *hton = plugin_data(plugin);
//調用引擎的flush_logs函數
if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
hton->flush_logs(hton, *(static_cast(arg))))
return true;
return false;
}
調用引擎層innobase_flush_logs函數
//由于在innodb初始化過程,handlerton的初始化中flush_logs賦值如下,故最終調用引擎層innobase_flush_logs函數
innobase_hton->flush_logs = innobase_flush_logs;
2.3 線程私有數據訪問方式
對于每個連接線程在引擎層都有私有數據,比如
對于binlog存儲引擎,有事務的binlog緩存;
對于innodb插件,有事務狀態相關信息(如read_view一致性視圖信息),本線程的用戶臨時表等信息。
我們知道每個線程都有一個thd結構,保存著該線程的私有信息,同樣,對于引擎層私有數據,也保存在該結構體中,通過ha_data成員。
//ha_data定義
Prealloced_array ha_data;
//ha_data操作函數
Ha_data *get_ha_data(int slot) { return &ha_data[slot]; }
//從thd結構獲得對應插件的私有數據
void **thd_ha_data(const MYSQL_THD thd, const struct handlerton *hton) {
return &(const_cast(thd))->get_ha_data(hton->slot)->ha_ptr;
}
每類引擎插件都對應一個Ha_data結構體,這樣可以每個線程保存引擎層的私有數據。
struct Ha_data {
void *ha_ptr; //引擎層私有數據結構體指針,生命周期為整個會話連接
void *ha_ptr_backup; //用于XA transaction,保存原來引擎數據
/**
0: Life time: one statement within a transaction. If @@autocommit is
on, also represents the entire transaction.
@sa trans_register_ha()
1: Life time: one transaction within a connection.
If the storage engine does not participate in a transaction,
this should not be used.
@sa trans_register_ha()
*/
Ha_trx_info ha_info[2];
/**
NULL: engine is not bound to this thread
non-NULL: engine is bound to this thread, engine shutdown forbidden
*/
plugin_ref lock;
Ha_data() : ha_ptr(NULL), ha_ptr_backup(NULL), lock(NULL) {}
};
下面我們以innodb引擎的線程私有數據訪問方式來說明。
2.3.1 私有數據的設置和訪問
對于innodb引擎層私有數據保存在innodb_session_t結構體中,而該結構體的創建和綁定到thd結構體函數為thd_to_innodb_session
//獲得thd結構innodb私有數據
innodb_session_t *&thd_to_innodb_session(THD *thd) {
//首先innodb私有數據結構體指針,注意這里是引用
innodb_session_t *&innodb_session =
*(innodb_session_t **)thd_ha_data(thd, innodb_hton_ptr);
//如果不為null,即返回私有數據指針
if (innodb_session != NULL) {
return (innodb_session);
}
/*
如果為null,說明還沒有構建私有數據,
則新創建一個innodb私有數據結構體,并賦值給innodb_session,
這樣就為thd即綁定了一個innodb私有數據結構體,
并返回
*/
innodb_session = UT_NEW_NOKEY(innodb_session_t());
return (innodb_session);
}
所以innodb引擎私有數據可以通過上述函數進行創建和訪問,這樣在引擎層即可通過綁定到thd結構的事務信息明白各會話事務狀態。
而該私有數據結構的釋放通常是在handlerton->close_connection指定函數中,對于innodb則是innobase_close_connection函數中。
UT_DELETE(thd_to_innodb_session(thd));
thd_to_innodb_session(thd) = NULL;
從這一點可以看出,私有數據結構體的生命周期為整個連接過程。
總結
以上是生活随笔為你收集整理的mysql plugin 调用_MySQL插件接口的调用方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL实战面试题_Mysql实战面试
- 下一篇: mysql查询cpu使用率100%_数据