2.13.1 临时表优化
1. 背景
MySQL包括两类临时表,一类是通过create temporary table创建的临时表,一类是在query过程中using temporary而创建的临时表。 5.7之前,using temporary创建的临时表,默认只能使用myisam引擎,而在5.7之后,可以选择InnoDB引擎来创建。
临时表的引擎选择使用下面的这两个参数来决定:
mysql>show global variables like '%tmp%';
+----------------------------------+---------------------------------------+
| Variable_name | Value |
+----------------------------------+---------------------------------------+
| default_tmp_storage_engine | InnoDB |
| internal_tmp_disk_storage_engine | InnoDB |
2. 临时表空间
5.7之后,使用了独立的临时表空间来存储临时表数据,但不能是压缩表。临时表空间在实例启动的时候进行创建,shutdown的时候进行删除。
例如如下的配置:
mysql> show global variables like '%innodb_temp%';
+----------------------------+-----------------------+
| Variable_name | Value |
+----------------------------+-----------------------+
| innodb_temp_data_file_path | ibtmp1:12M:autoextend |
+----------------------------+-----------------------+
create temporary table和using temporary table将共用这个临时表空间。
3. 临时表优化
临时表会伴随着大量的数据写入和读取,尤其是internal_tmp_table。所以,InnoDB专门对临时表进行了优化。 InnoDB使用如下两个标示临时表:
dict_tf2_temporary: 表示普通临时表
dict_tf2_intrinsic: 表示内部临时表
这两个标示,会在IBD文件的segment header占用两个bit位。intrinsic一定是temproary,也就是temproary上进行的优化 完全适用于intrinsic表上。
下面来看下具体的优化:
3.1. redo
临时表在连接断开或者数据库实例关闭的时候,会进行删除,所以,临时表的数据不需要redo来保护,即recovery的过程中 不恢复临时表,只有临时表的metadata使用了redo保护,保护元数据的完整性,以便异常启动后进行清理工作。
临时表的元数据,5.7之后,使用了一个独立的表进行保存,这样就不要使用redo保护,元数据也只保存在内存中。 但这有一个前提,必须使用共享的临时表空间,如果使用file-per-table,仍然需要持久化元数据,以便异常恢复清理。
3.2 undo
temporary table仍然需要语句级的回滚,所以,需要为数据生成undo。但intrinsic table不需要回滚,所以,intrinsic table 减少了undo的生成,性能更高。
3.3 lock
因为临时表只有本线程可以看见,所以减少了InnoDB的加锁过程。
可以看下insert的时候,进行的分支判断:
row_insert_for_mysql(
const byte* mysql_rec,
row_prebuilt_t* prebuilt)
{
/* For intrinsic tables there a lot of restrictions that can be
relaxed including locking of table, transaction handling, etc.
Use direct cursor interface for inserting to intrinsic tables. */
if (dict_table_is_intrinsic(prebuilt->table)) {
return(row_insert_for_mysql_using_cursor(mysql_rec, prebuilt));
} else {
return(row_insert_for_mysql_using_ins_graph(
mysql_rec, prebuilt));
}
}
row_insert_for_mysql_using_cursor直接跳过了加锁的lock_table过程。
然后,如果是intrinsic table,就直接插入,减少了undo的生成。 如果不是,需要加lock,并生成undo信息。
if (dict_table_is_intrinsic(index->table)) {
index->rec_cache.rec_size = rec_size;
*rec = page_cur_tuple_direct_insert(
page_cursor, entry, index, n_ext, mtr);
} else {
/* Check locks and write to the undo log,
if specified */
err = btr_cur_ins_lock_and_undo(flags, cursor, entry,
thr, mtr, &inherit);
插入的时候,如果是临时表。就关闭redo的生成。如下面的代码所示:
if (dict_table_is_temporary(index->table)) {
/* Disable REDO logging as the lifetime of temp-tables is
limited to server or connection lifetime and so REDO
information is not needed on restart for recovery.
Disable locking as temp-tables are local to a connection. */
ut_ad(flags & BTR_NO_LOCKING_FLAG);
ut_ad(!dict_table_is_intrinsic(index->table)
|| (flags & BTR_NO_UNDO_LOG_FLAG));
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
Last updated
Was this helpful?