天天看點

MySQL特性 - 表屬性上的表達式 - 如何計算和存儲

MySQL從5.7版本開始支援Generated Column, 并在最近的8.0版本中支援了Functional index, 以及default值支援表達式, 這幾個特性都通過建立使用表達式進行描述的列來實作的。筆者之前滿好奇這些表達式資訊都是怎麼存儲的,本文主要記錄了涉及到的相關函數,主要是做個筆記,不會深入解讀。

本文以Generated Column為例進行描述,代碼基于8.0.15

使用

我們建立一個簡單的表,表上包含兩種類型的generated column:實體存儲和虛拟列;并在虛拟列上建立索引

root@information_schema 05:38:49>show create table test.t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `a` int(11) NOT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  `v1` int(11) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  `g1` int(11) GENERATED ALWAYS AS ((`a` * `v1`)) STORED,
  PRIMARY KEY (`a`),
  KEY `v1` (`v1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

root@information_schema 05:40:58>SELECT * FROM INNODB_VIRTUAL WHERE table_id = (SELECT TABLE_ID FROM INNODB_TABLES WHERE NAME LIKE 'test/t1')\G
*************************** 1. row ***************************
TABLE_ID: 1354
     POS: 65539
BASE_POS: 0
*************************** 2. row ***************************
TABLE_ID: 1354
     POS: 65539
BASE_POS: 1
2 rows in set (0.00 sec)

root@information_schema 05:41:04>           

POS值實際上是一個encode的值,是以看起來很大,他包含了virtual column的序列和在所有列上的序列:

((nth virtual generated column for the InnoDB instance + 1) << 16)
+ the ordinal position of the virtual generated column           

如上例,column v1, (0 + 1) << 16 + 3 = 65539

Generated column的表達式資訊可以通過i_s表來查詢:

root@information_schema 06:15:09>SELECT COLUMN_NAME, ORDINAL_POSITION,COLUMN_TYPE,EXTRA, GENERATION_EXPRESSION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME LIKE 't1';
+-------------+------------------+-------------+-------------------+-----------------------+
| COLUMN_NAME | ORDINAL_POSITION | COLUMN_TYPE | EXTRA             | GENERATION_EXPRESSION |
+-------------+------------------+-------------+-------------------+-----------------------+
| a           |                1 | int(11)     |                   |                       |
| b           |                2 | int(11)     |                   |                       |
| c           |                3 | int(11)     |                   |                       |
| g1          |                5 | int(11)     | STORED GENERATED  | (`a` * `v1`)          |
| v1          |                4 | int(11)     | VIRTUAL GENERATED | (`a` + `b`)           |
+-------------+------------------+-------------+-------------------+-----------------------+
5 rows in set (0.00 sec)           

相關代碼

存儲表達式

和其他中繼資料資訊一樣, 表達式也以字元串的形式存儲到mysql庫下面的columns表中,注意這個表是隐藏的,你隻能通過information_schame.columns來查詢。

系統掉定義在檔案定義在檔案sql/dd/impl/tables/columns.h中

ref:

dd::Column_impl::store_attributes

讀取表達式

通過dd接口(

dd::Column_impl::restore_attributes

),存儲于系統表的表達式字元串被讀取出來,并被存儲到TABLE_SHARE的field成員的gcol_info中,類型為類型為Value_generator, 字元串存儲于類型為Value_generator::expr_str中

fill_column_from_dd

當會話打開自己的TABLE對象時,會基于上述的字元串資訊建構item樹,存儲于自己的Value_generator的item樹中。

open_table_from_share --> unpack_value_generator

show create table時,通過TABLE對象,從generated column列的gcol_info中中建構出表達式資訊

store_create_info()

讀和更新表達式

當generated column需要被更新時(

TABLE::is_field_used_by_generated_columns

),或者産生新的插入時,需要計算其結果值

update_generated_write_fields()

當讀取列時,如果virtual generated column, 需要去計算其真正的值. 當然如果virtual column上建立了innodb索引,實際上其值是被存儲到實體索引上的,那麼就無需去計算列值

update_generated_read_fields()

InnoDB内計算表達式

當InnoDB選擇使用virtual column上的索引來進行查詢時,如果需要讀取之前的版本,需要sec record和clust record檢查是否比對時(

row_sel_sec_rec_is_for_clust_rec

), 也需要基于clust record,根據表達式去建構出virtual column的值,這時候就需要去回調server層的計算函數,因為clust record中并不存在virtual column的值, 相應堆棧:

row_search_mvcc
|--> Row_sel_get_clust_rec_for_mysql::operator()
    |--> row_sel_sec_rec_is_for_clust_rec
        |--> innobase_get_computed_value
            |-->  handler::my_eval_gcolumn_expr           

參考文檔: