如何优化使用 PHP 或 Mysql 或 Laravel 将 12K 的 JSON 插入数据库
Posted
技术标签:
【中文标题】如何优化使用 PHP 或 Mysql 或 Laravel 将 12K 的 JSON 插入数据库【英文标题】:How to optimize inserting 12K of JSON into database using PHP or Mysql or Laravel 【发布时间】:2015-05-19 19:38:50 【问题描述】:我对此很陌生,但我下面的代码运行良好,它拉入 json,循环并将它们插入数据库,同时检查玩家是否存在,如果存在则只更新分数和奖金。如果是新玩家,则插入新行。
我的问题是我拥有的玩家列表非常庞大,大约 10-15K(玩家 1 到玩家 15000)。当我监控 Laravel 查询日志中是否存在潜在的性能问题时,我注意到由于 foreach 一个一个地循环查询,正在运行的查询量非常大。
因为所有 10-15K 数据都在那里并且可以从 $scores 变量中获得。 有没有办法让它运行一个大查询,而不是 10-15K 单个查询,同时受到 PDO 占位符的保护?
我正在考虑在 php 中构建一个数组,然后将该数组提供给 mysql 查询,但是您将如何使用占位符来保护它呢? 另外,这将每天运行几次
我真的很困惑如何最好地做到这一点,所以这里是代码,原始代码要大得多,因为它包含团队、位置、IP 地址等。但这是一个精简的例子
Unique key in database set as (id)
$scores = '
"1":
"id":"1",
"player":"1",
"name":"James",
"score":"10.25",
"bonus":"2.10"
,
"2":
"id":"2",
"player":"2",
"name":"John",
"score":"11.50",
"bonus":"1.10"
';
$decoded = json_decode($scores);
foreach($decoded AS $value)
$update = DB::insert("INSERT INTO players (id,player,name,score,bonus) VALUES (:id,:player,:name,:score,:bonus)
ON DUPLICATE KEY UPDATE score=VALUES(score), bonus=VALUES(bonus)",
array(
':id' => ,$value->id
':player' => ,$value->player
':name' => ,$value->name
':score' => ,$value->score
':bonus' => ,$value->bonus
)
);
【问题讨论】:
INSERT
语句支持多个 VALUES
块,因此您应该能够构建 1 个查询来插入(例如)20 条记录。由于您使用的是参数化查询,因此它可能需要以数字结尾的变量名...INSERT INTO ... VALUES (:id1,:player1,:name1,:score1,:bonus1) (:id1,:player2,:name2,:score2,:bonus2)
等...否则,您可能会发现从 sql 服务器上的命令行批量插入将是数量级更快,因为它切断了网络跃点,您可以进行更大的批量插入(假设您可以将数据放到机器上)
这些不会由我更新,所以我需要一个可以通过 php 更新的解决方案,对查询的大小或长度是否有任何限制?因为会有 10K-15K 行的数据?
我会尝试几件事... 1) 将 foreach 循环包装在 transaction 中。这样你只有一个“提交”。 2)将“插入”查询移出循环并“准备”一次。在“循环”“bindValue”和“执行”内。这将“sql 解析”减少到一次。此时可能已经足够快了。
@adamgouldie 是的,肯定有限制,但并不是一成不变的。自从我使用 MySQL 以来已经有一段时间了,但我记得在尝试远程运行 20MB 脚本时遇到了问题。 IIRC,问题是由于 MySQL 中配置的输入缓冲区大小造成的。它是可以修复的,但最后我们只是 SCP'd 脚本并在本地运行它。顺便说一句,不要假设你的开发服务器和生产服务器上的配置是相同的,因为 prod 通常已经过强化/限制收紧。
blimmin 地狱,Ryan Vincent 你是对的,占位符让我头疼,不断出现数组到字符串的转换错误,并且使用占位符构建数组的代码变得非常混乱。一旦我尝试了事务选项,运行时间就减少了近 3/4。我想我会解决的。谢谢
【参考方案1】:
首先,您的 INSERT 可能如下所示:
INSERT INTO table_name(col1, col2) VALUES(val1, val2), (val1, val2)
这是一个插入 2 行的 INSERT,它比 2 个 INSERT 快
对于 laravel,您可以使用插入函数,如 here 所示(参见“将多条记录插入表中”)
不确定它是如何在幕后工作的,我的猜测比 foreach 快
【讨论】:
感谢您的帮助,我确实尝试了重新搜索 Laravel,但找不到任何解决方案来插入和更新某些字段,例如 ON DUPLICATE KEY UPDATE。估计是不存在的。因此我不得不求助于使用 MYSQL 方法。代码看起来不错,但我希望将它们参数化以防止 SQL 注入【参考方案2】:这是一种非常快速的方法(除了您的 IODKU 循环或@Basic 批处理)。
-
将
INSERT
的所有数据批处理到一个临时表中。 (@Traxo 回答。)
复制“新”行:INSERT INTO real SELECT ... FROM tmp LEFT JOIN real ON ... WHERE ... IS NULL
更新现有行:UPDATE real JOIN tmp ON ... SET real.bonus = tmp.bonus, real.score = bonus.score;
有一些细节,例如BEGIN
...COMMIT
假设您正在使用 InnoDB(您应该使用)。
如果您的定期更新确实是完全替代,那么请这样做:
CREATE TABLE new LIKE real;
加载new
RENAME TABLE real TO old, new TO real;
-- 原子和瞬时
DROP TABLE old;
【讨论】:
嗨 Rick,这是否意味着您首先必须创建循环来获取正确格式化的 JSON 值才能执行批量插入。其次,如果您需要占位符来防止 SQL 注入,则必须构建另一组循环来格式化占位符。所以真正的问题不在于 SQL 方面,而更多的是如何将 JSON 处理成一种单一 SQL 查询的格式。 在您的原始帖子中,$value->score
(etc) 需要转义以防止 SQL 摄取。 JSON 本身没有注入问题(我认为)。转义可以与构建批处理 INSERT
或 IODKU 或其他任何内容的循环相同。性能问题:转义是微不足道的开销;批量插入可以将过程加快 10 倍。以上是关于如何优化使用 PHP 或 Mysql 或 Laravel 将 12K 的 JSON 插入数据库的主要内容,如果未能解决你的问题,请参考以下文章
PHP+MySQL高效的分页方法,如何优化LIMIT,OFFSET进行的分页?
低价高级讲师燕十八自学PHP高性能架构班之mysql优化教程视频教程分享32集
mysql 千万级数据库如何进行多张结构相同的表联合查询?如何优化或设置提高查询速度?