关于mysql:MyISAM与InnoDB

MyISAM versus InnoDB

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

我在网上读了很多关于myisam和innodb之间区别的文章,对于我来说,myisam似乎是我在这个任务中使用的特定数据库/表的明显选择。从我看来,如果需要事务,InnoDB很好,因为支持行级锁定。

有人对这种类型的负载(或更高)有经验吗?Myisam是该走的路吗?


我在表格中简要地讨论了这个问题,这样您就可以得出结论,是使用innodb还是myisam。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
                                                 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

总结:

1
2
Frequent reading, almost no writing   => MyISAM
Full-text search in MySQL <= 5.5      => MyISAM

在所有其他情况下,InnoDB通常是最好的方法。


我不是数据库专家,也不是经验丰富的人。然而:

myisam表使用表级锁定。根据您的流量估计,您的每秒写入次数接近200次。有了Myisam,任何时候都只能进行其中一个。您必须确保您的硬件能够跟上这些事务,以避免溢出,也就是说,单个查询所需时间不超过5毫秒。

这就意味着你需要一个支持行级锁定的存储引擎,即innodb。

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


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

试试这个:

  • 对myisam表发出一个需要5秒钟的更新。
  • 当更新正在进行时,比如说2.5秒后,点击ctrl-c中断更新。
  • 观察桌子上的效果。更新了多少行?有多少未更新?表是否可读,或者当您按下ctrl-c时它是否已损坏?
  • 尝试对innodb表进行相同的更新实验,中断正在进行的查询。
  • 观察InnoDB表。零行已更新。InnoDB已经向您保证有原子更新,如果无法提交完整更新,它将回滚整个更改。此外,该表未损坏。即使您使用killall -9 mysqld来模拟碰撞,这也是有效的。
  • 当然,性能是可取的,但不丢失数据应该比这更重要。


    我使用mysql开发了一个大容量系统,我尝试过myisam和innodb。

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

    最后,我通过分割数据来解决争用问题,这样插入就进入了一个"hot"表,选择从不查询hot表。

    这还允许在"陈旧"表上进行删除(数据是时间敏感的,我们只保留了x天的值),而这些表又不会被select查询所触及。InnoDB在批量删除上的性能似乎很差,因此如果您计划清除数据,您可能希望以这样的方式对其进行结构:旧数据位于陈旧的表中,可以简单地删除该表,而不是在其上运行删除。

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


    有点晚了……但这是我几个月前写的一篇相当全面的文章,详细介绍了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。提交-您可以编写多个DML操作,但只有在提交2时才会保存更改。回滚-您可以放弃尚未提交的任何操作3。savepoint-在回滚操作可以回滚到的操作列表中设置一个点好的。可靠性

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

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

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

    但是,这不是使用myisam的有效理由。最好改为支持MySQL最新版本的宿主提供程序。但使用全文索引的myisam表不能转换为innodb表。好的。结论

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


    对于具有更多写入和读取的负载,您将受益于InnoDB。因为innodb提供了行锁定而不是表锁定,所以您的SELECT不仅可以与其他的INSERT同步,而且可以与许多INSERT同步。但是,除非您打算使用SQL事务,否则请将innodb commit flush设置为2(innodb flush_log_at_trx_commit)。这会使您返回许多原始性能,否则在将表从myisam移动到innodb时会丢失这些性能。

    另外,考虑添加复制。这给了您一些读伸缩性,而且由于您声明了您的读不必是最新的,所以您可以让复制稍微落后一点。只要确保它能在交通最拥挤的情况下赶上,否则它将永远落后,永远赶不上。但是,如果您这样做,我强烈建议您将从从属服务器的读取和对数据库处理程序的复制延迟管理隔离开来。如果应用程序代码不知道这一点,就简单多了。

    最后,要注意不同的表负载。所有表的读/写比率不同。一些小桌子的读写率接近100%,可以保持myisam。同样,如果您有一些表接近100%写入,那么您可能会从INSERT DELAYED中受益,但这仅在myisam中受支持(对于innodb表,DELAYED子句被忽略)。

    但可以肯定的是,基准。


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

    就纯速度而言,myisam并不总是比innodb快,但根据我的经验,它在纯读取工作环境中的速度往往比innodb快2.0-2.5倍。显然,这并不适用于所有环境——正如其他人所写,myisam缺少事务和外键等内容。

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

  • 香草精选
  • 计数
  • 条件选择
  • 索引和非索引子选择
  • 首先,我使用以下SQL创建了三个表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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"。

    nbsp;

    1)香草精选

    查询:SELECT * FROM tbl WHERE index_col = xx

    结果:绘制

    Comparison of vanilla selects by different database engines

    这些列的速度大体相同,如预期的那样,在要选择的列数上是线性的。InnoDB似乎比Myisam稍微快一点,但这确实是边缘化的。

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    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) )

    nbsp;

    2)计数

    查询:SELECT count(*) FROM tbl

    结果:Myisam获胜

    Comparison of counts by different database engines

    这个例子说明了myisam和innodb之间的一个很大的区别——myisam(和memory)跟踪表中记录的数量,所以这个事务是快速的并且是O(1)。在我研究的范围内,InnoDB计数所需的时间随表大小呈超线性增长。我怀疑在实践中观察到的myisam查询的许多加速都是由于类似的效果。

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    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) )

    nbsp;

    3)条件选择

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

    结果:Myisam获胜

    Comparison of conditional selects by different database engines

    在这里,myisam和memory的性能大致相同,对于较大的表,其性能比innodb高出50%。这类查询似乎最大限度地发挥了myisam的优势。

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    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) )

    nbsp;

    4)子选择

    结果:InnoDB获胜

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

    1
    2
    3
    4
    5
    6
    7
    8
    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,而改为改变子选择列的大小。

    Comparison of sub-selects by different database engines

    在这里,InnoDB很容易获胜。当我们得到一个合理的尺寸表后,两个发动机的比例与子选择的尺寸成线性关系。索引加快了myisam命令的速度,但有趣的是,它对innodb的速度几乎没有影响。PNG

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    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) )

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


    稍微题外话,但是文档索引的目的,我想添加以下。

    在一般使用InnoDB不想多个复杂的应用程序可能是一个BUG,所以免费。因为你可以把所有的完整性(Foreign Key约束指)到数据模型,你不需要任何近为多应用代码你需要与MyISAM。

    每一次你插入,删除或替换的A记录,你将要检查和维护的关系。例如,如果你删除a parent,所有儿童应该被删除。例如,即使在一个简单的博客系统,如果你想你blogposting删除A记录,记录要删除的评论,像等,这是自动完成的InnoDB引擎(如果你指定的数据库中的contraints Freundlich模型)和应用程序代码或没有。你一定要在MyISAM编码到应用程序,这是非常困难的在Web服务器。Web服务器是由自然是并行和并行/原子和行动,因为论文应使用MyISAM支持MyISAM不真实交易,Web服务器是危险的和易错的。

    因此,在最一般的情况下,会将更多更好的,一个原因是,他们可以使用一个表记录级锁锁反对一个层次。在a的情况。在读写更频繁的情况下比在一个复杂,所以在大datasets连接。我们注意到增加3倍的性能只是使用MyISAM和InnoDB表过超大的连接表(以几分钟)。

    我会说在InnoDB将军(3NF数据模型使用一个完整的应该是指完整性)当使用MySQL的默认选择。MyISAM是只能用在特定的情况下。它会在最不可能的结果将更大,A车和更多的应用。

    有说这个的。一种在datamodelling /程序员很很少发现。NO offence,但它是使用MyISAM是这么解释的。


    InnoDB提供:

    1
    2
    3
    4
    5
    6
    ACID transactions
    row-level locking
    foreign key constraints
    automatic crash recovery
    table compression (read/write)
    spatial data types (no spatial indexes)

    所有数据在一行在InnoDB除了文本和8000字节blob CAN占据在最。没有可用的全文索引是InnoDB。在InnoDB伯爵(*)(当在哪里,或是由加入集团,将用于执行比MyISAM)因为行数是存储在内部。InnoDB数据和商店都在一个文件索引。InnoDB缓冲池使用A是到缓存数据和指标。

    MyISAM提供:

    1
    2
    3
    4
    5
    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表级别的锁,但没有行级锁。没有交易。没有自动崩溃恢复,但它不提供修复表的功能。没有对外的关键约束。MyISAM表通常是更紧凑的尺寸比在磁盘上,当innodb的表。MyISAM表可以进一步由高压缩和精简型myisampack如果需要,但成为只读。MyISAM索引文件和数据在一个商店到另一个。使用密钥索引缓冲区的MyISAM数据缓存和缓存管理着友好的操作系统。

    我会很好的工作最为InnoDB和MyISAM只有专门的用途使用。InnoDB引擎,现在是默认的MySQL版本新。


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

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

    每秒300个事务听起来相当多。如果您绝对需要这些事务在断电时保持持久,请确保您的I/O子系统能够轻松地处理每秒如此多的写操作。您至少需要一个带电池备份缓存的RAID控制器。

    如果你能获得一个小的耐久性点击,你可以使用innodb与innodb_flush_log_at_trx_commit设置为0或2(详见文档),你可以提高性能。

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


    这个问题和大多数答案都过时了。

    是的,这是一个老太太的故事,Myisam比InnoDB更快。注意这个问题的日期:2008年;现在差不多十年后了。从那时起,InnoDB已经取得了显著的性能进步。

    这张戏剧性的图表是针对米萨姆获胜的一个案例:没有WHERE条款的COUNT(*)。但这真的是你花时间做的吗?

    如果您运行并发测试,InnoDB很有可能获胜,即使与MEMORY相比也是如此。

    如果在基准SELECTs时进行任何写入,myisam和MEMORY可能会由于表级锁定而丢失。

    事实上,Oracle非常肯定InnoDB更好,他们几乎把Myisam从8.0中删除了。

    这个问题是5.1年代早期写的。此后,这些主要版本被标记为"一般可用性":

    • 2010年5月5日(12月8日)
    • 2013年5月6日(2月10日)
    • 2015年5月7日(10月9日)
    • 2018年8月0日(4月11日)

    底线:不要使用myisam


    请注意,我的正规教育和经验是与甲骨文,而我与MySQL的工作完全是个人的和我自己的时间,所以,如果我说的事情是真的甲骨文,但不是真的MySQL,我道歉。虽然这两个系统共享很多,但是关系理论/代数是相同的,并且关系数据库仍然是关系数据库,仍然有很多不同之处!!好的。

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

    注意:在Oracle中,create/alter/drop语句称为"ddl"(数据定义)语句,并隐式触发提交。插入/更新/删除语句(称为"DML"(数据操作))不会自动提交,但仅在执行DDL、提交或退出/退出时(或者如果将会话设置为"自动提交",或者如果客户端自动提交)才会自动提交。使用Oracle时必须注意这一点,但我不确定MySQL如何处理这两种类型的语句。因此,我想澄清一下,当涉及到MySQL时,我不确定这一点;只有Oracle。好的。基于事务的引擎excel的示例:

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

    在这种情况下,可能会有一个表供客人使用(姓名、电话、电子邮件等),还有第二个表用于跟踪已注册的客人数量。因此,我们对一个"交易"有两个操作。现在假设在将来宾信息添加到来宾表之后,存在连接丢失或具有相同影响的错误。客人桌已更新(插入),但在"可用座位"更新之前,连接已断开。好的。

    现在我们在客人桌上增加了一位客人,但是现在可用座位的数量是不正确的(例如,值是85,而实际上是84)。好的。

    当然,有很多方法可以处理这个问题,比如用"100减去来宾表中的行数"跟踪可用的座位,或者用一些代码检查信息是否一致,等等。但是,使用基于事务的数据库引擎(如innodb),要么提交所有操作,要么不提交任何操作。这在许多情况下都是有用的,但正如我所说,这不是确保安全的唯一方法,不是(不过,这是一种很好的方法,由数据库处理,而不是由程序员/脚本编写者)。好的。

    这都是"基于事务"的基本意思,在这种情况下,除非我遗漏了一些东西——要么整个事务按它应该的方式成功,要么什么都没有改变,因为只做部分的更改可能会使数据库变得一团糟,甚至可能会破坏数据库……好的。

    但我会再说一遍,这不是避免弄乱的唯一方法。但这是引擎本身处理的方法之一,只需担心"事务是否成功,如果不成功,我该怎么做"(如重试),"而不是从数据库外部手动编写代码检查它",并为此类事件做更多的工作。好的。最后,关于表锁定与行锁定的注释:

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

    不管怎样,我很有信心同意那些认为一次允许多少连接在一个锁着的桌子周围不起作用的人的观点。实际上,多个连接是锁定表的整个点!!这样其他进程/用户/应用程序就不能同时进行更改来损坏数据库。好的。

    在同一排工作的两个或多个连接将如何使您的一天变得非常糟糕??假设有两个进程都希望/需要在同一行中更新相同的值,比如说,因为该行是一个总线巡更的记录,并且这两个进程中的每个进程都同时希望将"乘客"或"可用座位"字段更新为"当前值加1"。好的。

    让我们假设一下,一步一步地:好的。

  • 流程1读取当前值,假设它是空的,因此到目前为止为"0"。
  • 进程2也读取当前值,该值仍为0。
  • 处理一次写入(当前+1),即1。
  • 进程2应该写入2,但由于它在进程1写入新值之前读取了当前值,所以它也将1写入表中。
  • 我不确定两种联系会像那样交织在一起,两种联系在第一种联系写之前都会阅读…但如果没有,我仍然会发现以下问题:好的。

  • 进程1读取当前值,即0。
  • 处理一次写入(当前+1),即1。
  • 进程2现在读取当前值。但是,当进程一写(更新)时,它没有提交数据,因此只有同一进程才能读取它更新的新值,而所有其他进程都看到旧值,直到有提交为止。
  • 另外,至少在Oracle数据库中,存在隔离级别,我不会浪费时间尝试解释。这里有一篇关于这个主题的好文章,每一个隔离级别都有它的优点和缺点,这与基于事务的引擎在数据库中的重要性是一致的……好的。

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

    唉,如果您知道这些并发性问题,是的,您可以不那么安全地运行它,只需编写应用程序,设置系统,这样就不可能发生此类错误(您的代码是负责的,而不是数据库本身)。然而,在我看来,我想说的是,最好总是尽可能多地使用保护措施,以防御的方式编程,并且总是意识到人的错误是不可能完全避免的。这种情况发生在每个人身上,任何说自己对它免疫的人都一定是在撒谎,或者只是写了一个"你好世界"的应用程序/脚本。;-)好的。

    我希望其中一些对某些人有所帮助,甚至更进一步,我希望我现在不是假设的罪魁祸首,也不是犯错误的人!!如果是的话,我很抱歉,但是这些例子是很好的考虑,研究风险等等,即使它们在这个特定的上下文中没有潜力。好的。

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

    这是我的第一反应,所以请原谅由于所有免责声明等长度…我只是不想在我完全不确定的时候显得傲慢!好的。好啊。


    我认为这是一篇很好的文章,可以解释不同之处,以及何时应该使用其中一个:http://tag1consulting.com/mysql_engines_myisam_vs_innodb


    还可以查看一些mysql本身的插件替换:

    玛丽亚德

    网址:http://mariadb.org/

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

    佩尔科纳服务器

    https://launchpad.net/percona-server

    一个增强的mysql的替代品,具有更好的性能,改进的诊断和增加的功能。


    我发现尽管myisam有锁争用,但由于它使用的快速锁获取方案,它在大多数情况下仍然比innodb快。我试过几次InnoDB,总是出于某种原因回到Myisam。此外,innodb在巨大的写负载中也会占用大量的CPU。


    根据我的经验,只要不进行删除、更新、大量的单次插入、事务和全文索引,myisam是一个更好的选择。顺便说一句,检查台太可怕了。随着表的行数变旧,您不知道它何时结束。


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

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


    我尝试在myisam和innodb表中插入随机数据。结果相当令人震惊。myisam插入100万行所需的时间比innodb只需10000秒!


    Myisam对于这种类型的工作负载(高并发性写入)来说是一个nogo,我对innodb没有太多经验(测试了3次,发现在每种情况下性能都很糟糕,但从上一次测试开始已经有一段时间了)。如果不强制运行MySQL,考虑让Postgres尝试一下,因为它可以更好地处理并发写操作。


    简而言之,如果您正在处理一些需要可靠的数据库来处理大量的插入和更新指令,那么InnoDB是很好的。

    而且,如果您需要一个数据库,考虑到Myisam在表锁方面的缺点,该数据库通常会接受大量的读(选择)指令,而不是写(插入和更新)指令,那么Myisam是很好的。

    您可能想退房;
    InnoDB的优缺点
    Myisam的利弊


    我知道这不受欢迎,但下面是:

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

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

    InnoDB已经存在了,用它来代替!尽管myisam是所有遗留系统中默认的原始引擎,但即使mysql开发人员最终也承认要将其更改为较新版本中的默认引擎。

    不,不管您是在读还是在写,还是在考虑性能,使用myisam都会导致各种问题,例如我刚遇到的这个问题:我正在执行数据库同步,同时有人访问了访问设置为myisam的表的应用程序。由于缺乏事务支持和该引擎的可靠性普遍较差,整个数据库崩溃,我不得不手动重启MySQL!

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

    我同意这里的其他答案,即高质量、高可用性、高性能的应用程序不应使用myisam,因为它将不起作用,它不够健壮或稳定,无法产生无挫折的体验。更多详情请参见比尔·卡温的回答。

    P.S.一定很喜欢当Myisam Fanboys投反对票的时候,但不能告诉你这个答案的哪一部分是错误的。


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

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

    我有一些表要在其中进行键/值查找…就这些。我需要获取哈希键的值(0-512字节)。这个数据库上没有很多事务。该表偶尔会得到更新(整体上),但0个事务。

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

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

    虽然,我不会使用myisam表执行财务事务,但对于简单的查找,您应该测试一下。通常是2到5倍的查询/秒。

    测试一下,我欢迎辩论。


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


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


    底线:如果你在离线处理大数据块的selects,myisam可能会给你更好(更好)的速度。

    在某些情况下,myisam比innodb效率无限高:在脱机操作大型数据转储时(由于表锁)。

    示例:我正在从使用varchar字段作为键的noaa转换一个csv文件(15M记录)。即使有大量可用的内存,InnoDB也会一直占用。

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

    1
    2
    3
    4
    5
    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表接收数据,并对键运行join,这样我就可以清理传入的文件,并用int键替换varchar字段(这些键与存储原始varchar值的外部表相关)。