> For the complete documentation index, see [llms.txt](https://hhyo.gitbook.io/mysql5-7/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hhyo.gitbook.io/mysql5-7/2/24innodb-gong-neng-you-hua/241-lin-shi-biao-you-hua.md).

# 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的时候，进行的分支判断：

```c
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);
    }
```
