为什么不能在MySQL中使用MEMORY存储引擎的原因是什么?


28

最近,我发现MySQL具有一个我不知道的“内存”引擎(我的大部分数据库工作是针对业余项目的,因此我会随时了解自己的需求)。看来此选项应该可以大大提高我的性能,所以我想知道它是否有任何缺点。我知道的两个是:

  1. 我需要有足够的RAM来容纳有问题的表。
  2. 如果机器关闭,表将丢失。

我认为#1应该不是问题,因为我正在使用AWS EC2,并且可以根据需要移到具有更多内存的实例类型。我相信我可以通过根据需要转储回磁盘来减轻#2的压力。

还有什么其他问题?内存引擎能否提供比MyISAM或InnoDB更差的性能?我认为我读到的东西与此引擎的索引不同;这是我需要担心的事情吗?

Answers:


27

查看http://dev.mysql.com/doc/refman/5.1/en/memory-storage-engine.html上的功能可用性列表,可以跳出两个可能的问题:

  1. 没有事务或FK支持,这意味着您将需要在自己的代码中管理事务完整性和引用完整性(最终效率可能比让数据库为您降低效率要低得多,尽管这在很大程度上取决于应用程序的预期的行为模式)。
  2. 仅表级锁定:如果您的应用程序需要多个并发写入器到同一组表,或者在您的读取操作使用锁来确保读取一致的数据的情况下,这可能是可伸缩性的重大障碍-在这种情况下,基于磁盘的表支持更精细的锁粒度,如果当前有足够多的内容缓存在RAM中,性能将会好得多。

除此之外,假设您有足够的RAM,那么基于内存的表应该比基于磁盘的表快。显然,您需要考虑将快照拍摄到磁盘上以解决服务器实例重置时发生的问题,如果数据需要经常捕获(如果您可能会损失一天的时间),则这可能完全抵消总体性能优势。在这种情况下,您每天只能备份一次,但是在大多数情况下是不可接受的)。

一种替代方法可能是:

  1. 使用基于磁盘的表,但是请确保您有足够的RAM在任何给定时间将它们全部保存在RAM中(并且“足够的RAM”可能比您认为的要多,因为您需要考虑计算机,操作系统上的任何其他进程) IO缓冲区/缓存等)
  2. 在每次启动时扫描表的全部内容(所有数据和索引页),以将内容预加载到内存中,SELECT * FROM <table> ORDER BY <pkey fields>然后针对每个表,随后SELECT <indexed fields> FROM <table> ORDER BY <index fields>针对每个索引

这样,所有数据都在RAM中,您只需担心写入操作的I / O性能。如果您的应用程序的通用工作集比整个数据库小(通常是这种情况-在大多数应用程序中,大多数用户只会在最近的时间查看最新数据),所以最好选择更多的数据您可以扫描以将其预加载到内存中,从而允许其余磁盘按需从磁盘加载。


为什么您需要内存数据库的事务完整性?如果电源关闭,则无论如何您将失去一切。
osa 2014年

@osa:假设它允许并发访问而不是序列化所有内容,那么您将需要某种形式的完整性管理。
David Spillett 2014年

14

在很多情况下,不使用内存存储引擎-以及何时使用InnoDB会更快。您只需要考虑并发性,而不是琐碎的单线程测试。

如果您有足够大的缓冲池,那么InnoDB也将完全驻留在内存中以进行读取操作。数据库具有缓存。他们热身!

另外-不要低估行级锁定和MVCC的值(读取器不会阻止写入器)。当写入必须持续到磁盘时,它可能会“变慢”。但是至少在写操作期间不会像在内存表上那样阻塞(没有MVCC;表级锁定)。


3

作为记录。我在内存中测试了Mysql表以存储一些信息。而且我测试了PHP的APC(APCu)来存储相同的信息。

适用于58000个注册表。(varchar +整数+日期)。

  1. 原始信息24 mb(文本格式)(csv格式)。
  2. PHP的APC使用44.7mb RAM。
  3. Mysql's Table使用575mb RAM。

该表只有一个索引,所以我不认为这是主要因素。

结论:

内存表不是“大”表的选项,因为它使用了过多的内存。


2
这是一个过于简单的答案。可以通过使用较小的数据类型,将BTREE明确定义为索引类型并限制加载到RAM中的数据量来调整MEMORY存储引擎。对于较小的集合,它仍然是可行的选择。还有其他因素,例如主动磁盘I / O。见我的帖子dba.stackexchange.com/questions/6156/...dba.stackexchange.com/questions/2868/...
RolandoMySQLDBA

实际上,我检查过更改索引(甚至完全删除了索引),并且大小没有太大变化(包括从头开始重建表)。恕我直言,Mysql在后台进行了某些操作,可能是某种优化或在每列中分配最大大小(例如varchar)。
magallanes 2014年

6
原因几乎可以肯定是由于您的VARCHAR:"MEMORY tables use a fixed-length row-storage format. Variable-length types such as VARCHAR are stored using a fixed length." dev.mysql.com/doc/refman/5.6/en/memory-storage-engine.html实际上,VARCHAR在MEMORY引擎中看起来像是CHAR。
马修1471年

2

基于MEMORY的表的另一个缺点是它们不能在同一查询中多次引用。至少在v5.4之前发现了该行为。对于CTE(自v8.x版本起),如何无需针对复杂过程使用基于内存的中间表。


1

根据MySQL和MariaDB手册,MEMORY存储不支持BLOB和CLOB(各种TEXT类型)。出于我们自己的目的,这使MEMORY存储引擎几乎无用。

http://dev.mysql.com/doc/refman/5.7/en/memory-storage-engine.html

MEMORY表不能包含BLOB或TEXT列。

https://mariadb.com/kb/zh-CN/mariadb/memory-storage-engine/

可变长度类型(如VARCHAR)可以在MEMORY表中使用。MEMORY表不支持BLOB或TEXT列。

当尝试仅将数据库的一部分转换为MEMORY存储时,我发现不支持存储间引擎外键。因此,所有应该包含表外键引用的表(包含BLOB / CLOB)也应该属于非内存存储类型(至少,这会影响InnoDB子表)。


1

MEMORY表不用于持久存储,尤其是大数据子集或对保留至关重要的任何内容。根据我的经验,它们的最佳目的是在复杂过程中创建和填充临时表期间存储瞬态记录,如果您将引擎的关键缓冲区阈值设置得足够高而不会引起这种情况,则此表的执行速度将比大多数其他表类型快得多。磁盘写入。为此,由于没有磁盘I / O,因此它可以比MyISAM或InnoDB快一个数量级,并且在一个表封装在特定过程中的情况下,索引和关系的重要性不如它们将在期望持久性的地方。


1

除了以前的答案。直接来自MySQL 5.7手册:

“由于执行更新时单线程执行和表锁定开销导致的争用,内存性能受到限制。这会在负载增加时限制可伸缩性,尤其是对于包含写操作的语句混合而言。”

...这是一个非常实际的限制。例如:当您有多个会话试图创建快速的MEMORY临时表时,单线程可能会导致严重的性能瓶颈。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.