1

私は3つのテーブルを持っています:

grade         (grade_id,       grade_value,       grade_date)     ~100M rows
grade_archive (grade_id,       grade_value,       grade_date)         0 rows
peer_review   (grade_id, peer_review_value, peer_review_date)      ~10M rows

table から1 か月以上経過していてtablegradeにないすべての行を移動したいと考えています。grade_archivepeer_review

テーブルはアクティブに使用されるため、実行中に既存のプロセスと新しいプロセスが中断されないように、挿入の優先度を低くする必要があります。

完了すると、予期されるテーブル行は次のようになります。

grade          ~10M rows
grade_archive  ~90M rows
peer_review    ~10M rows

私はそれが近いものだと想像します:

INSERT
    LOW_PRIORITY
    INTO grade_archive
        (grade_id,grade_value,grade_date)
    SELECT
        grade_id,grade_value,grade_date
    FROM
        grade
    WHERE
            grade_date < DATE_ADD(NOW(), INTERVAL -1 MONTH)
        AND grade_id NOT IN
            (
                SELECT grade_id FROM peer_review
            );

grade次に、アーカイブ テーブルのすべての行を削除して、テーブルをクリーンアップします。

DELETE LOW_PRIORITY FROM grade WHERE grade_id IN (SELECT grade_id FROM grade_archive);

しかし、これらのサブセレクトは、大きなテーブルでは非常に遅く、結果が気になります。より良い方向を探しています。

4

2 に答える 2

1

過去に、大きなアクティブ テーブルからアーカイブ テーブルにデータの一部を移行する際に、同様の問題が発生しました。私が使用したアプローチ(ユースケースに合わせて変更)は次のとおりです。

/* Set time for calculation basis */
SET@calc_time = NOW();
/* Create empty copy of grade table */
CREATE TABLE grade_temp LIKE grade;
/* Add rows you want to save from grade into temp table */
INSERT INTO grade_temp
SELECT
    g.grade_id AS grade_id,
    g.grade_value AS grade_value,
    g.grade_date AS grade_date
FROM grade AS g
LEFT JOIN peer_review AS pr
  ON g.grade_id = pr.grade_id
WHERE
/*
To keep the record it must either have an entry in peer review
or it is less than a month old
*/
    pr.grade_id IS NOT NULL
    OR g.grade_date >= DATE_SUB(@calc_time, INTERVAL 1 MONTH);
/*
Switch new temp table for active table.
This happens really fast (it is just file name switching on the system).
*/
RENAME TABLE grade TO grade_old, grade_temp TO grade;
/*
You are now taking new records into new version of grade table
and free to do your much slower operations against the grade_old table
*/
/* Delete more recent rows */
DELETE FROM grade_old
WHERE grade_date >= DATE_SUB(@calc_time, INTERVAL 1 MONTH);
/* Delete rows that exist in peer review */
DELETE FROM grade old
WHERE grade_id IN (
    SELECT grade_id
    FROM peer_review
    WHERE grade_date < DATE_SUB(@calc_time, INTERVAL 1 MONTH)
);
/*
As an alternate to the above action, you could also try deleting across join as shown below. Which is faster will likely depend upon number of records that are returned from that subquery shown above. You can try both out and see what works best
*/
DELETE go FROM grade_old AS go
INNER JOIN peer_review AS pr
  ON go.grade_id = pr.grade_id
WHERE pr.grade_date < DATE_SUB(@calc_time, INTERVAL 1 MONTH);
/* Add all rows from grade_old to grade_archive */
INSERT INTO grade_archive
SELECT
    grade_id,
    grade_value,
    grade_date
FROM grade_old;
/* Drop date_old table */
DROP TABLE date_old;

ここで重要なのは、必要な行のみを含む新しいバージョンの成績表をできるだけ早く取得し、事後にアーカイブ表に何が入るかを整理することです。そのサイズのテーブルに対して一括削除操作を実行したくない場合。これにより、成績表がこれらのアーカイブ操作に結び付けられる時間を最小限に抑えます。

ただし、データベース スキーマは、この種の操作用に最適化できるようです。たとえば、成績表にピア レビュー フラグを設定して、結合全体をフィルタリングするのではなく、より迅速にフィルタリングできるようにすることができます。成績表と多対1の関係がない限り、私は実際にその査読表全体の必要性を疑問視しています(あなたの質問には示されていないようです)。grade_id ごとに査読エントリが 1 つしかない場合は、これらの列を成績テーブルに正規化するだけでよいと思います。これにより、このメンテナンス プロセスが大幅に簡素化されます。

于 2015-03-02T19:26:32.733 に答える