本章嘗試回答兩個問題: 一、memcached plugin與MySQL的關係; 二、MySQL系統如何啟動memcached plugin。 1. memcached plugin與MySQL的關係 該圖是從MySQL官方文檔里對memcached的介紹圖片。從圖中可以看出,memcached的結 ...
本章嘗試回答兩個問題:
一、memcached plugin與MySQL的關係;
二、MySQL系統如何啟動memcached plugin。
1. memcached plugin與MySQL的關係
該圖是從MySQL官方文檔里對memcached的介紹圖片。從圖中可以看出,memcached的結構是由三個部份組成:memcached plugin、innodb_memcached和Innodb API。
在源碼中,memcached plugin部份的代碼會被編譯成libmemcached.so;innodb_memcache和InnoDB API會被編譯成innodb_engine.so。
memcached plugin層通過一組回調函數調用libmemcached.so提供的介面實現為App服務。
InnoDB API層同樣也是通過一組回調函數調用InnoDB層的介面實現,功能類似於圖中左側的Handler API。而innodb_memcache通過包裝這組回調函數為memcached plugin提供介面實現。
2. MySQL系統如何啟動memcached plugin
在MySQL中,InnoDB存儲引擎和memcached都是以插件的形式實現。memcached最底層的回調函數是由InnoDB存儲引擎提供,於是在插件初始化的順序上是InnoDB插件先於memcached插件。
InnoDB存儲引擎插件調用plugin_initialize函數初始化時將一組回調函數數組的地址傳給全局指針變數innodb_callback_data。memcached引擎插件調用plugin_initialize函數初始化時將innodb_callback_data作為自己的參數傳入。
插件初始化使用的函數plugin_initialize的函數原型是int plugin_initialize(st_plugin_int *plugin);
參數類型為st_plugin_int的結構體。MySQL使用該結構體描述一個插件。其中有兩個變數,data和plugin。plugin變數存放了插件的init和deinit回調函數,這是插件初始化時真實調用的函數;data作為plugin中的回調函數的參數存在。
int plugin_initialize(st_plugin_int *plugin);
/* A handle of a plugin */
struct st_plugin_int
{
...
st_mysql_plugin *plugin;
void *data; /* plugin type specific, e.g. handlerton */
...
};
struct st_mysql_plugin
{
...
int (*init)(MYSQL_PLUGIN); /* the function to invoke when plugin is loaded */
int (*deinit)(MYSQL_PLUGIN);/* the function to invoke when plugin is unloaded */
...
};
3.1 InnoDB如何將回調函數傳給全局變數innodb_callback_data
InnoDB層提供的回調函數是個數組,數組名為innodb_api_cb。
ib_cb_t innodb_api_cb[] = {
(ib_cb_t) ib_cursor_open_table,
...
};
在plugin_initialize函數里,調用plugin->plugin->init函數(innobase_init函數)。如上圖:
- 將innodb_api_cb數組的地址賦值到在plugin->data->data中。
- 將plugin->data->data賦值到innodb_callback_data。
到此可以看到innodb_callback_data已經指向了一個回調函數數組的地址。
3.2 memcached如何將innodb_callback_data賦值給innodb_engine.so中的回調函數
初始化memcached插件同樣調用的是plugin_initialize函數,將innodb_callback_data賦值給plugin->data之後,調用plugin->plugin->init函數(daemon_memcached_plugin_init函數)。
完成了插件初始化的基本操作之後,開啟一個新的線程daemon_memcached_main,這個線程是memcached插件系統的daemon線程。
void daemon_memcached_main(memcached_context_t* p);
struct memcached_context
{
char* m_engine_library;
char* m_mem_option;
void* m_innodb_api_cb;
unsigned int m_r_batch_size;
unsigned int m_w_batch_size;
bool m_enable_binlog;
};
在開啟線程daemon_memcached_main之前,會將plugin->data(innodb_callback_data)賦值給p->m_innodb_api_cb。
daemon_memcached_main在進入守護狀態之前,還有兩個重要的操作要做,分別是load engine和init engine函數。而這兩個函數分別做了什麼?
- load engine: 連接libmemcached.so的回調函數與innodb_engine.so的實現。
- init engine: 連接innodb_engine.so的innodb_callback_API的回調函數與InnoDB層的實現。
3.2.1 load engine
load_engine(engine, get_server_api,settings.extensions.logger,
&engine_handle);
load_engine的函數原型如上。在這個過程中,會load兩個引擎。一個innodb_engine,一個是default_engine。
過程如下:
- 調用dlopen打開innodb_engine.so。
- 調用dlsym函數獲得create_instance函數的地址。
- 調用create_instance,malloc一個struct innodb_engine的結構體innodb_engine,為innodb_engine回調函數賦值。
- 調用create_instance|create_my_default_instance為預設引擎的回調函數賦值。
load engine之後,lib_memcached.so中的回調函數會被賦值,實現是在innodb_engine.so中。
InnoDB Engine與Default Engine是否啟用?是否同時啟用?
/** Cache options, tells if we will used Memcached default engine or InnoDB
Memcached engine to handle the request */
typedef enum meta_cache_opt {
META_CACHE_OPT_INNODB = 1, /*!< Use InnoDB Memcached Engine only */
META_CACHE_OPT_DEFAULT, /*!< Use Default Memcached Engine only */
META_CACHE_OPT_MIX, /*!< Use both, first use default memcached engine */
META_CACHE_OPT_DISABLE, /*!< This operation is disabled */
META_CACHE_NUM_OPT /*!< Number of options */
} meta_cache_opt_t;
預設的參數值為META_CACHE_OPT_INNODB,僅僅啟用InnoDB Engine,數據通過InnoDB引擎提供的Callback方法操作;若同時啟用了Memcached Default Engine(META_CACHE_OPT_MIX),那麼數據讀取時,首先從Default Engine中讀取;記錄刪除時,也需要先刪除Default Engine中的記錄。
3.3.2 init engine
init engine調用的函數是
innodb_eng->engine.v1->initialize
實際上調用的是實現在在innodb_engine.so的innodb_initialize函數。
在這個innodb_initialize函數中調用register_innodb_cb函數為innodb_memcached_api數組賦值。兩個數組的指針順序要一一對應。
innnodb_memcached_api數組是innodb_engine.so中調用innodb層操作的回調函數數組。
以下這圖是這個過程中innodb的函數指針一路傳遞到memcached的innodb_engine.so中的函數指針的過程。