自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL 深潛 - 一文詳解 MySQL Data Dictionary

數(shù)據(jù)庫 MySQL
MySQL data dictionary 解決了背景所述舊架構(gòu)中的諸多問題,使元數(shù)據(jù)的訪問更加安全,存儲和管理成本更低。

[[419360]]

一、背景

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

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

二、整體架構(gòu)

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

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

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

1.Table_impl

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

  1. class Table_impl : public Abstract_table_impl, virtual public Table {  
  2.   // Fields.  
  3.   Object_id m_se_private_id;  
  4.   String_type m_engine;  
  5.   String_type m_comment;  
  6.   // - Partitioning related fields.  
  7.   enum_partition_type m_partition_type;  
  8.   String_type m_partition_expression;  
  9.   String_type m_partition_expression_utf8;  
  10.   enum_default_partitioning m_default_partitioning;  
  11.   // References to tightly-coupled objects.  
  12.   Index_collection m_indexes;  
  13.   Foreign_key_collection m_foreign_keys;  
  14.   Foreign_key_parent_collection m_foreign_key_parents;  
  15.   Partition_collection m_partitions;  
  16.   Partition_leaf_vector m_leaf_partitions;  
  17.   Trigger_collection m_triggers;  
  18.   Check_constraint_collection m_check_constraints;  
  19. }; 

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

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

  1. class Column_impl : public Entity_object_impl, public Column {  
  2.   // Fields.  
  3.   enum_column_types m_type;  
  4.   bool m_is_nullable;  
  5.   bool m_is_zerofill;  
  6.   bool m_is_unsigned;  
  7.   bool m_is_auto_increment;  
  8.   bool m_is_virtual;  
  9.   bool m_default_value_null;  
  10.   String_type m_default_value;   
  11.   // References to tightly-coupled objects.  
  12.   Abstract_table_impl *m_table;  
  13. }; 

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

  1. class Partition_impl : public Entity_object_impl, public Partition { 
  2.   // Fields.  
  3.   Object_id m_parent_partition_id;  
  4.   uint m_number;  
  5.   Object_id m_se_private_id;  
  6.   String_type m_description_utf8;  
  7.   String_type m_engine; 
  8.   String_type m_comment;  
  9.   Properties_impl m_options;  
  10.   Properties_impl m_se_private_data;  
  11.   // References to tightly-coupled objects.  
  12.   Table_impl *m_table;  
  13.   const Partition *m_parent;  
  14.   Partition_values m_values;  
  15.   Partition_indexes m_indexes;  
  16.   Table::Partition_collection m_sub_partitions;  
  17. }; 

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

2.Table_impl 是如何持久化存儲和訪問的

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

  1. root@localhost:test 8.0.18-debug> SHOW CREATE TABLE mysql.tables\G  
  2. *************************<strong> 1. row </strong>*************************  
  3.        Table: tables  
  4. Create Table: CREATE TABLE `tables` (  
  5.   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  
  6.   `schema_id` bigint(20) unsigned NOT NULL,  
  7.   `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 
  8.   `type` enum('BASE TABLE','VIEW','SYSTEM VIEW') COLLATE utf8_bin NOT NULL,  
  9.   `engine` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,  
  10.   `mysql_version_id` int(10) unsigned NOT NULL,  
  11.   `row_format` enum('Fixed','Dynamic','Compressed','Redundant','Compact','Paged') COLLATE utf8_bin DEFAULT NULL,  
  12.   `collation_id` bigint(20) unsigned DEFAULT NULL,  
  13.   `comment` varchar(2048) COLLATE utf8_bin NOT NULL,  
  14.   `hidden` enum('Visible','System','SE','DDL') COLLATE utf8_bin NOT NULL,  
  15.   `options` mediumtext COLLATE utf8_bin,  
  16.   `se_private_data` mediumtext COLLATE utf8_bin,  
  17.   `se_private_id` bigint(20) unsigned DEFAULT NULL,  
  18.   `tablespace_id` bigint(20) unsigned DEFAULT NULL,  
  19.   `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, 
  20.   `partition_expression` varchar(2048) COLLATE utf8_bin DEFAULT NULL,  
  21.   `partition_expression_utf8` varchar(2048) COLLATE utf8_bin DEFAULT NULL,  
  22.   `default_partitioning` enum('NO','YES','NUMBER') COLLATE utf8_bin DEFAULT NULL,  
  23.   `subpartition_type` enum('HASH','KEY_51','KEY_55','LINEAR_HASH','LINEAR_KEY_51','LINEAR_KEY_55') COLLATE utf8_bin DEFAULT NULL,  
  24.   `subpartition_expression` varchar(2048) COLLATE utf8_bin DEFAULT NULL, 
  25.   `subpartition_expression_utf8` varchar(2048) COLLATE utf8_bin DEFAULT NULL,  
  26.   `default_subpartitioning` enum('NO','YES','NUMBER') COLLATE utf8_bin DEFAULT NULL,  
  27.   `created` timestamp NOT NULL,  
  28.   `last_altered` timestamp NOT NULL,  
  29.   `view_definition` longblob,  
  30.   `view_definition_utf8` longtext COLLATE utf8_bin,  
  31.   `view_check_option` enum('NONE','LOCAL','CASCADED') COLLATE utf8_bin DEFAULT NULL,  
  32.   `view_is_updatable` enum('NO','YES') COLLATE utf8_bin DEFAULT NULL,  
  33.   `view_algorithm` enum('UNDEFINED','TEMPTABLE','MERGE') COLLATE utf8_bin DEFAULT NULL,  
  34.   `view_security_type` enum('DEFAULT','INVOKER','DEFINER') COLLATE utf8_bin DEFAULT NULL,  
  35.   `view_definer` varchar(288) COLLATE utf8_bin DEFAULT NULL,  
  36.   `view_client_collation_id` bigint(20) unsigned DEFAULT NULL,  
  37.   `view_connection_collation_id` bigint(20) unsigned DEFAULT NULL,  
  38.   `view_column_names` longtext COLLATE utf8_bin,  
  39.   `last_checked_for_upgrade_version_id` int(10) unsigned NOT NULL,  
  40.   PRIMARY KEY (`id`),  
  41.   UNIQUE KEY `schema_id` (`schema_id`,`name`),  
  42.   UNIQUE KEY `engine` (`engine`,`se_private_id`),  
  43.   KEY `engine_2` (`engine`),  
  44.   KEY `collation_id` (`collation_id`),  
  45.   KEY `tablespace_id` (`tablespace_id`),  
  46.   KEY `type` (`type`),  
  47.   KEY `view_client_collation_id` (`view_client_collation_id`),  
  48.   KEY `view_connection_collation_id` (`view_connection_collation_id`),  
  49.   CONSTRAINT `tables_ibfk_1` FOREIGN KEY (`schema_id`) REFERENCES `schemata` (`id`), 
  50.   CONSTRAINT `tables_ibfk_2` FOREIGN KEY (`collation_id`) REFERENCES `collations` (`id`),  
  51.   CONSTRAINT `tables_ibfk_3` FOREIGN KEY (`tablespace_id`) REFERENCES `tablespaces` (`id`),  
  52.   CONSTRAINT `tables_ibfk_4` FOREIGN KEY (`view_client_collation_id`) REFERENCES `collations` (`id`),  
  53.   CONSTRAINT `tables_ibfk_5` FOREIGN KEY (`view_connection_collation_id`) REFERENCES `collations` (`id`)  
  54. ) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB AUTO_INCREMENT=549 DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC  
  55. 1 row in set (0.00 sec) 

通過以上 mysql.tables 的表定義可以獲得存儲引擎中實際存儲的元信息字段。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 是訪問持久存儲引擎的處理類,包括 get() / drop() / store() 等接口。當(dāng)初次獲取一個表的元信息時,會調(diào)用 Storage_adapter::get() 接口,處理過程如下: 

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

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

  1. class Tables : public Entity_object_table_impl {  
  2.   enum enum_fields {  
  3.     FIELD_ID,  
  4.     FIELD_SCHEMA_ID,  
  5.     FIELD_NAME,  
  6.     FIELD_TYPE,  
  7.     FIELD_ENGINE,  
  8.     FIELD_MYSQL_VERSION_ID,  
  9.     FIELD_ROW_FORMAT,  
  10.     FIELD_COLLATION_ID,  
  11.     FIELD_COMMENT,  
  12.     FIELD_HIDDEN,  
  13.     FIELD_OPTIONS,  
  14.     FIELD_SE_PRIVATE_DATA,  
  15.     FIELD_SE_PRIVATE_ID,  
  16.     FIELD_TABLESPACE_ID,  
  17.     FIELD_PARTITION_TYPE,  
  18.     FIELD_PARTITION_EXPRESSION,  
  19.     FIELD_PARTITION_EXPRESSION_UTF8,  
  20.     FIELD_DEFAULT_PARTITIONING,  
  21.     FIELD_SUBPARTITION_TYPE,  
  22.     FIELD_SUBPARTITION_EXPRESSION,  
  23.     FIELD_SUBPARTITION_EXPRESSION_UTF8,  
  24.     FIELD_DEFAULT_SUBPARTITIONING,  
  25.     FIELD_CREATED,  
  26.     FIELD_LAST_ALTERED,  
  27.     FIELD_VIEW_DEFINITION,  
  28.     FIELD_VIEW_DEFINITION_UTF8,  
  29.     FIELD_VIEW_CHECK_OPTION,  
  30.     FIELD_VIEW_IS_UPDATABLE,  
  31.     FIELD_VIEW_ALGORITHM,  
  32.     FIELD_VIEW_SECURITY_TYPE,  
  33.     FIELD_VIEW_DEFINER,  
  34.     FIELD_VIEW_CLIENT_COLLATION_ID,  
  35.     FIELD_VIEW_CONNECTION_COLLATION_ID,  
  36.     FIELD_VIEW_COLUMN_NAMES,  
  37.     FIELD_LAST_CHECKED_FOR_UPGRADE_VERSION_ID,  
  38.     NUMBER_OF_FIELDS  // Always keep this entry at the end of the enum  
  39.   };  
  40. }; 

四、多級緩存

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

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

  1. template <typename T>  
  2. class Multi_map_base {  
  3.  private:  
  4.   Element_map<const T *, Cache_element<T>> m_rev_map;  // Reverse element map.  
  5.   Element_map<typename T::Id_key, Cache_element<T>>  
  6.       m_id_map;  // Id map instance.  
  7.   Element_map<typename T::Name_key, Cache_element<T>>  
  8.       m_name_map;  // Name map instance.  
  9.   Element_map<typename T::Aux_key, Cache_element<T>>  
  10.       m_aux_map;  // Aux map instance.  
  11. };  
  12. template <typename K, typename E>  
  13. class Element_map {  
  14.  public:  
  15.   typedef std::map<K, E *, std::less<K> 
  16.                    Malloc_allocator<std::pair<const K, E *>>>  
  17.       Element_map_type;  // Real map type.  
  18.  private:  
  19.   Element_map_type m_map;  // The real map instance.  
  20.   std::set<K, std::less<K> 
  21.            Malloc_allocator<K>>  
  22.       m_missed;  // Cache misses being handled.  
  23. }; 

之所以叫 Multi_map_base,是因為其中包含了多個 hash map,適合用戶根據(jù)不同類型的 key 來獲取緩存對象,比如 id、name、DD cache 本身等。Element_map 就是對 std::map 的一個封裝,key 為前述幾種類型之一,value 為 DD cache 對象指針的一個封裝 Cache_element,封裝了對象本身和引用計數(shù)。

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

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

1.局部緩存

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

  1. class Dictionary_client { 
  2.   Object_registry m_registry_committed;    // Registry of committed objects.  
  3.   Object_registry m_registry_uncommitted;  // Registry of uncommitted objects.  
  4.   Object_registry m_registry_dropped;      // Registry of dropped objects.  
  5.   THD *m_thd;                        // Thread context, needed for cache misses.  
  6.   Auto_releaser m_default_releaser;  // Default auto releaser.  
  7.   Auto_releaser *m_current_releaser;  // Current auto releaser.  
  8. };  
  9. class Object_registry {  
  10.   std::unique_ptr<Local_multi_map<Abstract_table>> m_abstract_table_map;  
  11.   std::unique_ptr<Local_multi_map<Charset>> m_charset_map;  
  12.   std::unique_ptr<Local_multi_map<Collation>> m_collation_map;  
  13.   std::unique_ptr<Local_multi_map<Column_statistics>> m_column_statistics_map;  
  14.   std::unique_ptr<Local_multi_map<Event>> m_event_map;  
  15.   std::unique_ptr<Local_multi_map<Resource_group>> m_resource_group_map;  
  16.   std::unique_ptr<Local_multi_map<Routine>> m_routine_map;  
  17.   std::unique_ptr<Local_multi_map<Schema>> m_schema_map;  
  18.   std::unique_ptr<Local_multi_map<Spatial_reference_system>>  
  19.       m_spatial_reference_system_map;  
  20.   std::unique_ptr<Local_multi_map<Tablespace>> m_tablespace_map;  
  21. };  
  22. template <typename T>  
  23. class Local_multi_map : public Multi_map_base<T> {}; 

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

2.共享緩存

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

  1. class Shared_dictionary_cache {  
  2.   Shared_multi_map<Abstract_table> m_abstract_table_map;  
  3.   Shared_multi_map<Charset> m_charset_map;  
  4.   Shared_multi_map<Collation> m_collation_map;  
  5.   Shared_multi_map<Column_statistics> m_column_stat_map;  
  6.   Shared_multi_map<Event> m_event_map;  
  7.   Shared_multi_map<Resource_group> m_resource_group_map;  
  8.   Shared_multi_map<Routine> m_routine_map;  
  9.   Shared_multi_map<Schema> m_schema_map;  
  10.   Shared_multi_map<Spatial_reference_system> m_spatial_reference_system_map;  
  11.   Shared_multi_map<Tablespace> m_tablespace_map;  
  12. };   
  13. template <typename T>  
  14. class Shared_multi_map : public Multi_map_base<T> { 
  15.  private:  
  16.   static const size_t initial_capacity = 256 
  17.   mysql_mutex_t m_lock;         // Single mutex to lock the map.  
  18.   mysql_cond_t m_miss_handled;  // Broadcast a miss being handled.  
  19.   Free_list<Cache_element<T>> m_free_list;  // Free list.  
  20.   std::vector<Cache_element<T> *>  
  21.       m_element_pool;  // Pool of allocated elements. 
  22.   size_t m_capacity;   // Total capacity, i.e., if the  
  23.                        // number of elements exceeds this  
  24.                        // limit, shrink the free list.  

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

3.緩存獲取過程

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

  1. // Get a dictionary object.  
  2. template <typename K, typename T>  
  3. bool Dictionary_client::acquire(const K &key, const T **object,  
  4.                                 bool *local_committed,  
  5.                                 bool *local_uncommitted) {  
  6.   // Lookup in registry of uncommitted objects  
  7.   T *uncommitted_object = nullptr 
  8.   bool dropped = false 
  9.   acquire_uncommitted(key, &uncommitted_object, &dropped); 
  10.   ...  
  11.   // Lookup in the registry of committed objects.  
  12.   Cache_element<T> *element = NULL 
  13.   m_registry_committed.get(key, &element);  
  14.   ...  
  15.   // Get the object from the shared cache.  
  16.   if (Shared_dictionary_cache::instance()->get(m_thd, key, &element)) {  
  17.     DBUG_ASSERT(m_thd->is_system_thread() || m_thd->killed ||  
  18.                 m_thd->is_error());  
  19.     return true;  
  20.   }  

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

Cache miss

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

  1. // Get a wrapper element from the map handling the given key type.  
  2. template <typename T>  
  3. template <typename K>  
  4. bool Shared_multi_map<T>::get(const K &key, Cache_element<T> **element) {  
  5.   Autolocker lock(this);  
  6.   *element = use_if_present(key);  
  7.   if (*element) return false;  
  8.   // Is the element already missed?  
  9.   if (m_map<K>()->is_missed(key)) {  
  10.     while (m_map<K>()->is_missed(key))  
  11.       mysql_cond_wait(&m_miss_handled, &m_lock);  
  12.     *element = use_if_present(key);  
  13.     // Here, we return only if element is non-null. An absent element  
  14.     // does not mean that the object does not exist, it might have been  
  15.     // evicted after the thread handling the first cache miss added  
  16.     // it to the cache, before this waiting thread was alerted. Thus,  
  17.     // we need to handle this situation as a cache miss if the element  
  18.     // is absent.  
  19.     if (*element) return false;  
  20.   }  
  21.   // Mark the key as being missed.  
  22.   m_map<K>()->set_missed(key);  
  23.   return true;  

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

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

五、Auto_releaser

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

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

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

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

  1. {      
  2.   if (thd->dd_client()->drop(table_def)) goto cleanup2;  
  3.   table_def = nullptr 
  4.   DEBUG_SYNC_C("alter_table_after_dd_client_drop");  
  5.   // Reset check constraint's mode.  
  6.   reset_check_constraints_alter_mode(altered_table_def);  
  7.   if ((db_type->flags & HTON_SUPPORTS_ATOMIC_DDL)) {  
  8.     /*  
  9.       For engines supporting atomic DDL we have delayed storing new  
  10.       table definition in the data-dictionary so far in order to avoid  
  11.       conflicts between old and new definitions on foreign key names.  
  12.       Since the old table definition is gone we can safely store new 
  13.       definition now.  
  14.     */  
  15.     if (thd->dd_client()->store(altered_table_def)) goto cleanup2; 
  16.    }  
  17.  
  18. ...  
  19. /*  
  20.   If the SE failed to commit the transaction, we must rollback the  
  21.   modified dictionary objects to make sure the DD cache, the DD 
  22.   tables and the state in the SE stay in sync.  
  23. */  
  24. if (res) 
  25.   thd->dd_client()->rollback_modified_objects();  
  26. else  
  27.   thd->dd_client()->commit_modified_objects(); 

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

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

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

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

七、總結(jié)

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

 

責(zé)任編輯:龐桂玉 來源: 阿里云數(shù)據(jù)庫
相關(guān)推薦

2021-08-26 10:44:31

MySQL MySQL Data 阿里云

2021-09-28 10:59:53

MYSQLPerformance 內(nèi)存管理

2022-11-06 21:14:02

數(shù)據(jù)驅(qū)動架構(gòu)數(shù)據(jù)

2022-03-11 09:12:06

MySQLMDL

2020-03-18 14:00:47

MySQL分區(qū)數(shù)據(jù)庫

2021-03-04 00:09:31

MySQL體系架構(gòu)

2020-10-20 13:50:47

MySQL數(shù)據(jù)庫

2021-10-19 10:10:51

MySQL事務(wù)隔離級別數(shù)據(jù)庫

2020-06-23 08:41:47

JavaScript開發(fā)技術(shù)

2022-06-26 00:18:05

企業(yè)產(chǎn)品化變量

2025-03-28 11:47:38

2021-02-11 09:01:32

CSS開發(fā) SDK

2023-02-27 07:33:14

MySQL數(shù)據(jù)庫服務(wù)器

2020-05-12 15:40:06

MySQ數(shù)據(jù)庫索引

2023-03-27 17:58:34

MySQL加鎖間隙鎖

2019-02-25 08:58:16

Python深拷貝淺拷貝

2023-02-28 18:09:53

Javascript定時器

2020-12-21 06:13:52

高可用Nacos服務(wù)端

2021-05-11 11:05:43

SAL子查詢

2022-08-05 08:22:10

eBPFHTTP項目
點贊
收藏

51CTO技術(shù)棧公眾號