sqlアンチパターンnight ライトニングトーク

Post on 30-Jul-2015

575 Views

Category:

Engineering

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

SQL アンチパターン Nightライトニングトーク

株式会社ヴァル研究所板橋正之2015/04/10

自己紹介■ 名前

板橋 正之(いたばし まさゆき)

■ 所属株式会社ヴァル研究所JAWS-UG 中央線

■ 仕事自社広告配信サーバの開発、運用

■ 好きな RDBMSOracle Database

itabashi.masayuki

今日は SQL アンチパターン Night ということで

失敗談を持ってきました。

今から 5 年ぐらい前の案件です。

13 時間程度で終了すると思った3,500 万件弱のデータ洗い替え作業が

5 日以上掛かった

失敗談

某システムの会員情報の ID 変更

案件

テーブルイメージ

単純に UPDATE 文の羅列で古い会員コードを

一旦テンポラリコードに編集してその後、新しい会員コードに振り直す。

やったこと

会員コードは主キーの一部でインデックスが設定されているので更新時にはインデックス書き換えの

オーバーヘッドを減らすためインデックスを解除した。

検討事項

更新が多くなる分、 UNDO 表領域、 REDO ログファイルを作成されることも予想されるため、ディスクを節約したかった。

検討事項

• テスト環境でサンプルのテーブルを使ってインデックスあり、なしの時間を計測して、処理時間を見積もりました。• テーブル A: 448,251件

• テーブル B: 561,018件

• アーカイブ REDOログによるディスク使用量もテーブル Aが1,062MB/556MB、テーブル Bが 1,821MB/607MBと計測しました。

• 上記の結果からインデックスなしの方が明らかに処理時間が短いことから、インデックスなしで処理を行なう方がよいと判断しました。

処理時間見積り

操作内容 時間 合計インデックスあ

りテンポラリ会員コード更新 0:06:48

0:18:37新会員コード更新 0:11:49

インデックスなし

インデックス無効 & テンポラリ会員コード更新

0:01:090:09:23

新会員コード更新 0:07:20

インデックス再作成 0:00:54

操作内容 時間 合計インデックスあ

りテンポラリ会員コード更新 0:09:24

0:33:28新会員コード更新 0:24:04

インデックスなし

インデックス無効 & テンポラリ会員コード更新

0:01:150:09:05

新会員コード更新 0:06:48

インデックス再作成 0:01:02

• 更新対象データが 34,971,205 件• 検証データが 448,251 件で 10 分弱• 34,971,205÷448,251×10 780≒ 分

= 13 時間

大間違い

サンプリングが少ない。

• 2 テーブル程度のデータで信頼性のある値がだせるのか?

• 見積もりを行なったテーブルは、比較的データ件数が近い。

データ件数と処理時間の相関関係を見出していない。

本番サーバの方が性能いい?

(当時でも)もう 5 年前のサーバだよ?

思い込みと楽観視

まず、テンポラリ会員コードへの更新

テーブル データ件数

  更新(秒 ) 

 更新(分 ) 

  1万件当たりの秒数 

会員マスタ 69,322 3.063 0.1 0.4419

会員所属マスタ 61,543 14.047 0.2 2.2825

受付テーブル 159,330 26.454 0.4 1.6603

予約テーブル 159,330 16.313 0.3 1.0239

ヘッダーテーブル 159,330 13.125 0.2 0.8238

コーステーブル 159,330 73.75 1.2 4.6288

明細テーブル A 8,564,614 609.703 10.2 0.7119

明細テーブル B 454,350 23.204 0.4 0.5107

明細テーブル C 17,845,016 1721.546 28.7 0.9647

明細テーブル D 2,668,722 179.672 3.0 0.6733

明細テーブル E 1,193,156 249.61 4.2 2.0920

明細パラメータテーブル 159,329 46.453 0.8 2.9155

明細付加情報テーブル 360,250 33.515 0.6 0.9303

印刷集計管理テーブル 335,480 68.75 1.1 2.0493

印刷履歴テーブル 1,545,991 599.188 10.0 3.8757

ワークフロー履歴テーブル 910,836 188.672 3.1 2.0714

ワークフロー制御テーブル 1,285,598 826.844 13.8 6.4315

洗い替え開始

SQL> SET TRANSACTION READ WRITE;SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘X’ || 会員コード ;SQL> COMMIT;

すでに更新秒数にバラツキがっ!

続いて新しい会員コードへの更新洗い替え開始

SQL> SET TRANSACTION READ WRITE;SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘2157’SQL> WHERE ( 会員コード = ‘X1356’);SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘3952’SQL> WHERE ( 会員コード = ‘X65165’);SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘4510’SQL> WHERE ( 会員コード = ‘X2157’);SQL> ・・・SQL> COMMIT;

続いて新しい会員コードへの更新洗い替え開始

SQL> SET TRANSACTION READ WRITE;SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘2157’SQL> WHERE ( 会員コード = ‘X1356’);SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘3952’SQL> WHERE ( 会員コード = ‘X65165’);SQL> UPDATE 会員マスタ SQL> SET 会員コード = ‘4510’SQL> WHERE ( 会員コード = ‘X2157’);SQL> ・・・SQL> COMMIT;

テーブル データ件数 更新 (分 )   1万件当たりの秒数 

会員マスタ 69,322 約 7 分 60.5868

会員所属マスタ 61,543 約 10 分 97.4928

受付テーブル 159,330 約 64 分 241.0092

予約テーブル 159,330 約 140 分 527.2077

ヘッダーテーブル 159,330 約 60 分 225.9461

コーステーブル 159,330 約 65 分 244.7750

明細テーブル A 8,564,614

明細テーブル B 454,350

明細テーブル C 17,845,016

明細テーブル D 2,668,722

明細テーブル E 1,193,156

明細パラメータテーブル 159,329 約 96 分 361.5161

明細付加情報テーブル 360,250 約 900分 1498.9590

印刷集計管理テーブル 335,480 約 70 分 125.1938

印刷履歴テーブル 1,545,991

ワークフロー履歴テーブル 910,836

ワークフロー制御テーブル 1,285,598

• 金曜の夜にテンポラリ会員コードへの更新を行って、土日で新会員コードへの洗い替えをやっていて日曜日の 23:00 くらいにこんな感じでしたね。

• 直後にお詫びのメールを送って翌日朝一でお詫びの電話しました。

• この時点で更新のオーバーヘッドより、更新対象検索のオーバーヘッドが大きいと判断して、いくつかのサンプリングで確認した結果、無効にしていたインデックスを有効にして更新を行なうこととしました。

• 土日で終了しなかったので月曜日は当該年度のデータのみを更新、この時点でインデックスを復活。当然、この日はサービス停止。テーブル データ件数 更新 (分 )

  1万件当たりの秒数 

会員マスタ 69,322 約 7 分 60.5868

会員所属マスタ 61,543 約 10 分 97.4928

受付テーブル 159,330 約 64 分 241.0092

予約テーブル 159,330 約 140 分 527.2077

ヘッダーテーブル 159,330 約 60 分 225.9461

コーステーブル 159,330 約 65 分 244.7750

明細テーブル A 854,569 20 分 46 秒 14.5805

明細テーブル B 44,081 約 90 分 1225.0176

明細テーブル C 1,770,240 36 分 6 秒 12.2356

明細テーブル D 360,356 約 910 分 1515.1683

明細テーブル E 162,150 約 47 分 173.9130

明細パラメータテーブル 159,329 約 96 分 361.5161

明細付加情報テーブル 360,250 約 900 分 1498.9590

印刷集計管理テーブル 335,480 約 70 分 125.1938

印刷履歴テーブル 124,474 約 38 分 183.1708

ワークフロー履歴テーブル 157,888 約 92 分 349.6149

ワークフロー制御テーブル 131,976 約 48 分 218.2215

テーブル データ件数 更新 (分 )  1万件当たりの秒

数 会員マスタ 69,322 約 7 分 60.5868

会員所属マスタ 61,543 約 10 分 97.4928

受付テーブル 159,330 約 64 分 241.0092

予約テーブル 159,330 約 140 分 527.2077

ヘッダーテーブル 159,330 約 60 分 225.9461

コーステーブル 159,330 約 65 分 244.7750

明細テーブル A854,569

7,710,04520 分 46 秒

3 時間 28 分 56 秒14.580516.2593

明細テーブル B44,081

410,269約 90 分

1 時間 39 分 14 秒1225.0176

145.1242

明細テーブル C1,770,240

16,074,77636 分 6 秒

5 時間 22 分 29 秒12.235612.0368

明細テーブル D360,356

2,308,366約 910 分

1 時間 20 分 3 秒1515.168320.8069

明細テーブル E162,150

1,031,006約 47 分

1 時間 27 分 13 秒173.913050.7562

明細パラメータテーブル 159,329 約 96 分 361.5161

明細付加情報テーブル 360,250 約 900 分 1498.9590

印刷集計管理テーブル 335,480 約 70 分 125.1938

印刷履歴テーブル 124,4741,421,517

約 38 分26 時間 20 分 5 秒

183.1708666.9283

ワークフロー履歴テーブル 157,888752,948

約 92 分11 時間 20 分 43 秒

349.6149542.4412

ワークフロー制御テーブル 131,9761,153,622

約 48 分20 時間 10 分 21 秒

218.2215629.5043

残りも更新

原因は?

エクステントの分断化?

インデックスの更新にはオーバーヘッドがかかるという思い込み

ある件数を超えるとインデックス更新のオーバーヘッドより全表検索のオー

バーヘッドが大きくなる

全表検索+データ更新 < インデックス検索+データ更新+インデックス更新

全表検索+データ更新 > インデックス検索+データ更新+インデックス更新

データの件数が一定数を超えると

インデックスの構造

年次単位での参照が多いため年月日、会員コードの順番で複合インデックスが作られていた

• 余計なアクセスパスが多い• 一部のテーブルは年月日と会員 ID の順番が逆でインデックスが生成されていており、実際そちらの方がパフォーマンスがよかった。

• 正確にいうとマルチテナントのシステムだったので年月日の上にさらにコードがあった。

UPDATE 文 1 行 1 行流すんじゃなくて変換テーブルとの JOIN で更新するよう

なUPDATE 文で更新する方法も

あったんじゃないかと。

(先にあげた原因を解決するのはもちろんとして)

今後の反省として

自然キーもいいけど疑似キーもね

最近だったらクラウド環境で停止時間を最小限に

• 例えば別のインスタンスをスナップショット等から作成して、そちらを更新後に差分を適用するなど。

• 見積もりも本番と同じ環境でできるし。• 昔と違って DB のコピーを(お金はかかり

ますが)ポコポコ立てられる時代になったので。

以上、ご清聴ありがとうございました。

top related