システム性能改善について

投稿日時: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つ講演しました。内容が多いため、帰社の際に是非一度閲覧しましょう。