システム性能改善について
投稿日時:2018/11/22(木) 10:12
システム性能改善について
2018/11/18午後2時から、Tさんの提議で「性能改善」の技術交流会を事務所で開きました。一部の交流内容を整理し、公開します。その内容に興味のある場合、直接に講演者本人に連絡してください。一、Kさんの講演内容:
プログラムの外側であるハードデイスク、ネットワークなどで性能を向上させますが、
今回プログラミングの観点で説明する。
開発完了後の機能に対して、パフォーマンスの問題が指摘された時に、どの部分問題があるか
下記の方法で確認できる。
①デバッグでクライアントとサーバのどちら性能の問題があるか確定する
②実行ログを確認して処理の実行時間を特定する
クライアントとサーバの改善ポイントをそれぞれ説明する
■クライアント
①イベント実行時、同じサーバ処理複数回実行されているか
②ループにサーバとの通信があるか
③無駄なサーバ通信が存在するか、開発時できるだけサーバとの通信を減らす
④複数個所共通部品を利用するときに、事前に変数定義で取得しておき、処理時間を減らせるか
⑤メモリ節約するために、無駄な変数定義されているか、定義されている変数は適当なタイミングで初期化したか
■サーバ
①ループの中にSQL実行があるか
②SQL実行を減らすため、メモリ上問題ない前提でキャッシュで改善できるか。
③大量データを扱う時の存在チェックはArrayListよりHashSetを利用する
④SQLの改善
A.IN句よりEXISTSを使う
B.MAX値、MIN値を求めるとき、対象列にインデックスが作成されているか
C. GROUP BY、ORDER BYでインデックスを利用できるから、対象列にインデックスがあるか
D.デフォルトの型変換を回避する
E.。SELECT文に不要な列を取得しないように注意する。
など
二、Sさんの講演内容:
■ 原則:
1 必ず依頼されましたから、やる
◇例:折角色々調べて、性能が1時間から10分まで短縮しましたのに、「何で開発したのが」と怒られた ⇒開発しなくても、運用や順番などを調整するによって、済むことある
▲ 全件検索の場合、DB全件が対象になり、結構遅いですが、運用上「必ず条件の選択を」一言で解決 ▲ データ量が多い過ぎで、物理削除で済む
2 何を依頼されたを、確認
◆ 瞬時大量
◆ 長時間平準安定
◆ 負荷超過
3 どこから着手を、慎重に
◇DBアクセスが遅い、ソース幾ら調整してもだめ、HDD⇒SSDに変わったら、速くになって、問題がなくなった
◇一口に「高負荷になった」といっても、アプリケーションサーバ(APサーバ)とデータベースサーバ(DBサーバ)の両方ともとはが同じように遅くなるこありません。システム構成や処理の内容を確認。
◇サーバのCPUとメモリをそれぞれ倍に増設しましても、CPUもメモリも増設分はまったく使われず、 使用率が半分になっただけで、応答時間はほぼ改善されませんでした。
◇大体、性能改善と言われたら、稼働実績があるので、影響範囲を徹底的に調査
SQL文、ソースだけではなく、プロシージャ、シェル、バッチも確認
★必ず実際で測定すること
机上試算は必ず”上記設定条件”の場合です。仮設条件と必ず一致とは限らない
■ 注意ポイント
1 DBアクセスは、ルップの中では、ほぼだめ
⇒処理ロジックとSQL文を改善しべき
DB接続して、CRUD、結果を貰って、接続を切るの短時間で繰り返しは、DBサーバーとAPサーバー間の通信が多くなったためでければ、DB接続は一回だけ
⇒1 SQLを改修
⇒2 大量処理できる仕組みやツールを探す(mybatis bulk)
bulk
mybatis
2 SQL文の中、テーブルの結合が遅くなり
▲ 複数のテーブルを結合するSQLを実行すると、RDBは内部的にテーブルを結合する処理を実行します。 結合アルゴリズムは、Nested Loop Joinが多い。
forループの中でforループが実行されています。(これがNested Loop Joinと呼ばれる理由です。) 外側でループしているテーブルを駆動表、内側でループしているテーブルを内部表
▲SELECT *
FROM t1, t2
WHERE t1.c1 = 1
AND t1.c2 = t2.c3;
NLJの擬似コード
# 1. t1テーブルから条件にあうものを1レコードずつ取ってくる。
for row1 in fetch(t1, { "c1": 1 }):
# 2. 1の結果に対してあうレコードをt2テーブルから1つずつ取ってくる。
for row2 in fetch(t2, { "c3": row1.c2 }):
# 3. それをクライアントに返す。
send_to_client(row1 + row2)
t1からフェッチされるレコード数をn、それに対してt2からフェッチされるレコード数をmとすると、このアルゴリズムの計算量はO(nm)
⇒ 1、SQLを工夫してnとmを小さくする
2、適切にインデックスを作ってそのn*m個を高速にフェッチできるようにする
3 SQL文の中、型不一致であれば、想像以上の遅くになり
SQLにおいて「暗黙の型変換」を使うべきでない
▲ 検索時にインデックスが使用できずにパフォーマンスが低下する
社員テーブルに社員ID(employeeId)(varChar)がKEYで索引設定されているのに、
WHERE employeeId = 0123 の場合、列employeeidの方が数値型に変換されながら検索が実行されます。 これはパフォーマンスの低下をもらたらします。
△ 暗黙の型変換により精度が損なわれる
△ DBのバージョンアップにより暗黙の型変換の仕様が変わる
△ 暗黙の型変換により、型の範囲外になるというエラーが発生する
4 SQLチューニング(実行計画)
・ 索引の作成
・ SQLの変更/ヒントの挿入
https://www.oracle.com/technetwork/jp/database/articles/tsushima/tsushima-hakushi-38-2236622-ja.html
5 Java 小さいJavaオブジェクトが大量に生成されることに伴い、GC発生回数に影響
▲String連結でString文字列大量生成
String sql = "";
sql = sql + sql2;
sql = sql + sql3;
sql = sql + sql4;
sql = sql + sql5;
exec(sql);
Stringクラスのインスタンスが連結のたびに生成されるため、小さいJavaオブジェクトが大量に生成されることに伴い、GC発生回数に影響します。 (GC発生回数増大⇒パフォーマンス悪化)
⇒可変長文字列編集クラス(StringBuffer、StringBuilder)を使用
△類似DATEクラス使用など
6 Java batchで巨大ファイル処理
数百Mや数G以上のファイルなどを扱う場合にパフォーマンス問題
⇒1行ずつでやられるか検討
⇒シェルバッチで処理べきか検討
7 DBもThreadもロックは、必要時だけ
◆Java synchronizeブロック
⇒本当に同期処理しないと行けないか
◆DB Data Lock
⇒行ロックできないか
⇒楽観ロック、悲観ロック検討したか
楽観ロック:データそのものに対してロックは行わずに、更新対象のデータがデータ取得時と同じ状態であることを確認してから更新することで、データの整合性を保証する方式。
悲観ロック:更新対象のデータを取得する際にロックをかけることで、他のトランザクションから更新されないようにする方式。
ロックされたら、みんなが待つしかできないので
▲Oracle
Tableロック確認
select s.sid, s.serial#, s.username, s.osuser, s.machine, s.terminal, s.program, o.object_name from v$session s, v$locked_object l, dba_objects o where s.sid = l.session_id and l.object_id = o.object_id; ロック解除
alter system kill session 'セッションID, シリアル値';
表領域サイズや使用量確認
select
d.tablespace_name,
現サイズ "現サイズ[MB]",
round(現サイズ-空き容量) "使用量[MB]",
round((1 - (空き容量/現サイズ))*100) "使用率(%)",
空き容量 "空き容量[MB]"
from
(select tablespace_name, round(SUM(bytes)/(1024*1024)) "現サイズ"
from dba_data_files GROUP BY tablespace_name) d,
(select tablespace_name, round(SUM(bytes)/(1024*1024)) "空き容量"
from dba_free_space GROUP BY tablespace_name) f
where d.tablespace_name=f.tablespace_name
三、Tさんの講演内容:
AWRなどのツールを利用し原因分析し性能改善出来た実例を3つ講演しました。内容が多いため、帰社の際に是非一度閲覧しましょう。