首先来看tablespace的定义:
这里system tablespace是一个特殊的tablespace,他包含了很多数据文件(ibdata files).而且如果没有设置 file-per-table的话,所有的新创建的表的数据以及索引信息都会保存在它里面.
下面就是ibdata可能包含的内容
假设设置了file-per-table(默认打开),那么每一个表都会有自己的tablespace文件(.ibd).
还有一种就是temporary tablespace,也就是临时表的tablespace.
源码分析
既然我们已经知道tablespace是和table相关的,那么我们就按照这个逻辑来分析源码.
首先来看system tablespace的创建,在InnoDB中每一个tablespace都会有一个uint32类型的id,每一个唯一的id用来表示对应的tablespace,而system tablespace的space id就是0.
而由于只有system tablespace和temporary tablespace是共享的,因此他们在InnoDB中有专门的数据结构来表示他们(Tablespace表示所有的shared tablespace的基类).
public:
SysTablespace()
: m_auto_extend_last_file(),
m_last_file_size_max(),
m_created_new_raw(),
m_is_tablespace_full(false),
m_sanity_checks_done(false) {
/* No op */
}
而对应的变量则是
/** The control info of the system tablespace. */
SysTablespace srv_sys_space;
/** The control info of a temporary table shared tablespace. */
SysTablespace srv_tmp_space;
tablespace数据文件的创建则是在SysTablespace::open_or_create中,因此我们来看这个函数.这个函数主要功能是遍历当前system tablespace的所有文件(m_files),然后存在就打开,不存在就创建对应的文件.
files_t::iterator begin = m_files.begin();
files_t::iterator end = m_files.end();
ut_ad(begin->order() == 0);
for (files_t::iterator it = begin; it != end; ++it) {
if (it->m_exists) {
err = open_file(*it);
} else {
err = create_file(*it);
}
当打开文件之后,将会把打开的文件进行缓存(InnoDB会将所有的tablespace缓存在fil_system中).
fil_node_create就是将当前的文件加入到文件cache中,这是因为在InnoDB中所有的文件都会统一管理,包括redo/undo,所有的tablespace都会根据他们的spaceid进行缓存.
char *fil_node_create(const char *name, page_no_t size, fil_space_t *space,
bool is_raw, bool atomic_write, page_no_t max_pages) {
auto shard = fil_system->shard_by_id(space->id);
fil_node_t *file;
file = shard->create_node(name, size, space, is_raw,
IORequest::is_punch_hole_supported(), atomic_write,
max_pages);
return (file == nullptr ? nullptr : file->name);
此时的疑问就是m_files是何时被初始化的,也就是system tablespace文件名的初始化,这里system tablespace的文件名初始化是在数据库init的时候被初始化的,因此我们来看相关代码.
InnoDB中有一个变量叫做innobase_data_file_path,这个变量是一个字符串,这个字符串包含了所有的system tablespace需要创建 的文件以及一些属性,这个字符串默认值是在InnoDB引擎初始化的时候初始化的.
/* Set default InnoDB temp data file size to 12 MB and let it be
auto-extending. */
innobase_data_file_path = (char *)"ibdata1:12M:autoextend";
}
因此system tablespace创建文件也会根据这个配置来创建,也就是当InnoDB参数解析完毕之后进入system tablespace的文件创建.
if (int error = innodb_init_params()) {
DBUG_RETURN(error);
}
/* After this point, error handling has to use
innodb_init_abort(). */
if (!srv_sys_space.parse_params(innobase_data_file_path, true)) {
ib::error(ER_IB_MSG_545)
<< "Unable to parse innodb_data_file_path=" << innobase_data_file_path;
DBUG_RETURN(innodb_init_abort());
}
parse_params这个函数将会解析innobase_data_file_path,然后根据不同的属性创建对应的文件,先来看参数解析.
然后第二步就是存储对应的文件名以及属性到m_files中,这里有一个重要的数据结构就是Datafile,每一个数据文件在InnoDB中的抽象就是Datafile.
while (*ptr != '\0') {
filepath = ptr;
ptr = parse_file_name(ptr);
if (*ptr == ':') {
/* Make filepath a null-terminated string */
*ptr = '\0';
ptr++;
}
size = parse_units(ptr);
ut_ad(size > 0);
if (0 == strncmp(ptr, ":autoextend", (sizeof ":autoextend") - 1)) {
m_auto_extend_last_file = true;
ptr += (sizeof ":autoextend") - 1;
if (0 == strncmp(ptr, ":max:", (sizeof ":max:") - 1)) {
ptr += (sizeof ":max:") - 1;
m_last_file_size_max = parse_units(ptr);
}
Datafile *datafile = &m_files.back();
datafile->make_filepath(path(), filepath, NO_EXT);
if (0 == strncmp(ptr, "new", (sizeof "new") - 1)) {
ptr += (sizeof "new") - 1;
}
if (0 == strncmp(ptr, "raw", (sizeof "raw") - 1)) {
ut_a(supports_raw);
ptr += (sizeof "raw") - 1;
/* Initialize new raw device only during initialize */
m_files.back().m_type =
#ifndef UNIV_HOTBACKUP
opt_initialize ? SRV_NEW_RAW : SRV_OLD_RAW;
#else /* !UNIV_HOTBACKUP */
SRV_OLD_RAW;
#endif /* !UNIV_HOTBACKUP */
}
if (*ptr == ';') {
++ptr;
}
order++;
}
非共享的tablespace
然后我们来看非共享的tablespace的创建,一般来说每创建一个表都会创建一个新的ibd文件,也就是tablespace. 而这种tablespace以及Redolog/undolog都是属于fil_space_t这个结构体.通过上面的代码我们知道system tablespace最终也是创建一个fil_space_t然后再接入整个系统的tablespace的管理的.
这里先来看两个特殊的tablespace,也就是REDO log和UNDO log,由于这两个log也都是磁盘上的文件,因此在InnoDB中会讲这两个log文件作为一种特殊的tablespace,来看他们的初始化.
static dberr_t create_log_files(char *logfilename, size_t dirnamelen, lsn_t lsn,
char *&logfile0, lsn_t &checkpoint_lsn) {
........................
/* Disable the doublewrite buffer for log files. */
fil_space_t *log_space = fil_space_create(
"innodb_redo_log", dict_sys_t::s_log_space_first_id,
fsp_flags_set_page_size(0, univ_page_size), FIL_TYPE_LOG);
.......................
}
下面的undo log.
static dberr_t srv_undo_tablespace_open(space_id_t space_id) {
....................................
space = fil_space_create(undo_name, space_id, flags, FIL_TYPE_TABLESPACE);
..............
以后我们详细分析undolog/redolog的时候会再来分析这个地方
接下来我们就来看当一般表的创建的时候会发生什么(设置了file-per-table),对于一般的表,创建tablespace是跟随着ibd文件一起创建的(上面的都是在初始化的时候创建)。