MyISAM与InnoDB

我正在处理一个涉及大量数据库写入的项目,我会说(70%插入和30%读取)。这个比率还包括我认为是一次读取和一次写入的更新。读取可能是脏的(例如,我不需要读取时100%准确的信息)。
该任务将在一小时内完成100万数据库事务。

我在网上读了很多关于MyISAM和InnoDB之间差异的东西,对于我将用于此任务的特定数据库/表来说,MyISAM似乎是我的明显选择。从我似乎正在阅读的内容来看,如果需要事务,InnoDB很好,因为支持行级锁定。

有没有人有过这种类型的负载(或更高)的经验?MyISAM是要走的路吗?

302985 次浏览

我不是数据库专家,也不是凭经验说话。但是:

MyISAM表使用表级锁定。根据您的流量估计,您每秒有近200次写入。使用MyISAM,任何时候都可能只有一个在进行中。您必须确保您的硬件能够跟上这些事务以避免超载,即单个查询不能超过5ms。

这表明您需要一个支持行级锁定的存储引擎,即InnoDB。

另一方面,编写一些简单的脚本来模拟每个存储引擎的负载,然后比较结果应该相当简单。

每个应用程序都有自己的使用数据库的性能配置文件,并且可能会随着时间的推移而改变。

您可以做的最好的事情是测试您的选项。在MyISAM和InnoDB之间切换是微不足道的,因此加载一些测试数据并针对您的站点启动jmeter,看看会发生什么。

如果您使用MyISAM,您将不会每小时执行任何个事务,除非您认为每个DML语句都是一个事务(在任何情况下,在崩溃的情况下都不会是持久的或原子的)。

因此,我认为你必须使用InnoDB。

每秒300个事务听起来很多。如果您绝对需要这些事务在电源故障期间保持持久,请确保您的I/O子系统可以轻松处理每秒如此多的写入。您至少需要一个带有电池支持缓存的RAID控制器。

如果你能承受一点持久性的打击,你可以使用InnoDB将innodb_flush_log_at_trx_commit设置为0或2(有关详细信息,请参阅文档),你可以提高性能。

有许多补丁可以增加Google和其他补丁的并发-如果没有它们仍然无法获得足够的性能,这些补丁可能会很有趣。

我曾使用MySQL开发过一个大容量系统,我试过MyISAM和InnoDB。

我发现MyISAM中的表级锁定给我们的工作负载带来了严重的性能问题,听起来和你的工作负载很相似。不幸的是,我还发现InnoDB下的性能也比我希望的要差。

最后,我通过将数据分段来解决争用问题,以便插入到“热”表中并选择从未查询过的热表。

这也允许删除(数据是时间敏感的,我们只保留了X天的价值)发生在“陈旧”表上,这些表同样没有被选择查询触及。InnoDB在批量删除上似乎性能很差,所以如果你打算清除数据,你可能希望以这样一种方式构建它,即旧数据在一个陈旧的表中,可以简单地删除而不是对其运行删除。

当然,我不知道你的应用程序是什么,但希望这能让你深入了解MyISAM和InnoDB的一些问题。

对于具有更多写入和读取的负载,您将受益于InnoDB。由于InnoDB提供行锁定而不是表锁定,因此您的SELECT可以并发,不仅可以相互并发,还可以与许多INSERT并发。但是,除非您打算使用SQL事务,否则请将InnoDB提交刷新设置为2(innodb_flush_log_at_trx_commit)。这为您提供了大量原始性能,否则在将表从MyISAM移动到InnoDB时会丢失这些性能。

此外,考虑添加复制。这给了你一些读取缩放,并且因为你说你的读取不必是最新的,你可以让复制落后一点。只要确保它可以在除了最重流量之外的任何情况下赶上,否则它将永远落后并且永远不会赶上。但是,如果你采用这种方式,我强烈建议你将读取与从服务器隔离开来,并将复制延迟管理与数据库处理程序隔离开来。如果应用程序代码不知道这一点,那就简单多了。

最后,要注意不同的表负载。你不会在所有表上具有相同的读/写比率。一些读取接近100%的小表可以保持MyISAM。同样,如果你有一些接近100%写入的表,你可能会从INSERT DELAYED中受益,但这只在MyISAM中受支持(DELAYED子句对于InnoDB表被忽略)。

但基准可以肯定。

根据我的经验,只要您不执行DELETE、UPDATE、大量单个INSERT、事务和全文索引,MyISAM是更好的选择。顺便说一句,CHECK TABLE很糟糕。随着表的行数越来越老,你不知道它什么时候会结束。

myisam是这种类型的工作负载的NOGO(高并发写入),我没有那么多的经验,与innoDB(测试了3次,发现在每种情况下,性能吸,但它已经有一段时间以来,最后一次测试)如果您没有被迫运行mysql,请考虑尝试使用postgres,因为它可以更好地处理并发写入

对于这个读/写比率,我想InnoDB会表现得更好。由于你可以处理脏读,你可以(如果你负担得起)复制到一个从服务器,让你所有的读都去从服务器。此外,考虑批量插入,而不是一次插入一条记录。

我认为这是一篇关于解释差异以及何时应该使用一个而不是另一个的优秀文章:http://tag1consulting.com/MySQL_Engines_MyISAM_vs_InnoDB

我发现即使Myisam有锁争用,在大多数情况下它仍然比InnoDb快,因为它使用了快速锁获取方案。我试过好几次InnoDB,总是出于这样或那样的原因回退到MyIsam。此外,InnoDB在巨大的写入负载中可能是非常CPU密集型的。

我在一个表中简要地讨论这个问题,所以你可以得出结论是InnoDB还是MyISAM

以下是您应该在何种情况下使用哪个数据库存储引擎的小概述:

MyISAM   InnoDB----------------------------------------------------------------Required full-text search                        Yes      5.6.4----------------------------------------------------------------Require transactions                                      Yes----------------------------------------------------------------Frequent select queries                          Yes----------------------------------------------------------------Frequent insert, update, delete                           Yes----------------------------------------------------------------Row locking (multi processing on single table)            Yes----------------------------------------------------------------Relational base design                                    Yes

总结

  • 在几乎所有情况下,InnoDB是最好的方法
  • 但是,经常阅读,几乎没有写作,使用MyISAM
  • 在MySQL中全文搜索<=5.5,使用MyISAM

我尝试在MyISAM和InnoDB表中插入随机数据。结果非常令人震惊。MyISAM插入100万行所需的时间比InnoDB少了几秒钟,只需1万!

还可以查看MySQL本身的一些直接替换:

MariaDB

http://mariadb.org/

MariaDB是一款为MySQL提供直接替换功能的数据库服务器。MariaDB由一些MySQL的原始作者构建,并得到了更广泛的自由和开源软件开发人员社区的帮助。除了MySQL的核心功能外,MariaDB还提供了一组丰富的功能增强,包括备用存储引擎、服务器优化和补丁。

percona服务器

https://launchpad.net/percona-server

MySQL的增强插件替代品,具有更好的性能、改进的诊断和添加的功能。

稍微偏离主题,但为了留档和完整性,我想补充以下内容。

一般来说,使用InnoDB将导致更少的复杂应用程序,可能也更bug。因为你可以将所有参照完整性(外键约束)放入数据模型中,所以你不需要像MyISAM那样多的应用程序代码。

每次插入、删除或替换一条记录时,您都必须检查和维护关系。例如,如果您删除了父记录,也应该删除所有子记录。例如,即使在一个简单的博客系统中,如果您删除了博客记录,您也必须删除评论记录、喜欢等。在InnoDB中,这是由数据库引擎自动完成的(如果您在模型中指定了禁忌),并且不需要应用程序代码。在MyISAM中,这必须编码到应用程序中,这在Web服务器中非常困难。Web服务器本质上是非常并发/并行的,因为这些操作应该是原子的,而MyISAM不支持真正的事务,所以将MyISAM用于Web服务器是有风险/容易出错的。

同样在大多数一般情况下,InnoDB的性能会好得多,原因有很多,其中之一是能够使用记录级锁定而不是表级锁定。不仅在写比读更频繁的情况下,也在大型数据集上有复杂连接的情况下。我们注意到,仅仅在非常大的连接中使用InnoDB表而不是MyISAM表,性能就提高了3倍(需要几分钟)。

我想说,一般来说,InnoDB(使用具有参照完整性的3NF数据模型)应该是使用MySQL时的默认选择。MyISAM应该只在非常特定的情况下使用。它很可能会执行更少,导致更大、更有错误的应用程序。

话虽如此。数据建模是一门在网页设计师/程序员中很少发现的艺术。无意冒犯,但它确实解释了MyISAM被如此广泛地使用。

请注意我的正规教育和经验是在Oracle,而我与MySQL的工作完全是个人和自己的时间,所以如果我说的事情对Oracle来说是正确的,但对MySQL来说不是正确的,我道歉。虽然两个系统有很多共同点,关系理论/代数是一样的,关系数据库仍然是关系数据库,仍然有很多差异!!

我特别喜欢(以及行级锁定)InnoDB是基于事务的,这意味着你可能会为Web应用程序的一个“操作”多次更新/插入/创建/更改/删除/等。出现的问题是,如果这些更改/操作中只有一些最终被提交,而其他更改/操作没有被提交,你大多数时候(取决于数据库的具体设计)最终会得到一个数据/结构冲突的数据库。

备注:在Oracle中,创建/更改/删除语句被称为“DDL”(数据定义)语句,并隐式触发提交。插入/更新/删除语句被称为“DML”(数据操作),是没有自动提交的,但只有在执行DDL、提交或退出/退出时(或者如果您将会话设置为“自动提交”,或者如果您的客户端自动提交)。在使用Oracle时必须意识到这一点,但我不确定MySQL如何处理这两种类型的语句。因此,我想明确表示,我不确定MySQL的这一点;只有与Oracle一起。

基于事务的引擎Excel的示例:

假设我或你在一个网页上注册参加一个免费的活动,该系统的主要目的之一是只允许最多100人注册,因为这是该活动的座位限制。一旦达到100个注册,系统将禁止进一步的注册,至少直到其他人取消。

在这种情况下,可能有一个表用于客人(姓名、电话、电子邮件等),以及第二个表用于跟踪已注册的客人数量。因此,对于一个“事务”,我们有两个操作。现在假设在将客人信息添加到GUESTS表之后,存在连接丢失,或者具有相同影响的错误。GUESTS表已更新(插入),但在“可用座位”更新之前连接丢失。

现在我们有一个客人添加到客人表,但可用座位的数量现在是不正确的(例如,值是85,而实际上是84)。

当然有很多方法可以处理这个问题,例如使用“100减去客人表中的行数”跟踪可用座位,或者一些检查信息是否一致的代码,等等。但是对于InnoDB这样的基于事务的数据库引擎,要么提交所有个操作,要么提交个操作。这在很多情况下都很有帮助,但就像我说的,这不是唯一安全的方法,不(然而,这是一种很好的方法,由数据库处理,而不是程序员/脚本编写者)。

这就是“基于事务”在这种情况下的基本含义,除非我遗漏了什么——要么整个事务按预期成功,要么什么都没有被更改,因为只进行部分更改可能会使数据库变得非常混乱,甚至可能破坏它。

但是我再说一遍,这不是避免弄得一团糟的唯一方法,但它是引擎自己处理的方法之一,让你写代码/脚本,只需要担心“事务成功与否,如果不成功我该怎么办(比如重试)”,而不是手动写代码从数据库外部“手动”检查它,并为此类事件做更多的工作。

最后,关于表锁定与行锁定的注意事项:

免责声明:关于MySQL,我可能在以下所有方面都是错误的,假设/示例情况需要研究,但我可能在完全可能导致MySQL损坏的方面是错误的。然而,这些例子在一般编程中非常真实,即使MySQL有更多的机制来避免这样的事情…

无论如何,我相当有信心同意那些认为不是一次允许多少连接围绕锁定的表工作的人的观点。事实上,多个连接是锁定桌子的全部意义!!这样其他进程/用户/应用程序就无法通过同时进行更改来破坏数据库。

如何将两个或更多的连接工作在同一行使一个非常糟糕的一天?假设有两个进程都想要/需要更新同一行中的相同值,假设该行是公共汽车游览的记录,并且两个进程中的每个进程同时想要将“骑手”或“available_seats”字段更新为“当前值加1”。

让我们假设一下,一步一步:

  1. 进程一读取当前值,假设它为空,因此到目前为止为“0”。
  2. 进程二也读取当前值,该值仍然为0。
  3. 进程1写入(当前+1),即1。
  4. 进程二应该写入2,但由于它读取当前值之前进程一写入新值,它也将1写入表。

我是不确定,两个连接可以这样混合,在第一个写入之前都读取……但如果不是,那么我仍然会看到一个问题:

  1. 进程一读取当前值,即0。
  2. 进程1写入(当前+1),即1。
  3. 进程2现在读取当前值。但是当进程1确实写入(更新)时,它没有提交数据,因此只有同一进程可以读取它更新的新值,而所有其他进程都看到旧值,直到有提交。

此外,至少对于Oracle数据库,有隔离级别,我不会浪费时间试图解释。这是一篇关于这个主题的好文章,每个隔离级别都有它的优点和缺点,这与基于事务的引擎在数据库中的重要性有关…

最后,MyISAM中可能有不同的保护措施,而不是外键和基于事务的交互。首先,整个表都被锁定了,这使得事务/FK不太可能是需要

唉,如果你意识到这些并发问题,是的,你可以不那么安全,只是编写你的应用程序,设置你的系统,以便不可能出现此类错误(然后你的代码负责,而不是数据库本身)。然而,在我看来,我想说,最好总是使用尽可能多的保护措施,进行防御性编程,并始终意识到人为错误是不可能完全避免的。它发生在每个人身上,任何说他们对此免疫的人一定是在撒谎,或者只是写了一个“Hello World”应用程序/脚本。;-)

我希望其中一些对某人有帮助,甚至更多,我希望我刚才不是假设的罪魁祸首,是错误的人!!如果是这样,我道歉,但这些例子很值得思考,研究风险,等等,即使它们在这个特定的背景下没有潜力。

请随时纠正我,编辑这个“答案”,甚至投票否决它。只是请尝试改进,而不是用另一个来纠正我的错误假设。;-)

这是我的第一个回应,所以请原谅所有免责声明等的长度……我只是不想在我不完全确定的时候听起来傲慢!

InnoDB提供:

ACID transactionsrow-level lockingforeign key constraintsautomatic crash recoverytable compression (read/write)spatial data types (no spatial indexes)

在InnoDB中,除了TEXT和BLOB之外,一行中的所有数据最多可以占用8,000个字节。InnoDB没有全文索引。在InnoDB中,COUNT(*)(当不使用WHERE、GROUP BY或JOIN时)的执行速度比在MyISAM中慢,因为行数不是内部存储的。InnoDB将数据和索引存储在一个文件中。InnoDB使用缓冲池来缓存数据和索引。

MyISAM提供:

fast COUNT(*)s (when WHERE, GROUP BY, or JOIN is not used)full text indexingsmaller disk footprintvery high table compression (read only)spatial data types and indexes (R-tree)

MyISAM具有表级锁定,但没有行级锁定。没有事务。没有自动崩溃恢复,但它确实提供修复表功能。没有外键约束。与InnoDB表相比,MyISAM表在磁盘上的大小通常更紧凑。如果需要,MyISAM表可以通过使用myisampack压缩进一步大大减小大小,但变得只读。MyISAM将索引存储在一个文件中,数据存储在另一个文件中。MyISAM使用键缓冲区来缓存索引,并将数据缓存管理留给操作系统。

总的来说,我会推荐InnoDB用于大多数目的,而MyISAM仅用于特殊用途。InnoDB现在是新MySQL版本中的默认引擎。

人们经常谈论性能,读与写,外键等,但在我看来,存储引擎还有一个必备的功能:原子更新。

试试这个:

  1. 对您的MyISAM表发出UPDATE,需要5秒。
  2. 当UPDATE正在进行时,例如2.5秒,按Ctrl-C中断它。
  3. 观察对表的影响。更新了多少行?有多少行没有更新?表还可读吗?还是在按Ctrl-C时损坏了?
  4. 对InnoDB表尝试相同的UPDATE实验,中断正在进行的查询。
  5. 观察InnoDB表。行已更新。InnoDB已确保您有原子更新,如果无法提交完整更新,它会回滚整个更改。此外,表没有损坏。即使您使用killall -9 mysqld模拟崩溃,这也有效。

性能当然是可取的,但不丢失数据应该胜过它。

底线:如果您离线处理大块数据的选择,MyISAM可能会为您提供更好(更好)的速度。

在某些情况下,MyISAM比InnoDB更高效:当操作大型数据转储离线时(因为表锁)。

示例:我正在转换来自NOAA的csv文件(15M记录),它使用VARCHAR字段作为键。InnoDB花费了很长时间,即使有大量可用存储器。

这是一个csv的例子(第一个和第三个字段是键)。

USC00178998,20130101,TMAX,-22,,,7,0700USC00178998,20130101,TMIN,-117,,,7,0700USC00178998,20130101,TOBS,-28,,,7,0700USC00178998,20130101,PRCP,0,T,,7,0700USC00178998,20130101,SNOW,0,T,,7,

由于我需要做的是运行观察到的天气现象的批量离线更新,我使用MyISAM表接收数据并在键上运行JOINS,以便我可以清理传入文件并将VARCHAR字段替换为INT键(与存储原始VARCHAR值的外部表相关)。

几乎每次我开始一个新项目时,我都会谷歌同样的问题,看看我是否能找到任何新的答案。

它最终归结为-我使用最新版本的MySQL并运行测试。

我有一些表,我想在那里进行键/值查找……仅此而已。我需要获取哈希键的值(0-512字节)。这个数据库上没有很多事务。表偶尔会更新(全部更新),但0个事务。

所以我们在这里谈论的不是一个复杂的系统,我们谈论的是一个简单的查找,以及如何(除了使表RAM常驻)我们可以优化性能。

我也在其他数据库(即非关系型数据库)上进行测试,看看是否有任何地方我可以获得优势。我发现的最大优势是键映射,但就查找而言,MyISAM目前是他们中的佼佼者。

尽管如此,我不会使用MyISAM表执行财务交易,但对于简单的查找,您应该对其进行测试…通常是查询的2倍到5倍/秒。

测试一下,我欢迎辩论。

如果它是70%的插入和30%的读取,那么它更像是在InnoDB方面。

游戏有点晚了……但这里有一个相当全面的0,详细说明了MYISAM和InnoDB之间的主要区别。拿杯茶(也许还有一块饼干),享受吧。


MyISAM和InnoDB之间的主要区别在于参照完整性和事务。还有其他区别,例如锁定、回滚和全文搜索。

参照完整性

参照完整性可确保表之间的关系保持一致。更具体地说,这意味着当表(例如列表)具有指向不同表(例如产品)的外键(例如产品ID)时,当指向的表发生更新或删除时,这些更改会级联到链接表。在我们的示例中,如果产品被重命名,链接表的外键也会更新;如果从“产品”表中删除产品,则指向已删除条目的任何列表也将被删除。此外,任何新列表都必须具有指向有效的现有条目的外键。

InnoDB是一种关系DBMS(RDBMS),因此具有参照完整性,而MyISAM则没有。

事务和原子性

表中的数据使用数据操作语言(DML)语句进行管理,例如SELECT、INSERT、UPDATE和DELETE。事务将两个或多个DML语句组合成一个工作单元,因此要么应用整个单元,要么不应用。

MyISAM不支持事务,而InnoDB支持。

如果操作在使用MyISAM表时被中断,操作将立即中止,受影响的行(甚至每行中的数据)仍然受到影响,即使操作没有完成。

如果操作在使用InnoDB表时被中断,因为它使用具有原子性的事务,任何没有完成的事务都不会生效,因为没有提交。

表锁定vs行锁定

当查询针对MyISAM表运行时,它正在查询的整个表将被锁定。这意味着后续查询只会在当前表完成后执行。如果您正在读取一个大表,和/或有频繁的读写操作,这可能意味着巨大的查询积压。

当查询对InnoDB表运行时,只有涉及的行被锁定,表的其余部分仍然可用于CRUD操作。这意味着查询可以在同一个表上同时运行,只要它们不使用同一行。

InnoDB中的此功能称为并发。尽管并发很棒,但适用于选定范围的表有一个主要缺点,即在内核线程之间切换会产生开销,您应该对内核线程设置限制以防止服务器停止。

事务和回滚

当您在MyISAM中运行操作时,会设置更改;在InnoDB中,可以回滚这些更改。用于控制事务的最常见命令是COMMIT、ROLLBACK和SAVEPOINT。1. COMMIT-您可以编写多个DML操作,但只有在进行COMMIT时才会保存更改2. ROLLBACK-您可以丢弃任何尚未提交的操作3. SAVEPOINT-在ROLLBACK操作可以回滚到的操作列表中设置一个点

可靠性

MyISAM不提供数据完整性-硬件故障、不干净的关闭和取消的操作可能会导致数据损坏。这需要完全修复或重建索引和表。

另一方面,InnoDB使用事务日志、双写缓冲区以及自动校验和验证来防止损坏。在InnoDB进行任何更改之前,它会将事务之前的数据记录到一个名为ibdata1的系统表空间文件中。如果发生崩溃,InnoDB会通过重播这些日志来自动恢复。

全文索引

InnoDB在MySQL版本5.6.4之前不支持FULLTEXT索引。截至撰写本文时,许多共享主机提供商的MySQL版本仍低于5.6.4,这意味着InnoDB表不支持FULLTEXT索引。

但是,这不是使用MyISAM的正当理由。最好更改为支持最新版本MySQL的托管提供商。并不是说使用FULLTEXT索引的MyISAM表不能转换为InnoDB表。

结论

总之,InnoDB应该是您选择的默认存储引擎。当它们满足特定需求时,选择MyISAM或其他数据类型。

简而言之,如果您正在处理需要可靠数据库的东西,可以处理大量INSERT和UPDATE指令,InnoDB是很好的。

而且,如果您需要一个主要采用大量读取(SELECT)指令而不是写入(INSERT和UPDATES)的数据库,MyISAM是很好的,考虑到它在表锁方面的缺点。

您可能需要检查;
InnoDB的优点和缺点
MyISAM的优点和缺点

为了增加这里涵盖两个发动机之间机械差异的广泛响应选择,我提出了一个经验速度比较研究。

就纯速度而言,MyISAM并不总是比InnoDB快,但根据我的经验,PURE READ工作环境的速度往往快2.0-2.5倍。显然,这并不适用于所有环境-正如其他人所写,MyISAM缺乏事务和外键等功能。

我在下面做了一些基准测试——我使用python进行循环和timeit库进行计时比较。出于兴趣,我还包括了内存引擎,这提供了全面的最佳性能,尽管它只适用于较小的表(当你超过MySQL内存限制时,你会不断遇到The table 'tbl' is full)。我看的四种类型的选择是:

  1. 香草精选
  2. 计数
  3. 条件选择
  4. 索引和非索引子选择

首先,我使用以下SQL创建了三个表

CREATE TABLEdata_interrogation.test_table_myisam(index_col BIGINT NOT NULL AUTO_INCREMENT,value1 DOUBLE,value2 DOUBLE,value3 DOUBLE,value4 DOUBLE,PRIMARY KEY (index_col))ENGINE=MyISAM DEFAULT CHARSET=utf8

在第二个和第三个表中,'MyISAM'替换了'InnoDB'和'内存'。

 

1)香草选择

查询:SELECT * FROM tbl WHERE index_col = xx

结果:绘制

不同数据库引擎选择的香草比较

它们的速度大致相同,并且正如预期的那样,要选择的列数是线性的。InnoDB似乎比MyISAM快,但这实际上是微不足道的。

代码:

import timeitimport MySQLdbimport MySQLdb.cursorsimport randomfrom random import randint
db = MySQLdb.connect(host="...", user="...", passwd="...", db="...", cursorclass=MySQLdb.cursors.DictCursor)cur = db.cursor()
lengthOfTable = 100000
# Fill up the tables with random datafor x in xrange(lengthOfTable):rand1 = random.random()rand2 = random.random()rand3 = random.random()rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)cur.execute(insertString2)cur.execute(insertString3)
db.commit()
# Define a function to pull a certain number of records from these tablesdef selectRandomRecords(testTable,numberOfRecords):
for x in xrange(numberOfRecords):rand1 = randint(0,lengthOfTable)
selectString = "SELECT * FROM " + testTable + " WHERE index_col = " + str(rand1)cur.execute(selectString)
setupString = "from __main__ import selectRandomRecords"
# Test time taken using timeitmyisam_times = []innodb_times = []memory_times = []
for theLength in [3,10,30,100,300,1000,3000,10000]:
innodb_times.append( timeit.timeit('selectRandomRecords("test_table_innodb",' + str(theLength) + ')', number=100, setup=setupString) )myisam_times.append( timeit.timeit('selectRandomRecords("test_table_myisam",' + str(theLength) + ')', number=100, setup=setupString) )memory_times.append( timeit.timeit('selectRandomRecords("test_table_memory",' + str(theLength) + ')', number=100, setup=setupString) )

 

2)计数

查询:SELECT count(*) FROM tbl

结果:MyISAM赢了

不同数据库引擎的计数比较

这个演示了MyISAM和InnoDB之间的一个很大的区别——MyISAM(和内存)跟踪表中的记录数量,所以这个事务很快并且是O(1)。InnoDB计数所需的时间随着表大小在我调查的范围内超线性增加。我怀疑在实践中观察到的MyISAM查询的许多加速是由于类似的效果。

代码:

myisam_times = []innodb_times = []memory_times = []
# Define a function to count the recordsdef countRecords(testTable):
selectString = "SELECT count(*) FROM " + testTablecur.execute(selectString)
setupString = "from __main__ import countRecords"
# Truncate the tables and re-fill with a set amount of datafor theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:
truncateString = "TRUNCATE test_table_innodb"truncateString2 = "TRUNCATE test_table_myisam"truncateString3 = "TRUNCATE test_table_memory"
cur.execute(truncateString)cur.execute(truncateString2)cur.execute(truncateString3)
for x in xrange(theLength):rand1 = random.random()rand2 = random.random()rand3 = random.random()rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)cur.execute(insertString2)cur.execute(insertString3)
db.commit()
# Count and time the queryinnodb_times.append( timeit.timeit('countRecords("test_table_innodb")', number=100, setup=setupString) )myisam_times.append( timeit.timeit('countRecords("test_table_myisam")', number=100, setup=setupString) )memory_times.append( timeit.timeit('countRecords("test_table_memory")', number=100, setup=setupString) )

 

3)条件选择

查询:SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5

结果:MyISAM赢了

不同数据库引擎条件选择的比较

在这里,MyISAM和内存的性能大致相同,对于较大的表,它比InnoDB高出大约50%。这是MyISAM的好处似乎最大化的查询类型。

代码:

myisam_times = []innodb_times = []memory_times = []
# Define a function to perform conditional selectsdef conditionalSelect(testTable):selectString = "SELECT * FROM " + testTable + " WHERE value1 < 0.5 AND value2 < 0.5 AND value3 < 0.5 AND value4 < 0.5"cur.execute(selectString)
setupString = "from __main__ import conditionalSelect"
# Truncate the tables and re-fill with a set amount of datafor theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:
truncateString = "TRUNCATE test_table_innodb"truncateString2 = "TRUNCATE test_table_myisam"truncateString3 = "TRUNCATE test_table_memory"
cur.execute(truncateString)cur.execute(truncateString2)cur.execute(truncateString3)
for x in xrange(theLength):rand1 = random.random()rand2 = random.random()rand3 = random.random()rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)cur.execute(insertString2)cur.execute(insertString3)
db.commit()
# Count and time the queryinnodb_times.append( timeit.timeit('conditionalSelect("test_table_innodb")', number=100, setup=setupString) )myisam_times.append( timeit.timeit('conditionalSelect("test_table_myisam")', number=100, setup=setupString) )memory_times.append( timeit.timeit('conditionalSelect("test_table_memory")', number=100, setup=setupString) )

 

4)子选择

结果:InnoDB赢了

对于这个查询,我为子选择创建了一组额外的表。每个表只是两列BIGINT,一列有主键索引,一列没有任何索引。由于表大小很大,我没有测试内存引擎。SQL表创建命令是

CREATE TABLEsubselect_myisam(index_col bigint NOT NULL,non_index_col bigint,PRIMARY KEY (index_col))ENGINE=MyISAM DEFAULT CHARSET=utf8;

在第二个表中,“MyISAM”再次取代了“InnoDB”。

在此查询中,我将选择表的大小保留为1000000,而是改变子选定列的大小。

不同数据库引擎的子选择比较

在这里InnoDB轻松获胜。在我们得到一个合理的大小表后,两个引擎都随着子选择的大小线性缩放。索引加快了MyISAM命令的速度,但有趣的是对InnoDB速度几乎没有影响。subSelect.png

代码:

myisam_times = []innodb_times = []myisam_times_2 = []innodb_times_2 = []
def subSelectRecordsIndexed(testTable,testSubSelect):selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT index_col FROM " + testSubSelect + " )"cur.execute(selectString)
setupString = "from __main__ import subSelectRecordsIndexed"
def subSelectRecordsNotIndexed(testTable,testSubSelect):selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT non_index_col FROM " + testSubSelect + " )"cur.execute(selectString)
setupString2 = "from __main__ import subSelectRecordsNotIndexed"
# Truncate the old tables, and re-fill with 1000000 recordstruncateString = "TRUNCATE test_table_innodb"truncateString2 = "TRUNCATE test_table_myisam"
cur.execute(truncateString)cur.execute(truncateString2)
lengthOfTable = 1000000
# Fill up the tables with random datafor x in xrange(lengthOfTable):rand1 = random.random()rand2 = random.random()rand3 = random.random()rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)cur.execute(insertString2)
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:
truncateString = "TRUNCATE subselect_innodb"truncateString2 = "TRUNCATE subselect_myisam"
cur.execute(truncateString)cur.execute(truncateString2)
# For each length, empty the table and re-fill it with random datarand_sample = sorted(random.sample(xrange(lengthOfTable), theLength))rand_sample_2 = random.sample(xrange(lengthOfTable), theLength)
for (the_value_1,the_value_2) in zip(rand_sample,rand_sample_2):insertString = "INSERT INTO subselect_innodb (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"insertString2 = "INSERT INTO subselect_myisam (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"
cur.execute(insertString)cur.execute(insertString2)
db.commit()
# Finally, time the queriesinnodb_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString) )myisam_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString) )        
innodb_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString2) )myisam_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString2) )

我认为所有这一切的关键信息是,如果你对速度的关注度为0,你需要对你正在做的查询进行基准测试,而不是对哪个引擎更合适做出任何假设。

我知道这不会受欢迎,但这里有:

myISAM缺乏对数据库基本要素的支持,例如事务和参照完整性,这通常会导致应用程序出现故障/错误。如果您的数据库引擎甚至不支持它们,您就无法学习正确的数据库设计基础知识。

在数据库世界中不使用参照完整性或事务就像在软件世界中不使用面向对象程序设计一样。

InnoDB现在存在,用它代替!甚至MySQL开发人员最终也承认在新版本中将其更改为默认引擎,尽管myISAM是所有遗留系统中默认的原始引擎。

不,不管你是读还是写,或者你有什么性能考虑,使用myISAM可能会导致各种问题,比如我刚刚遇到的这个:我正在执行数据库同步,同时其他人访问了一个访问表集到myISAM的应用程序。由于缺乏事务支持和这个引擎的可靠性普遍很差,这使整个数据库崩溃,我不得不手动重启mysql!

在过去15年的开发中,我使用了许多数据库和引擎。在此期间,myISAM在我身上崩溃了大约十几次,其他数据库,只有一次!那是一个微软SQL数据库,一些开发人员编写了错误的CLR代码(公共语言运行时-基本上是在数据库内执行的C#代码)顺便说一句,这不是数据库引擎的错误。

我同意这里的其他答案,即高质量的高可用性、高性能应用程序不应该使用myISAM,因为它不起作用,它不够健壮或稳定,无法带来无挫败感的体验。更多细节请参阅Bill Karwin的答案。

附注:当我的ISAM粉丝们投反对票时,我很喜欢它,但不能告诉你这个答案的哪一部分是不正确的。

问题和大多数答案已经过时了

是的,MyISAM比InnoDB快是老生常谈。请注意问题的日期:2008年;现在已经快十年了。从那时起,InnoDB取得了重大的性能进步。

这个戏剧性的图表是MyISAM胜诉的一个案例:COUNT(*)没有 aWHERE条款。但这真的是你花时间做的事情吗?

如果你运行并发测试,InnoDB很可能会赢,甚至对#0

如果您在基准测试SELECTs时进行任何写入,MyISAM和MEMORY可能会因为表级锁定而丢失。

事实上,Oracle非常确定InnoDB更好,以至于他们几乎从8.0中删除了MyISAM。

问题是在5.1早期编写的。从那时起,这些主要版本被标记为“通用”:

  • 2010年:5.5(12月为8)
  • 2013年:5.6(2月为0.10)
  • 2015年:5.7(10月为9)
  • 2018年:8.0(4月.11)

底线:不要使用MyISAM