MEMORY引擎不是Redis平替,因无持久化、无主从复制、表级锁致高并发写瓶颈、重启即丢数据,仅适用于短生命周期临时计算;真需缓存小表应选InnoDB+Redis Cache-Aside模式。

MySQL内存表(MEMORY引擎)不能替代Redis做缓存,尤其在生产环境。它不是Redis的平替,而是完全不同的东西——没有持久化、不支持主从复制、不处理并发写竞争、且重启即丢全部数据。拿它去“替代Redis缓存小表”,大概率会在凌晨三点收到告警。
为什么MEMORY表不适合当缓存层
很多人看到“内存存储”“快”就下意识对标Redis,但MEMORY表的设计目标根本不是缓存:
- 它不支持事务、无行级锁,
INSERT/UPDATE/DELETE并发高时极易锁表或死锁 - 所有数据仅存于RAM,mysqld进程重启、OOM killer干掉进程、甚至
FLUSH TABLES都会清空全表 - 不支持BLOB/TEXT类型,索引只能是哈希或B树,且哈希索引不支持范围查询——而很多“小表”恰恰需要
BETWEEN或LIKE 'prefix%' - 内存占用不可控:每行数据在内存中实际开销≈磁盘表的2–3倍,且
max_heap_table_size是全局限制,不是单表配额
MEMORY表唯一靠谱的用法场景
它只适合极短生命周期、强临时性、且能容忍丢失的中间计算结果:
- 大表JOIN前的临时维度映射(比如把城市ID→名称预加载进
MEMORY表,跑完即DROP) - 存储过程里做快速去重或聚合中间集(
CREATE TEMPORARY TABLE ... ENGINE=MEMORY) - 离线ETL任务中的高速暂存区(脚本控制生命周期,不跨会话)
注意:TEMPORARY表比普通MEMORY表更安全——它只对当前连接可见,断连自动销毁,不会被其他会话误读或锁住。
真要缓存小表,该怎么做
如果一张表只有几百~几千行、读远多于写、更新不频繁(比如配置项、状态码字典、地区列表),正确路径是:
- 保持原表用
InnoDB(保证ACID和持久化),在应用层加Redis缓存——用Cache-Aside模式,SELECT走Redis,UPDATE后DEL对应key - 若担心Redis单点,可改用
redis-py连接池 +sentinel模式,或直接上Redis Cluster - 若连Redis都不想加,退一步用MyBatis二级缓存(
org.apache.ibatis.cache.decorators.ScheduledCache),但要注意失效粒度粗、集群节点间不同步 - 极端情况:用
MySQL Query Cache?不行——MySQL 8.0已彻底移除,5.7也默认关闭,且命中率极低、易被FLUSH TABLES清空
容易被忽略的致命细节
即使你坚持要用MEMORY表,也必须确认三件事:
-
max_heap_table_size和tmp_table_size两个参数值是否一致?不一致会导致隐式转磁盘临时表,性能断崖下跌 - 表里有没有
auto_increment字段?MEMORY引擎的自增计数器不持久化,重启后从1开始,极易主键冲突 - 是否开了
sql_log_bin=OFF?否则所有MEMORY表变更都会写入binlog,从库同步失败(因为从库没这个引擎)
最后说一句:缓存的本质是“可控的冗余”。MySQL内存表既不可控(易丢)、又不冗余(无副本),它只是个带SQL接口的数组。别把它当缓存使。


















