【问题标题】:Low speed of saving to the MySQL (PHP - Yii2)保存到 MySQL 的速度低(PHP - Yii2)
【发布时间】:2021-12-03 14:32:48
【问题描述】:

我正在尝试将数据从 JSON 文件导入 MySQL。

public function importProductFile($file, $return = true)
    {    
        $products = json_decode($file);
        $dubTableName = Product::tableName() . "_dub";
        $start = time();
        if ($this->db->createDuplicateTable(Product::tableName(), $dubTableName)) {
    
            $i = 0;
    
            foreach ($products as $product) {
                $i++;
                $item = new Product_dub();
                $item->id_1c_product = $product->id;
                $category = Category_dub::findOne(['id_1c_category' => $product->category_id]);
    
                if (!$category) {
                    Answer::failure("В этом товаре отсутствует категория или такой категории не существует: " . $product->title);
                }
    
                $item->category_id = $category->id;
                $item->title = $product->title;
                $brand = Brands_dub::findOne(['id_1c_brand' => $product->brand_id]);
    
                if (!$brand) {
                    Answer::failure("В этом товаре отсутствует бренд/изготовитель: " . $product->title);
                }
    
                $item->brand_id = $brand->id;
                // $item->shortdesc = $product->shortdesc;
                $item->content1 = $product->content1;
                $item->content2 = $product->content2;
                $item->content3 = $product->content3;
                $item->link_order = $product->link_order;
                $item->img = $product->img;
                $item->in_stock = $product->in_stock ? 1 : 0;
                $item->is_popular = $product->is_popular ? 1 : 0;
    
                if (!$item->save()) {
                    Answer::failure("Не удалось импортировать: Проверьте данные в " . $product->title);
                }
    
                if ($i == 200) {
                    break;
                }
            }
        }
    
        $finish = time();
        $res = $finish - $start . "sec. ";
    
        if ($return) {
            echo $res;
            Answer::success();
        }
    }

我的 JSON 文件中有大约 1100 个对象。将 100 行添加到数据库需要 7 秒。添加 200 行 - 15 秒。 300 = 33 秒,400 = 58 秒。为什么它会随着时间的推移而变慢?如何加快这个过程?

我在本地 OpenServer 服务器上做所有事情。 PHP 7.2 版本,至强 2620v3 处理器,16 GB DDR4,硬盘。

UPD 1.

“您可以尝试不导入并确定阅读速度吗” - 我评论$item->save() 并为所有 JSON 文件获得 1-2 秒。 “在您的周期的每次迭代中,您都在运行 2 个数据库查询来加载类别和品牌。” - 我尝试删除这些行进行测试 - 但结果比使用 2 个 DB 查询快 1-2 秒。

UPD 2.

我将save() 更改为insert() - 速度提高了。现在所有 JSON(1107 行)都在 40 秒内导入。

有没有更快的方法将现成的数据从 JSON 加载到数据库中? 如果有 10 万行或 100 万行怎么办?等待几个小时是正常的做法吗?

public function importProductFile($file, $return = true)
    {    
        $products = json_decode($file);
        $dubTableName = Product::tableName() . "_dub";
        $start = time();

        if ($this->db->createDuplicateTable(Product::tableName(), $dubTableName)) {
            $start = time();
            $categoryMap = Category_dub::find()->select(['id', 'id_1c_category'])->indexBy('id_1c_category')->column();
            $brandMap = Brands_dub::find()->select(['id', 'id_1c_brand'])->indexBy('id_1c_brand')->column();

            foreach ($products as $product) {
                Yii::$app->db->createCommand()->insert('product_dub', [
                    'id_1c_product' => $product->id,
                    'category_id' => $categoryMap[$product->category_id] ?? '0',
                    'title' => $product->title,
                    'brand_id' => $brandMap[$product->brand_id] ?? 'No brand',
                    'content1' => $product->content1,
                    'content2' => $product->content2,
                    'content3' => $product->content3,
                    'link_order' => $product->link_order,
                    'img' => $product->img ?? 'no-image.png',
                    'in_stock' => $product->in_stock ? 1 : 0,
                    'is_popular' => $product->is_popular ? 1 : 0,
                ])->execute();
            }
        }
        }
    
        $finish = time();
        $res = $finish - $start . "sec. ";
    
        if ($return) {
            echo $res;
            Answer::success();
        }
    }

【问题讨论】:

  • 你可以尝试不导入,只确定读取文件和创建所有对象的速度吗?
  • 在循环的每次迭代中,您都在运行 2 个数据库查询来加载类别和品牌。循环前在查询中加载类别/品牌地图怎么样?像这样的东西:$categoryMap = Category_dub::find()->select(['id', 'id_1c_category'])->indexBy('id_1c_category')->column();。然后在循环内你会做$item->category_id = $categoryMap[$product->category_id];。品牌也是一样。另外,如果您需要一次插入多行,最好使用Command::batchInsert()
  • INSERT 语句在哪里?
  • “你能不能尝试不导入,只确定阅读速度” - 我评论 $item->save() 并为所有 JSON 文件获得 1-2 秒。 “在您的周期的每次迭代中,您都在运行 2 个数据库查询来加载类别和品牌。” - 我试图删除这些行进行测试 - 但结果比使用 2 个 DB 查询快 1-2 秒。现在我正在寻找一些有关 batchInsert() 的信息
  • 使用yiiframework.com/doc/api/2.0/… 应该比逐行插入要快。使用活动记录会明显变慢,因为 AR 比简单的插入命令要做更多的工作(验证、打字)。

标签: php mysql yii2


【解决方案1】:

我将 save() 更改为 insert() - 速度提高了。现在所有 JSON(1107 行)在 40 秒内导入。 有没有更快的方法将现成的数据从 JSON 加载到数据库中? 如果有 10 万行或 100 万行怎么办?等待几个小时是正常的做法吗?

public function importProductFile($file, $return = true)
    {    
        $products = json_decode($file);
        $dubTableName = Product::tableName() . "_dub";
        $start = time();

        if ($this->db->createDuplicateTable(Product::tableName(), $dubTableName)) {
            $start = time();
            $categoryMap = Category_dub::find()->select(['id', 'id_1c_category'])->indexBy('id_1c_category')->column();
            $brandMap = Brands_dub::find()->select(['id', 'id_1c_brand'])->indexBy('id_1c_brand')->column();

            foreach ($products as $product) {
                Yii::$app->db->createCommand()->insert('product_dub', [
                    'id_1c_product' => $product->id,
                    'category_id' => $categoryMap[$product->category_id] ?? '0',
                    'title' => $product->title,
                    'brand_id' => $brandMap[$product->brand_id] ?? 'No brand',
                    'content1' => $product->content1,
                    'content2' => $product->content2,
                    'content3' => $product->content3,
                    'link_order' => $product->link_order,
                    'img' => $product->img ?? 'no-image.png',
                    'in_stock' => $product->in_stock ? 1 : 0,
                    'is_popular' => $product->is_popular ? 1 : 0,
                ])->execute();
            }
        }
        }
    
        $finish = time();
        $res = $finish - $start . "sec. ";
    
        if ($return) {
            echo $res;
            Answer::success();
        }
    }

【讨论】:

  • 这是回答您的问题还是您想提供更多信息?
  • 这使录制速度更快,但我想知道是否有任何其他方法可以加快这个过程。
  • 答案不应包含任何后续问题。更新当前问题或提出新问题。无论如何,使用事务应该加快进程
【解决方案2】:

您可以使用this answerYii2 docs 中提到的批量插入。使用此批量插入,您需要记住该事件不会被触发。

Yii::$app->db->createCommand()->batchInsert('product_dub', array_keys(reset($products)), $products)->execute();

【讨论】:

    猜你喜欢
    • 2014-01-26
    • 1970-01-01
    • 2022-01-26
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    相关资源
    最近更新 更多