Mysql源代码分析(6): Plugin架构介绍(续)--转载

时间:2024-04-17 10:37:04

上篇文章我们分析了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操作。