日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

由于本人水平有限,有錯(cuò)誤的地方還請(qǐng)大家?guī)兔χ刚?

我們知道MySQL是一個(gè)插件式存儲(chǔ)引擎的數(shù)據(jù)庫,不同存儲(chǔ)引擎的對(duì)象的元數(shù)據(jù)的存儲(chǔ)方式是不一樣的.例如:InnoDB的表的元數(shù)據(jù)信息都是存儲(chǔ)在SYS_TABLES和SYS_INDEXES等數(shù)據(jù)字典中,數(shù)據(jù)結(jié)構(gòu)也是dict_table_t 、dict_index_t等結(jié)構(gòu)體,而MyISAM的表結(jié)構(gòu)只有.frm文件存儲(chǔ).那么MySQL Server層怎樣識(shí)別以及使用不同Engine的對(duì)象結(jié)構(gòu)呢?

  • TABLE_SHARE

   MySQL Server層在緩存不同Engine的表對(duì)象過程中使用TABLE_SHARE的結(jié)構(gòu)體,這里是不區(qū)分任何存儲(chǔ)引擎的表結(jié)構(gòu),并且每一個(gè)表名(帶模式名即庫名)都一一對(duì)應(yīng)一個(gè)TABLE_SHARE結(jié)構(gòu)體對(duì)象.

TABLE_SHARE結(jié)構(gòu)體主要成員如下:

struct TABLE_SHARE{ TABLE_CATEGORY table_category; //表的類型 ... Field **field; //表的field字段 KEY *key_info; //表定義的KEY信息(即索引信息) LEX_STRING table_cache_key; //TABLE_SHARE對(duì)象在table_cache中的key LEX_STRING db; //表所在的DB name LEX_STRING table_name; //表名 LEX_STRING path; //.frm文件路徑名  // 指向每個(gè)table_cache包含該表的el地址 數(shù)組大小等于table_cache的數(shù)組大小  Table_cache_element **cache_element;  ...ulong   version;             //TABLE_SHARE的版本 如果版本變了 必須重新reopenulong mysql_version; /* 0 if .frm is created before 5.0 */ulong reclength; //記錄長(zhǎng)度ulong stored_rec_length; //存儲(chǔ)的記錄長(zhǎng)度uint ref_count;              // TABLE 對(duì)象在使用的個(gè)數(shù) 即存在多少個(gè)TABLE對(duì)象 plugin_ref db_plugin; //存儲(chǔ)引擎對(duì)象指針 ...}

當(dāng)MySQL Server層在open table時(shí),需要從frm文件(不區(qū)分存儲(chǔ)引擎)中將這個(gè)表的表名、庫名、所有的列信息、列的默認(rèn)值、表的字符集、對(duì)應(yīng)的.frm文件路徑、所屬的Engine、索引等信息存儲(chǔ)到TABLE_SHARE結(jié)構(gòu)體對(duì)象中,然后TABLE_SHARE對(duì)象在table_def_cache中緩存.(open_table_def中完成從frm到TABLE_SHARE寫入)

  • TABLE

當(dāng)我們獲得TABLE_SHARE的對(duì)象之后,該如何使用TABLE_SHARE對(duì)象呢?同一時(shí)刻可能存在多個(gè)不同的session同時(shí)訪問同一個(gè)表進(jìn)行不同的操作,那么怎樣保證每個(gè)不同的session的對(duì)象都是獨(dú)立的互不影響的呢?

每個(gè)連接到MySQL Server層的thread在獲得TABLE_SHARE對(duì)象之后(所有的線程可以共用一個(gè)TABLE_SHARE對(duì)象),都會(huì)創(chuàng)建一個(gè)TABLE結(jié)構(gòu)體的對(duì)象,這個(gè)對(duì)象是該thread在使用期間獨(dú)占的.(open_table_from_share )

TABLE結(jié)構(gòu)體精簡(jiǎn)如下:

struct TABLE{  TABLE_SHARE   *s; //TABLE_SHARE對(duì)象指針  handler   *file; //存儲(chǔ)引擎句柄 對(duì)存儲(chǔ)引擎的操作通過該對(duì)象指針操作  TABLE *next, *prev; //TABLE對(duì)象前后節(jié)點(diǎn)指針private:  TABLE *cache_next, **cache_prev;//用于table_cache中的list鏈表節(jié)點(diǎn)指針friend class Table_cache_element; //用于訪問cache_next和cache_prev兩個(gè)成員public:  THD   *in_use;            //thread 對(duì)象指針  Field **field;            //表的列的存儲(chǔ)對(duì)象 同TABLE_SHARE中的列  uchar *record[2];         //記錄數(shù)據(jù)的存儲(chǔ)地址  uchar *write_row_record;     //THE::write_row中優(yōu)化使用  uchar *insert_values;        //用于INSERT ... UPDATE  ....  KEY  *key_info;           //表定義的KEY信息 同TABLE_SHARE  ....};
  • Table_cache管理

      MySQL Server層對(duì)TABLE對(duì)象的管理主要通過table_cache_manager完成,主要結(jié)構(gòu)如下:

class Table_cache_element{// 緩存一個(gè)表名的所有TABLE對(duì)象 一個(gè)element對(duì)象只緩存一個(gè)表名的所有TABLEprivate:  typedef I_P_List <TABLE,                    I_P_List_adapter<TABLE,                                     &TABLE::cache_next,                                     &TABLE::cache_prev> > TABLE_list;  TABLE_list used_tables; // 正在使用的TABLE對(duì)象  TABLE_list free_tables; // 可以直接使用的TABLE 對(duì)象  TABLE_SHARE *share; // TABLE_SHARE 緩存對(duì)象  }  class Table_cache {// 緩存TABLE對(duì)象 一個(gè)Table_cache包含N個(gè)不同表名的TABLE對(duì)象 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對(duì)象};
class Table_cache_manager{  Table_cache m_table_cache[MAX_TABLE_CACHES]; // table_cache對(duì)象數(shù)組}extern Table_cache_manager table_cache_manager; // table_cache全局管理對(duì)象
HASH table_def_cache // 緩存TABLE_SHARE的hash表

table_cache的精簡(jiǎn)架構(gòu)圖如下:

一個(gè)thread怎樣獲得緩存的TABLE* 對(duì)象:

1)根據(jù)thread_id%table_cache_instances 獲得tc對(duì)象,假設(shè)為tc1

2)根據(jù)key找到對(duì)應(yīng)的el對(duì)象,假設(shè)為el1

3)獲得el1中free_tables的TABLE* 對(duì)象

如果已經(jīng)創(chuàng)建了一個(gè)TABLE*對(duì)象,那怎樣快速知道一個(gè)TABLE* 是屬于哪個(gè)el的呢而加入到對(duì)應(yīng)的used_tables鏈表中呢?

TABLE_SHARE存在一個(gè)el*的s數(shù)組,數(shù)組的大小為table_cache_instances個(gè)數(shù),如下圖:

假設(shè)thread獲得的為tc1和el1,那么cache_element[1]存儲(chǔ)的為tc1->el1對(duì)象.

  •  
el= table->s->cache_element[table_cache_manager.cache_index(thd->id)];
  • open_table和close_table

open_table:

接下來看MySQLServer層在open表的過程中,加載表結(jié)構(gòu).frm文件,轉(zhuǎn)換TABLE對(duì)象的主要步驟.

open_table|->get_table_def_key //庫名.表名  得到對(duì)應(yīng)的key|->retry_share:|->Table_cache *tc = table_cache_manager::get_cache(thd) |     //首先根據(jù)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) //一個(gè)key對(duì)應(yīng)一個(gè)el 一個(gè)el對(duì)應(yīng)一個(gè)TABLE_SHARE* N個(gè)TABLE*|  |->if(el_it == m_cache.end()) return NULL|  |->*share = el->share|  |->if((table = el->free_tables.front()))//從el的free_tables鏈表獲取TABLE*對(duì)象|  |   |->el->free_tables.remove(table)|  |   |->el->used_tables.push_front(table) //close_thread時(shí)候會(huì)將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 //根據(jù)engine類型獲得plugin對(duì)象 初始化在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) |   |      |-> file->table_share=share file->ht=db_type //innobase_create_handler創(chuàng)建handler對(duì)象|   |  |->file->init() //handler::init()|   |->//copy share中的key、column信息賦值到TABLE中|   |->outparam->file->ha_open(TABLE,table_name,mode)|   |   |-> file->table=outparam //handler::ha_open|           |->ha_innobase::open(table_name)|              |->ib_table=open_dict_table(table_name,...,)//根據(jù)name加載innodb的系統(tǒng)表中的表的元數(shù)據(jù)|              |->m_prebuilt=row_create_prebuilt(ib_table,TABLE->S->reclength)//創(chuàng)建prebuit對(duì)象在查詢數(shù)據(jù)時(shí)的元組結(jié)構(gòu)|->Table_cache::add_used_table //將TABLE對(duì)象加入Table_cache中|  |->el=table->s->cache_element[this->idx] // 獲取該表名在該table_cache中的el|  |->if(!el) // 如果不存在|  |  |->el= new Table_cache_element(table->s) // 創(chuàng)建el對(duì)象|  |  |->my_hash_insert(&m_cache, (uchar*)el) // 加入當(dāng)前table_cache的el的hash鏈表|  |  |->table->s->cache_element[this->idx]=el // 存到TABLE_SHARE對(duì)應(yīng)的el位置 其他線程訪問當(dāng)前table_cache使用|  |->el->used_tables.push_front(table) // TABLE對(duì)象加入el的used鏈表|  |->m_table_count++ |->table_found:|->thd->set_open_tables(table)|->table->init(thd, table_list)

流程解釋:

    1)首先根據(jù)thd的thread_id找到對(duì)應(yīng)的tc,然后根據(jù)key定位找到tc中緩存的el,如果el的share和TABLE都存在,則直接使用TABLE對(duì)象.

 

    2)如果el的share存在,無緩存的TABLE對(duì)象,則調(diào)用open_table_from_share,根據(jù)share構(gòu)造TABLE對(duì)象,并加入used_tables鏈表.

 

   3)如果el的share不存在,則先調(diào)用get_table_share_with_discover根據(jù)frm文件構(gòu)造share對(duì)象,然后再構(gòu)造TABLE對(duì)象. 

 

close_table:

我們看下當(dāng)一個(gè)thd一條語句執(zhí)行完畢之后,對(duì)于打開的TABLE對(duì)象如何處理

mysql_execute_command|->close_thread_tables|  |->while (thd->open_tables)|     |->close_open_tables(thd, &thd->open_tables)|        |->close_thread_table|           |->if(!table->needs_reopen())ha_innobase::reset()//reset某些成員變量|           |->tc->lock();|           |->if(table->s->has_old_version()||table->needs_reopen())|           |  |->tc->remove_table(table) //TABLE* 從tc緩存中刪除|           |  |->intern_close_table(table) //close TABLE->file 釋放file對(duì)應(yīng)的內(nèi)存對(duì)象同時(shí)free_table_share釋放share內(nèi)存對(duì)象|           |->else|           |  |->tc->release_table(thd, table)//不close TABLE->file|           |     |->table->in_use = nullptr  |           |     |->//將TABLE* 指針對(duì)象從el->used_tables移除加入el->free_tables鏈表同時(shí)加入tc->m_unused_tables|           |->tc->unlock()|->MDL_context::release_transactional_locks //釋放所有的MDL鎖
intern_close_table(TABLE *table)|->closefrm(table, free_share=1)   |->table->file->ha_close()   |->release_table_share(table->s)      |->if(!--share->ref_count)//使用share open TABLE才會(huì)+1         |->if(share->has_old_version())            |->my_hash_delete(&table_def_cache, (uchar*) share)               |->table_def_free_entry(share)                  |->free_table_share(share)                     |->while(ticket=share->m_flush_tickets)                        |->ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED) // 喚醒其他等待old_version的thread

注:

 TABLE_SHARE::m_version在alloc_table_share初始化為refresh_version 在DDL過程中會(huì)調(diào)用TABLE_SHARE::clear_version()將m_version重置為0

從上面的過程我們可以知道:

   TABLE對(duì)象已經(jīng)不僅僅是MySQL Server層的對(duì)象了,它是可以具體操作某一個(gè)存儲(chǔ)引擎的對(duì)象,所以TABLE對(duì)象作為MySQL層和存儲(chǔ)引擎之間的橋梁,可以直接使用TABLE對(duì)象的存儲(chǔ)引擎的句柄執(zhí)行各個(gè)公共C++ API來操作具體執(zhí)行動(dòng)作.

總結(jié):

   通過對(duì)源碼的淺析,我們已經(jīng)基本了解了MySQL Server層怎樣通過一步步獲得不同存儲(chǔ)引擎表結(jié)構(gòu)的信息,希望對(duì)大家理解MySQL Server層對(duì)象的緩存有一定的幫助.

分享到:
標(biāo)簽:MySQL
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定