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