我正在从事一个涉及大量数据库写入的项目(70%的插入和30%的读取)。这个比率还包括我认为是一个读一个写的更新。读取可能是脏的(例如,在读取时我不需要100%准确的信息)。 该任务每小时将处理超过100万个数据库事务。

我在网上读了一堆关于MyISAM和InnoDB之间区别的东西,对于我将用于这个任务的特定数据库/表来说,MyISAM似乎是显而易见的选择。从我看来,InnoDB在需要事务时是很好的,因为它支持行级锁。

有人有这种负载(或更高)的经验吗?MyISAM是正确的选择吗?


当前回答

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

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

Note: With Oracle, create/alter/drop statements are called "DDL" (Data Definition) statements, and implicitly trigger a commit. Insert/update/delete statements, called "DML" (Data Manipulation), are not committed automatically, but only when a DDL, commit, or exit/quit is performed (or if you set your session to "auto-commit", or if your client auto-commits). It's imperative to be aware of that when working with Oracle, but I am not sure how MySQL handles the two types of statements. Because of this, I want to make it clear that I'm not sure of this when it comes to MySQL; only with Oracle.

一个基于事务的引擎擅长的例子:

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

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

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

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

这就是“基于事务”在这个上下文中的所有本质含义,除非我遗漏了一些东西——要么整个事务像它应该的那样成功,要么什么都没有改变,因为只做部分更改可能会对数据库造成轻微到严重的混乱,甚至可能破坏它……

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

最后,关于表锁和行锁的注意事项:

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

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

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

让我们假设一下,一步一步来:

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

我不确定两个连接能像这样混合在一起,在第一个人写之前都是阅读……但如果不是,那么我仍然会看到一个问题:

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

而且,至少在Oracle数据库中,有隔离级别,我就不浪费时间解释了。这里有一篇关于这个主题的好文章,每个隔离级别都有它的优点和缺点,这将与基于事务的引擎在数据库中的重要性有关……

最后,在MyISAM内部可能会有不同的安全措施,而不是外键和基于事务的交互。首先,事实上整个表都是锁定的,这使得不太可能需要事务/ fk。

And alas, if you are aware of these concurrency issues, yes you can play it less safe and just write your applications, set up your systems so that such errors are not possible (your code is then responsible, rather than the database itself). However, in my opinion, I would say that it is always best to use as many safeguards as possible, programming defensively, and always being aware that human error is impossible to completely avoid. It happens to everyone, and anyone who says they are immune to it must be lying, or hasn't done more than write a "Hello World" application/script. ;-)

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

请随意纠正我,编辑这个“答案”,甚至投票否决。请尽量改进,而不是用另一个错误的假设来纠正我的错误假设。: -)

这是我的第一个回应,所以请原谅由于所有的免责声明,等等。我只是不想在不确定的时候显得傲慢!

其他回答

有点偏离主题,但为了文档的目的和完整性,我想添加以下内容。

一般来说,使用InnoDB会使应用程序变得不那么复杂,可能也更没有bug。因为可以将所有引用完整性(外键约束)放入数据模型中,所以不需要像使用MyISAM时那样多的应用程序代码。

Every time you insert, delete or replace a record, you will HAVE to check and maintain the relationships. E.g. if you delete a parent, all children should be deleted too. For instance, even in a simple blogging system, if you delete a blogposting record, you will have to delete the comment records, the likes, etc. In InnoDB this is done automatically by the database engine (if you specified the contraints in the model) and requires no application code. In MyISAM this will have to be coded into the application, which is very difficult in web-servers. Web-servers are by nature very concurrent / parallel and because these actions should be atomical and MyISAM supports no real transactions, using MyISAM for web-servers is risky / error-prone.

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

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

话虽如此。数据模型是一门很少在网页设计师/程序员中发现的艺术。无意冒犯,但这确实解释了MyISAM被如此广泛使用的原因。

我认为这是一篇很好的文章,解释了两者之间的区别,以及什么时候应该使用其中一种: http://tag1consulting.com/MySQL_Engines_MyISAM_vs_InnoDB

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

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

这就意味着你需要一个支持行级锁的存储引擎,比如InnoDB。

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

InnoDB offers:

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 has table-level locking, but no row-level locking. No transactions. No automatic crash recovery, but it does offer repair table functionality. No foreign key constraints. MyISAM tables are generally more compact in size on disk when compared to InnoDB tables. MyISAM tables could be further highly reduced in size by compressing with myisampack if needed, but become read-only. MyISAM stores indexes in one file and data in another. MyISAM uses key buffers for caching indexes and leaves the data caching management to the operating system.

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

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

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