質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.97%

MySQLで膨大な数のレコード全体に、別テーブルからのデータを用いてUPDATEをかける手法

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 6,063

masaya_ohashi

score 8923

前提・実現したいこと

1レコードのカラム数が200を超え、レコード数が30万件近くあるテーブルに対し、
別テーブルから引いてくるデータを使ってUPDATEをかける必要が出来てしまいました。
※もらった元データの雑さにかなりうんざりしています。

テーブルA カラム数が200以上、レコード数30万件
テーブルB カラム数3、レコード数1万件程度

いっぺんに更新をかけるとタイムアウトしてしまうので、
ある程度レコードを絞って何度か更新をかける方法を模索しています。
なにかよい方法をご存じの方、おられましたらご教示いただけますと幸いです。

発生している問題・エラーメッセージ

Error Code: 2013. Lost connection to MySQL server during query

該当のソースコード

UPDATE A, B SET A.example = B.example WHERE A.dataId = B.dataId;

試したこと

  • PROCEDUREの中で1000件ずつ更新しようとしてみましたがうまく行きませんでした
  • a.dataId BETWEEN 0 AND 10000 等でWHERE条件を増やしても結局タイムアウトしました
  • MySQLはUPDATEにLIMITが使えると聞いてやってみましたが構文エラーとして実行できませんでした
UPDATE A, B SET A.example = B.example WHERE A.example IS NULL AND A.dataId = B.dataId ORDER BY a.dataId LIMIT 1000;

補足情報(言語/FW/ツール等のバージョンなど)

MySQL 5.5.43-MariaDB-37.2

追記

UPDATE A
INNER JOIN B
ON A.dataId = B.dataId
SET A.example = B.example


このやり方で実行していますが、MySQL上のプロセスリストを見るとStateがSending dataでずっと止まっています。やはりいずれのやり方も、そもそもテーブルの連結方法が問題になるようです…

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+4

DBにログインして

SHOW VARIABLES LIKE '%timeout';


というクエリを実行してみてください。

以下のように"〜timeout"という名称のシステム変数とその値が表示されるはずです。

# 以下はMySQLで実行した結果
mysql> SHOW VARIABLES LIKE '%timeout';
+----------------------------+----------+
| Variable_name              | Value    |
+----------------------------+----------+
| connect_timeout            | 5        |
| delayed_insert_timeout     | 300      |
| innodb_lock_wait_timeout   | 50       |
| innodb_rollback_on_timeout | OFF      |
| interactive_timeout        | 28800    |
| lock_wait_timeout          | 31536000 |
| net_read_timeout           | 20       |
| net_write_timeout          | 10       |
| slave_net_timeout          | 3600     |
| wait_timeout               | 28800    |
+----------------------------+----------+
10 rows in set (0.00 sec)


この中に、エラーが発生する秒数とほぼ一致する値の変数はありませんか?
(恐らく、wait_timeoutinteractive_timeoutのいずれかまたは両方と思われます)
https://mariadb.com/kb/en/mariadb/server-system-variables/#wait_timeout
https://mariadb.com/kb/en/mariadb/server-system-variables/#interactive_timeout

原因と思われる変数が見つかった場合、問題のUPDATE文を実行する前に以下のクエリを実行してみてください。

SET SESSION [原因と思われる変数名] = [十分に長い秒数];


https://mariadb.com/kb/en/mariadb/set/

例えば、wait_timeoutを31536000秒(たぶん365日)変更する場合、以下のようになります。

SET SESSION wait_timeout = 31536000;

それらしい変数が見つからない場合、DBのシステム変数とは別の要因でコネクションが切断されている可能性があります。
その際は、お使いのネットワーク環境やSSHクライアントなどの設定を確認してみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/08 18:27

    MySQL Workbench側のタイムアウトの秒数がちょうどそれのようでした。
    ついでにサーバ側のタイムアウトもSET SESSIONで1年に変えてみて試行してみます。
    ありがとうございます!

    キャンセル

check解決した方法

+1

初歩的な失敗で申し訳ありませんが、自己解決いたしました。
いろいろとテーブル定義を見直してみた結果、テーブルのdataIdがTEXTで定義されているのが負荷の最大要因だったようです。
AとBのテーブルのdataIdをVARCHARに変更し、BのdataIdにUNIQUE制限をかけることで高速に処理できるようになりました。
本来求めていた「分割処理できないか」という解決方法ではありませんでしたが、これで解決しました。

 結論

テーブル連結する際はキーとなるデータをTEXTで組むのは負荷になる

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

SQLが落ちてるのは「UPDATE A, B」この部分でキャッシュに300万*1万という膨大なクロステーブルを形成しようとするからでは?この手法はA,Bが関連テーブルで、Bにしか存在しない要素を条件にAを更新する時に使うものではないかと思います。

こんなSQLでもやっぱり止まりますか?

UPDATE A SET A.exsample = B.exsample FROM B WHERE A.id = B.id

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/08 17:28

    うぉ、すみません。どうやらmysqlだとこのコード動きませんね。失礼しました。

    キャンセル

  • 2016/06/08 17:29

    最初に試させていただきましたがだめでした^^;
    ご助力ありがとうございます。

    キャンセル

0

件数を絞る方法ではないですが、以下のように明示的に内部結合させてもタイムアウトしてしまいますか?

UPDATE A
INNER JOIN B
ON A.dataId = B.dataId
SET A.example = B.example

 試したことについて

MySQLはUPDATEにLIMITが使えると聞いてやってみましたが構文エラーとして実行できませんでした

複数テーブル指定時はORDER BY および LIMIT は使用できません。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.11 UPDATE 構文

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/08 17:32

    なるほど、複数テーブル指定だったからだめだったのですね。
    いまINNER JOINを試してみているところですが、相変わらずレスポンスが無いのでこれもダメなようです…

    キャンセル

  • 2016/06/08 17:48

    逆にすると対象レコードは1万以下になると思いますが、
    UPDATE B INNER JOIN A ON A.dataId = B.dataId
    SET A.example = B.example
    これは駄目なんですかね?
    ※怖いのでいきなり本番で試さずに一度実験用のテーブルで動作確認してください。

    キャンセル

0

効果があるかは分かりませんが、トランザクションを使って
自動コミットをおさえるのはどうでしょう。

https://dev.mysql.com/doc/refman/5.6/ja/commit.html

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/08 18:19

    どうやらテーブルの結合処理の問題のようですので、トランザクションあるなし関わらず1件も処理できずタイムアウトしてしまうようです…

    キャンセル

0

データベース的には邪道な気もしますが、
適当なスクリプト言語を使って、
少量づつ処理する
という方法もあるかと思いますが、いかがでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/08 19:18

    データ連結自体に問題があったため、少しずつ処理をしようにも絞り込む以前でタイムアウトしていたため、自己解決の記述にあるような解決となりました。ご提案ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.97%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる