我正在从事一个涉及大量数据库写入的项目(70%的插入和30%的读取)。这个比率还包括我认为是一个读一个写的更新。读取可能是脏的(例如,在读取时我不需要100%准确的信息)。 该任务每小时将处理超过100万个数据库事务。
我在网上读了一堆关于MyISAM和InnoDB之间区别的东西,对于我将用于这个任务的特定数据库/表来说,MyISAM似乎是显而易见的选择。从我看来,InnoDB在需要事务时是很好的,因为它支持行级锁。
有人有这种负载(或更高)的经验吗?MyISAM是正确的选择吗?
我正在从事一个涉及大量数据库写入的项目(70%的插入和30%的读取)。这个比率还包括我认为是一个读一个写的更新。读取可能是脏的(例如,在读取时我不需要100%准确的信息)。 该任务每小时将处理超过100万个数据库事务。
我在网上读了一堆关于MyISAM和InnoDB之间区别的东西,对于我将用于这个任务的特定数据库/表来说,MyISAM似乎是显而易见的选择。从我看来,InnoDB在需要事务时是很好的,因为它支持行级锁。
有人有这种负载(或更高)的经验吗?MyISAM是正确的选择吗?
当前回答
有点晚了…但这是我几个月前写的一篇相当全面的文章,详细介绍了MYISAM和InnoDB之间的主要区别。喝杯茶(或者一块饼干),好好享受吧。
MyISAM和InnoDB之间的主要区别在于引用完整性和事务。还有其他区别,如锁定、回滚和全文搜索。
参照完整性
Referential integrity ensures that relationships between tables remains consistent. More specifically, this means when a table (e.g. Listings) has a foreign key (e.g. Product ID) pointing to a different table (e.g. Products), when updates or deletes occur to the pointed-to table, these changes are cascaded to the linking table. In our example, if a product is renamed, the linking table’s foreign keys will also update; if a product is deleted from the ‘Products’ table, any listings which point to the deleted entry will also be deleted. Furthermore, any new listing must have that foreign key pointing to a valid, existing entry.
InnoDB是一个关系DBMS (RDBMS),因此具有引用完整性,而MyISAM没有。
事务和原子性
表中的数据使用数据操作语言(DML)语句进行管理,例如SELECT、INSERT、UPDATE和DELETE。事务将两个或多个DML语句组合到一个工作单元中,因此可以应用整个工作单元,也可以不应用任何工作单元。
MyISAM不支持事务,而InnoDB支持。
如果在使用MyISAM表时操作中断,操作将立即中止,受影响的行(甚至是每行中的数据)仍然受到影响,即使操作没有完成。
如果一个操作在使用InnoDB表时被中断,因为它使用事务,具有原子性,任何没有完成的事务都不会生效,因为没有提交。
表锁vs行锁
当对MyISAM表执行查询时,所查询的整个表将被锁定。这意味着后续查询只会在当前查询完成后执行。如果您正在读取一个大的表,并且/或者有频繁的读和写操作,这可能意味着大量的查询积压。
当对InnoDB表执行查询时,只有涉及的行被锁定,表的其余部分仍可用于CRUD操作。这意味着查询可以在同一个表上同时运行,前提是它们不使用同一行。
这个特性在InnoDB中被称为并发。尽管并发性很好,但对于选择的表范围有一个主要缺点,即在内核线程之间切换时会产生开销,您应该对内核线程设置一个限制,以防止服务器停止。
事务和回滚
当你在MyISAM中运行一个操作时,更改被设置;在InnoDB中,这些更改可以回滚。用于控制事务的最常用命令是COMMIT、ROLLBACK和SAVEPOINT。1. COMMIT -你可以写多个DML操作,但是只有在COMMIT时更改才会被保存。ROLLBACK -你可以放弃任何尚未提交的操作。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或其他数据类型。
其他回答
对于这样的读写比率,我猜InnoDB会表现得更好。 既然您可以接受脏读,那么您可以(如果您负担得起)复制到一个从服务器,并让您的所有读都到从服务器。另外,考虑批量插入,而不是一次插入一条记录。
请注意,我的正式教育和经验是在甲骨文,而我的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. ;-)
我希望其中一些对别人有所帮助,甚至更多,我希望我现在不是假设的罪魁祸首,也不是一个错误的人!!如果是这样的话,我很抱歉,但是这些例子很值得思考,研究风险,等等,即使它们在这个特定的上下文中不是潜在的。
请随意纠正我,编辑这个“答案”,甚至投票否决。请尽量改进,而不是用另一个错误的假设来纠正我的错误假设。: -)
这是我的第一个回应,所以请原谅由于所有的免责声明,等等。我只是不想在不确定的时候显得傲慢!
我发现即使Myisam有锁争用,它在大多数情况下仍然比InnoDb快,因为它使用了快速的锁获取方案。我尝试了几次Innodb,总是因为这样或那样的原因回到MyIsam。此外,InnoDB在巨大的写负载下会非常消耗CPU。
简而言之,如果你的工作需要一个可靠的数据库来处理大量的INSERT和UPDATE指令,InnoDB是很好的选择。
而且,如果你需要的数据库需要大量的读(SELECT)指令而不是写(INSERT和UPDATES), MyISAM是很好的,考虑到它在表锁方面的缺点。
你可能想去看看; InnoDB的优缺点 MyISAM的利弊
有点偏离主题,但为了文档的目的和完整性,我想添加以下内容。
一般来说,使用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被如此广泛使用的原因。