提高先select后插入的性能以避免重复记录(在mysql和php中)?

问题描述 投票:0回答:3

为了避免重复插入,我知道我可以在 mysql 中使用“INSERT IGNORE”或“INSERT … ON DUPLICATE KEY UPDATE”。

但我正在使用 laravel 并且我知道 firstOrCreate 不会这样做。它首先执行 SELECT 来查看条目是否存在,并且仅当 SELECT 没有返回记录时才执行 INSERT。我想这是因为

DUPLICATE KEY UPDATE
是 MySQL 特有的。

从性能角度来看,我先选择然后插入真的很糟糕吗?与“INSERT IGNORE”或“INSERT … ON DUPLICATE KEY UPDATE”相比,这会对性能产生多大影响?

不使用firstOrCreate并编写自己的php代码来“INSERT IGNORE”或“INSERT ... ON DUPLICATE KEY UPDATE”值得麻烦吗?

php mysql laravel insert
3个回答
2
投票

假设您只需要

INSERT INTO ... IGNORE
功能,我建议只在相关表上创建唯一索引。

ALTER TABLE yourTable ADD CONSTRAINT cnstr UNIQUE KEY (col1, col2, ...);

有了这个,任何插入重复记录的尝试都会导致 PHP 端出现应用程序错误,您可以轻松捕获并处理该错误。

在尝试插入之前先执行插入以检查是否存在重复项的问题在于,只有在确保从选择时间到插入完成期间表中没有发生其他 DML 活动时,这种逻辑才会起作用。否则,可能会出现如下情况:

process1: SELECT to check if record exists (assume it does not)
process2: INSERT record (which process1 wants to insert)
process1: INSERT same record (now a duplicate exists)

换句话说,您的 PHP 应用程序会“认为”不存在重复记录,并插入该记录。但是,在选择和插入之间的时间里,另一个进程恰好插入了相同的记录。

为了避免这种情况,我相信您需要类似可序列化事务的东西。但是,使用唯一约束要干净得多,并且将此责任主要交给数据库来处理。


0
投票

也许尝试在 mysql 中替换:

REPLACE ...

0
投票

4年前我提出这个问题后,我仍然受到它的打击,所以我提供一个答案来分享我的经验。我一直被这个问题困扰的主要原因是 Laravel 提供了很多方法来处理 insert 或 update 并且令人困惑。

首先,有

updateOrCreate
updateOrInsert
。他们的区别已经令人困惑。查看这篇文章以获得一些解释,Laravel 中 UpdateOrCreate 和 UpdateOrInsert 之间的差异。例如,
updateOrInsert
不会影响
created_at
updated_at
时间戳。

与我的问题相关的是它们都生成两个 SQL 子句,请参阅我的另一个问题为什么 Laravel updateOrCreate() 和 upsert() 都需要参数来唯一标识记录?。正如蒂姆·比格莱森(Tim Biegeleisen)在他的回答中所解释的那样,问题在于它有一个严重的缺陷。此外,它们不支持批量插入。但遗憾的是,我们的代码经常使用它们。

其次,我们的代码使用开源项目https://github.com/yadakhov/insert-on-duplicate-key因为我们使用mysql。我们也经常使用它。

第三,我们升级到laravel 9后,发现laravel现在有了

upset
,由https://github.com/laravel/framework/pull/34698介绍

第四,一个新人加入了团队,总是使用

replace
,而不知道重复时替换和插入之间的确切区别(请参阅中的“REPLACE”和“INSERT ... ON DUPLICATE KEY UPDATE”之间的实际区别是什么) MySQL?

所以现在在我们的代码库中,我们有五种不同的方法来处理插入或更新。我不确定当框架正在发展并且团队成员也来来去去时这是否是典型的(尽管我不会说我们的人员流动率很高)

关于重复更新插入与 2 个 SQL 子句的性能,我同意 “INSERT ... ON DUPLICATE KEY UPDATE”的性能与“UPDATE”相比,我需要自己对它们进行基准测试。

所以我做了一个小测试,当然这远不能令人信服,但在我的测试中,使用 InsertOnDuplicateKey 向 mysql 服务器(通过网络)插入 10,000 条记录需要 41 秒,而 2 个 sql 子句则需要 881 秒。我测试了好几次。所以性能差异是巨大的。

© www.soinside.com 2019 - 2024. All rights reserved.