我要说的是一个涉及大量数据库写入的项目( 70%的插入和30%的读取 )。 这个比率还将包括我认为是一次读取和一次写入的更新。 读取内容可能很脏(例如,读取时我不需要100%准确的信息)。
有问题的任务将是每小时进行超过一百万次数据库事务。
我已经在网上阅读了很多有关MyISAM和InnoDB之间差异的内容,对于我要用于此任务的特定数据库/表,MyISAM似乎是我的明显选择。 从我看来,由于需要支持行级锁定,因此如果需要事务处理,InnoDB很好。
是否有人对这种负载(或更高负载)有任何经验? MyISAM是要走的路吗?
稍微偏离主题,但出于文档目的和完整性考虑,我想添加以下内容。
通常,使用InnoDB会导致更少的复杂应用程序,而且可能也没有更多的错误。 因为您可以将所有参照完整性(外键约束)放入数据模型,所以您不需要像MyISAM那样需要那么多应用程序代码。
每次插入,删除或替换记录时,您都必须检查和维护关系。 例如,如果您删除父母,则所有孩子也应被删除。 例如,即使在简单的博客系统中,如果您删除博客发布记录,也将必须删除评论记录,喜欢等等。在InnoDB中,这是由数据库引擎自动完成的(如果您在模型中指定了约束, ),不需要任何应用代码。 在MyISAM中,必须将其编码到应用程序中,这在Web服务器中非常困难。 Web服务器本质上是非常并行/并行的,并且由于这些操作应该是原子性的,并且MyISAM不支持任何真实的事务,因此对于Web服务器使用MyISAM会带来风险/容易出错。
在大多数情况下,由于多种原因,InnoDB的性能也会好得多,其中一个原因是,他们能够使用记录级锁定而不是表级锁定。 不仅在写入比读取更频繁的情况下,还在大型数据集上具有复杂联接的情况下。 我们注意到,对于非常大的联接(耗时数分钟),仅使用InnoDB表而不是MyISAM表可将性能提高3倍。
我要说的是,在一般情况下,使用MySQL时,InnoDB(使用具有参照完整性的3NF数据模型)应该是默认选择。 MyISAM仅应在非常特殊的情况下使用。 它最有可能表现不佳,导致应用程序更大,更bug。
话虽如此。 数据建模是网页设计师/程序员中很少发现的艺术。 没有违法,但这确实解释了MyISAM被如此广泛地使用。
请注意 ,我的正式教育和经验是在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的,要么所有的操作都承诺,或者他们都不是。 这在许多情况下可能会有所帮助,但是就像我说的那样,这并不是唯一安全的方法,不是(不是一种好方法,但是,由数据库而不是程序员/脚本编写者来处理)。
在这种情况下,这全是“基于交易”的意思,除非我遗漏了一些东西,否则整个交易都会按预期成功,或者什么都不会更改,因为仅进行部分更改可能会使严重的混乱变得微不足道。数据库,甚至可能损坏它...
但我要再说一次,这不是避免弄乱的唯一方法。 但这是引擎本身处理的方法之一,您无需担心手动操作,而只需要担心“事务是否成功以及如果不成功该怎么办(例如重试)”就可以使用代码/脚本了。编写代码以从数据库外部“手动”检查它,并为此类事件做更多的工作。
免责声明:关于MySQL,我可能在所有后续事情中都是错误的,假设/示例情况是需要研究的事情,但在可能导致MySQL损坏的确切原因上,我可能是错误的。 然而,即使MySQL具有更多避免此类情况的机制,这些示例在常规编程中还是非常真实的。
无论如何,我与那些谁主张,多少个连接同时允许不解决锁定表同意相当有信心。 实际上,多个连接是锁定表的全部要点!! 这样其他进程/用户/应用程序就无法通过同时进行更改来破坏数据库。
在同一行上工作的两个或多个连接如何为您带来一个非常糟糕的一天? 假设有两个进程都希望/需要在同一行中更新相同的值,例如,由于该行是公交旅行的记录,并且两个进程中的每个进程都同时希望更新“搭便车”或“ available_seats”字段为“当前值加1”。
假设我们逐步进行此操作:
我不确定两个连接是否会像这样混合在一起,都在第一个连接写之前就读了……但是,如果没有,那么我仍然会遇到以下问题:
另外,至少对于Oracle数据库而言,存在隔离级别,我不会浪费时间尝试解释。 这是一篇关于该主题的好文章,每个隔离级别都有其优缺点,这将与基于事务的引擎在数据库中的重要性结合在一起...
最后,MyISAM中可能有不同的保护措施,而不是外键和基于事务的交互。 好吧,有一个事实是,整个表都被锁定,这使得需要事务/ FK的可能性降低了。
a,如果您知道这些并发问题,可以的话,您可以放心使用它并不太安全,只需编写您的应用程序,设置系统,这样就不可能发生此类错误(然后由您的代码负责,而不是数据库本身)。 但是,我认为,总是最好使用尽可能多的防护措施,进行防御性编程,并且始终意识到不可能完全避免人为错误。 它发生在每个人身上,任何说对它免疫的人都必须撒谎,或者除了编写“ Hello World”应用程序/脚本以外,别无所求。 ;-)
我希望其中的一些对某人有帮助,甚至更多。所以,我希望我现在不仅是假设的罪魁祸首,而且是一个犯错误的人! 如果是的话,我表示歉意,但是即使在特定的情况下这些例子都没有潜力,这些例子还是值得思考的,研究其风险等等。
随时纠正我,编辑此“答案”,甚至予以否决。 只是请尝试改进,而不是与另一个人纠正我的错误假设。 ;-)
这是我的第一反应,因此请原谅所有免责声明等内容的冗长。。。我只是不想在不确定的情况下显得自大!
myisam是那种类型的工作负载(高并发写入)的NOGO,如果您使用innodb,我没有那么多的经验(对其进行了3次测试,发现每种情况下性能都很糟糕,但是自上次测试以来已经有一段时间了)不必强迫运行mysql,请考虑尝试一下postgres,因为它可以更好地处理并发写入
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版本中的默认引擎。
人们经常谈论性能,读取与写入,外键等,但是在我看来,存储引擎还有另一个必备功能: 原子更新。
尝试这个:
killall -9 mysqld
模拟崩溃,此方法也有效。 性能当然是可取的,但不会丢失数据应该胜过这一点。
底线:如果您正在离线处理大量数据,则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值的外部表)。
几乎每次我启动一个新项目时,我都会用同样的问题搜索Google,以查看是否有新的答案。
最终归结为-我采用了最新版本的MySQL并运行测试。
我有要在其中进行键/值查找的表...仅此而已。 我需要获取哈希键的值(0-512字节)。 该数据库上没有很多事务。 该表偶尔会(全部)获得更新,但是有0个事务。
因此,这里我们不是在讨论复杂的系统,而是在进行简单的查找,以及如何(除了使表RAM驻留在外)优化性能。
我还对其他数据库(即NoSQL)进行了测试,以查看是否有我可以受益的地方。 我发现的最大优点是在键映射中,但就查找而言,MyISAM目前是所有这些中的佼佼者。
虽然,我不会使用MyISAM表执行财务交易,但是对于简单的查询,您应该对其进行测试。通常,查询/秒的速度是2到5倍。
测试一下,我欢迎辩论。
如果是70%的插入和30%的读取,则它更像InnoDB方面。
我不是数据库专家,我也不是凭经验说话。 然而:
MyISAM表使用表级锁定 。 根据流量估算,每秒将近200次写入。 使用MyISAM, 任何时候都只能进行其中之一 。 您必须确保您的硬件可以跟上这些事务,以避免被超载,即,单个查询最多可以花费5毫秒。
对我来说,这建议您需要一个支持行级锁定的存储引擎,即InnoDB。
另一方面,编写一些简单的脚本来模拟每个存储引擎的负载,然后比较结果应该是相当简单的。
每个应用程序都有其自己的使用数据库的性能配置文件,并且随着时间的推移它可能会发生变化。
您可以做的最好的事情就是测试您的选择。 在MyISAM和InnoDB之间切换很简单,因此请加载一些测试数据并针对您的站点启动jmeter,然后看看会发生什么。
游戏有点晚了...但是这是我几个月前写的一篇非常全面的文章 ,详细介绍了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或其他数据类型。
简而言之,如果您正在处理需要可靠的数据库并且可以处理很多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
,但它The table 'tbl' is full
提供最佳的性能。 我查看的四种选择是:
首先,我使用以下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”和“内存”。
查询: 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) )
查询: 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) )
查询: 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) )
结果: 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) )
我认为所有这一切的基本含义是,如果您真正关心速度,则需要对正在执行的查询进行基准测试,而不是对哪种引擎更合适进行任何假设。
对于这样的读写比,我猜想InnoDB的性能会更好。 由于您可以接受脏读操作,因此您可以(如果负担得起)将其复制到从属设备,然后将所有读取内容发送给该从属设备。 另外,请考虑批量插入,而不是一次插入一条记录。
我认为这是一篇出色的文章,解释了这些差异以及何时应该使用另一种差异: http : //tag1consulting.com/MySQL_Engines_MyISAM_vs_InnoDB
我知道这不会流行,但可以这样:
myISAM缺乏对诸如事务和引用完整性之类的数据库必需品的支持,这常常导致应用程序出现故障。 如果您的数据库引擎甚至不支持正确的数据库设计基础知识,则您将无法学习。
在数据库世界中不使用参照完整性或事务就像在软件世界中不使用面向对象的编程。
InnoDB现在存在,请改用它! 即使myISAM是所有旧系统中默认的原始引擎,即使MySQL开发人员也最终同意将其更改为较新版本的默认引擎。
没关系,无论您是阅读还是写作,或者您有什么性能方面的考虑,使用myISAM都会导致各种问题,例如我刚遇到的一个问题:我正在执行数据库同步,而同时有人访问了访问设置为myISAM的表的应用程序。 由于缺乏事务支持以及该引擎的可靠性普遍较差,这使整个数据库崩溃,我不得不手动重新启动mysql!
在过去的15年的发展中,我使用了许多数据库和引擎。 在此期间,myISAM在我身上崩溃了大约十二次,其他数据库也只有一次! 而且那是一个Microsoft SQL数据库,其中一些开发人员通过这种方式编写了错误的CLR代码(通用语言运行时-基本是在数据库内部执行的C#代码),这并不是数据库引擎的错误。
我同意这里的其他答案,即高质量,高可用性,高性能的应用程序不应使用myISAM,因为它无法工作,它的鲁棒性或稳定性不足以带来无挫折的体验。 有关更多详细信息,请参见Bill Karwin的答案。
PS当我的ISAM粉丝们投票否决时,PS Gotta会喜欢上它,但无法告诉您此答案的哪一部分不正确。
我发现即使Myisam具有锁定争用,但由于Myisam使用了快速锁定获取方案,因此它在大多数情况下仍比InnoDb快。 我已经尝试了几次Innodb,并且总是出于某种原因退回到MyIsam。 此外,InnoDB在巨大的写入负载中可能占用大量CPU。
对于具有更多读写操作的负载,您将从InnoDB中受益。 因为InnoDB提供行锁定而不是表锁定,所以您的SELECT
可以是并发的,不仅可以相互并发,而且可以与许多INSERT
并发。 但是,除非打算使用SQL事务,否则将InnoDB提交刷新设置为2( innodb_flush_log_at_trx_commit )。 这给您带来了很多原始性能,否则您将表从MyISAM移到InnoDB时可能会失去这些性能。
另外,请考虑添加复制。 这为您提供了一些读取扩展,并且由于您声明读取不必是最新的,因此可以让复制落后一些。 只要确保它可以在流量最大的任何情况下都可以追上,否则它将永远落后并且永远不会追上。 但是,如果您采用这种方式,我强烈建议您将读取从从服务器和复制滞后管理隔离到数据库处理程序。 如果应用程序代码不知道这一点,则非常简单。
最后,要注意不同的表负载。 您不会在所有表上具有相同的读/写比率。 一些读取率接近100%的较小表可以承受MyISAM。 同样,如果有些表的写入率接近100%,则可以受益于INSERT DELAYED
,但这仅在MyISAM中受支持(对于InnoDB表, DELAYED
子句将被忽略)。
但是可以确定基准。
以我的经验,只要您不执行DELETE,UPDATE,大量单个INSERT,事务和全文本索引,MyISAM是一个更好的选择。 顺便说一句,检查表是可怕的。 随着表格在行数上的变老,您不知道表格何时结束。
该问题和大多数答案已经过时 。
是的,这是一个古老的妻子的故事,那就是MyISAM比InnoDB更快。 注意问题的日期:2008年; 现在已经快十年了。 从那时起,InnoDB取得了显着的性能进步。
戏剧性的图表是MyISAM获胜的一种情况: 没有 WHERE
子句的COUNT(*)
。 但这真的就是您花费时间做的事情吗?
如果您运行并发测试, 即使对抗MEMORY
,InnoDB也很有可能获胜。
如果在对SELECTs
进行基准测试时执行任何写操作,则由于表级锁定,MyISAM和MEMORY
可能会丢失。
实际上,Oracle确信InnoDB会更好,因为他们几乎从8.0中删除了MyISAM。
该问题是在5.1年代初编写的。 从那时起,这些主要版本被标记为“常规可用性”:
底线:不要使用MyISAM
我已经在一个表中简要讨论了这个问题,以便您可以得出结论是否要使用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
总结一下:
Frequent reading, almost no writing => MyISAM Full-text search in MySQL <= 5.5 => MyISAM
在所有其他情况下, InnoDB通常是最好的方法。
如果使用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的某些问题有一些了解。
我试图将随机数据插入MyISAM和InnoDB表中。 结果令人震惊。 MyISAM插入一百万行所需的时间比InnoDB少一万秒!
另外,请查看MySQL本身的一些替代产品:
玛丽亚数据库
http://mariadb.org/
MariaDB是一种数据库服务器,为MySQL提供了嵌入式替换功能。 MariaDB是由MySQL的某些原始作者在自由和开源软件开发人员的广泛社区的协助下构建的。 除了MySQL的核心功能外,MariaDB还提供了丰富的功能增强集,包括备用存储引擎,服务器优化和补丁。
Percona服务器
https://launchpad.net/percona-server
MySQL的增强的嵌入式替代品,具有更好的性能,改进的诊断功能和附加功能。