31 临时表,内存表
- 临时表
- 建表语法是 create temporary table …。
- 一个临时表只能被创建它的 session 访问,对其他线程不可见。所以,session A 创建的临时表 t,对于 session B 就是不可见的。
- 临时表可以与普通表同名。
- session A 内有同名的临时表和普通表的时候,show create 语句,以及增删改查语句访问的是临时表。
- show tables 命令不显示临时表。
由于临时表只能被创建它的 session 访问,所以在这个 session 结束的时候,会自动删除临时表。也正是由于这个特性,临时表就特别适合join 优化这种场景。为什么呢?
- 内存表
使用Memory引擎的表为内存表,其所有数据都存放在内存中
内存表根据数据的插入顺序存放数据,主键索引上记录的是数据存放的位置(即hash索引)
InnoDB与Memory引擎的比较:
- InnoDB 表的数据总是有序存放的,而内存表的数据就是按照写入顺序存放的;
- 当数据文件有空洞的时候,InnoDB 表在插入新数据的时候,为了保证数据有序性,只能在固定的位置写入新值,而内存表找到空位就可以插入新值;
- 数据位置发生变化的时候,InnoDB 表只需要修改主键索引,而内存表需要修改所有索引;
- InnoDB 表用主键索引查询时需要走一次索引查找,用普通索引查询的时候,需要走两次索引查找。而内存表没有这个区别,所有索引的“地位”都是相同的。
- InnoDB 支持变长数据类型,不同记录的长度可能不同;内存表不支持 Blob 和 Text 字段,并且即使定义了 varchar(N),实际也当作 char(N),也就是固定长度字符串来存储,因此内存表的每行数据长度相同。
但是内存表不适合在生产环境上使用,因为其存在锁粒度问题和数据持久化问题。
但是内存表适合用作临时表,因为
- 相比于 InnoDB 表,使用内存表不需要写磁盘,往表 temp_t 的写数据的速度更快;
- 索引 b 使用 hash 索引,查找的速度比 B-Tree 索引快;
- 临时表数据只有 2000 行,占用的内存有限。
所以很多时候在内部会使用内存临时表
如果要自己建内存临时表,可采用以下语句
create temporary table temp_t(id int primary key, a int, b int, index (b))engine=memory;
32 自增主键
- 自增主键存放位置
- MyISAM 引擎的自增值保存在数据文件中。
- 在 MySQL 5.7 及之前的版本,自增值保存在内存里,并没有持久化
- 在 MySQL 8.0 版本,将自增值的变更记录在了 redo log 中,重启的时候依靠 redo log 恢复重启之前的值。
- 自增值修改机制
- 如果插入数据时 id 字段指定为 0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段;
- 如果插入数据时 id 字段指定了具体的值,就直接使用语句里指定的值。
根据要插入的值和当前自增值的大小关系,自增值的变更结果也会有所不同。假设,某次要插入的值是 X,当前的自增值是 Y
- 如果 X<Y,那么这个表的自增值不变;
- 如果 X≥Y,就需要把当前自增值修改为新的自增值。
新的自增值生成算法是:从 auto_increment_offset 开始,以 auto_increment_increment 为步长,持续叠加,直到找到第一个大于 X 的值,作为新的自增值。
其中,auto_increment_offset 和 auto_increment_increment 是两个系统参数,分别用来表示自增的初始值和步长,默认值都是 1。
- 自增键不连续的原因
为了提高性能和并发度,MySQL在获取自增主键的值时会加锁,获取完后无须等待语句执行完即释放锁,之后即使语句执行失败,也不会将自增主键回滚
因此唯一键冲突,事务回滚均会导致自增主键不连续。
另外,在批量申请自增主键时,MySQL会作优化,也有可能会导致自增主键不连续。
- 自增锁的优化
MySQL 5.1.22 版本引入了一个新策略,新增参数 innodb_autoinc_lock_mode,默认值是 1。
- 这个参数的值被设置为 0 时,表示采用之前 MySQL 5.0 版本的策略,即语句执行结束后才释放锁;
- 这个参数的值被设置为 1 时:普通 insert 语句,自增锁在申请之后就马上释放;类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放;
- 这个参数的值被设置为 2 时,所有的申请自增主键的动作都是申请后就释放锁。
在生产上,尤其是有 insert … select 这种批量插入数据的场景时,从并发插入数据性能的角度考虑,我建议你这样设置:innodb_autoinc_lock_mode=2 ,并且 binlog_format=row. 这样做,既能提升并发性,又不会出现数据一致性问题。
对于批量插入数据的语句,MySQL 有一个批量申请自增 id 的策略:
- 语句执行过程中,第一次申请自增 id,会分配 1 个;
- 1 个用完以后,这个语句第二次申请自增 id,会分配 2 个;
- 2 个用完以后,还是这个语句,第三次申请自增 id,会分配 4 个;
- 依此类推,同一个语句去申请自增 id,每次申请到的自增 id 个数都是上一次的两倍。
因此在批量申请时,可能会造成申请多了导致有些申请的主键浪费了没用上,也有可能导致自增主键不连续