多対多の関係を記述し、数百万のfoobar
、数百万のfoo
、およびすべてのbar
が数百を含む1つの大きなテーブルbar
があります。 foo
の->数十億行。
CREATE TABLE `foobar` (
`foo_id` INT(10) UNSIGNED NOT NULL,
`bar_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`foo_id`, `bar_id`),
INDEX `bar_id_idx` (`bar_id`))
foobar
内のfoo_id
をカウントする別のテーブルがあります。
CREATE TABLE `foo_amount` (
`foo_id` INT(10) UNSIGNED NOT NULL,
`amount` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`foo_id`),
INDEX `amount_idx` (`amount`))
カウントは次のように行うことができます。
INSERT INTO foo_amount (SELECT foo_id, COUNT(*) AS amount FROM foobar GROUP BY foo_id);
しかし、foobar
に挿入/削除されたすべての行でテーブルを再計算する必要があります。
挿入は通常、新しいbar
-オブジェクトをいくつかのfoo
で追加します。たとえば、bar
とbar_id
42を挿入し、foo
とfoo_id
の3、8、26、44、...を挿入すると、次のようになります。
INSERT INTO foobar VALUES (3,42), (8,42), (26,42), (44,42), ...;
2番目の試みは、挿入されたbar
オブジェクトごとにfoo_count
テーブルを更新することでした。
INSERT INTO foo_amount (SELECT foo_id, 1 FROM foobar WHERE bar_id = 42)
ON DUPLICATE KEY UPDATE amount = amount + 1;
しかし、これは非常に遅いです。これを最適化する方法について何かアイデアはありますか?オプションとして、新しいbar
を一時的なfoo_count_tmp
に蓄積し、それをfoo_count
とマージすることがあります。 foo_count
テーブルは常に最新であるとは限りませんが、問題はありません。しかし、どのように更新をトリガーしますか?
GROUP BY
を最初からfoobarに頼るのはどうですか?
まず、新しいデータをfoobarに挿入します
次に、foobarで新しいGROUP BY
カウントを一時テーブルに実行します。
CREATE TABLE foo_amount_new LIKE foo_amount;
INSERT INTO foo_amount_new
SELECT foo_id,COUNT(1)
FROM foobar WHERE bar_id = ...
GROUP BY foo_id;
最後に、一時テーブルを交換して、古いfoo_amountを削除します
ALTER TABLE foo_amount RENAME foo_amount_zap;
ALTER TABLE foo_amount_new RENAME foo_amount;
DROP TABLE foo_amount_zap;
ただし、数十億のテーブルがある場合、再構築するインデックスがあるため、これは困難な戦いです。以下はすべてのINSERT ... ON DUPLICATE KEY
で発生するため:
amount
インデックスでシフトする必要がありますINSERTとUPDATEを高速化するために、amount
インデックスを削除してみてください。
別の方法を使用して一時テーブルソリューションを試してください
ステップ01)CREATE TABLE foobar_new LIKE foobar;
ステップ02)foobar_new
に一括INSERTを実行します
ステップ03)CREATE TABLE foo_amount_new LIKE foo_amount;
ステップ04)最新の一括INSERTバッチでGROUP BY
カウントを実行します
INSERT INTO foo_amount_new
SELECT foo_id,COUNT(1) FROM foobar_new WHERE bar_id = ...
GROUP BY foo_id;
ステップ05)foobar_new
からfoobar
への一括INSERTを実行します
INSERT INTO foobar SELECT * FROM foobar_new;
ステップ06)foo_amount
からfoo_amount_new
の一括更新を実行します
UPDATE foo_amount A INNER JOIN foo_amount_new B
USING (foo_id) SET A.amount = A.amount + B.amount;
ステップ07)一時テーブルを削除します
DROP TABLE foobar_new;
DROP TABLE foo_amount_new;
次のクエリはあなたの典型的なものですか?
_INSERT INTO foobar VALUES (3,42), (8,42), (26,42), (44,42), ...;
_
もしそうなら、そしてこれが(手作業ではなく)コードによって生成されていると私は仮定しているので、次のクエリを作成することをお勧めします:
_UPDATE foo_amount SET amount=amount+1 WHERE foo_in IN (3, 8, 26, 44, ...);
_
しかし、いくつかのことが私にははっきりしていません。
INSERTは機能することが保証されていますか?つまり、INSERT INTO foobar VALUES (3,42), (8,42), (26,42), (44,42)
に重複が含まれているため、操作が失敗する可能性がありますか?
また、IGNORE
のソートを使用している場合は、_foo_amount
_のamount
をインクリメントする必要があるかどうかの理解が複雑になります(ソリューションにも適用されます)
最後に、あなたがしていることは本質的に要約テーブルを管理することです。私はあなたがすべきではないと言うつもりはありません-しかしあなたは絶対にそれらを必要としていると確信していますか?必要なときにデータをフェッチすることはできますか?それでも、すべての書き込みを管理するよりも全体的に効率的であることが証明される場合があります。もちろん、「効率的」とは、最適化の優先度が高いのは誰であるか(読み取りまたは書き込み)を決定する必要があるため、ここでは多少曖昧です。