日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

MySQL 深潜 - 一文详解 MySQL Data Dictionary

發(fā)布時(shí)間:2024/8/23 数据库 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL 深潜 - 一文详解 MySQL Data Dictionary 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

簡(jiǎn)介:?在 MySQL 8.0 之前,Server 層和存儲(chǔ)引擎(比如 InnoDB)會(huì)各自保留一份元數(shù)據(jù)(schema name, table definition 等),不僅在信息存儲(chǔ)上有著重復(fù)冗余,而且可能存在兩者之間存儲(chǔ)的元數(shù)據(jù)不同步的現(xiàn)象。不同存儲(chǔ)引擎之間(比如 InnoDB 和 MyISAM)有著不同的元數(shù)據(jù)存儲(chǔ)形式和位置(.FRM, .PAR, .OPT, .TRN and .TRG files),造成了元數(shù)據(jù)無(wú)法統(tǒng)一管理。此外,將元數(shù)據(jù)存放在不支持事務(wù)的表和文件中,使得 DDL 變更不會(huì)是原子的,crash recovery 也會(huì)成為一個(gè)問(wèn)題。

作者 | 泊歌
來(lái)源 | 阿里技術(shù)公眾號(hào)

一 背景

在 MySQL 8.0 之前,Server 層和存儲(chǔ)引擎(比如 InnoDB)會(huì)各自保留一份元數(shù)據(jù)(schema name, table definition 等),不僅在信息存儲(chǔ)上有著重復(fù)冗余,而且可能存在兩者之間存儲(chǔ)的元數(shù)據(jù)不同步的現(xiàn)象。不同存儲(chǔ)引擎之間(比如 InnoDB 和 MyISAM)有著不同的元數(shù)據(jù)存儲(chǔ)形式和位置(.FRM, .PAR, .OPT, .TRN and .TRG files),造成了元數(shù)據(jù)無(wú)法統(tǒng)一管理。此外,將元數(shù)據(jù)存放在不支持事務(wù)的表和文件中,使得 DDL 變更不會(huì)是原子的,crash recovery 也會(huì)成為一個(gè)問(wèn)題。

為了解決上述問(wèn)題,MySQL 在 8.0 中引入了 data dictionary 來(lái)進(jìn)行 Server 層和不同引擎間統(tǒng)一的元數(shù)據(jù)管理,這些元數(shù)據(jù)都存儲(chǔ)在 InnoDB 引擎的表中,自然的支持原子性,且 Server 層和引擎層共享一份元數(shù)據(jù),不再存在不同步的問(wèn)題。

二 整體架構(gòu)

data dictionary 提供了統(tǒng)一的 client API 供 Server 層和引擎層使用,包含對(duì)元數(shù)據(jù)訪問(wèn)的 acquire() / drop() / store() / update() 基本操作。底層實(shí)現(xiàn)了對(duì) InnoDB 引擎存放的數(shù)據(jù)字典表的讀寫操作,包含開(kāi)表(open table)、構(gòu)造主鍵、主鍵查找等過(guò)程。client 和底層存儲(chǔ)之間通過(guò)兩級(jí)緩存來(lái)加速對(duì)元數(shù)據(jù)對(duì)象的內(nèi)存訪問(wèn),兩級(jí)緩存都是基于 hash map 實(shí)現(xiàn)的,一層緩存是 local 的,由每個(gè) client(每個(gè)線程對(duì)應(yīng)一個(gè) client)獨(dú)享;二級(jí)緩存是 share 的,為所有線程共享的全局緩存。下面我將對(duì) data dictionary 的數(shù)據(jù)結(jié)構(gòu)和實(shí)現(xiàn)架構(gòu)做重點(diǎn)介紹,也會(huì)分享一個(gè)支持原子的 DDL 在 data dictionary 層面的實(shí)現(xiàn)過(guò)程。

三 metadata 在內(nèi)存和引擎層面的表示

data dictionary (簡(jiǎn)稱DD)中的數(shù)據(jù)結(jié)構(gòu)是完全按照多態(tài)、接口/實(shí)現(xiàn)的形式來(lái)組織的,接口通過(guò)純虛類來(lái)實(shí)現(xiàn)(比如表示一個(gè)表的 Table),其實(shí)現(xiàn)類(Table_impl)為接口類的名字加 _impl 后綴。下面以 Table_impl 為例介紹一個(gè)表的元數(shù)據(jù)對(duì)象在 DD cache 中的表示。

1 Table_impl

Table_impl 類中包含一個(gè)表相關(guān)的元數(shù)據(jù)屬性定義,比如下列最基本引擎類型、comment、分區(qū)類型、分區(qū)表達(dá)式等。

class Table_impl : public Abstract_table_impl, virtual public Table {// Fields.Object_id m_se_private_id;String_type m_engine;String_type m_comment;// - Partitioning related fields.enum_partition_type m_partition_type;String_type m_partition_expression;String_type m_partition_expression_utf8;enum_default_partitioning m_default_partitioning;// References to tightly-coupled objects.Index_collection m_indexes;Foreign_key_collection m_foreign_keys;Foreign_key_parent_collection m_foreign_key_parents;Partition_collection m_partitions;Partition_leaf_vector m_leaf_partitions;Trigger_collection m_triggers;Check_constraint_collection m_check_constraints; };

Table_impl 也是代碼實(shí)現(xiàn)中 client 最常訪問(wèn)的內(nèi)存結(jié)構(gòu),開(kāi)發(fā)者想要增加新的屬性,直接在這個(gè)類中添加和初始化即可,但是僅僅如此不會(huì)自動(dòng)將該屬性持久化到存儲(chǔ)引擎中。除了上述簡(jiǎn)單屬性之外,還包括與一個(gè)表相關(guān)的復(fù)雜屬性,比如列信息、索引信息、分區(qū)信息等,這些復(fù)雜屬性都是存在其他的 DD 表中,在內(nèi)存 cache 中也都會(huì)集成到 Table_impl 對(duì)象里。

從 Abstract_table_impl 繼承來(lái)的 Collection m_columns 就表示表的所有列集合,集合中的每一個(gè)對(duì)象 Column_impl 表示該列的元信息,包括數(shù)值類型、是否為 NULL、是否自增、默認(rèn)值等。同時(shí)也包含指向 Abstract_table_impl 的指針,將該列與其對(duì)應(yīng)的表聯(lián)系起來(lái)。

class Column_impl : public Entity_object_impl, public Column {// Fields.enum_column_types m_type;bool m_is_nullable;bool m_is_zerofill;bool m_is_unsigned;bool m_is_auto_increment;bool m_is_virtual;bool m_default_value_null;String_type m_default_value;// References to tightly-coupled objects.Abstract_table_impl *m_table; };

此外 Table_impl 中也包含所有分區(qū)的元信息集合 Collection m_partitions,存放每個(gè)分區(qū)的 id、引擎、選項(xiàng)、范圍值、父子分區(qū)等。

class Partition_impl : public Entity_object_impl, public Partition {// Fields.Object_id m_parent_partition_id;uint m_number;Object_id m_se_private_id;String_type m_description_utf8;String_type m_engine;String_type m_comment;Properties_impl m_options;Properties_impl m_se_private_data;// References to tightly-coupled objects.Table_impl *m_table;const Partition *m_parent;Partition_values m_values;Partition_indexes m_indexes;Table::Partition_collection m_sub_partitions; };

因此獲取到一個(gè)表的 Table_impl,我們就可以獲取到與這個(gè)表相關(guān)聯(lián)的所有元信息。

2 Table_impl 是如何持久化存儲(chǔ)和訪問(wèn)的

DD cache 中的元信息都是在 DD tables 中讀取和存儲(chǔ)的,每個(gè)表存放一類元信息的基本屬性字段,比如 tables、columns、indexes等,他們之間通過(guò)主外鍵關(guān)聯(lián)連接起來(lái),組成 Table_impl 的全部元信息。DD tables 存放在 mysql 的表空間中,在 release 版本對(duì)用戶隱藏,只能通過(guò) INFORMATION SCHEMA 的部分視圖查看;在 debug 版本可通過(guò)設(shè)置 SET debug='+d,skip_dd_table_access_check' 直接訪問(wèn)查看。比如:

root@localhost:test 8.0.18-debug> SHOW CREATE TABLE mysql.tables\G *************************< strong> 1. row < /strong>*************************Table: tables Create Table: CREATE TABLE `tables` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`schema_id` bigint(20) unsigned NOT NULL,`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,`type` enum('BASE TABLE','VIEW','SYSTEM VIEW') COLLATE utf8_bin NOT NULL,`engine` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`mysql_version_id` int(10) unsigned NOT NULL,`row_format` enum('Fixed','Dynamic','Compressed','Redundant','Compact','Paged') COLLATE utf8_bin DEFAULT NULL,`collation_id` bigint(20) unsigned DEFAULT NULL,`comment` varchar(2048) COLLATE utf8_bin NOT NULL,`hidden` enum('Visible','System','SE','DDL') COLLATE utf8_bin NOT NULL,`options` mediumtext COLLATE utf8_bin,`se_private_data` mediumtext COLLATE utf8_bin,`se_private_id` bigint(20) unsigned DEFAULT NULL,`tablespace_id` bigint(20) unsigned DEFAULT NULL,`partition_type` enum('HASH','KEY_51','KEY_55','LINEAR_HASH','LINEAR_KEY_51','LINEAR_KEY_55','RANGE','LIST','RANGE_COLUMNS','LIST_COLUMNS','AUTO','AUTO_LINEAR') COLLATE utf8_bin DEFAULT NULL,`partition_expression` varchar(2048) COLLATE utf8_bin DEFAULT NULL,`partition_expression_utf8` varchar(2048) COLLATE utf8_bin DEFAULT NULL,`default_partitioning` enum('NO','YES','NUMBER') COLLATE utf8_bin DEFAULT NULL,`subpartition_type` enum('HASH','KEY_51','KEY_55','LINEAR_HASH','LINEAR_KEY_51','LINEAR_KEY_55') COLLATE utf8_bin DEFAULT NULL,`subpartition_expression` varchar(2048) COLLATE utf8_bin DEFAULT NULL,`subpartition_expression_utf8` varchar(2048) COLLATE utf8_bin DEFAULT NULL,`default_subpartitioning` enum('NO','YES','NUMBER') COLLATE utf8_bin DEFAULT NULL,`created` timestamp NOT NULL,`last_altered` timestamp NOT NULL,`view_definition` longblob,`view_definition_utf8` longtext COLLATE utf8_bin,`view_check_option` enum('NONE','LOCAL','CASCADED') COLLATE utf8_bin DEFAULT NULL,`view_is_updatable` enum('NO','YES') COLLATE utf8_bin DEFAULT NULL,`view_algorithm` enum('UNDEFINED','TEMPTABLE','MERGE') COLLATE utf8_bin DEFAULT NULL,`view_security_type` enum('DEFAULT','INVOKER','DEFINER') COLLATE utf8_bin DEFAULT NULL,`view_definer` varchar(288) COLLATE utf8_bin DEFAULT NULL,`view_client_collation_id` bigint(20) unsigned DEFAULT NULL,`view_connection_collation_id` bigint(20) unsigned DEFAULT NULL,`view_column_names` longtext COLLATE utf8_bin,`last_checked_for_upgrade_version_id` int(10) unsigned NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `schema_id` (`schema_id`,`name`),UNIQUE KEY `engine` (`engine`,`se_private_id`),KEY `engine_2` (`engine`),KEY `collation_id` (`collation_id`),KEY `tablespace_id` (`tablespace_id`),KEY `type` (`type`),KEY `view_client_collation_id` (`view_client_collation_id`),KEY `view_connection_collation_id` (`view_connection_collation_id`),CONSTRAINT `tables_ibfk_1` FOREIGN KEY (`schema_id`) REFERENCES `schemata` (`id`),CONSTRAINT `tables_ibfk_2` FOREIGN KEY (`collation_id`) REFERENCES `collations` (`id`),CONSTRAINT `tables_ibfk_3` FOREIGN KEY (`tablespace_id`) REFERENCES `tablespaces` (`id`),CONSTRAINT `tables_ibfk_4` FOREIGN KEY (`view_client_collation_id`) REFERENCES `collations` (`id`),CONSTRAINT `tables_ibfk_5` FOREIGN KEY (`view_connection_collation_id`) REFERENCES `collations` (`id`) ) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB AUTO_INCREMENT=549 DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC 1 row in set (0.00 sec)

通過(guò)以上 mysql.tables 的表定義可以獲得存儲(chǔ)引擎中實(shí)際存儲(chǔ)的元信息字段。DD tables 包括 tables、schemata、columns、column_type_elements、indexes、index_column_usage、foreign_keys、foreign_key_column_usage、table_partitions、table_partition_values、index_partitions、triggers、check_constraints、view_table_usage、view_routine_usage 等。

Storage_adapter 是訪問(wèn)持久存儲(chǔ)引擎的處理類,包括 get() / drop() / store() 等接口。當(dāng)初次獲取一個(gè)表的元信息時(shí),會(huì)調(diào)用 Storage_adapter::get() 接口,處理過(guò)程如下:

Storage_adapter::get()// 根據(jù)訪問(wèn)對(duì)象類型,將依賴的 DD tables 加入到 open table list 中|--Open_dictionary_tables_ctx::register_tables< T>() |--Table_impl::register_tables()|--Open_dictionary_tables_ctx::open_tables() // 調(diào)用 Server 層接口打開(kāi)所有表|--Raw_table::find_record() // 直接調(diào)用 handler 接口根據(jù)傳入的 key(比如表名)查找記錄|--handler::ha_index_read_idx_map() // index read// 從讀取到的 record 中解析出對(duì)應(yīng)屬性,調(diào)用 field[field_no]->val_xx() 函數(shù)|--Table_impl::restore_attributes()// 通過(guò)調(diào)用 restore_children() 函數(shù)從與該對(duì)象關(guān)聯(lián)的其他 DD 表中根據(jù)主外鍵讀取完整的元數(shù)據(jù)定義|--Table_impl::restore_children() |--返回完整的 DD cache 對(duì)象

上述在獲取列和屬性的對(duì)應(yīng)關(guān)系時(shí),根據(jù)的是 Tables 對(duì)象的枚舉類型下標(biāo),按順序包含了該類型 DD 表中的所有列,與上述表定義是一一對(duì)應(yīng)的。因此如果我們需要新增 DD 表中存儲(chǔ)的列時(shí),也需要往下面枚舉類型定義中加入對(duì)應(yīng)的列,并且在 Table_impl::restore_attributes() / Table_impl::store_attributes() 函數(shù)中添加對(duì)新增列的讀取和存儲(chǔ)操作。

class Tables : public Entity_object_table_impl {enum enum_fields {FIELD_ID,FIELD_SCHEMA_ID,FIELD_NAME,FIELD_TYPE,FIELD_ENGINE,FIELD_MYSQL_VERSION_ID,FIELD_ROW_FORMAT,FIELD_COLLATION_ID,FIELD_COMMENT,FIELD_HIDDEN,FIELD_OPTIONS,FIELD_SE_PRIVATE_DATA,FIELD_SE_PRIVATE_ID,FIELD_TABLESPACE_ID,FIELD_PARTITION_TYPE,FIELD_PARTITION_EXPRESSION,FIELD_PARTITION_EXPRESSION_UTF8,FIELD_DEFAULT_PARTITIONING,FIELD_SUBPARTITION_TYPE,FIELD_SUBPARTITION_EXPRESSION,FIELD_SUBPARTITION_EXPRESSION_UTF8,FIELD_DEFAULT_SUBPARTITIONING,FIELD_CREATED,FIELD_LAST_ALTERED,FIELD_VIEW_DEFINITION,FIELD_VIEW_DEFINITION_UTF8,FIELD_VIEW_CHECK_OPTION,FIELD_VIEW_IS_UPDATABLE,FIELD_VIEW_ALGORITHM,FIELD_VIEW_SECURITY_TYPE,FIELD_VIEW_DEFINER,FIELD_VIEW_CLIENT_COLLATION_ID,FIELD_VIEW_CONNECTION_COLLATION_ID,FIELD_VIEW_COLUMN_NAMES,FIELD_LAST_CHECKED_FOR_UPGRADE_VERSION_ID,NUMBER_OF_FIELDS // Always keep this entry at the end of the enum}; };

四 多級(jí)緩存

為了避免每次對(duì)元數(shù)據(jù)對(duì)象的訪問(wèn)都需要去持久存儲(chǔ)中讀取多個(gè)表的數(shù)據(jù),使生成的元數(shù)據(jù)內(nèi)存對(duì)象能夠復(fù)用,data dictionary 實(shí)現(xiàn)了兩級(jí)緩存的架構(gòu),第一級(jí)是 client local 獨(dú)享的,核心數(shù)據(jù)結(jié)構(gòu)為 Local_multi_map,用于加速在當(dāng)前線程中對(duì)于相同對(duì)象的重復(fù)訪問(wèn),同時(shí)在當(dāng)前線程涉及對(duì) DD 對(duì)象的修改(DDL)時(shí)管理 committed、uncommitted、dropped 幾種狀態(tài)的對(duì)象。第二級(jí)就是比較常見(jiàn)的多線程共享的緩存,核心數(shù)據(jù)結(jié)構(gòu)為 Shared_multi_map,包含著所有線程都可以訪問(wèn)到其中的對(duì)象,所以會(huì)做并發(fā)控制的處理。

兩級(jí)緩存的底層實(shí)現(xiàn)很統(tǒng)一,都是基于 hash map 的,目前的實(shí)現(xiàn)是 std::map。Local_multi_map 和 Shared_multi_map都是派生于 Multi_map_base。

之所以叫 Multi_map_base,是因?yàn)槠渲邪硕鄠€(gè) hash map,適合用戶根據(jù)不同類型的 key 來(lái)獲取緩存對(duì)象,比如 id、name、DD cache 本身等。Element_map 就是對(duì) std::map 的一個(gè)封裝,key 為前述幾種類型之一,value 為 DD cache 對(duì)象指針的一個(gè)封裝 Cache_element,封裝了對(duì)象本身和引用計(jì)數(shù)。

Multi_map_base 對(duì)象實(shí)現(xiàn)了豐富的 m_map() 模板函數(shù),可以很方便的根據(jù) key 的類型不同選擇到對(duì)應(yīng)的 hash map。

Shared_multi_map 與 Local_multi_map 的不同在于,Shared_multi_map 還引入了一組 latch 與 condition variable 用于并發(fā)訪問(wèn)中的線程同步與 cache miss 的處理。同時(shí)對(duì) Cache_element 對(duì)象做了內(nèi)存管理和復(fù)用的相關(guān)能力。

1 局部緩存

一級(jí)緩存位于每個(gè) Dictionary_client (每個(gè) client 與線程 THD 一一對(duì)應(yīng))內(nèi)部,由不同狀態(tài)(committed、uncommitted、dropped)的 Object_registry 組成。每個(gè) Object_registry 由不同元數(shù)據(jù)類型的 Local_multi_map 組成,用于管理不同類型的對(duì)象(比如表、schema、字符集、統(tǒng)計(jì)數(shù)據(jù)、Event 等)緩存。

其中 committed 狀態(tài)的 registry 就是我們?cè)L問(wèn)數(shù)據(jù)庫(kù)中已經(jīng)存在的對(duì)象時(shí),將其 DD cache object 存放在局部緩存中的位置。uncommitted 和 dropped 狀態(tài)的存在,主要用于當(dāng)前連接執(zhí)行的是一條 DDL 語(yǔ)句,在執(zhí)行過(guò)程中會(huì)將要 drop 的舊表對(duì)應(yīng)的 DD object 存放在 dropped 的 registry 中,將還未提交的新表定義對(duì)應(yīng)的 DD object 存放在 uncommitted 的 registry 中,用于執(zhí)行狀態(tài)的區(qū)分。

2 共享緩存

共享緩存是 Server 全局唯一的,使用單例 Shared_dictionary_cache 來(lái)實(shí)現(xiàn)。與上述局部緩存中 Object_registry 相似,Shared_dictionary_cache 也需要包含針對(duì)各種類型對(duì)象的緩存。與 Multi_map_base 實(shí)現(xiàn)根據(jù) key 類型自動(dòng)選取對(duì)應(yīng) hash map 的模版函數(shù)相似,Object_registry 和 Shared_dictionary_cache 也都實(shí)現(xiàn)了根據(jù)訪問(wèn)對(duì)象的類型選擇對(duì)應(yīng)緩存的 m_map() 函數(shù),能夠很大程度上簡(jiǎn)化函數(shù)調(diào)用。

與局部緩存可以無(wú)鎖訪問(wèn) hash map 不同,共享緩存在獲取 / 釋放 DD cache object 時(shí)都需要加鎖來(lái)完成引用計(jì)數(shù)的調(diào)整和防止訪問(wèn)過(guò)程中被 destroy 掉。

3 緩存獲取過(guò)程

用戶通過(guò) client 調(diào)用元數(shù)據(jù)對(duì)象獲取函數(shù),傳入元數(shù)據(jù)的 name 字符串,然后構(gòu)建出對(duì)應(yīng)的 name key,通過(guò) key 去緩存中獲取元數(shù)據(jù)對(duì)象。獲取的整體過(guò)程就是一級(jí)局部緩存 -> 二級(jí)共享緩存 -> 存儲(chǔ)引擎。

// Get a dictionary object. template < typename K, typename T> bool Dictionary_client::acquire(const K &key, const T **object,bool *local_committed,bool *local_uncommitted) {// Lookup in registry of uncommitted objectsT *uncommitted_object = nullptr;bool dropped = false;acquire_uncommitted(key, &uncommitted_object, &dropped);...// Lookup in the registry of committed objects.Cache_element< T> *element = NULL;m_registry_committed.get(key, &element);...// Get the object from the shared cache.if (Shared_dictionary_cache::instance()->get(m_thd, key, &element)) {DBUG_ASSERT(m_thd->is_system_thread() || m_thd->killed ||m_thd->is_error());return true;} }

在一級(jí)局部緩存中獲取時(shí),會(huì)優(yōu)先去 uncommitted 和 dropped 的 registry 獲取,因?yàn)檫@兩者是最新的修改,同時(shí)判斷獲取對(duì)象是否已經(jīng)被 dropped。之后再會(huì)去 committed 的 registry 獲取,如果獲取到就直接返回,反之則去二級(jí)共享緩存中嘗試獲取。

Cache miss

共享緩存的獲取過(guò)程在 Shared_multi_map::get() 中實(shí)現(xiàn)。就是加鎖后直接的 hash map 查找,如果存在則給引用計(jì)數(shù)遞增后返回;如果不存在,就會(huì)進(jìn)入到 cache miss 的處理過(guò)程,調(diào)用上面介紹的存儲(chǔ)引擎的接口 Storage_adapter::get() 從 DD tables 中讀取,創(chuàng)建出來(lái)后依次加入共享緩存和局部緩存 committed registry 中。

由于開(kāi)表訪問(wèn) DD tables,構(gòu)建 DD cache object 的過(guò)程相對(duì)耗時(shí),不會(huì)一直給 Shared_multi_map 加鎖,因此需要對(duì)并發(fā)訪問(wèn)的 client 做并發(fā)控制。DD 的實(shí)現(xiàn)方法是第一個(gè)訪問(wèn)的 client 會(huì)將 cache miss 的 key 加入到 Shared_multi_map的 m_missed 集合中,這個(gè)集合包含著現(xiàn)在所有正在讀取元數(shù)據(jù)的對(duì)象 key 值。之后訪問(wèn)的 client 看到目標(biāo) key 值在 m_missed 集合中就會(huì)進(jìn)入等待。

當(dāng)?shù)谝粋€(gè) client 獲取到完整的 DD cache object,加入到共享緩存之后,移除 m_missed 集合中對(duì)應(yīng)的 key,并通過(guò)廣播的方式通知之前等待的線程重新在共享緩存中獲取。

五 Auto_releaser

Auto_releaser 是一個(gè) RAII 類,基本上在使用 client 訪問(wèn) DD cache 前都會(huì)做一個(gè)封裝,保證在整個(gè) Auto_releaser 對(duì)象存在的作用域內(nèi),所獲取到的 DD cache 對(duì)象都會(huì)在局部緩存中存在不釋放。Auto_releaser 包含需要 release 的對(duì)象 registry,通過(guò) auto_release() 函數(shù)收集著當(dāng)前 client 從共享緩存中獲取到的 DD cache 對(duì)象,在超出其作用域進(jìn)行析構(gòu)時(shí)自動(dòng) release 對(duì)象,從局部緩存 committed 的 registry 中移除對(duì)象,并且在共享緩存中的引用計(jì)數(shù)遞減。

在嵌套函數(shù)調(diào)用過(guò)程中,可能在每一層都會(huì)有自己的 Auto_releaser,他們之間通過(guò)一個(gè)簡(jiǎn)單的鏈表指針連接起來(lái)。在函數(shù)返回時(shí)將本層需要 release 的對(duì)象 release 掉,需要返回給上層使用的 DD cache 對(duì)象交給上層的 Auto_releaser 來(lái)負(fù)責(zé)。通過(guò) transfer_release() 可以在不同層次的 Auto_releaser 對(duì)象間轉(zhuǎn)移需要 release 的對(duì)象,可以靈活的指定不再需要 DD cache 對(duì)象的層次。

六 應(yīng)用舉例:inplace DDL 過(guò)程中對(duì) DD 的操作

在 MySQL inplace DDL 執(zhí)行過(guò)程中,會(huì)獲取當(dāng)前表定義的 DD cache 對(duì)象,然后根據(jù)實(shí)際的 DDL 操作內(nèi)容構(gòu)造出新對(duì)應(yīng)的 DD 對(duì)象。然后依次調(diào)用 client 的接口完成對(duì)當(dāng)前表定義的刪除和新表定義的存儲(chǔ)。

{ if (thd->dd_client()->drop(table_def)) goto cleanup2;table_def = nullptr;DEBUG_SYNC_C("alter_table_after_dd_client_drop");// Reset check constraint's mode.reset_check_constraints_alter_mode(altered_table_def);if ((db_type->flags & HTON_SUPPORTS_ATOMIC_DDL)) {/*For engines supporting atomic DDL we have delayed storing newtable definition in the data-dictionary so far in order to avoidconflicts between old and new definitions on foreign key names.Since the old table definition is gone we can safely store newdefinition now.*/if (thd->dd_client()->store(altered_table_def)) goto cleanup2;} }.../*If the SE failed to commit the transaction, we must rollback themodified dictionary objects to make sure the DD cache, the DDtables and the state in the SE stay in sync. */ if (res)thd->dd_client()->rollback_modified_objects(); elsethd->dd_client()->commit_modified_objects();

在 drop() 過(guò)程中,會(huì)將當(dāng)前表定義的 DD cache 對(duì)象對(duì)應(yīng)的數(shù)據(jù)從存儲(chǔ)引擎中刪除,然后從共享緩存中移除(這要求當(dāng)前對(duì)象的引用計(jì)數(shù)僅為1,即只有當(dāng)前線程使用),之后加入到 dropped 局部緩存中。

在 store() 過(guò)程中,會(huì)將新的表定義寫入存儲(chǔ)引擎,并且將對(duì)應(yīng)的 DD cache 對(duì)象加入 uncommitted 緩存中。

在事務(wù)提交或者回滾后,client 將局部緩存中的 dropped 和 uncommitted registry 清除。由于 InnoDB 引擎支持事務(wù),持久存儲(chǔ)層面的數(shù)據(jù)會(huì)通過(guò)存儲(chǔ)引擎的接口提交或回滾,不需要 client 額外操作。

在這個(gè)過(guò)程中,由于 MDL(metadata lock) 的存在,不會(huì)有其他的線程嘗試訪問(wèn)正在變更對(duì)象的 DD object,所以可以安全的對(duì) Shared_dictionary_cache 進(jìn)行操作。當(dāng) DDL 操作結(jié)束(提交或回滾),釋放 EXCLUSIVE 鎖之后,新的線程就可以重新從存儲(chǔ)引擎上加載新的表定義。

七 總結(jié)

MySQL data dictionary 解決了背景所述舊架構(gòu)中的諸多問(wèn)題,使元數(shù)據(jù)的訪問(wèn)更加安全,存儲(chǔ)和管理成本更低。架構(gòu)實(shí)現(xiàn)非常的精巧,通過(guò)大量的模版類實(shí)現(xiàn)使得代碼能夠最大程度上被復(fù)用。多層緩存的實(shí)現(xiàn)也能顯著提升訪問(wèn)效率。通過(guò) client 簡(jiǎn)潔的接口,讓 Server 層和存儲(chǔ)層能在任何地方方便的訪問(wèn)元數(shù)據(jù)。

原文鏈接

本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?

總結(jié)

以上是生活随笔為你收集整理的MySQL 深潜 - 一文详解 MySQL Data Dictionary的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。