如何解决MySQL Transactions如何在后台运行? MySQL在哪里存储字段的临时值?
我了解到MySQL事务可以让您一次执行多个插入/更新,而全部或全部都不成功,并且我知道如何使用它们。
我不确定的是MySQL如何长时间地保存所有数据,以及可能对性能产生什么影响。
在提交另一笔交易之前进行选择
我有一个由100个人组成的表,名为“ John”,我在交易内部的一个循环中将每个名称更新为“ Jane”,每次更新需要1秒,这意味着完成交易需要100秒。
如果在这100秒钟内从另一个进程中进行选择,结果将是“ John”而不是“ Jane”。如果我在交易提交后做出选择,它将返回“ Jane”。
一切都很好,我对此工作并不感到困惑。
在交易中选择
这是更令人困惑的地方。
我有一个由100个人组成的表,名为“ John”,我启动了一个事务,在其中循环浏览并逐行选择每一行。每个选择查询需要1秒,因此需要100秒。
50秒后,不在事务内的另一个过程将每行更新为“简”。
在我的第一个流程中,即使在完成对“简”的更新之后,我仍然会收到“约翰”的结果。
要清楚的时间是这样的:
- 12:00:00-所有行都说约翰,并且选择开始于每行花费1秒的事务中
- 12:00:30-所有行都更新为Jane
- 12:00:31-从第一笔交易中选择了第31行,但仍返回“ John”而不是“ Jane”。
它是如何工作的
因此,现在我可以在同一时间执行SELECT name FROM names WHERE id = 31
,并根据我是在进行交易还是在交易开始的时间执行一次返回“ John”和一次返回“ Jane”。
然后,MySQL必须以某种方式两次存储该字段的值。
需要复印吗?
我不认为它需要数据库或表的副本,因为当您开始事务时,它不知道您将要接触哪些表。您可能要等到交易开始10分钟后才能触摸表格,但是无论该过程中其他过程进行了多少修改,数据都是在10分钟之前。
我还尝试了大小为GB的数据库和表,并且要花费数分钟的时间进行转储,因此无法制作整个副本。
临时在某个地方举行吗?
也许它暂时将字段的值保存在等待事务完成的地方?
然后在执行选择时将需要检查是否存在待处理的值。
因此,进行SELECT name FROM names WHERE id = 31
类似于:
// John
if (pending_value('names','name',31) {
// Jane
name = get_pending_value('names',31);
} else {
// John
name = get_db_value('name',31);
}
那显然是非常愚蠢的伪代码,但实际上是在说:“是否有待处理的更新?如果是,请改用该代码”。
这大概会保存在内存中的某个地方吗?还是一个文件?还是系统数据库之一?
它如何影响性能
如果我的names
表中有10亿行并且我们执行了相同的查询,那么MySQL将同时知道10亿行的值是“ John”,而10亿行的值是“ Jane”。这肯定会影响性能。
但是受到影响的是事务内的查询还是事务外的查询?
例如
- 过程1 =
Begin transaction
- 过程2 =
UPDATE names SET name = "Jane"
- 进程1 =
SELECT name FROM names WHERE id = 31
//约翰 - 进程2 =
SELECT name FROM names WHERE id = 31
//简(Jane)
步骤(3)或步骤(4)中的查询对性能有影响还是对两者都有影响?
解决方法
一些线索:
- 了解有关“ MVCC”的信息-多版本并发控制
- 临时更改的行将保留到
COMMIT
或ROLLBACK
。 (请参阅文档中的“历史记录列表”。)这是逐行的,而不是整个表或数据库。不会“将行锁升级为表锁”。 - 每个表的每一行都有一个transaction_id。每个新交易都有一个新的更高ID。
- 该xaction ID与“事务隔离模式”一起确定了事务可以“看到”每行的哪个副本。因此,是的,可以短暂存在多个“行”
WHERE id = 31
。 - 行被锁定,而不是表。在您的某些示例中,交易运行了一段时间,然后偶然发现了“相同”行。
- 在某些情况下,行之间的“间隙”被锁定。 (我在您的示例中没有注意到这一点。)
- 整个表仅针对DDL(Drop,Alter等)锁定,而不针对DML(Select,Update等)锁定
- 发生冲突时,可能会发生“死锁”。这是每个事务都在等待另一个事务释放锁的时候。一笔交易会自动回滚。
- 发生冲突时,可能会发生“锁定等待”。这是带有锁定的事务最终将放开,让等待的事务继续进行的时间。
- 发生冲突并发生“锁定等待”时,
innodb_lock_wait_timeout
控制放弃的时间。 - 每个语句都在事务内部。当
autocommit=ON
时,每个语句本身就是一个事务。 (您的最后一个示例缺少BEGIN,在这种情况下,流程2具有2个独立的交易。)
在您的第一个示例中,read_uncommitted
的隔离模式将使您看到其他事务的更改。那是一种很少使用的模式。其他模式只有在COMMITted
之前,您才能看到更改,如果是ROLLBACK'd
,则永远看不到更改。 (是的,每个更改的行都有一个副本。)
repeatable_read
模式(及其他)有效地限制您仅查看具有transaction_id或更早版本的行。因此,即使在12:00:31,您仍然可以看到“约翰”。
一般建议:
- 不要编写运行时间超过几秒钟的事务
- 请记住在适当的地方使用
SELECT ... FOR UPDATE
-这会在SELECT
中的行上添加更强的锁定,以防万一它们将在事务中被更新或删除。 - 在可行的情况下,最好是一个
INSERT
加100行;这将是100个单行INSERTs
的10倍。 (与UPDATE
和DELETE
类似。) - 使用
SHOW ENGINE=InnoDB STATUS;
(我发现它在处理死锁方面很有用,但对于其他目的却是隐秘的。)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。