文章目录
MySQL45讲 第38讲 都说InnoDB好,那还要不要使用Memory引擎?——阅读总结
一、引言
在 MySQL 数据库的世界里,InnoDB 引擎因其诸多优良特性而备受青睐。然而,Memory 引擎也以其独特的优势吸引着人们的目光。那么,在实际应用中,既然 InnoDB 如此出色,我们是否还需要使用 Memory 引擎呢?
二、内存表与 InnoDB 表数据组织结构对比
(一)InnoDB 引擎
InnoDB 采用索引组织表(Index Organizied Table)方式,数据存放在主键索引树上,主键索引是 B + 树结构。
这使得数据在存储时自然有序,例如在执行 “select * from t2;”(假设 t2 为 InnoDB 表)时,会按照叶子节点从左到右扫描,所以如果主键值为 0 的数据存在,它会出现在结果集的第一行。
(二)Memory 引擎
Memory 引擎则是堆组织表(Heap Organizied Table)的数据组织形式,数据单独存放,主键 id 索引中保存的是每个数据的位置,其主键 id 为 hash 索引,key 无序。当执行 “select * from t1;”(假设 t1 为 Memory 表)时,走全表扫描,即顺序扫描数据数组,因此 0 会在最后被读到并放入结果集。
如果使用范围查询,select * from t1 where id<5;
因为主键索引是哈希,所以会全表扫描。
(三)B-Tree 索引对内存表的影响
内存表虽然主键索引是 hash 索引,但也支持 B-Tree 索引。例如,在表 t1 的alter table t1 add index a_btree_index using btree (id);
id 列上创建 B-Tree 索引后,
执行 select * from t1 where id<5;
时,优化器会选择 B-Tree 索引,返回结果为 0 到 4。
而使用 force index (primary)
强行使用主键 id 索引时,id = 0 这一行就在结果集的最末尾,这表明 B-Tree 索引使内存表在特定查询下能实现有序返回结果,但与 InnoDB 的 B + 树索引在查询性能和数据组织上仍存在差异。
不建议在生产环境上使用内存表。这里的原因主要包括 两个方面: 1. 锁粒度问题; 2. 数据持久化问题。
三、内存表的锁机制及其影响
(一)锁类型
内存表不支持行锁,仅支持表锁。这意味着只要有一个更新操作在执行,就会阻塞其他所有对该表的**任何读写操作。**例如,在下图的场景中,session A 执行 update t1 set id = sleep (50) where id=1;
时,session B 执行 select * from t1 where id=2;
会进入锁等待状态,直到 session A 的更新操作完成。
(二)与 MDL 锁的区别
需要注意的是,这里的表锁与之前介绍的 MDL 锁虽然都是表级锁,但有所不同。MDL 锁主要用于控制对表结构的修改等操作,而内存表的表锁则针对数据的读写操作。
(三)对并发性能的影响
**与 InnoDB 支持行锁相比,内存表的表锁在处理并发事务时性能较差。**在高并发场景下,InnoDB 能够更细粒度地控制锁,允许多个事务同时对不同行进行读写操作,从而提高系统的整体并发性能。
四、内存表的数据持久性问题
(一)数据库重启对内存表的影响
内存表的数据存放在内存中,这一特性在数据库重启时会导致所有内存表被清空。在高可用架构下,这可能引发一系列问题。例如,在 M - S 架构中,若**备库硬件升级重启,内存表 t1 内容被清空,之后客户端发送更新语句修改表 t1 数据行时,备库应用线程会报错 “找不到要更新的行”,进而导致主备同步停止。**若此时发生主备切换,客户端会发现表 t1 的数据 “丢失”。
(二)双 M 结构中的特殊情况
在双 M 结构中,情况更为复杂。**由于 MySQL 担心主库重启后出现主备不一致,在数据库重启后会往 binlog 写入 “DELETE FROM t1”。**当备库重启时,备库 binlog 里的 delete 语句会传到主库,导致主库内存表内容被删除,使得主库内存表数据突然清空,这对业务的影响更加难以预测和控制。
五、InnoDB 与 Memory 引擎的适用场景分析
(一)InnoDB 引擎的优势与适用场景
- 数据安全与并发性能
InnoDB 支持行锁,在高并发场景下能够提供更好的并发性能,同时其数据持久性较好,适合处理大量更新操作和对数据一致性要求较高的业务场景。 - 读性能优化
即使是读操作较多且数据量不大的表,使用 InnoDB 也能通过其 Buffer Pool 将数据缓存到内存中,从而获得不错的读性能。
(二)Memory 引擎的适用场景 - 内存临时表
- 无并发问题
内存临时表不会被其他线程访问,不存在并发性问题,因此在复杂查询的优化过程中,如分库分表系统的跨库查询中,可用于暂存中间数据。 - 数据可清空
临时表在重启后需要删除数据,这与内存表重启清空数据的特性相契合,不会产生额外的数据管理问题。 - 不影响主库用户线程
在主备架构中,备库的临时表不会影响主库的用户线程,保证了系统的正常运行。
(三)内存临时表的优化示例
在第 35 篇 join 语句优化的例子中,使用内存临时表效果更好。如将临时表改为内存临时表并在字段 b 上创建 hash 索引后,导入数据和执行 join 的速度都比使用 InnoDB 临时表更快。具体操作如下:
create temporary table temp_t(id int primary key, a int, b int, index (b))engine=memory;
insert into temp_t select * from t2 where b>=1 and b<=2000;
select * from t1 join temp_t on (t1.b=temp_t.b);
六、总结
综上所述,在生产环境中,一般不建议使用普通内存表作为常规数据表,因其存在锁粒度粗和数据持久性差等问题。而 InnoDB 引擎在数据安全和并发性能方面表现出色,适用于大多数业务场景。但内存临时表利用了 Memory 引擎的特性,在特定场景下能够发挥重要作用,如复杂查询中的临时数据存储和加速查询执行等。