浅析MySQL的Table_cache实现( 二 )

class Table_cache {// 缓存TABLE对象 一个Table_cache包含N个不同表名的TABLE对象 HASH m_cache; // The hash of Table_cache_element objects,Table_cache_element::share::table_cache_ke作为hash的key TABLE *m_unused_tables; // 所有unused的TABLE对象};class Table_cache_manager{  Table_cache m_table_cache[MAX_TABLE_CACHES]; // table_cache对象数组}extern Table_cache_manager table_cache_manager; // table_cache全局管理对象HASH table_def_cache // 缓存TABLE_SHARE的hash表table_cache的精简架构图如下:

浅析MySQL的Table_cache实现

文章插图
一个thread怎样获得缓存的TABLE* 对象:
【浅析MySQL的Table_cache实现】1)根据thread_id%table_cache_instances 获得tc对象,假设为tc1
2)根据key找到对应的el对象,假设为el1
3)获得el1中free_tables的TABLE* 对象
如果已经创建了一个TABLE*对象,那怎样快速知道一个TABLE* 是属于哪个el的呢而加入到对应的used_tables链表中呢?
TABLE_SHARE存在一个el*的s数组,数组的大小为table_cache_instances个数,如下图:
浅析MySQL的Table_cache实现

文章插图
假设thread获得的为tc1和el1,那么cache_element[1]存储的为tc1->el1对象.
  •  
el= table->s->cache_element[table_cache_manager.cache_index(thd->id)];
  • open_table和close_table
open_table:
接下来看MySQLServer层在open表的过程中,加载表结构.frm文件,转换TABLE对象的主要步骤.
open_table|->get_table_def_key //库名.表名得到对应的key|->retry_share:|->Table_cache *tc = table_cache_manager::get_cache(thd) |//首先根据thd的m_thread_id%table_cache_instances 获取table_cache_manager.m_table_cache[i] |->table = tc->get_table(thd, key, key_length, &share)||->el_it = tc->m_cache.find(key_str) //一个key对应一个el 一个el对应一个TABLE_SHARE* N个TABLE*||->if(el_it == m_cache.end()) return NULL||->*share = el->share||->if((table = el->free_tables.front()))//从el的free_tables链表获取TABLE*对象|||->el->free_tables.remove(table)|||->el->used_tables.push_front(table) //close_thread时候会将table从el的used_tables移除,加入free_tables||->return table|->get_table_share_with_discover||->get_table_share||->my_hash_search_using_hash_value从table_def_cache中HASH查找TABLE_SHARE||->alloc_table_share 新建share变量赋值share的frm路径信息||->my_hash_insert //share插入hash表table_def_cache中||->open_table_def |      |  |->open_binary_frm //读取.frm文件赋值engine类型、KEY、FIELD信息|      |     |->legacy_db_type= (enum legacy_db_type) (uint) *(head+3);//获得engine类型|      |     |->share->db_plugin= ha_lock_engine //根据engine类型获得plugin对象 初始化在innobase_init中完成|      |->// 若table_def_cache记录超过table_def_size 则从hash删除oldest_unused_share|->share_found:|->if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))|   |->if (share->has_old_version())|      |->release_table_share(share) // 如果TABLE_SHARE没有引用 则从table_def_cache中删除|      |->tdc_wait_for_old_version(lock_wait_timeout)|      |  |->TABLE_SHARE::wait_for_old_version|      |     |->m_flush_tickets.push_front(&ticket)// 增加ticket到SHARE的m_flush_tickets链表中|      |     |->thd->mdl_context->m_wait.timed_wait(thd,wait) // MDL_wait::timed_wait等待其他线程唤醒|      |->goto retry_share // 唤醒之后再次获取TABLE_SHARE|->open_table_from_share||->outparam->file=get_new_handler(share->db_type())|||->file= db_type->create(db_type, share, alloc)


推荐阅读