0

15M 行のテーブルを反復処理する select ステートメントで開くストアド プロシージャがあります(このテーブルは、大きな CSV の単純なインポートです)。CURSOR

各行のさまざまな部分を 3 つの異なるテーブルに挿入して、そのデータを正規化する必要があります(自動更新 ID を取得し、それらを外部キー制約で使用するなど)。

そこで、単純なストアド プロシージャ openCURSORFETCH作成し、フィールドを varialbes に変換し、3 つの挿入ステートメントを実行しました。

私は小さなDBサーバー、デフォルトのmysqlインストール(1 cpu、1.7GB RAM)を使用しています。このタスクに数時間かかることを望んでいました。私は 24 時間以上で、トップは85% の無駄な CPUを示しています。

私はある種のひどい非効率性を持っていると思います。タスクの効率を改善するためのアイデアはありますか? それとも、ボトルネックがどこにあるかを判断するだけですか?


root@devapp1:/mnt/david_tmp# vmstat 10
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 0  1    256  13992  36888 1466584    0    0     9    61    1    1  0  0 98  1
 1  2    256  15216  35800 1466312    0    0    57  7282  416  847  2  1 12 85
 0  1    256  14720  35984 1466768    0    0    42  6154  387  811  2  1 10 87
 0  1    256  13736  36160 1467344    0    0    51  6979  439  934  2  1  9 89

DROP PROCEDURE IF EXISTS InsertItemData;

DELIMITER $$
CREATE PROCEDURE InsertItemData() BEGIN 
    DECLARE spd TEXT;
    DECLARE lpd TEXT;
    DECLARE pid INT;
    DECLARE iurl TEXT;

    DECLARE last_id INT UNSIGNED;
    DECLARE done INT DEFAULT FALSE;

    DECLARE raw CURSOR FOR select t.shortProductDescription, t.longProductDescription, t.productID, t.productImageURL 
                           from frugg.temp_input t;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    OPEN raw;

    read_loop: LOOP
        FETCH raw INTO spd, lpd, pid, iurl;

        IF done THEN
            LEAVE read_loop;
        END IF;

        INSERT INTO item (short_description, long_description) VALUES (spd, lpd);
        SET last_id = LAST_INSERT_ID();
        INSERT INTO item_catalog_map (catalog_id, catalog_unique_item_id, item_id) VALUES (1, CAST(pid AS CHAR), last_id);
        INSERT INTO item_images (item_id, original_url) VALUES (last_id, iurl);
    END LOOP;

    CLOSE raw;
END$$
DELIMITER ;
4

2 に答える 2

1

私はRossが提供したのと同じような考えですが、あなたのテーブル、インデックス、「自動インクリメント」列名が何であるかをもっと知らなくても、直接挿入するだけです...ただし、次の場合は問題が発生しますチェックしていない重複が発生しました..次のように挿入し、再結合に役立つ適切なインデックスを用意します(短い製品説明と長い製品説明に基づいて)。

選択から挿入して挿入し、その選択から挿入してみます...など...

INSERT INTO item 
      ( short_description, 
        long_description ) 
   SELECT
        t.ShortProductDescription,
        t.LongProductDescription
     from
        frugg.temp_input t;

done, 15 million inserted... into items table... Now, add to the catalog map table...

INSERT INTO item_catalog_map
      ( catalog_id,
        catalog_unique_item_id,
        item_id )
   SELECT
         1 as Catalog_id,
         CAST( t.productID as CHAR) as catalog_unique_item_id,
         item.AutoIncrementIDColumn as item_id
      from
         frugg.temp_input t
            JOIN item on t.ShortProductDescription = item.short_desciption
                     AND t.LongProductDescription = item.long_description

done, all catalog map entries with corresponding "Item ID" inserted...

INSERT INTO item_images
      ( item_id,
        original_url )
   SELECT
         item.AutoIncrementIDColumn as item_id,
         t.productImageURL as original_url
      from
         frugg.temp_input t
            JOIN item on t.ShortProductDescription = item.short_desciption
                     AND t.LongProductDescription = item.long_description

画像の URL で完了です。

于 2012-09-28T17:07:29.447 に答える
1

MySQL は、ほとんどの場合、ストアド プロシージャ内でループするよりも、直接 SQL ステートメントを実行する方が優れたパフォーマンスを発揮します。

つまり、InnoDB テーブルを使用している場合、プロシージャはSTART TRANSACTION/COMMITブロック内でより高速に実行されます。

AUTO_INCREMENTのレコードにを追加し、frugg.temp_inputそのテーブルに対してクエリを実行することをお勧めします。

DROP TABLE IF EXISTS temp_input2;

CREATE TABLE temp_input2 (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    shortProductDescription TEXT, 
    longProductDescription TEXT,
    productID INT,
    productImageURL TEXT,
    PRIMARY KEY (id)
);

START TRANSACTION;

INSERT INTO 
    temp_input2
SELECT
    NULL AS id,
    shortProductDescription, 
    longProductDescription,
    productID,
    productImageURL
FROM
    frugg.temp_input;

INSERT 
    INTO item 
(
    id, 
    short_description, 
    long_description
) 
SELECT 
    id,
    shortProductDescription AS short_description, 
    longProductDescription AS long_description
FROM
    temp_input2
ORDER BY
    id;

INSERT INTO 
    item_catalog_map
(
    catalog_id, 
    catalog_unique_item_id, 
    item_id
)
SELECT 
    1 AS catalog_id,
    CAST(productID AS CHAR) AS catalog_unique_item_id,
    id AS item_id
FROM
    temp_input2
ORDER BY
    id;

INSERT INTO 
    item_images 
(
    item_id, 
    original_url
) 
SELECT 
    id AS item_id,
    productImageURL AS original_url
FROM
    temp_input2
ORDER BY
    id;

COMMIT;

上記よりもさらに優れているのは、.CSV ファイルを にロードする前にfrugg.temp_input、フィールドを追加して、上記AUTO_INCREMENTの作成/ロードの余分な手順を節約することです。temp_input2

于 2012-09-28T06:23:56.463 に答える