38-都说InnoDB好,那还要不要使用Memory引擎?
# 内存表的数据组织结构
create table t1(id int primary key, c int) engine=Memory;
create table t2(id int primary key, c int) engine=innodb;
insert into t1 values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
insert into t2 values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
2
3
4
t1 的 (0,0) 在最后一行,而 t2 (0,0) 在第一行。这是因为 InnoDB 主键索引上的存储的数据值是有序的。但是 Memory 的数据和索引是分开的,Memory 主键是 hash 索引并且上面存储的是值的位置。
笔记
两者的不同
- InnoDB 引擎把数据放在主键索引上,其他索引上保存的是主键 id。这种方式,称为索引组织表(Index Organizied Table)。
- Memory 引擎采用的是把数据单独存放,索引上保存数据位置的数据组织形式,称为堆组织表(Heap Organizied Table)。
- InnoDB 存放的数据总是有序的,而 Memory 是按照写入顺序来存放的。
- 当数据文件有空洞时候,InnoDB 插入新的数据,为了保证顺序性只能在固定位置插入,而 Memory 会找到空洞位置插入。
- 数据位置发生变化的,InnoDB 只用修改主键索引,Memory 需要修改所有索引。
- InnoDB 使用主键索引走一次索引查询,普通索引走两次索引查询,Memory 没有这个说法,所有的索引都是相同 “地位”。
- InnoDB 支持变长数据类型,不同记录的长度可能不同;内存表不支持 Blob 和 Text 字段,并且即使定义了 varchar (N),实际也当作 char (N),也就是固定长度字符串来存储,因此内存表的每行数据长度相同。
# hash 索引和 B-Tree 索引
内存表也是可以用使用 B-Tree 索引的。通过下面语句将 t1 改成 b 树。
alter table t1 add index a_btree_index using btree (id);
执行这两个查询语句可以发现,第一条使用了 b 树索引返回了 0、1、2、3、4,第二个强制使用了主键索引结果为 1、2、3、4、0。
注意
这里要弄清楚一个概念,memory 表的默认索引类型为 hash 索引。
alter table t1 add index a_btree_index using btree (id);
这条语句表示你为 'id' 字段添加了一个名为 'a_btree_index' 的 B-Tree 索引。但是这并没有改变仍存在的主键的哈希索引。只是意味着,当查询可以选择时(不强制使用特定索引),它可以选择使用你新添加的 B-Tree 索引。但是强制使用了主键索引后,就还是用到了 hash 索引类型,所以读取顺序就和写入顺序一致。
select * from t1 where id < 5;
select * from t1 force index(primary) where id < 5;
2
虽然看起来 memory 表很多好处,但是还是不建议在生产环境使用。主要有两个问题
- 锁粒度问题;
- 数据持久化问题。
# 内存表的锁
内存表不支持行锁,只支持表锁。这就意味着一旦有更新就会锁住整个表。如果存在并发问题,这个总体效果很差,会明显不如 innodb,更不用说如果有长事务了。
# 持久性问题
数据库重启的时候会把内存表中的数据清空。在主从架构下,这就是一个 bug 了。看这个流程
- 业务正常访问主库。
- 备库升级,重启,内存表 t 被清空。
- 客户端更新 t 的数据,备库线程会报错 “找不到更新的行”。
这是其一,其二是由于 MySQL 知道重启之后,内存表的数据会丢失。所以,担心主库重启之后,出现主备不一致,MySQL 在实现上做了这样一件事儿:在备库重启的时候就传了这条语句到主库,导致主库的内容也没了。
但是还是有些情况可以用的,比如之前说的 join 的时候如果没办法用到索引,就可以用到临时表将一部分数据放进去然后再与临时表进行 join。