如果MyISAM需要对数据进行磁盘读取,那么MyISAM如何比InnoDB“更快”?
InnoDB使用缓冲池存储索引和数据,而MyISAM仅用于存储索引和数据。索引?


评论

MyISAM允许操作系统缓存数据块,因此它并不总是“对数据进行磁盘读取”。

#1 楼

在这种独特情况下,MyISAM可以使InnoDB更快的唯一方法
MyISAM
读取时,可以从.MYI文件中读取一次MyISAM表的索引,并将其加载到MyISAM密钥缓存中(按大小调整)按key_buffer_size)。如何使MyISAM表的.MYD读起来更快?与此相关:
ALTER TABLE mytable ROW_FORMAT=Fixed;

我在过去的帖子中对此进行了介绍


MyISAM和InnoDB的最佳选择(请先阅读此内容)

在固定大小的字段上使用CHAR vs VARCHAR会对性能产生什么影响? (TRADEOFF#2)

针对高端和繁忙的服务器优化了my.cnf(在“复制”标题下)

哪种DBMS适合超快读取和简单数据结构? (第3段)

InnoDB
好吧,InnoDB呢? InnoDB是否进行任何磁盘I / O查询?令人惊讶的是,是的!您可能以为我为之疯狂,但这是绝对正确的,即使对于SELECT查询也是如此。在这一点上,您可能想知道“ InnoDB在世界上如何进行磁盘I / O查询?”
这一切都可以追溯到InnoDB成为具有ACID的事务存储引擎。为了使InnoDB具有事务性,它必须支持I中的ACID,即隔离。维护事务隔离的技术是通过MVCC(多版本并发控制)完成的。简单来说,InnoDB会在事务尝试更改数据之前记录其数据。在哪里记录?在系统表空间文件中,更好地称为ibdata1。这需要磁盘I / O。
比较
由于InnoDB和MyISAM都进行磁盘I / O,是什么随机因素决定了谁更快?

列大小
列格式
字符集
数值范围(需要足够大的INT)
行被跨块分割(行链接)
DELETEsUPDATEs引起的数据碎片

主键的大小(InnoDB具有聚集索引,需要两次键查找)
索引条目的大小
列表还在继续...

因此,在阅读量大的环境中,具有固定行格式的MyISAM表可能胜过InnoDB从表中读出的数据InnoDB缓冲池,如果有足够的数据写入ibdata1中包含的撤消日志中,以支持强加于InnoDB数据上的事务行为。
结论
仔细计划您的数据类型,查询和存储引擎。数据增长后,移动数据可能会变得非常困难。只是问问Facebook ...

评论


罗兰多,很好的回答。我不得不质疑您是否包含迈克尔·斯通布雷克(Michael Stonebreaker)的令人难以置信的主张,他只是想出售自己的产品,而对Facebook一无所知。在多次听过Facebook关于MySQL的介绍后,很显然他们对自己的选择感到满意。

–亚伦·布朗
2012年5月4日在1:22

@AaronBrown我去年在Percona Live NYC上听过Harrison Fisk的讲话,您说的没错– Facebook对他们对InnoDB的独家使用以及他们如何花时间提出在整个系统范围内进行在线模式更改的方式感到非常满意。他甚至为观众提供了为Facebook处理大数据工作的机会。我收录了这篇文章,以表明有些人对此感到恐惧。我欢迎有机会使用海量数据。这将是有趣和挑战性的。想象一下要学习的技术。当然,我终生都不会碰MyISAM ...

– RolandoMySQLDBA
2012年5月4日在2:09



我也在那个会议上(并有幸能够发表演讲),哈里森的演讲非常出色。

–亚伦·布朗
2012年5月4日下午2:57

#2 楼

在一个简单的世界中,MyISAM的读取速度更快,InnoDB的写入速度更快。

一旦开始引入混合读写,InnoDB的行锁定机制也将使读取速度更快。

几年前,我写了一个MySQL存储引擎的比较,直到今天仍然如此,概述了MyISAM和InnoDB之间的独特区别。

根据我的经验,应该将InnoDB用于除读取大量缓存表以外的所有内容,在缓存表中,由于损坏而丢失数据并不那么重要。

评论


该答案已过期5年。 InnoDB实际上已经在所有方面赶上了。使用MyISAM不再有太多争论。 MySQL 8.0正在一起删除MyISAM。

–里克·詹姆斯(Rick James)
17-10-26在14:24

链接现在已过期9年。

–里克·詹姆斯(Rick James)
17-10-26在15:01

更正后,答案已过期9年(阅读第一句话的任何人在使用数据库时都会遇到一些严重问题),链接已过期11年。追上里克·詹姆斯(Rick James),您已经落后了:)。

– CYREX
19年10月10日在13:51

您说得对@CYREX :-) 11年后,这篇文章仍然很吸引人,这真是令人惊讶。我的生活和InnoDB的优化方式都发生了很大的变化。如今,几乎没有理由使用MyISAM

– Mike Peters
19年2月11日在14:05

我今天不得不看一些濒死的数据库,而这两个引擎仍在使用旧版本的mysql。表既是InnoDB又是MyISAM,我的好奇心使我到这篇文章非常有帮助。

– Farrukh Subhani
19-3-15在9:17



#3 楼

为了在这里回答有关两个引擎之间的机械差异的更多信息,我提供了一个经验速度比较研究。
就纯速度而言,MyISAM并不总是比InnoDB快,但以我的经验在PURE READ工作环境中,速度往往要快2.0到2.5倍。显然,这并不适合所有环境-正如其他人所写的那样,MyISAM缺少事务和外键之类的东西。
我在下面做了一些基准测试-我使用python进行循环和timeit库用于时间比较。出于兴趣,我还包括了内存引擎,尽管它仅适用于较小的表(尽管超过了MySQL内存限制,您会不断遇到The table 'tbl' is full),但它仍可提供最佳的整体性能。我查看的四种选择类型是:

普通SELECTs
计数
条件SELECTs
索引和非索引子选择

首先,我使用以下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) Vanilla选择
查询:SELECT * FROM tbl WHERE index_col = xx
结果:draw

它们的速度大致相同,并且与预期的一样,要选择的列数是线性的。 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查询的许多加速都是由于类似的影响。
代码: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 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) )


4)子选择
结果:InnoDB胜出
对于此查询,我为子选择创建了一组附加表。每行仅是两列BIGINT,一列具有主键索引,一列不具有任何索引。由于表很大,所以我没有测试内存引擎。 SQL表创建命令为
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) )

,其中再次用“ MyISAM”代替了第二个表中的“ InnoDB”。
在此查询中,我将选择表的大小保留为1000000,而是更改子选定列的大小。

InnoDB轻松获胜。到达合理的尺寸表后,两个引擎都随子选择的尺寸线性缩放。索引加快了MyISAM命令的速度,但有趣的是,它对InnoDB的速度影响很小。
subSelect.png
代码:就是如果您真的关心速度,则需要对正在执行的查询进行基准测试,而不要对哪种引擎更合适做任何假设。

评论


我喜欢您的回答,因为它有利于您进行基准测试和决定的人。没有两个系统可以从不同的存储引擎中以相同的方式受益,因此选择存储引擎需要进行尽职调查。为您+1,欢迎使用DBA StackExchange!

– RolandoMySQLDBA
2015年6月11日在16:43

另外,请参阅我的文章dba.stackexchange.com/questions/1/…以及其他答案。您的帖子超越了一切。

– RolandoMySQLDBA
2015年6月11日在16:46

SELECT * FROM tbl WHERE index_col = xx-这是两个可能导致图形更多变化的因素:主键与辅助键;索引是否缓存。

–里克·詹姆斯(Rick James)
15年6月24日在3:35

在添加WHERE子句之前,SELECT COUNT(*)是MyISAM的明显赢家。

–里克·詹姆斯(Rick James)
15年6月24日在3:36

我想我的意思是每个查询都需要分别进行基准测试。我已经在答案中包含了代码-如果您想尝试其他查询,请成为我的客人-或明确说明您想要的查询,然后我将其添加。

– StackG
15年6月24日在7:58

#4 楼

哪个更快?两者可能更快。 YMMV。

您应该使用哪个? InnoDB-防撞等,

评论


请定义“等”。

–德拉萨维亚
17-10-26在12:48

@dellasavia-最新的“ etc”是Oracle计划删除MyISAM。他们对InnoDB充满信心。

–里克·詹姆斯(Rick James)
17-10-26在14:43