天天看點

MySQL · 8.0新特性 · New data dictionary嘗鮮篇

衆所周知,由于mysql采用統一server層+不同的底層引擎插件的架構模式,在server層為每個表建立了frm檔案,以儲存與表定義相關的中繼資料資訊。然而某些引擎(例如innodb)本身也會存儲中繼資料,這樣不僅産生了中繼資料備援,而且由于server層和引擎層分别各自管理,在執行ddl之類的操作時,很難做到crash-safe,更别說讓ddl具備事務性了。

為了解決這些問題(尤其是ddl無法做到atomic),從mysql8.0開始取消了frm檔案及其他server層的中繼資料檔案(frm, par, trn, trg, isl,db.opt),所有的中繼資料都用innodb引擎進行存儲, 另外一些諸如權限表之類的系統表也改用innodb引擎。

本文是筆者初次了解這塊内容,是以不會過多深入,由于涉及的改動太多,後面有空再逐個展開。

本文所有測試和代碼相關部分都是基于mysql8.0.0版本,由于這是8.0大版本的第一個開發版本,不排除未來行為會發生變化。

首先我們建立一個新庫,并在庫下建立兩個表來開啟我們的測試

可以看到在庫目錄下隻有ibd檔案,并沒有frm檔案,而在資料目錄下,相應的生成了一個sdi檔案,來描述這個sbtest庫的資訊。

我們再來看看建立一個myisam引擎的表:

這裡我們建立了一個myisam表t1,相應的一個sdi檔案被建立,檔案中以json的格式記錄了該表的詳細資訊。根據官方檔案的描述,這個檔案的存在是為了一個還未完全實作的功能。

一些新is表使用view進行了重新設計,主要包括這些表:

也就是說,雖然dd系統表被隐藏不可見了,但你依然可以通過視圖獲得大部分資訊。這種方式實際上大大加快了is表的查詢速度,轉換成實體表的查詢後,将無需為每個is表的查詢建立臨時表(臨時表的操作包含了server層建立frm, 引擎層擷取資料or需要鎖保護的全局資料)。另外優化器也能為is表的查詢選擇更好的執行計劃(例如使用系統表上的索引進行查詢)。

官方對此做了測試,結果顯示對is表的查詢性能大幅度提升,官方部落格傳送門:

<a href="http://mysqlserverteam.com/mysql-8-0-improvements-to-information_schema/">mysql 8.0: improvements to information_schema</a>

<a href="http://mysqlserverteam.com/mysql-8-0-scaling-and-performance-of-information_schema/">mysql 8.0: scaling and performance of information_schema</a>

資料詞典的結構發生巨大的變化後,相應的對于記憶體資料詞典cache也做改動,

hardcode的字元集cache:

和權限相關的表轉換成innodb引擎

// 包括:user, db, tables_priv, columns_priv, procs_priv, proxies_priv

func表轉換成innodb事務表

// 基于此變化,對function的操作(例如create function或者drop function, 或者使用者定義的udf)可能會導緻一次隐式送出

mysql庫下的routine表及event表不再使用,這些資訊被存儲到新的dd table中,并且在mysql庫下是不可見的。

外鍵系統表

// 使用兩個不可見的系統表foreign_keys和foreign_key_column_usage來存儲外鍵資訊

// 由于這兩個系統表不可見,你需要通過is庫下的referential_constraints和key_column_usage表來獲得外鍵資訊

// 引入的不相容:foreign key的名字不可以超過64個字元(之前版本是允許的)

我們回到源代碼目錄下,大量的新代碼檔案被引入,以從server層管理new dd,主要定義了一系列統一的api,代碼存于sql/dd目錄下,函數和類定義在namespace dd下

針對不同的中繼資料分别定義了不同的類及其繼承關系:

資料詞典cache管理類:

mysql庫存儲的是系統表,但通過show tables指令,我們隻能看到37個表,而從磁盤來看mysql目錄下ibd檔案遠遠超過37個,這意味着有些系統表對使用者是不可見的,這些表也是用于管理核心資料詞典資訊,不可見的原因是避免使用者不恰當的操作。(當然也不排除未來這一行為發生變化),關于這些表的通路,在目錄<code>sql/dd/impl/tables/</code>中進行了接口定義,這些隐藏的表包括:

我們以對一個表的常見操作為例,看看其中一些代碼是如何被調用的。

(由于new dd的代碼改動很大,相關的worklog有幾十個,筆者通過測試+代碼debug的方式第一步先熟悉代碼,記錄的比較淩亂)

建立database

入口函數:<code>mysql_create_db</code>

-- 建立database目錄

-- 建構binlog并寫入檔案

-- 調用dd api接口: dd::create_schema

修改database

入口函數: <code>mysql_alter_db</code>

-- 調用dd api接口: <code>dd::alter_schema</code>

-- 寫binlog

show databases

執行該指令時,實際上會對其進行一個sql轉換,将其轉換成一個标準的查詢語句,堆棧如下:

轉換後的sql類似:

由于直接從系統表中讀取, 這意味着在資料目錄下建立一個檔案夾将不會當作新的資料庫目錄。

删除database

-- 删除相關檔案

-- 删除系統表mysql/schemata中記錄

建立表

入口函數:

-- 先在dd中插入新的記錄(<code>dd::create_table</code> --&gt; <code>dd::create_dd_user_table</code>)

-- 然後再建立引擎檔案

open table

-- 将執行個體重新開機後,然後再打開表,表定義第一次載入記憶體,需要先去通路系統表拿到表定義:

wl#6379: schema definitions for new dd

wl#6380: formulate framework for api for dd

wl#6381: handler api changes for new dictionary

wl#6382: define and implement api for table objects

wl#6383: define and implement api for triggers

wl#6384: define and implement api for stored routines

wl#6385: define and implement api for schema

wl#6387: define and implement api for tablespaces

wl#6388: define and implement api for events

wl#6389: define and implement api for views

wl#6390: use new dd api for handling non-partitioned tables

wl#6391: protect data dictionary tables

wl#6392: upgrade to transactional data dictionary

wl#6394: bootstrap code for new dd

wl#6416: innodb: remove the use of *.isl files

wl#6599: new data dictionary and i_s integration

wl#6929: move foreign key constraints to the global data dictionary

wl#7053: innodb: provide storage for tablespace dictionary

wl#7066: external tool to extract innodb tablespace dictionary information

wl#7069: provide data dictionary information in serialized form

wl#7167: change ddl to update rows for view columns in dd.columns and other dependent values.

wl#7284: implement common code for different dd apis

wl#7464: innodb: provide a way to do non-locking reads

wl#7488: innodb startup refactoring

wl#7630: define and implement api for table partition info

wl#7771: make sure errors are properly handled in dd api

wl#7784: store temporary table metadata in memory

wl#7836: use new dd api for handling partitioned tables

wl#7896: use dd api to work with triggers

wl#7897: use dd api to work with stored routines

wl#7898: use dd api to work with events

wl#7907: runtime: use non-locking reads for dd tables under i_s view.

wl#8150: dictionary object cache

wl#8433: separate dd commands from regular sql queries in the parser grammar

wl#8980: move udf table from myisam to transactional storage

wl#9045: make user management ddls atomic

官方部落格:

<a href="https://mysqlserverteam.com/mysql-server-bootstrapping-and-dictionary-initialization/">https://mysqlserverteam.com/mysql-server-bootstrapping-and-dictionary-initialization/</a>

<a href="https://mysqlserverteam.com/bootstrapping-the-transactional-data-dictionary/">https://mysqlserverteam.com/bootstrapping-the-transactional-data-dictionary/</a>