上篇文章我们分析了Mysql的Plugin接口以及plugin的初始化过程,这里我们继续看plugin怎么被使用的。基本还是通过例子看问题,主要分析myisam如何通过plugin接口被调用的。
myisam是mysql最早的和默认的storage engine,前面我们也看到在plugin初始化的时候是优先初始化myisam,然后才初始化其他的存储引擎。这里我们假定用户要对一个myisam的表做操作,具体看看其中涉及的调用过程。
myisam的初始化
myisam plugin的定义可以在storage/myisam/ha_isam.cc中找到:
mysql_declare_plugin(myisam)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&myisam_storage_engine,
"MyISAM",
"MySQL AB",
"Default engine as of MySQL 3.23 with great performance",
PLUGIN_LICENSE_GPL,
myisam_init, /* Plugin Init */
NULL, /* Plugin Deinit */
0x0100, /* 1.0 */
NULL, /* status variables */
NULL, /* system variables */
NULL /* config options */
}
mysql_declare_plugin_end;
初始化函数是myisam_init。在前面文章中提到,storage engine类型的plugin均是通过ha_initialize_handlerton初始化。myisam_init的输入参数是void *p,实际上是handlerton*。 handlerton在mysql中封装了访问一个存储引擎需要的接口,每个存储引擎在全局空间有一个handlerton对象,保存在对应的内存中 plugin结构的data域中。该结构具体定义可以在sql/handler.h中找到。myisam_init做的事情很简单,设置 handlerton中的各个域,其中最重要的域是create,被指向了一个函数myisam_create_handler,这个函数用来创建handler,用来对于数据库文件进行操作。
打开一个表
数 据库表是数据库中所有操作的基础,我们看看打开一个表需要做些什么。当一个select命令进来的时候,sql_parse.cc中的 execute_sqlcom_select被执行,并被传入parse出来的所有该命令要用的到表。它会调用open_and_lock_tables 来打开指定的表,然后调用open_and_lock_tables_derived,再调用open_tables,再调用 open_table(sql_base.cc)。一大堆调用之后真正开始干实事儿的是open_unireg_entry,名字很奇怪,但是确实就是它 开始打开表了,我们仔细将仔细看这个函数,以及它调用的函数。这个函数很长,其实大部分都是在做错误处理,最重要的就以下几行:
static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, const char *alias, char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags) {
...
share= get_table_share_with_create(thd, table_list, cache_key, cache_key_length, OPEN_VIEW |table_list->i_s_requested_object, &error);
open_table_from_share(thd, share, alias, ...);
...
}
get_table_share_with_create是创建一个table_share结构,包括了同一个类型的表公用的数据结构,open_table_from_share则通过这个公用结构打开对应的要操作的表。
TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, ...) {
share= alloc_table_share(table_list, key, key_length)); //分配内存
my_hash_insert(&table_def_cache, (uchar*) share); // 加入cache,以后可以直接用
open_table_def(thd, share, db_flags); // 代开表的定义,需要读frm文件
}
open_table_def是用来打开存储表定义的文件。mysql中,每个表都有一个.frm文件,存储了表的定义,这个函数就是要打开表对应的frm文件,读入定义信息,填入TABLE_SHARE结构。
int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) {
file= my_open(path, O_RDONLY | O_SHARE, MYF(0))
open_binary_frm(thd, share, head, file);
}
open_binary_frm读入二进制的frm文件信息。这个函数超长,但是我们暂时只是对与plugin相关的部分感兴趣。因为每个表的storage engine信息就是从frm文件中读出来的,我们看相关的代码片段:
open_binary_frm(...) {
...
plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name); // name就是storage engine的名字,比如"myisam"。这里根据名字找到对应的plugin。
share->db_plugin= my_plugin_lock(NULL, &tmp_plugin); // 保存plugin的引用,供以后使用。plugin中的"data"域就是handlerton*,这将是主要的使用plugin的入口。
...
}
好了,TABLE_SHARE设置好了,我们回到open_unireg_entry中,继续看 open_table_from_share。这才是真正打开表的地方。这个函数还是在sql/table.cc中。这个函数还是超长...,万幸的是我 们还是只想关注plugin相关的内容。TABLE中有一个file结构,类型是handler*,我们以前提到过,handler就是一个打开的表的引 用,显然open_table_from_share的责任之一就是要设置这个域。
int open_table_from_share(THD *thd, TABLE_SHARE *share, ... TABLE *outparam, ...) { // outparam是打开后的表信息。
...
outparam->file= get_new_handler(share, &outparam->mem_root, share->db_type())); // 直奔主题,获取一个handler。 share->db_type()返回plugin对应的handlerton,其实就是将plugin->data强制转换成 handlerton.
...
outparam->file->ha_open(outparam, ...); // 调用plugin的handler定义的open函数,做自定义的open操作。
...
}
get_new_handler负责根据TABLE_SHARE的内容构造一个handler对象。这个函数在sql/handler.cc中。
handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc, handlerton *db_type) {
file= db_type->create(db_type, share, alloc); // 调用plugin的create函数,来创建handler成员。
file->init();
}
前面我们提到过对于myisam对应的create函数是myisam_create_handler,这个函数就是new了一个ha_myisam对象,而ha_myisam又是从handler继承下来的,重载了handler对应的函数。
这样一个对于应数据库表文件的handler就可以使用啦,它的第一个使用就是在 open_table_from_share中被调用ha_open。ha_open在handler.cc中定义,其实就是调用了重载后了open函 数。在ha_myisam中,我们可以看到open函数的定义,这里我们就不仔细看了,实现细节和myisam的文件结构相关。
看到这里一个"SELECT * from test"语句如何打开表的部分就基本清楚了,主要过程包括:
- 从frm文件寻找storage engine的名字,并获取对应的storage engine plugin的handlerton
- 调用handlerton的create函数生成handler
- 通过handler重载的open函数打开表文件
挺清楚的。
到了这里,我们就有了表的handler,以后凡是涉及到存储引擎的操作,都通过这个接口调用来做,这样,storage engine plugin就和mysql核心紧密结合到了一起,各司其事,共同完成复杂的sql操作。