数据字典¶
Conventions
- 每一个代码块的顶部都有它所属的文件(相对)路径,如果代码块属于某个函数(方法),那么顶部会有函数(方法)的声明。
- 代码块中的对象,如果很重要,会在行尾补充该对象的声明。
- 我使用的MySQL 版本是 8.0.41。你看到这篇时,可能有了更新的版本,比如 8.0.42,区别不大的。
数据字典是什么?¶
数据字典就是存放元数据的多张表集合。这里的元数据指的是数据库、表、索引、视图等数据库对象的信息,比如用户创建了一个数据库 test
,那么要把这个数据库的名字记下来,还有这个数据库使用什么字符集记下来,创建表也有很多信息,表的名字、字符集、使用的存储引擎、行格式等等,这些信息也是存进一张表里,和我们开发把业务数据写进表里没什么分别,区别在于数据字典表是 MySQL 负责创建和维护的。
数据字典都有哪些表¶
源码 sql/dd/impl/tables
目录下的每一个头文件都代表着一张表,所有表的集合就是数据字典。
以 sql/dd/impl/tables/schemata.h
为例,它代表存放数据库元数据的表 schemata
,接下来我们看看这张表有哪些字段。
schemata
这张表存在于 mysql
系统数据库中,但是当我们打开 mysql
数据库时,却发现找不到 schemata
,因为 MySQL 开发将它隐藏起来了。我们只能通过源码查看 schemata
的表结构。换做是你,你也不会把数据字典的表暴露出来,因为这些表里存放的是元数据,是一切的基石,一旦被人改了,MySQL 就无法正常工作了。
我们看贴出来的源码。源码第 50 行到第 69 行定义了 schemata
的字段、索引和外键,以枚举的形式。这么看,好像还是差了一点意思。
我把 Schemata 这个类的构造函数贴了出来,这样是不是看得更明白了。以第 58 行和 59 行代码为例,m_target_def.add_field(FIELD_ID, "FIELD_ID", "id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT");
是在添加字段,继续看 add_field
定义。
sql/dd/impl/types/object_table_definition_impl.h | |
---|---|
add_field
函数的第一个形参是字段号,对应枚举中的 FIELD_ID
,也就是将枚举当作整型来用,表示的是这个字段在表中的顺序,所以枚举中的排序是按照字段顺序来排列的。
第二个形参是字段名,对应字符串 "FIELD_ID"
,第三个形参是字段定义,对应字符串 "id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT"
,看到这里我想大家有数了吧,这和我们平时的建表语句没什么区别。
MySQL 开发将数据字典表 schemata
的定义用上面这种方式定义好了,是面向对象编程的体现。如果是我们,可能直接就是写死 SQL。第 93 行,死 SQL 来了,原来是这张表的初始化语句,新增了一条记录,可以看到新增记录是库 information_schema
的元数据。开玩笑的,虽说初始化语句写死的,但是是通过 m_target_def
对象的函数 add_populate_statement
来维护在对象内的。
接下来,我们看看 m_target_def
对象是什么。
sql/dd/impl/types/object_table_impl.h | |
---|---|
m_target_def
对象的类型是 Object_table_definition_impl
,那就看它。
public: //line 43
typedef std::map<String_type, int> Element_numbers; // line 44
typedef std::map<int, String_type> Element_definitions; // line 45
Element_numbers m_field_numbers; // line 93
Element_definitions m_field_definitions; // line 94
void add_element(int element_number, const String_type &element_name, // line 107
const String_type &element_definition,
Element_numbers *element_numbers,
Element_definitions *element_definitions) {
assert(element_numbers != nullptr && element_definitions != nullptr &&
element_numbers->find(element_name) == element_numbers->end() &&
element_definitions->find(element_number) ==
element_definitions->end());
(*element_numbers)[element_name] = element_number;
(*element_definitions)[element_number] = element_definition;
}
void add_field(int field_number, const String_type &field_name, // line 216
const String_type field_definition) override {
add_element(field_number, field_name, field_definition, &m_field_numbers,
&m_field_definitions);
}
类 Object_table_definition_impl
中关于 add_field
函数的部分都在上面。先看第 44、45 行,定义了两个别名,Element_numbers 是 key 为字符串,value 为整型的散列表,Element_definitions 和 Element_numbers 正好 key 和 value 反过来。这是为了方便,既可以通过字符串查找下标,又可以通过下标查找字符串。
第 93、94 行用别名声明了两个变量,m_field_numbers 和 m_field_definitions。
第 216 行的函数声明上面我们看过它们的形式参数,现在看函数块,很简单,调用了 add_element 函数,将字段序号 field_number、字段名 field_name、字段定义 field_definition 原封不动传给了 add_element,还有 93、94 行声明的两个散列表变量 &m_field_numbers 和 &m_field_definitions。
add_element 函数在第 107 行,assert 断言不用管,因为它是做校验的,我们不关心。那这函数就剩两行了,原来两个散列表变量是分别用来存字段名和字段序号、字段序号和字段定义的。m_field_numbers 的 key 存字段名,value 存字段序号。m_field_definitions 的 key 存字段序号,value 存字段定义。
刚开始我搞错了,以为 m_field_numbers 和 m_field_definitions 都是存字段名和字段序号,只是 key 和 value 对调,现在看不是的,变量命名就已经说明了它们存放的东西。
Element_numbers m_index_numbers;
Element_definitions m_index_definitions;
Element_numbers m_foreign_key_numbers;
Element_definitions m_foreign_key_definitions;
void add_index(int index_number, const String_type &index_name,
const String_type &index_definition) override {
add_element(index_number, index_name, index_definition, &m_index_numbers,
&m_index_definitions);
}
virtual void add_foreign_key(int foreign_key_number,
const String_type &foreign_key_name,
const String_type &foreign_key_definition) {
add_element(foreign_key_number, foreign_key_name, foreign_key_definition,
&m_foreign_key_numbers, &m_foreign_key_definitions);
}
add_index、add_foreign_key,和 add_field 是差不多的,都是调用 add_element 方法,存放的散列表不同而已。索引放在 m_index_numbers 和 m_index_definitions 中,外键放在 m_foreign_key_numbers 和 m_foreign_key_definitions 中。
好了,看完了 schemata 这张数据字典表的定义,再去看 sql/dd/impl/tables
目录下其他数据字典表的定义就很轻松了,都差不多的。
小结¶
数据字典是 MySQL 用来存放数据库对象(库、表、索引、约束、触发器等)元数据的表集合,具体有多少张表,表的定义又是如何的,要去 sql/dd/impl/tables
目录下看,通过查询语句是查看不到的,因为被 MySQL 开发隐藏起来了。好心的 MySQL 开发还是给用户留了查看的地方的,在 information_schema 数据库中的视图,数据都是来自数据字典表,不信你看 information_schema.SCHEMATA
视图的定义。