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

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

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Q&A

解決済

6回答

16281閲覧

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

masaya_ohashi

総合スコア9206

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

0グッド

2クリップ

投稿2016/06/08 07:52

編集2016/06/08 10:10

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

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

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

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

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

###該当のソースコード

SQL

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

###試したこと

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

SQL

1UPDATE 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

###追記

SQL

1UPDATE A 2INNER JOIN B 3ON A.dataId = B.dataId 4SET A.example = B.example

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

DBにログインして

sql

1SHOW 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文を実行する前に以下のクエリを実行してみてください。

sql

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

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

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

sql

1SET SESSION wait_timeout = 31536000;

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

投稿2016/06/08 09:10

KiyoshiMotoki

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2016/06/08 09:27

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

0

自己解決

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

結論

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

投稿2016/06/08 10:16

編集2016/06/08 10:17
masaya_ohashi

総合スコア9206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

投稿2016/06/08 10:16

tanat

総合スコア18713

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2016/06/08 10:18

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

0

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

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

投稿2016/06/08 08:35

takasima20

総合スコア7458

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2016/06/08 09:19

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

0

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

sql

1UPDATE A 2INNER JOIN B 3ON A.dataId = B.dataId 4SET A.example = B.example

試したことについて

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

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

投稿2016/06/08 08:24

sho_cs

総合スコア3541

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2016/06/08 08:32

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

2016/06/08 08:48

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

0

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

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

SQL

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

投稿2016/06/08 08:23

hirohiro

総合スコア2068

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirohiro

2016/06/08 08:28

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

2016/06/08 08:29

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問