整个MySQL 8.0的数据字典实现在数据字典对象分布上呈现
|--Dictionary_client
|--Shared_dictionary_cache
|--Storage_adapte
这种三级存储的方式。
Dictionary_client是整个数据字典的客户端,用户对于数据字典的操作都是通过该client实现的。
Dictionary_client中维护有三个map作为该客户端私有的缓存,如果能在私有缓存命中的话,就不需要去全局公有的Shared_dictionary_cache,甚至持久化存储中获取了。
client调用store/update接口时将object放到uncommitted map中
事务提交后,object从uncommitted map移到committed map中
执行drop操作后,object移入dropped map
这三个map的类型都是Local_multi_map,本质是对std::map的封装。
每个client退出后,调用Auto_releaser的析构函数将每个map清空。
主要接口:
上面说到,在client的三个私有map中(极大)可能会有cache miss的现象,比如一个client中已commit的对象不可能被另一个client读到,这时候就要用到全局的缓存Shared_dictionary_cache,Shared_dictionary_cache本身也是map,30多张数据字典表各自维护一个map,其中存放的对象是Object_key + Element_cache,Element_cache是对数据字典对象的一层封装,目的在于可以统一管理所有类型的数据字典对象。以下是一个element_cache所包含的内容,实际上就是一个指向原数据字典对象的指针以及属于这个数据字典对象的key信息。
const T *m_object; // Pointer to the actual object.
uint m_ref_counter; // Number of concurrent object usages.
/*
全局对象的 priamry key,一个ulonglong类型的id
Primary_id_key Id_key;
*/
Key_wrapper<typename T::Id_key> m_id_key; // The id key for the object.
/*
全局对象的 name
Item_name_key Name_key;
*/
Key_wrapper<typename T::Name_key> m_name_key; // The name key for the object.
/*
辅助kye,暂时用不到
Void_key Aux_key
*/
Key_wrapper<typename T::Aux_key> m_aux_key; // The aux key for the object.
主要接口:
Shared_dictionary_cache基于一个最重要的数据结构便是Shared_multi_map,Share_multi_map继承自Multi_map_base(与上述client的Local_multi_map一致,再往上找的话就是std::map),通过扩展了Autolocker内部类实现multi_map的线程间同步,以及map中元素的生命周期管理。
To ensure that the mutex is locked and unlocked correctly. To delete unused elements and objects outside the scope where the mutex is locked.
重要成员变量:
/*
m_free_list实际上维护了一个LRU队列,引用计数归零的element
说明当前没有被使用,因此被放到free_list的尾部。可以看作是map的
缓存。
*/
Free_list<Cache_element<T>> m_free_list; // Free list.
/*
1.在进行put操作时,我们需要根据给定的object创建新的element,
此时并不知道cache中是否有该对象,执行get之后,如果找到了该element,
那么该element就应该被丢弃,我们不会把它直接删除,只要m_element_pool
还有空间,就会把它先存到pool中,供下次使用。
2.在进行remove操作时,被remove的element也会被存入element_pool中。
*/
std::vector<Cache_element<T> *> m_element_pool; // Pool of allocated elements.
其中重要的成员函数就是对map的读写:
1. template <typename K>
Cache_element<T> *use_if_present(const K &key);
/*
对应的其实就是get,通过key返回被封装成Cache_element的object的指针,
返回指针的同时该element对象的引用计数+1。
*/
2. void remove(Cache_element<T> *element, Autolocker *lock);
/*
调用父类Multi_map_base的remove_single_element()方法。
*/
3. void add_single_element(Cache_element<T> *element)方法。
/*
而put则直接调用父类Multi_map_base的add_single_element()方法。
*/
内部类 Autolocker
class Autolocker {
private:
// Vector containing objects to be deleted unconditionally.
typedef std::vector<const T *, Malloc_allocator<const T *>>
Object_list_type;
Object_list_type m_objects_to_delete;
// Vector containing elements to be deleted unconditionally, which
// happens when elements are evicted while the pool is already full.
typedef std::vector<const Cache_element<T> *,
Malloc_allocator<const Cache_element<T> *>>
Element_list_type;
Element_list_type m_elements_to_delete;
...
}
注: m_objects_to_delete->object对象不复用,用完即删。m_elements_to_delete->element对象放回element_pool,下次使用时直接从pool中取出init后继续使用。
既然是cache就有可能产生cache miss,这里的cache miss指的是通过特定的key在Shared_dictionary_cache维护的map实例中找不到目标对象,这时候就要调用Storage_adapter的接口来读取持久化存储中的数据对象了(MySQL数据字典持久化存储在InnoDB)
主要接口:
现在我们知道数据字典对象分布在dictionary_client/Shared_dictionary_cache/Storage_adapter中,那么查询中如何获取相应的数据字典对象呢?
一二级cache都是map结构,所以要拿数据字典对象只要知道key就可以,数据字典所有的key都继承自Object_key
,所有类型定义在sql/dd/impl/raw/object_keys.h
.
以Primary_id_key为例:每个数据字典表都有一个id列作为主键,而Primary_id_key就是用于基于id的读操作
Primary_id_key(Object_id object_id) : m_object_id(object_id) {}
// Update a preallocated instance.
void update(Object_id object_id) { m_object_id = object_id; }
public:
virtual Raw_key *create_access_key(Raw_table *db_table) const;
virtual String_type str() const;
bool operator<(const Primary_id_key &rhs) const {
return m_object_id < rhs.m_object_id;
}
private:
Object_id m_object_id;
};
Primary_id_key只有一个成员变量m_object_id,其对应的就是各个数据字典表的主键。
主要接口:
从map中查询比较简单,因为已经在key中已经重载了比较符,只要调用相应的get接口(实际上是map的find接口)就可以。
从持久化中查找信息需要重新构建key,由此引出Raw_key/Raw_record/Raw_table的概念。
struct Raw_key {
uchar key[MAX_KEY_LENGTH];
int index_no;
int key_len;
key_part_map keypart_map;
Raw_key(int p_index_no, int p_key_len, key_part_map p_keypart_map)
: index_no(p_index_no), key_len(p_key_len), keypart_map(p_keypart_map) {}
};
Raw_key是一个简单的结构体,可以把它看作是去查询持久化存储索引的key,需要关注的有两个变量。
a b c
1 0 0 // keypart_map = 1,只用第一列a
1 1 0 // keypart_map = 3,用a/b两列
1 0 1 // keypart_map = 5,用a/c两列
...
这个在基于复合key进行范围扫描的时候非常有用。
Raw_table维护了一个待访问数据字典表的table_list,从这里面可以拿到一个TABLE对象,进而可以通过它来读写持久化中的元组信息(TABLE->record0).
主要接口:
Raw_record是一个持久化元组buffer和可操作内存对象的一个转换载体。
主要接口:
Raw_record_set是由Raw_table::open_record_set产生的结果集,可以调用其提供的next()接口实现结果集的遍历。
主要接口:
tables.cc
,其中就定义了tables这张数据字典表是如何创建的,包括表名/列的定义/索引的定义等;而与之对应的tables.h
中则是一些枚举类型,用来表示各个列/索引在表中的相对位置。restore_attributes
(从持久化存储中读出数据拼出表的内存对象)store_attributes
(将内存对象分别写入持久化数据字典),serialize/deserialize
(内存对象到sdi文件之间的相互转换)等,其命名为xx_impl.h/xx_impl.cc
数据字典表2
namespace dd {
Weak_object
Entity_object
Dictionary_object
Tablespace
Schema
Event
Routine
Function
Procedure
Charset
Collation
Abstract_table
Table
View
Spatial_reference_system
Index_stat
Table_stat
Partition
Trigger
Index
Foreign_key
Parameter
Column
Partition_index
Partition_value
View_routine
View_table
Tablespace_file
Foreign_key_element
Index_element
Column_type_element
Parameter_type_element
Object_table
Dictionary_object_table
Object_type
Object_table_definition
}
数据字典缓存2
dd::cache {
dd::cache::Dictionary_client
Object_registry
Element_map
Multi_map_base
Local_multi_map
Shared_multi_map
Cache_element
Free_list
Shared_dictionary_cache
Storage_adapter
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。