MyISAM与InnoDB [关闭]


857

我要说的是一个涉及大量数据库写入的项目(70%的插入和30%的读取)。这个比率还将包括我认为是一次读取和一次写入的更新。读取内容可能很脏(例如,读取时我不需要100%准确的信息)。
有问题的任务将是每小时进行超过一百万次数据库事务。

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

是否有人对这种负载(或更高负载)有任何经验?MyISAM是要走的路吗?


13
MySQL性能博客是这种类型的事情一个很好的资源。
ceejayoz

3
这将取决于您的系统是面向OLTP还是面向更多数据仓库(其中大部分写入操作是批量加载)。

35
MyISAM不支持行锁定,事务,甚至不支持外键...该死,因为它不能提供ACID,所以它甚至不能被认为是一个合适的数据库!这就是为什么自MySQL 5.5起,InnoDB一直是默认引擎的原因……但是,无论出于何种原因,MyISAM仍然是PhpMyAdmin中创建的表的默认引擎,因此许多业余数据库都在MyISAM上运行。
BlueRaja-Danny Pflughoeft13年


Answers:


523

我已经在一个表中简要讨论了这个问题,以便您可以得出结论是否要使用InnoDBMyISAM

以下是在哪种情况下应使用的数据库存储引擎的简要概述:

                                                 MyISAM InnoDB
-------------------------------------------------- --------------
必需的全文本搜索是5.6.4
-------------------------------------------------- --------------
需要交易是
-------------------------------------------------- --------------
频繁选择查询是      
-------------------------------------------------- --------------
频繁插入,更新,删除是
-------------------------------------------------- --------------
行锁定(对单个表进行多次处理)是
-------------------------------------------------- --------------
关系基础设计是

摘要

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

11
InnoDB在MySQL 5.6中具有全文索引,但是到目前为止,它们还没有真正准备好用于生产。
Bill Karwin

3
符合12.9。全文搜索功能 “全文索引只能与InnoDB或MyISAM表一起使用”。对于MySQL> = 5.6,似乎可以,但是对于MySQL 5.5,同一页面上仍显示“全文索引只能与MyISAM表一起使用”。上表可以更新为表示它与MySQL版本的不同之处。不幸的是,到目前为止,MySQL 5.5似乎是标准。
Hibou57年

2
什么是平均的:InnoDB - full-text: 5.6.4?? 是还是不是?

2
MyISAM还在内部存储行数。因此,Count()函数在MyISAM中几乎是免费的,而在InnoDB中则需要花费大量时间。
Hedeshy 2015年

3
好的表,但是增加一行以保证质量和稳定性,MyIsam =否,innoDB = yes会使它变得更好
pilavdzice

268

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

MyISAM表使用表级锁定。根据流量估算,每秒将近200次写入。使用MyISAM,任何时候都只能进行其中之一。您必须确保您的硬件可以跟上这些事务,以避免被超载,即,单个查询最多可以花费5毫秒。

对我来说,这建议您需要一个支持行级锁定的存储引擎,即InnoDB。

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


12
接近200?如果他的平均交易使得2.5的查询,这是[(2.5 * 1M)/ 3600 =]接近700
奥兹

12
我也不同意,a single query can take no more than 5ms因为您做了2个不太可能的假设。A:所有查询都需要同一个表&B:只有1个连接可用!我应该通知您,具有高RAM的Linux&MySQL 5.5设置可以支持多达10,000个同时连接(请参阅:dev.mysql.com/doc/refman//5.5/en/too-many-connections.html
Ozzy 2012年

152
当表被表锁定时,一次只能针对它运行一个查询。服务器是否支持10000个并发连接并不重要,在表被锁定时,每个连接都会备份。
Ryaner 2012年

2
知道MyISAM支持空间索引而InnoDB不支持空间索引也可能会有所帮助。而且MyISAM似乎并没有使用外键,即使它不会阻止创建外键。
kriver 2012年

4
@kriver:MyISAM表中不能包含外键。您可以在CREATE TABLE语句中包括FK定义,但是它们(这些定义)将被忽略。
ypercubeᵀᴹ

191

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

尝试这个:

  1. 对MyISAM表发出UPDATE,耗时5秒。
  2. 在进行UPDATE(例如2.5秒钟)时,按Ctrl-C可以中断它。
  3. 观察桌子上的效果。更新了多少行?有多少未更新?该表甚至可读,还是在按Ctrl-C时损坏了?
  4. 对InnoDB表使用UPDATE尝试相同的实验,中断正在进行的查询。
  5. 观察InnoDB表。 行已更新。InnoDB向您保证您拥有原子更新,如果无法提交完整更新,它将回滚整个更改。此外,表未损坏。即使您killall -9 mysqld用来模拟崩溃,此方法也有效。

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


4
作为记录,MyISAM也不支持ACID数据库的其他特征-一致性,隔离性和持久性。
Bill Karwin 2014年

Control-C不应破坏表-如CHECK TABLE将返回成功,并且所有查询将继续进行而不会出现错误。MyISAM将中止更新而不更新所有记录,但是表将保持内部结构的完整性。用SIGTERM杀死mysqld将具有相同的效果。但是,如果您给它SIGKILL(杀死-9)或一些崩溃信号(或者当它遇到错误时靠它自己赚钱),或者操作系统崩溃/断电,那么情况就不一样了-您可以看到MyISAM级别的损坏。
萨沙·帕切夫

1
InnoDB也可以进行皇家破坏,通常比MyISAM进行破坏更大。ACID具有讽刺意味的是,我们拥有全部或全部的概念。因此,当InnoDB无法提供全部功能时,它什么也不提供-内部断言,并且它根本不运行,因为某些结构中的一个字节是错误的-90%的时间它本可以被忽略,并且最多只影响一个表。最近的Percona服务器可以选择处理它-innodb_pass_corrupt_table。
萨沙·帕切夫

1
我最近3天一直在搜索此类信息,现在知道了。InnoDB是最好的。谢谢Bill Karwin
user3833682

3
@ flow2k,这些天几乎没有。在我的上一份工作中,我们在一个服务器上的一个表上使用了MyISAM,唯一的原因是MyISAM能够以比InnoDB更少的空间存储该特定表。我们受到磁盘空间的限制,因此必须使用MyISAM,直到我们可以将数据库移至另一台服务器。在我的新工作中,已经有一个策略,即每个表都必须是InnoDB。
Bill Karwin

138

我已经在使用MySQL的高容量系统上工作,并且尝试了MyISAM和InnoDB。

我发现MyISAM中的表级锁定对我们的工作负载造成了严重的性能问题,这听起来与您的相似。不幸的是,我还发现InnoDB的性能也比我希望的要差。

最后,我通过对数据进行分段解决了争用问题,从而使插入内容进入“热”表,并选择了从不查询热表。

这还允许在“陈旧”表上进行删除(数据是时间敏感的,我们只保留了X天的时间),而选择查询仍然没有删除过该表。InnoDB在批量删除方面的性能似乎很差,因此,如果您打算清除数据,则可能需要以一种方式构造它,即旧数据位于一个陈旧的表中,可以将其删除而不是对其进行删除。

当然,我不知道您的应用程序是什么,但希望它可以使您对MyISAM和InnoDB的某些问题有一些了解。


3
“最后,我通过对数据进行分段解决了争用问题,从而使插入数据进入“热”表,并选择了从不查询该热表。” -实质上不是缓冲池的目的吗?
BlueRaja-Danny Pflughoeft

15
丹尼-不,不是真的。调整服务器设置很重要,但绝不能替代对架构进行周到的构造。如果您拥有一个比可用RAM大得多的DB,并且访问模式在整个DB中随机接触数据,那么世界上所有的缓冲池调整都将无济于事。如果您了解数据和访问模式,则可以通过精心设计减轻许多麻烦。
alanc10n

66

游戏有点晚了...但是这是我几个月前写的一篇非常全面的文章,详细介绍了MYISAM和InnoDB之间的主要区别。拿起一杯(可能还有饼干),然后享用。


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

参照完整性

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

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

交易与原子性

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

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

如果在使用MyISAM表时操作被中断,则该操作将立即中止,并且即使操作未完成,受影响的行(甚至每一行中的数据)仍会受到影响。

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

表锁定与行锁定

当查询针对MyISAM表运行时,查询所在的整个表将被锁定。这意味着后续查询仅在当前查询完成后才执行。如果您正在读取一个大表,并且/或者频繁进行读写操作,那么这可能意味着大量的查询积压。

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

InnoDB中的此功能称为并发。就并发性而言,最大的缺点是它适用于选定的表范围,因为在内核线程之间进行切换会产生开销,因此您应该对内核线程设置一个限制,以防止服务器停机。

交易和回滚

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

可靠性

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

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

全文索引

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

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

结论

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


我正在制作一个php会话校验和脚本,我的大多数键是[az09]的随机字符串... Innodb花费了30ms来做一个,INSERT ON DUPLICATE KEY UPDATE所以我尝试了MyISAM,现在它下降到了<1ms...。我看到很多回答说innodb很难处理“ unsortable”(随机字符串)唯一键...在这一点上您对我们有什么投入吗?实际上,我想知道使用MyISAM会产生什么影响,但是您的出色回答使我意识到这是解决特定情况的方法。
Louis Loudog Trottier

64

对于具有更多读写操作的负载,您将从InnoDB中受益。由于InnoDB提供行锁定而不是表锁定,因此您的SELECTs可以是并发的,不仅可以彼此并发,而且可以与许多INSERTs 并发。但是,除非打算使用SQL事务,否则请将InnoDB提交刷新设置为2(innodb_flush_log_at_trx_commit)。这给您带来了很多原始性能,否则您将表从MyISAM移到InnoDB时可能会失去这些性能。

另外,请考虑添加复制。这为您提供了一些读取扩展,并且由于您声明读取不必是最新的,因此可以让复制落后一些。只要确保它可以在流量最大的任何情况下都可以追上,否则它将永远落后并且永远不会追上。但是,如果您采用这种方式,我强烈建议您将读取与从属服务器以及复制滞后管理隔离到数据库处理程序。如果应用程序代码不知道这一点,则非常简单。

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

但是可以确定基准。


4
您是指“ InnoDB提交刷新” innodb_flush_log_at_trx_commit吗?
ceejayoz

2
我发现您的帖子非常有用-谢谢。当前评估何时在我的表上使用MyISAM / InnoDB和您的帖子很有帮助。干杯。
starmonkey

2
dev.mysql.com/doc/refman/5.5/en/insert-delayed.html状态:对于MyISAM表,如果数据文件中间没有空闲块,则支持并发SELECT和INSERT语句。在这种情况下,您很少需要对MyISAM使用INSERT DELAYED。
tymtam 2011年

非常丰富的帖子。我和操作员有同样的问题,我不得不说,您的帖子使我对数据库引擎的决策感到放心。谢谢!++
Joe Majewski 2012年

快速说明:5.7不再支持延迟。您可能想用LOW_PRIORITY进行测试。
webmat 2014年

59

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

就纯速度而言,MyISAM并不总是比InnoDB快,但以我的经验,在PURE READ工作环境中,它往往要快2.0到2.5倍。显然,这并不适合所有环境-正如其他人所写的那样,MyISAM缺少事务和外键之类的东西。

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

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

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

CREATE TABLE
    data_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 timeit
import MySQLdb
import MySQLdb.cursors
import random
from 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 data
for 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 tables
def 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 timeit
myisam_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 records
def countRecords(testTable):

    selectString = "SELECT count(*) FROM " + testTable
    cur.execute(selectString)

setupString = "from __main__ import countRecords"

# Truncate the tables and re-fill with a set amount of data
for 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 query
    innodb_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 selects
def 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 data
for 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 query
    innodb_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 TABLE
    subselect_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 records
truncateString = "TRUNCATE test_table_innodb"
truncateString2 = "TRUNCATE test_table_myisam"

cur.execute(truncateString)
cur.execute(truncateString2)

lengthOfTable = 1000000

# Fill up the tables with random data
for 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 data
    rand_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 queries
    innodb_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) )

我认为所有这一切的基本含义是,如果您真正关心速度,则需要对正在执行的查询进行基准测试,而不是对哪种引擎更合适做任何假设。


1
性能并非始终是唯一的考虑因素,关于稳定性的图表又如何呢?如果引擎崩溃并且不支持基本的数据库功能,那么它就无济于事。
pilavdzice

1
如果my.cnf未针对InnoDB优化文件,则MyISAM可能会在大多数情况下击败InnoDB。您没有提到my.cnf文件的外观,这实际上是InnoDB性能的最重要因素。
itoctopus

谢谢itoctopus-我很想听听有关您推荐的任何优化的更多信息。这些测试中使用的完整代码已在上方,可以通过各种优化重复进行实验,并让我们知道您是否发现结果有重大变化
StackG 2017年

32

稍微偏离主题,但出于文档目的和完整性考虑,我想添加以下内容。

通常,使用InnoDB会导致更少的复杂应用程序,而且可能也没有更多的错误。因为您可以将所有参照完整性(外键约束)放入数据模型,所以您不需要像MyISAM那样需要那么多应用程序代码。

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

同样在大多数一般情况下,由于多种原因,InnoDB的性能也会好得多,其中一个原因是他们能够使用记录级锁定而不是表级锁定。不仅在写入比读取更频繁的情况下,还在大型数据集上具有复杂联接的情况下。我们注意到,对于非常大的联接(耗时数分钟),仅使用InnoDB表而不是MyISAM表可将性能提高3倍。

我要说的是,在一般情况下,使用MySQL时,InnoDB(使用具有参照完整性的3NF数据模型)应该是默认选择。MyISAM仅应在非常特殊的情况下使用。它极有可能执行得更少,导致应用程序更大,更富bug。

话虽如此。数据建模是网页设计师/程序员中很少见的艺术。没有违法,但这确实解释了MyISAM的使用如此之多。


31

InnoDB提供:

ACID transactions
row-level locking
foreign key constraints
automatic crash recovery
table 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 indexing
smaller disk footprint
very high table compression (read only)
spatial data types and indexes (R-tree)

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

总的来说,我会建议将InnoDB用于大多数用途,并将MyISAM仅用于特殊用途。现在,InnoDB是新MySQL版本中的默认引擎。


2
首先,InnoDB中的VARCHAR也可以转到溢出页面,例如BLOB和TEXT。所有这些数据类型都在内部类似地存储。
Bill Karwin

很高兴知道,@ BillKarwin!我们在应用程序中大量使用了VARCHAR,让VARCHAR达到〜8kB的限制有点令人担忧。
rinogo


答案不是最新的annymore,因为MySQL 5.6+版本中的innodb引擎如今还支持全文索引,MySQL 5.5 + / 5.7 +还支持空间数据类型(5.5+)空间索引(r-tee)(5.7+) ..为了获得最佳支持,您至少需要拥有MySQL版本5.7+
Raymond Nijland

25

如果使用MyISAM,则不会每小时进行任何事务,除非您将每个DML语句都视为一个事务(无论如何,如果发生崩溃,它将不会持久或原子)。

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

每秒300个事务听起来很多。如果您绝对需要这些事务在断电时能够持久运行,请确保您的I / O子系统每秒可以轻松处理这么多的写入。您将至少需要一个具有电池后备缓存的RAID控制器。

如果可以减少耐用性,可以将Innodb_flush_log_at_trx_commit设置为0或2(请参阅文档以了解详细信息)来使用InnoDB,可以提高性能。

有许多补丁程序可以提高Google和其他公司的并发性-如果没有它们您仍然无法获得足够的性能,这些补丁程序可能会很有用。


24

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

是的,这是一个古老的妻子的故事,那就是MyISAM比InnoDB更快。注意问题的日期:2008年;现在已经快十年了。从那时起,InnoDB取得了重大的性能进步。

戏剧性的图表是为其中的MyISAM赢得了一个案例:COUNT(*) 没有一个WHERE条款。但这真的就是您花费时间做的事情吗?

如果您运行并发测试,即使与MEMORY竞争,InnoDB也很有可能获胜。

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

实际上,Oracle确信InnoDB会更好,因为他们几乎从8.0中删除了MyISAM。

问题是在5.1年代初编写的。从那时起,这些主要版本被标记为“常规可用性”:

  • 2010:5.5(12月为0.8)
  • 2013:5.6(2月为.10)
  • 2015年:5.7(十月为0.9)
  • 2018年:8.0(4月.11)

底线:不要使用MyISAM


2
MySQL数据库技术不断进步。而且,StackOverflow的问题和答案仍然沉迷于过去。MyISAM和InnoDB之间的主要区别在于服务器上的“负载” 较少,而更多地涉及对参照完整性事务的支持以及并发性可恢复性(+10)
spencer7593

12

还请检查一些MySQL本身的直接替代品:

玛丽亚数据库

http://mariadb.org/

MariaDB是一种数据库服务器,为MySQL提供了嵌入式替换功能。MariaDB由MySQL的某些原始作者构建,并得到了自由和开源软件开发人员的广泛支持。除了MySQL的核心功能外,MariaDB还提供了丰富的功能增强集,包括备用存储引擎,服务器优化和补丁。

Percona服务器

https://launchpad.net/percona-server

MySQL的增强的嵌入式替代品,具有更好的性能,改进的诊断功能和附加功能。


1
我正在使用这两种工具(Percona用于生产,Maria用于Windows开发)。它们运行速度更快,运行效果完美。
Moshe L

4
这不能回答问题。MariaDB和Percona是MySQL的分支,也使用InnoDB和MyISAM引擎。
dr_

12

请注意,我的正式教育和经验是在Oracle上进行的,而我在MySQL上的工作完全是个人化的,并且是我自己的时间,因此,如果我说对Oracle正确但对MySQL不正确的事情,我深表歉意。虽然两个系统共享很多,但是关系理论/代数是相同的,并且关系数据库仍然是关系数据库,仍然存在许多差异!

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

注意:对于Oracle,create / alter / drop语句称为“ DDL”(数据定义)语句,并隐式触发提交。不会自动提交称为“ DML”(数据操作)的插入/更新/删除语句,仅在执行DDL,提交或退出/退出时(或者将会话设置为“自动提交”时)才会自动提交,或者如果您的客户自动提交)。必须在使用Oracle时意识到这一点,但是我不确定MySQL如何处理这两种类型的语句。因此,我想明确地说,对于MySQL,我不确定。仅适用于Oracle。

基于事务的引擎何时出色的一个示例:

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

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

现在,我们向来宾表添加了一个来宾,但是可用座位数现在不正确(例如,值实际为84时为85)。

当然,有许多方法可以解决此问题,例如使用“ 100减去来宾表中的行数”来跟踪可用的座位,或一些检查信息是否一致的代码,等等。但是使用基于事务的数据库引擎,如InnoDB的,无论是全部操作都致力于,或NONE都是。这在许多情况下可能会有所帮助,但是就像我说的那样,这并不是唯一安全的方法,不是(不是一种好方法,但是,由数据库而不是程序员/脚本编写者来处理)。

在这种情况下,这全是“基于交易”的意思,除非我遗漏了一些东西,否则整个交易要么按预期成功,要么什么都不会更改,因为仅进行部分更改可能会使严重的混乱变得微不足道。数据库,甚至损坏它...

但我要再说一次,这不是避免弄乱的唯一方法。但这是引擎本身处理的方法之一,您无需担心手动操作,而只需要担心“事务是否成功以及如果不成功该怎么办(例如重试)”就可以使用代码/脚本了。编写代码以从数据库外部“手动”检查它,并为此类事件做更多的工作。

最后,关于表锁定与行锁定的说明:

免责声明: 关于MySQL,我在所有后续内容中可能都是错误的,假设/示例情况是需要研究的事情,但是在可能导致MySQL损坏的确切原因上,我可能是错误的。然而,即使MySQL有更多避免这种情况的机制,这些示例在一般编程中还是非常真实的。

无论如何,我与那些谁主张,多少个连接在同一时间被允许同意相当有信心,没有解决一个锁定的表。实际上,多个连接是锁定表的全部要点!! 这样其他进程/用户/应用程序将无法通过同时进行更改来破坏数据库。

在同一行上工作的两个或多个连接如何为您带来一个非常糟糕的一天?假设有两个进程都希望/需要在同一行中更新相同的值,假设由于该行是一次公共汽车旅行的记录,并且两个进程中的每个进程都同时希望更新“搭便车”或“ available_seats”字段为“当前值加1”。

假设我们逐步进行此操作:

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

不确定两个连接是否会像这样混合在一起,都在第一个连接写之前就读了……但是,如果没有,那么我仍然会遇到以下问题:

  1. 进程1读取当前值为0。
  2. 进程一写入(当前+1),即1。
  3. 进程2现在读取当前值。但是,在处理一个DID写入(更新)时,它尚未提交数据,因此只有同一个进程才能读取其更新的新值,而其他所有进程都将看到较旧的值,直到提交为止。

另外,至少对于Oracle数据库而言,存在隔离级别,我不会浪费时间尝试解释。这是一篇关于该主题的好文章,每个隔离级别都有其优缺点,这将与基于事务的引擎在数据库中的重要性结合在一起...

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

a,如果您知道这些并发问题,可以的话,您可以放心使用它并不太安全,只需编写应用程序,设置系统,以免发生此类错误(然后由您的代码负责,而不是数据库本身)。但是,我认为,总是最好使用尽可能多的防护措施,进行防御性编程,并且始终意识到不可能完全避免人为错误。它发生在每个人身上,任何说自己对此不起作用的人都必须撒谎,或者除了编写“ Hello World”应用程序/脚本以外没有做其他事情。;-)

我希望其中的一些对某人有所帮助,甚至更多,所以,我希望我现在不仅是假设的罪魁祸首,而且是一个犯错误的人!如果是的话,我表示歉意,但是即使在特定的情况下这些例子都没有潜力,这些例子还是值得思考的,研究其风险等等。

随时纠正我,编辑此“答案”,甚至予以否决。只是请尝试改进,而不是与另一个人纠正我的错误假设。;-)

这是我的第一反应,因此请原谅所有免责声明等内容的冗长。。。我只是不太想在我不确定的情况下自大!



5

以我的经验,只要您不进行DELETE,UPDATE,大量单个INSERT,事务和全文索引的操作,MyISAM是一个更好的选择。顺便说一句,检查表是可怕的。随着表格在行数上的变老,您不知道表格何时结束。


2
全文索引仅适用于MyISAM,不适用于InnoDB。
Pixel Elephant 2012年

2
@PixelElephant,这在MySQL 5.6中已经开始改变。InnoDB具有全文本索引类型,但是到目前为止,它还没有准备好供生产使用恕我直言。
比尔·卡温

1
“全文索引只能使用MyISAM,而不能使用InnoDB”:由于MySQL> = 5.6,因此不再适用。参见dev.mysql.com/doc/refman/5.6/en/fulltext-search.html
Hibou57

5

我发现即使Myisam具有锁定争用,但由于它使用了快速锁定获取方案,因此在大多数情况下它仍比InnoDb快。我已经尝试了几次Innodb,并且总是出于一个或另一个原因退回到MyIsam。同样,InnoDB在巨大的写入负载中可能会占用大量CPU。


4

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

您可以做的最好的事情就是测试您的选择。在MyISAM和InnoDB之间切换很简单,因此请加载一些测试数据并针对您的站点启动jmeter,然后看看会发生什么。


4

我试图将随机数据插入MyISAM和InnoDB表中。结果令人震惊。MyISAM插入一百万行所需的时间比InnoDB少一万秒!


2
如果您使用事务并关闭InnoDB引擎的自动提交,您将获得相同的性能。
stanleyxu2005 '09

IDK是否具有相同的性能,但这就是我在更复杂的应用程序中所做的,并且确实加快了速度。
user965748 2012年

1
您未能提供实验的确切详细信息-哪些配置设置?之前的表格中有什么?什么样的数据?也许最重要的是-是否有顺序插入?平行?他们的时间是几点?多少个CPU核心?线程?等
einpoklum 2014年

3

myisam是那种类型的工作负载(高并发写入)的NOGO,如果您使用innodb,我没有那么多的经验(对其进行了3次测试,发现在每种情况下性能都很糟糕,但是自上次测试以来已经有一段时间了)不被迫运行mysql,考虑尝试一下postgres,因为它可以更好地处理并发写入


3

简而言之,如果您正在处理需要可靠的数据库并且可以处理很多INSERT和UPDATE指令的数据库,那么InnoDB就是很好的选择。

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

您可能要签出;
InnoDB的
优缺点MyISAM的优缺点


2

我知道这不会流行,但可以这样:

myISAM缺乏对诸如事务和引用完整性之类的数据库必需品的支持,这常常导致应用程序出现故障。如果您的数据库引擎甚至不支持正确的数据库设计基础知识,您将无法学习。

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

InnoDB现在存在,请改用它!即使myISAM是所有旧系统中默认的原始引擎,即使MySQL开发人员也最终同意将其更改为较新版本的默认引擎。

没关系,无论您是阅读还是写作,或者您有什么性能方面的考虑,使用myISAM都会导致各种问题,例如我刚遇到的一个问题:我正在执行数据库同步,而同时有人访问了访问设置为myISAM的表的应用程序。由于缺乏事务支持以及该引擎的可靠性普遍较差,这使整个数据库崩溃,我不得不手动重新启动mysql!

在过去的15年的发展中,我使用了许多数据库和引擎。在此期间,myISAM在我身上崩溃了大约十二次,其他数据库也只有一次!而且那是一个Microsoft SQL数据库,其中一些开发人员通过这种方式编写了错误的CLR代码(通用语言运行时-基本是在数据库内部执行的C#代码),这并不是数据库引擎的错误。

我同意这里的其他答案,即高质量,高可用性,高性能的应用程序不应使用myISAM,因为它无法正常工作,它的鲁棒性或稳定性不足以带来无挫折的体验。有关更多详细信息,请参见Bill Karwin的答案。

PS当我的ISAM粉丝们投票否决时,PS Gotta会喜欢上它,但无法告诉您此答案的哪一部分不正确。


5
我没有投票,但是如果我这样做了,那就是建议不要使用。永远不要在开发人员的词汇表中打消这个词……刻骨铭心的是“永不言败”。
哈勃森·布罗帕

1

对于这样的读写比,我猜想InnoDB的性能会更好。由于您可以接受脏读操作,因此您可以(如果负担得起)将其复制到从属设备,然后将所有读取内容发送给该从属设备。另外,请考虑批量插入,而不是一次插入一条记录。


1

几乎每次我启动一个新项目时,我都会用同样的问题搜索Google,以查看是否有新的答案。

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

我有要在其中进行键/值查找的表...仅此而已。我需要获取哈希键的值(0-512字节)。该数据库上没有很多事务。该表偶尔会(全部)获得更新,但是有0个事务。

因此,这里我们不是在讨论复杂的系统,而是在进行简单的查找,以及如何(除了使表RAM驻留在外)优化性能。

我还对其他数据库(即NoSQL)进行了测试,以查看是否有我可以受益的地方。我发现的最大优点是在键映射中,但是就查找而言,MyISAM目前在所有这些键中都占了上风。

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

测试一下,我欢迎辩论。


1

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


0

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

在某些情况下,MyISAM比InnoDB效率更高:当脱机处理大型数据转储时(由于表锁定)。

示例:我正在从NOAA转换一个CSV文件(1500万个记录),该文件使用VARCHAR字段作为键。即使有大量可用内存,InnoDB也将永远占用时间。

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

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

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

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.