如何优化使用 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高级研发或架构师必了解---很多问题面试中常问到!

PHP+MySQL高效的分页方法,如何优化LIMIT,OFFSET进行的分页?

低价高级讲师燕十八自学PHP高性能架构班之mysql优化教程视频教程分享32集

mysql 千万级数据库如何进行多张结构相同的表联合查询?如何优化或设置提高查询速度?

如何使用 PHP 将 mdb 文件转换为 mysql 文件或 mysql 命令?

低价好课:高级讲师燕十八自学PHP高性能架构班之mysql优化教程视频教程分享32集百度云高清完整