私は約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');
彼らはデータ型としてCHAR
、VARCHAR2
、NUMBER
のみを使用しました(理由は不明です。おそらく別のデータベースからの移行が原因である可能性があります)。このクエリには、間隔を計算するこのFOO_FUNCTION
があります2つのタイムスタンプの間で、1つがデータベースのレコードで、1つがsysdateであるとします(日付と時刻は、CHAR
ではなくNUMBER
およびDATE
データ型で格納されます。またはTIMESTAMP
)関数は、TO_CHAR
を使用してsysdateを文字列に変換し、次にTIMEフィールドと連結してから、TO_DATE
を実行し、減算して、1日の秒数(60 * 60 * 24)に乗算して結果を返します。 (契約上の理由により、機能コードは公開できません)
この機能は私には非常に奇妙に見えますが、データベースのパフォーマンスを向上させるために、レポートを私のレポートの非常に重要なCPUボトルネックとして報告したいと思います。残りの関数を見たことはありませんが、DATE
が必要なところはどこでも、同じようなことが起こります。
しかし、これが問題になる可能性があるというオラクルのドキュメントや他のどこかで証拠を見たことがないので、私はそうすることに少し消極的です。 (私は知っている、関数が奇妙に書かれているので、私の抵抗は馬鹿げているように見えます)また、ソフトウェアベンダーがこれを変更として受け入れない可能性も十分にあります。
私の質問は:
上記の機能はCPU負荷が高い機能ですか? DATE
およびTIMESTAMP
データ型をリファクタリングして使用すると、パフォーマンスが向上しますか?どうして?いくつかの理由を控除することができますが、これを提示する必要があるため、問題を説明しているドキュメントがある場合は、リンクを確認していただければ幸いです。
主キーを除いて、テーブル自体にインデックスはありません-現在のコードと状況で行き詰まっている場合、インデックスはパフォーマンスの向上に役立ちますか?それはどれほど効果的で持続可能なものでしょうか?詳細な回答は期待できないことはわかっていますが、テーブルには約370万行が含まれており、更新クエリの場合、いくつかのインデックスが役立つと思います。
そして私の一般的な質問:
CHAR
とNUMBER
の代わりにDATE
とTIMESTAMP
を使用することによるパフォーマンスのマイナス面はありますか?
私のクライアントに何を勧めますか?
更新:
ようやく結果が出ました。次の複合インデックスを追加しました。
create index I_LIST_JOURNAL ON LIST_JOURNAL (TERM_ID, NODE_CD, STATUS);
すぐにいくつかの良い結果が得られ、CPUが80%に低下し、いくつかのクエリの実行時間は大幅に減少しました(問題のクエリの平均実行時間は1500万秒でしたが、現在は平均で約2400秒です)。数日のうちに、それは再び99パーセントになりました。統計収集の別の反復を行い、計画を説明したいと考えています。しかし、これが負荷の軽減に役立つかどうかはわかりません。 Explain Planの「Table Access Full」を排除するためにクエリが長時間実行されているテーブルに、他のいくつかの複合または単純なインデックスを追加することを考えていますが、上記の状況と私が持っているツールを考えると、他に方法がありますか?より良い結果を得るために使用しますか?
何に基づいて、CPUボトルネックの理由で関数にフラグを付けたいですか?
sql_id
を見つけ、v$sql
をチェックして、elapsed_time
とplsql_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_time
はelapsed_time
に比べて比較的高く、影響を受ける行の量は比較的少ないですか?確かに、機能を確認してください。
上記は、関数で使用されるデータ型について考える前に、私がチェックする絶対的な最小値です。最初に事実を収集してから、理論を立てる前に、問題の発生源をほんの少し考えてください。
元の質問に対するいくつかの回答:
テーブルが日付フィールドに正しいデータ型を使用した場合、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
列だけにビットマップインデックスを作成します。これらの更新中、テーブルへの同時アクセスは多くありません。 (ビットマップインデックスは、更新中にテーブルを一時的にロックします。)