web-dev-qa-db-ja.com

日付とパフォーマンスに関する考慮事項。 OracleのChar / Number / Varchar2

私は約7年間Oracle 11gを使用しており、小規模なプロジェクトのコンサルティングジョブを依頼されました。彼らが抱えていた問題は、Windows2008をプラットフォームとする11gR2での高いCPU使用率(90%以上)でした。彼らはこのサーバーをときどきクラッシュさせていたため、リソースを増やしましたが、依然としてCPU使用率が高くなっています。私の理解では、高いCPU使用率自体は問題を意味するものではありませんが、少しドリルダウンすると、より興味深いものになりました。

実行ごとにかなりの時間が経過したクエリがありました。彼らはOracle Standardエディションを使用していたため、AWRの利点を使用できませんでしたが、クエリを確認したところ、非常に奇妙なことがわかりました。

クエリは次のとおりで、関数fooを使用します。

update LIST_JOURNAL
   set STATUS       = '4',
       END_DATE     = :b1,
       END_TIME     = :b2,
       END_TYPE     = '1',
       USER_ID  = 'SYSTEM',
       ELAPSED_TIME = FOO_FUNCTION(START_DATE, START_TIME, :b1, :b2)
 where (((TERM_ID = :b5 and NODE_CD = :b6) and GROUP_CD = :b7) and
       STATUS < '4');

彼らはデータ型としてCHARVARCHAR2NUMBERのみを使用しました(理由は不明です。おそらく別のデータベースからの移行が原因である可能性があります)。このクエリには、間隔を計算するこのFOO_FUNCTIONがあります2つのタイムスタンプの間で、1つがデータベースのレコードで、1つがsysdateであるとします(日付と時刻は、CHARではなくNUMBERおよびDATEデータ型で格納されます。またはTIMESTAMP)関数は、TO_CHARを使用してsysdateを文字列に変換し、次にTIMEフィールドと連結してから、TO_DATEを実行し、減算して、1日の秒数(60 * 60 * 24)に乗算して結果を返します。 (契約上の理由により、機能コードは公開できません)

この機能は私には非常に奇妙に見えますが、データベースのパフォーマンスを向上させるために、レポートを私のレポートの非常に重要なCPUボトルネックとして報告したいと思います。残りの関数を見たことはありませんが、DATEが必要なところはどこでも、同じようなことが起こります。

しかし、これが問題になる可能性があるというオラクルのドキュメントや他のどこかで証拠を見たことがないので、私はそうすることに少し消極的です。 (私は知っている、関数が奇妙に書かれているので、私の抵抗は馬鹿げているように見えます)また、ソフトウェアベンダーがこれを変更として受け入れない可能性も十分にあります。

私の質問は:

  1. 上記の機能はCPU負荷が高い機能ですか? DATEおよびTIMESTAMPデータ型をリファクタリングして使用すると、パフォーマンスが向上しますか?どうして?いくつかの理由を控除することができますが、これを提示する必要があるため、問題を説明しているドキュメントがある場合は、リンクを確認していただければ幸いです。

  2. 主キーを除いて、テーブル自体にインデックスはありません-現在のコードと状況で行き詰まっている場合、インデックスはパフォーマンスの向上に役立ちますか?それはどれほど効果的で持続可能なものでしょうか?詳細な回答は期待できないことはわかっていますが、テーブルには約370万行が含まれており、更新クエリの場合、いくつかのインデックスが役立つと思います。

そして私の一般的な質問:

  1. CHARNUMBERの代わりにDATETIMESTAMPを使用することによるパフォーマンスのマイナス面はありますか?

  2. 私のクライアントに何を勧めますか?

更新:

ようやく結果が出ました。次の複合インデックスを追加しました。

create index I_LIST_JOURNAL ON LIST_JOURNAL (TERM_ID, NODE_CD, STATUS);

すぐにいくつかの良い結果が得られ、CPUが80%に低下し、いくつかのクエリの実行時間は大幅に減少しました(問題のクエリの平均実行時間は1500万秒でしたが、現在は平均で約2400秒です)。数日のうちに、それは再び99パーセントになりました。統計収集の別の反復を行い、計画を説明したいと考えています。しかし、これが負荷の軽減に役立つかどうかはわかりません。 Explain Planの「Table Access Full」を排除するためにクエリが長時間実行されているテーブルに、他のいくつかの複合または単純なインデックスを追加することを考えていますが、上記の状況と私が持っているツールを考えると、他に方法がありますか?より良い結果を得るために使用しますか?

3
aLuViAn

何に基づいて、CPUボトルネックの理由で関数にフラグを付けたいですか?

sql_idを見つけ、v$sqlをチェックして、elapsed_timeplsql_exec_timeを比較したところ、elapsed_timeの大部分がplsql_exec_timeに由来し、cpu_timeも高いことがわかりました。

これを確認してください:

select elapsed_time, plsql_exec_time, cpu_time from v$sql where sql_id = '...';

またはさらに良いことに、実際に時間を費やしている場所を確認できます。

alter session set statistics_level=all;

次のステートメントを実行します。

update LIST_JOURNAL
   set STATUS       = '4',
       END_DATE     = :b1,
       END_TIME     = :b2,
       END_TYPE     = '1',
       USER_ID  = 'SYSTEM',
       ELAPSED_TIME = FOO_FUNCTION(START_DATE, START_TIME, :b1, :b2)
 where (((TERM_ID = :b5 and NODE_CD = :b6) and GROUP_CD = :b7) and
       STATUS < '4');

次に、この実行の実際の統計を次のように表示します。

select * from table(dbms_xplan.display_cursor(format=>'allstats last'));

上記は、SQL実行の各ステップに関する正確な実行カウント、タイミング、カーディナリティ、バッファ取得、ディスク読み取り、一時、メモリなどの情報を提供します。これはデータベースのどのエディションでも機能し、ライセンスオプションは必要ありません。上記を達成するにはさまざまな方法があります

LIST_JOURNALテーブルにはインデックスはありませんが、370万行あり、上記の出力は、ステートメントがTABLE ACCESS FULLに対して100000バッファーでLIST_JOURNALを実行したことを示していますが、テーブルがキャッシュにあるため、読み取りは0ですが、このステップでは実際には5行しか返されません(A-Rows)そしてこのステップは10秒かかりましたが、実行全体は10.2秒かかりましたか?もちろん、インデックスが見つからないという問題が発生する可能性は非常に高いです。作成するインデックスは何ですか?データの分布は不明であるため、わかりません。述語にすべての列が含まれる複合インデックスまたはそれらの3つだけが必要な場合や、TERM_ID列のみの単純なインデックスで十分な場合があります。

または、ほとんどのステップで費やされた時間はわずかですが、UPDATEステップで費やされた時間は長く、plsql_exec_timeelapsed_timeに比べて比較的高く、影響を受ける行の量は比較的少ないですか?確かに、機能を確認してください。

上記は、関数で使用されるデータ型について考える前に、私がチェックする絶対的な最小値です。最初に事実を収集してから、理論を立てる前に、問題の発生源をほんの少し考えてください。

元の質問に対するいくつかの回答:

  1. はい、可能です。適切なデータ型でも。
  2. はい、できます。データの分布、影響を受ける行の数によって異なります。
  3. 依存します。通常、はい、日付を保存する場合。
2
Balazs Papp

テーブルが日付フィールドに正しいデータ型を使用した場合、foo_functionの必要性はなくなります。 2つの日付データ型を互いに減算するだけで、日数と日差の小数部分が得られます。 Oracleは内部的に日付データ型を数値として格納するため、簡単な日付計算を行うことができ、より高度な日付操作を行うための最適化された組み込み関数が多数あります。例が書かれているように、OracleはSQLと関数のPL/SQLエンジンの間でコンテキストスイッチを行う必要があります。おそらく各行について。ボトルネックがあり、CHARおよびNUMBERデータ型をFOO_FUNCTION内で変換するための追加コードと組み合わせます。

スキーマを変更することはできないが、仮想列定義を使用できる場合(Oracleのバージョンとベンダーのソフトウェアによって異なります)、正しいDATEデータ型の仮想列START_DATE_TIMEを作成すれば、必要はありません。 FOO_FUNCTIONを呼び出します。

そして、あなたがすることができるすべてがインデックスを追加することであるならば、ちょうど

create index I_LIST_JOURNAL ON LIST_JOURNAL 
  (TERM_ID, NODE_CD, GROUP_CD, STATUS);

列の順序は、各列のカーディナリティなどに基づいて異なる可能性があります。 STATUS列は、等価性チェックではなく範囲チェックであるため、最後にする必要があります。そして、STATUSが少数の値、たとえば20以下の場合、実際には上記のインデックスからそれを省略し、STATUS列だけにビットマップインデックスを作成します。これらの更新中、テーブルへの同時アクセスは多くありません。 (ビットマップインデックスは、更新中にテーブルを一時的にロックします。)

1
Mark Stewart