本番の大量のデータを、止めずに書き換える (バックフィル) には、どうしますか

1 回の UPDATE で全部を書き換えず、主キーの範囲で小さく分けて短いトランザクションで流し、どこまで済んだかを残して途中から再開でき、何度流しても同じ結果になる形にする、と答える問い。DB の負担を見ながら速さを絞ること、終わった後の照らし合わせ、戻し方まで話します。

まず 4 択で考える

2 問

答えに入れるべき考えを選びます。選ぶと、すぐに正誤と解説が出ます。そのあと下の欄で、自分の言葉で答えてみます。

  1. 問 1数千万行の表の新しい列に値を埋めたい。流し方として正しいものはどれか

  2. 問 2書き換えの途中で処理が落ちた。同じ塊をもう一度流しても困らないようにする書き方はどれか

2 問中 0 問に答えました

答えてみる

面接官に「本番の大量のデータを、止めずに書き換える (バックフィル) には、どうしますか」と聞かれたつもりで、答えを見る前に自分の言葉で、声に出して答えてみます。

解説を読む

聞かれ方から関連するコースまで、この問いの解説を全部ここに置いています。

こう聞かれます

  • 「本番の大量のデータを、止めずに書き換える (バックフィル) には、どうしますか」
  • 「数千万行の表で、新しく足した列に値を埋めたい。どう流しますか」
  • 「書き換えの途中で落ちたら、どうしますか」

面接官が見ている点

  • 1 回の大きな UPDATE が、長いロックや DB の詰まりを起こすと分かっているか
  • 小さく分ける、どこまで済んだかを残す、何度流しても同じ結果になる、の 3 つを押さえているか
  • 流している間も DB の様子を見て、速さを絞ったり止めたりできるか
  • 終わった後に、件数や中身を照らして確かめ、戻し方まで用意しているか

短い答え

  • 1 回の UPDATE で全部を書き換えず、主キーの範囲で小さく分けて、短いトランザクションで少しずつ流します。
  • どこまで済んだかを記録して途中から再開できるようにし、何度流しても同じ結果になる条件で書きます。
  • 流している間は DB の負担や複製の遅れを見て、間を空けたり止めたりし、終わったら件数と中身を照らして確かめます。

この問いを含む分野をレッスンで練習する

レッスンを開く

少し詳しい答え

スキーマを段階に分けて変える進め方 (列を足す、両方に書く、古いデータを移す…) のうち、ここでは「古いデータを移す」段階そのものの流し方を話します。

1 回で書き換えると何が起きるか

数千万行を 1 本の UPDATE で書き換えると、終わるまで 1 つの長いトランザクションになります。その間、書き換えた行のロックを持ち続けるので、同じ行を更新したい普通のリクエストが待たされます。変更の記録も一度に大量に出るので、複製の遅れが伸び、途中で失敗すると全部が巻き戻って、また最初からになります。

小さく分け、続きから流す

主キーの範囲で数百から数千行ずつに分け、1 つの塊を 1 つの短いトランザクションで流します。終わるたびに、最後に済んだ id を記録します。次の例は、7 行を 3 行ずつ流す動きを小さく書いた物です。

JavaScript

const rows = [];
for (let id = 1; id <= 7; id++) {
  rows.push({ id, name: "user" + id, fullName: null });
}

let lastId = 0;

function runBatch(size) {
  const batch = rows
    .filter((r) => r.id > lastId && r.fullName === null)
    .slice(0, size);
  for (const r of batch) {
    r.fullName = r.name.toUpperCase();
  }
  if (batch.length > 0) {
    lastId = batch[batch.length - 1].id;
  }
  return batch.length;
}

let count;
while ((count = runBatch(3)) > 0) {
  console.log("書いた " + count + " 件 続きは id " + lastId + " より後");
}
console.log("残り " + rows.filter((r) => r.fullName === null).length + " 件");
書いた 3 件 続きは id 3 より後
書いた 3 件 続きは id 6 より後
書いた 1 件 続きは id 7 より後
残り 0 件

ここで大事なのは 2 つです。1 つ目は、続きを「id がいくつより後」で探していることです。OFFSET で何件目からと数えると、先へ進むほど読み飛ばす行が増えて遅くなります。2 つ目は、「まだ値が入っていない行だけ」を対象にしていることです。途中で落ちて同じ塊をもう一度流しても、済んだ行は書き換えないので、結果は変わりません。

SQL で書くと、1 つの塊は次のような形です。

SQL クエリ

UPDATE users SET full_name = name
WHERE id > 30000 AND id <= 31000 AND full_name IS NULL;

様子を見ながら流す

塊と塊の間に少し間を空け、DB の CPU、ロックの待ち、複製の遅れを見ます。決めた線を超えたら自動で速さを落とすか止め、落ち着いたら続きから流します。利用者の少ない時間帯に寄せるのも手です。

書き換えはアプリの普段の保存の処理を通さず、専用の処理で行います。保存の処理には、メールを送る、外へ通知する、更新日時を変える、といった副作用が付いていることがあり、何千万回も動くと事故になります。

終わった後の確かめと戻し方

終わったら、対象の件数、まだ値が入っていない行の数、いくつかの行の新旧の値を照らします。流している間に普段の書き込みで作られた行に、値が入っているかも見ます。これはアプリの側で新しい行にも書くようにしておく必要があります。

戻せるように、古い列や古い値は、確かめが済むまで消さずに残します。上書きで元の値が消える書き換えなら、先に控え (バックアップ) を取ります。

トレードオフ

小さく分けて間を空けるほど安全ですが、終わるまでの時間は延びます。数時間で終わる量か、数日かかる量かを先に見積もり、急ぐ理由が無ければ安全な側に寄せます。行の少ない表なら、1 回で流して済むこともあります。本番に近い量のデータで先に流して、1 つの塊にかかる時間を測ってから決めます。

話す例は架空の例で構いません。「新しい列を埋めるために 1000 行ずつ流し、複製の遅れが伸びたら止まる仕組みを付け、途中で 1 回落ちたが続きから再開できた」のように話せると伝わります。

よくある間違い

  • 1 本の UPDATE で全部を書き換える 長いロックで普段のリクエストが待たされ、失敗すると全部が巻き戻ります
  • OFFSET で続きを数える後ろへ進むほど遅くなり、流している間に行が増減すると抜けや重なりが出ます。主キーの範囲で進めます
  • アプリの保存の処理をそのまま使うメールや通知のような副作用が、行の数だけ動きます

深掘りの問い

  • 「塊の大きさは、どう決めますか」本番に近いデータで流し、1 つの塊が短い時間で終わり、ロックの待ちや複製の遅れが目立たない大きさにする、と答えます
  • 「流している間に、同じ行が普段の処理で更新されたら」新しい行や更新にはアプリが新旧の両方を書くようにし、書き換えは値が入っていない行だけを対象にするので、上書きでぶつからない、と答えます
  • 「書き換えの中身に誤りが見つかったら」すぐ止め、控えや残しておいた古い列から直す範囲を決めて、同じく小さく分けて流し直す、と答えます

確認日 2026年10月30日書き手 小尾 知希 (講師)

技術面接問題の一覧へ戻る