886

I'm working on a projects which involves a lot of database writes, I'd say (70% inserts and 30% reads). This ratio would also include updates which I consider to be one read and one write. The reads can be dirty (e.g. I don't need 100% accurate information at the time of read).
The task in question will be doing over 1 million database transactions an hour.

I've read a bunch of stuff on the web about the differences between MyISAM and InnoDB, and MyISAM seems like the obvious choice to me for the particular database/tables that I'll be using for this task. From what I seem to be reading, InnoDB is good if transactions are needed since row level locking is supported.

Does anybody have any experience with this type of load (or higher)? Is MyISAM the way to go?

4

25 回答 25

538

我在表格中简要讨论了这个问题,因此您可以得出结论是使用InnoDB还是MyISAM

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

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

概括

  • 在几乎所有情况下,InnoDB都是最好的选择
  • 但是,经常阅读,几乎没有写作,使用MyISAM
  • MySQL <= 5.5 中的全文搜索,使用MyISAM
于 2011-07-22T22:01:34.190 回答
270

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

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

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

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

于 2008-08-22T16:03:00.493 回答
198

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

尝试这个:

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

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

于 2013-07-17T17:47:27.607 回答
138

我曾使用 MySQL 在大容量系统上工作,并且我尝试过 MyISAM 和 InnoDB。

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

最后,我通过对数据进行分段来解决争用问题,以便插入进入“热”表并且选择从不查询热表。

这也允许删除(数据是时间敏感的,我们只保留 X 天的价值)发生在“陈旧”的表上,这些表又没有被选择查询触及。InnoDB 似乎在批量删除方面表现不佳,因此如果您计划清除数据,您可能希望以这样一种方式构建它,即旧数据位于陈旧表中,可以简单地删除而不是对其运行删除。

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

于 2008-09-16T21:57:00.183 回答
68

游戏有点晚了……但这是我几个月前写的一篇相当全面的文章,详细介绍了 MISAM 和 InnoDB 之间的主要区别。拿杯咖啡(也许还有饼干),尽情享受吧。


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

参照完整性

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

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 将通过重播这些日志来自动恢复。

全文索引

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

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

结论

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

于 2015-01-21T15:32:33.573 回答
64

对于具有更多写入和读取的负载,您将从 InnoDB 中受益。因为 InnoDB 提供行锁而不是表锁,所以你SELECT的 s 可以是并发的,不仅彼此之间,还可以和许多INSERTs 并发。但是,除非您打算使用 SQL 事务,否则将 InnoDB 提交刷新设置为 2 ( innodb_flush_log_at_trx_commit )。这为您提供了很多原始性能,否则在将表从 MyISAM 移动到 InnoDB 时会失去这些性能。

此外,请考虑添加复制。这为您提供了一些读取扩展,并且由于您声明您的读取不必是最新的,您可以让复制落后一点。只要确保它可以赶上除了最繁忙的交通之外的任何东西,否则它将永远落后并且永远不会赶上。但是,如果您采用这种方式,我强烈建议您将读取从从属设备和复制滞后管理隔离到您的数据库处理程序。如果应用程序代码不知道这一点,那就简单多了。

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

但是可以确定基准。

于 2009-01-05T23:39:39.357 回答
60

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

就纯速度而言,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”和“memory”。

 

1) 香草精选

询问:SELECT * FROM tbl WHERE index_col = xx

结果:平局

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

这些的速度大致相同,并且正如预期的那样,与要选择的列数呈线性关系。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 速度几乎没有影响。子选择.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) )

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

于 2015-06-11T09:15:31.813 回答
33

有点离题,但出于文档目的和完整性,我想添加以下内容。

一般来说,使用 InnoDB 会导致应用程序变得更简单,也可能更没有错误。因为您可以将所有参照完整性(外键约束)放入数据模型中,所以您不需要像使用 MyISAM 所需的那样多的应用程序代码。

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

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

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

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

于 2012-08-26T12:18:07.053 回答
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 之外的所有数据最多可以占用 8000 字节。InnoDB 没有全文索引可用。在 InnoDB 中,COUNT(*)s(当不使用 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 表在磁盘上的大小通常更紧凑。如果需要,MyISAM 表可以通过使用 myisampack 进行压缩来进一步减小大小,但会变成只读的。MyISAM 将索引存储在一个文件中,将数据存储在另一个文件中。MyISAM 使用密钥缓冲区来缓存索引,并将数据缓存管理留给操作系统。

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

于 2013-05-28T07:03:17.287 回答
25

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

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

每秒 300 笔交易听起来相当多。如果您绝对需要这些事务在电源故障时保持持久性,请确保您的 I/O 子系统可以轻松处理每秒这么多的写入。您至少需要一个带有电池后备缓存的 RAID 控制器。

如果您可以承受小的持久性影响,您可以使用 InnoDB 并将 innodb_flush_log_at_trx_commit 设置为 0 或 2(有关详细信息,请参阅文档),您可以提高性能。

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

于 2008-09-16T21:34:54.137 回答
25

问题和大多数答案已过时

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

戏剧性的图表是针对 MyISAM 获胜的一个案例:COUNT(*) 没有WHERE子句。但这真的是你花时间做的事情吗?

如果你运行并发测试,InnoDB 很有可能获胜,即使是对MEMORY.

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

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

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

  • 2010 年:5.5(12 月 0.8)
  • 2013 年:5.6(2 月 0.10)
  • 2015 年:5.7(10 月 0.9)
  • 2018 年:8.0(4 月 0.11)

底线:不要使用 MyISAM

于 2017-05-03T18:26:39.687 回答
12

另请查看 MySQL 本身的一些替代品:

玛丽亚数据库

http://mariadb.org/

MariaDB 是一个数据库服务器,它为 MySQL 提供插入式替换功能。MariaDB 由 MySQL 的一些原始作者在更广泛的免费和开源软件开发人员社区的帮助下构建。除了 MySQL 的核心功能之外,MariaDB 还提供了一组丰富的功能增强功能,包括备用存储引擎、服务器优化和补丁。

Percona 服务器

https://launchpad.net/percona-server

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

于 2012-04-03T15:49:40.167 回答
12

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

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

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

基于事务的引擎擅长的示例:

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

在这种情况下,可能有一个供客人使用的表格(姓名、电话、电子邮件等),还有一个用于跟踪已注册客人数量的表格。因此,我们对一个“交易”有两个操作。现在假设guest info添加到GUESTS表后,出现连接丢失,或者同样影响的错误。GUESTS 表已更新(插入),但在更新“可用座位”之前连接已丢失。

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

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

在这种情况下,这就是“基于事务”本质上的意思,除非我遗漏了什么——要么整个事务按应有的方式成功,要么什么都没有改变,因为仅进行部分更改可能会造成轻微到严重的混乱数据库,甚至可能破坏它......

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

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

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

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

在同一行上工作的两个或多个连接如何对您造成非常糟糕的一天?假设有两个进程都想要/需要更新同一行中的相同值,假设因为该行是巴士旅行的记录,并且两个进程中的每一个都同时想要更新“riders”或“available_seats”字段为“当前值加 1”。

让我们假设性地一步一步地做到这一点:

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

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

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

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

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

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

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

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

这是我的第一个回应,所以请原谅由于所有免责声明等而造成的长度......我只是不想在我不确定的时候听起来很傲慢!

于 2013-04-21T01:54:14.097 回答
11

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

于 2010-08-20T22:15:32.657 回答
5

根据我的经验,MyISAM 是一个更好的选择,只要您不执行 DELETE、UPDATE、大量单个 INSERT、事务和全文索引。顺便说一句,检查表太可怕了。随着表的行数变老,您不知道它什么时候结束。

于 2009-01-06T00:14:42.890 回答
5

我发现即使 Myisam 有锁竞争,在大多数情况下它仍然比 InnoDb 快,因为它使用了快速的锁获取方案。我已经尝试过几次 Innodb 并且总是出于某种原因回到 MyIsam。此外,InnoDB 在巨大的写入负载中可能会占用大量 CPU。

于 2010-09-14T09:27:00.673 回答
4

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

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

于 2008-08-22T17:07:16.330 回答
4

我尝试将随机数据插入到 MyISAM 和 InnoDB 表中。结果相当令人震惊。MyISAM 插入 100 万行所需的时间比 InnoDB 只需 10000 少几秒钟!

于 2011-11-24T00:34:25.427 回答
3

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

于 2009-10-31T10:21:32.517 回答
3

简而言之,如果您正在处理需要能够处理大量 INSERT 和 UPDATE 指令的可靠数据库的工作,InnoDB 是很好的选择。

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

你可能想退房;
InnoDB 的
优点和缺点 MyISAM的优点和缺点

于 2015-02-08T16:54:37.237 回答
2

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

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

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

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

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

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

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

PS 当 myISAM 的粉丝们投反对票但不能告诉你这个答案的哪一部分不正确时,我一定会喜欢它。

于 2016-05-02T21:26:48.470 回答
1

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

于 2013-12-14T03:08:05.597 回答
1

对于这个读/写比率,我猜 InnoDB 会表现得更好。既然你对脏读没问题,你可以(如果你负担得起的话)复制到从属服务器,然后让你所有的读取都转到从属服务器。此外,请考虑批量插入,而不是一次插入一条记录。

于 2010-07-05T15:51:58.347 回答
1

几乎每次我开始一个新项目时,我都会用谷歌搜索同样的问题,看看是否有新的答案。

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

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

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

我还对其他数据库(即 NoSQL)进行了测试,看看是否有任何地方我可以获得优势。我发现的最大优势是在键映射方面,但就查找而言,MyISAM 目前是所有这些方面的佼佼者。

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

测试一下,欢迎讨论。

于 2013-08-20T07:54:48.777 回答
0

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

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

示例:我正在从 NOAA 转换一个 csv 文件(15M 记录),它使用 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 值的外部表)。

于 2013-08-06T19:02:18.073 回答