MySQLには、トランザクションをサポートしない特別なテーブルタイプMyISAMがあります。オラクルにはこのようなものがありますか?非常に高速で(大量のデータを格納する)、トランザクションを必要としない書き込み専用データベース(ロギング用)を作成したいと思います。
8 に答える
トランザクションはSQLデータベース操作の鍵です。それらは確かにOracleの基本です。コミットを発行せずにOracleテーブルに永続的に書き込む方法はありません。トランザクションがあります。
Oracleでは、REDOログを生成しないテーブルをNOLOGGINGに指定できます。これは、(ヒントを使用した)一括読み込みのみを目的としておりINSERT /*+ APPEND */
、LOGGINGに切り替えて、できるだけ早く元に戻すようにアドバイスしています。ログに記録されていないデータは回復できないためです。そして、あなたがそれを回復したくないのなら、なぜそもそもそれを書くのをわざわざするのですか?
別のアプローチは、メモリ内の書き込みをバッチ処理してから、一括挿入を使用してそれらを書き込むことです。これはかなり速いです。
簡単なログテーブルと概念実証パッケージを次に示します。
create table log_table
(ts timestamp(6)
, short_text varchar(128)
, long_text varchar2(4000)
)
/
create or replace package fast_log is
procedure init;
procedure flush;
procedure write (p_short log_table.short_text%type
, p_long log_table.long_text%type);
end fast_log;
/
ログレコードは、セッションスコープを持つメモリ内構造であるPL/SQLコレクションに保持されます。INIT()プロシージャは、バッファを初期化します。FLUSH()プロシージャは、バッファの内容をLOG_TABLEに書き込みます。WRITE()プロシージャは、バッファにエントリを挿入し、バッファに必要な数のエントリがある場合は、FLUSH()を呼び出します。
create or replace package body fast_log is
type log_buffer is table of log_table%rowtype;
session_log log_buffer;
write_limit constant pls_integer := 1000;
write_count pls_integer;
procedure init
is
begin
session_log := log_buffer();
session_log.extend(write_limit);
write_count := 0;
end init;
procedure flush
is
begin
dbms_output.put_line('FLUSH::'||to_char(systimestamp,'HH24:MI:SS.FF6')||'::'||to_char(write_count));
forall i in 1..write_count
insert into log_table
values session_log(i);
init;
end flush;
procedure write (p_short log_table.short_text%type
, p_long log_table.long_text%type)
is
pragma autonomous_transaction;
begin
write_count := write_count+1;
session_log(write_count).ts := systimestamp;
session_log(write_count).short_text := p_short;
session_log(write_count).long_text := p_long;
if write_count = write_limit
then
flush;
end if;
commit;
end write;
begin
init;
end fast_log;
/
ログテーブルへの書き込みはAUTONOMOUS_TRANSACTIONプラグマを使用するため、フラッシュをトリガーした周囲のトランザクションに影響を与えることなくCOMMITが発生します。
DBMS_OUTPUT.PUT_LINE()の呼び出しは、進行状況の監視を容易にするためにあります。それで、それがどれくらい速くなるか見てみましょう....
SQL> begin
2 fast_log.flush;
3 for r in 1..3456 loop
4 fast_log.write('SOME TEXT', 'blah blah blah '||to_char(r));
5 end loop;
6 fast_log.flush;
7 end;
8 /
FLUSH::12:32:22.640000::0
FLUSH::12:32:22.671000::1000
FLUSH::12:32:22.718000::1000
FLUSH::12:32:22.749000::1000
FLUSH::12:32:22.781000::456
PL/SQL procedure successfully completed.
SQL>
うーん、0.12秒で3456レコード、それはそれほど粗末ではありません。このアプローチの主な問題は、ルーズなレコードを切り上げるためにバッファーをフラッシュする必要があることです。これは、たとえばセッションの終了時の痛みです。何かが原因でサーバーがクラッシュした場合、フラッシュされていないレコードは失われます。メモリ内で処理を行う場合のもう1つの問題は、メモリ(durrrr)を消費するため、キャッシュを大きくしすぎることができないことです。
比較のために、パッケージにプロシージャを追加しました。このプロシージャは、呼び出されるたびにLOG_TABLEに直接単一のレコードを挿入します。これも自律トランザクションを使用します。
procedure write_each (p_short log_table.short_text%type
, p_long log_table.long_text%type)
is
pragma autonomous_transaction;
begin
insert into log_table values ( systimestamp, p_short, p_long );
commit;
end write_each;
タイミングは次のとおりです。
SQL> begin
2 fast_log.flush;
3 for r in 1..3456 loop
4 fast_log.write_each('SOME TEXT', 'blah blah blah '||to_char(r));
5 end loop;
6 fast_log.flush;
7 end;
8 /
FLUSH::12:32:44.157000::0
FLUSH::12:32:44.610000::0
PL/SQL procedure successfully completed.
SQL>
壁掛け時計のタイミングは信頼性が低いことで有名ですが、バッチアプローチは単一レコードのアプローチよりも2〜3倍高速です。それでも、(最高級のラップトップからはほど遠い)ラップトップで、0.5秒未満で3000をはるかに超える個別のトランザクションを実行できました。したがって、問題は、ログに記録されているボトルネックの量です。
誤解を避けるために:
@JulesLtは、私がPoCに取り組んでいるときに、彼の回答を投稿していました。私たちの見解には類似点がありますが、提案された回避策の違いはこれを投稿するメリットがあると思います。
「自律的ではないが最後に単一のコミットがないwrite_eachのタイミングは何ですか?私のタイミングは、それが重要ではないことを示唆しています-挿入をバルク化することが大きな勝利です」
私のタイミングは少し違うことを示唆しています。書き込みごとのCOMMITを最後に単一のCOMMITに置き換えると、経過時間が約半分になります。バルクアプローチよりもまだ遅いですが、それほどではありません。
ここで重要なのはベンチマークです。私の概念実証は、ジュールのテストよりも約6倍高速に実行されています(私のテーブルには1つのインデックスがあります)。これにはさまざまな理由があります。マシンの仕様、データベースのバージョン(Oracle 11gR1を使用しています)、テーブルの構造などです。つまり、YMMVです。
つまり、教えは次のとおりです。まず、アプリケーションに対して何をするのが正しいかを決定し、次にそれを環境に対してベンチマークします。ベンチマークが深刻なパフォーマンスの問題を示唆している場合にのみ、別のアプローチを検討してください。時期尚早の最適化に関するKnuthの警告が適用されます。
最も近いのは、NOLOGGING表領域を作成し、その中にテーブルを作成するNOLOGGINGオプションを使用することです。ただし、これは一括操作にのみ適用される場合があります(つまり、INSERT / * + APPEND * /ヒントが必要です)。
これにより、DBがダウンした場合に整合性とデータが失われるという犠牲を払って、REDOが削除されます。
実際に「高速」になるかどうかはわかりません。同時実行性も検討する必要があります(同じテーブルに書き込もうとするプロセスが多数ある場合は、保留中の更新をREDOログに書き込むトランザクションを使用する方がよい場合があります。すべての「実際の」テーブルを更新します)。
NOLOGGINGについては実際には調査していませんが、アプリケーションのボトルネックがINSERTの速度であるという点に到達することはめったにありません。調査した場合、問題となっているのはテーブルではなくインデックスの更新コストでした。
簡単なテストを実行し、非常に強力な開発DB(REDOが有効になっている)で実行しました。各行に自律トランザクションを使用する-各行が新しいトランザクションを開始し、コミットで終了するため、1秒で1000行を超える行をインデックス付きログテーブルに書き込み/コミットできますが、コミットなしで1000挿入を行うと約0.875秒になります。
バルク操作を使用して1回のヒットで1000行の挿入を行うのはほんの一瞬です。したがって、ログをバルクアップできる可能性がある場合は、それを実行してください。
その他の考え:外部テーブルがその役割を果たしますか?つまり、ログファイルに書き込み、ログファイルから読み取る必要がある場合に、Oracleに外部テーブルとしてマウントしますか?
私の経験では、ロギングはフラットファイルに対して行うのが最適です。私の見解では、ログは一般的に特に重要ではありません-何かがうまくいかない限り、ログは重要になります。このため、ロギングのトランザクション制御は必要ありません。問題があるためにトランザクションをロールバックする必要がある場合、ログデータをロールバックしたくないのは、それが問題の原因を特定するために使用するためです。さらに、接続できないデータベースにログが保存されている場合、データベースへの接続に問題があることをどのように記録しますか?
共有してお楽しみください。
「それは非常に高速である必要があります」
高速と回復可能の間には(時々)トレードオフがあります。
Oracleでは、回復可能性はREDOログファイルによって実現されます。コミットするたびに、データベースの「ログライター」は同期呼び出しを実行して、ファイルに未処理の変更を書き込みます。同期とは、コミットが成功したと言う前に、ファイルシステムが書き込みが成功したことを確認するのを待つことを意味します。
ログファイルの各行が独立してコミットされている(特に一度に多数のセッションから)大量のロギングを実行している場合(自律トランザクション)、これはボトルネックになる可能性があります。
そのレベルの回復可能性が必要ない場合(つまり、重大な障害が発生した場合にログからログデータの最後の数行を失う余裕がある場合)、commitのNOWAITオプションを確認してください。
何も失うわけにはいかない場合、最善の策は本当に高速なストレージ(バッテリーでバックアップされたキャッシュの可能性があります)です。
同様のケースで私が行うことは、ログをファイルに書き込み(ファイルに追加するのがおそらくログを保存する最も速い方法です)、プロセスにそれらのログを定期的にDBにバッチ挿入させることです。もちろん、DBに直接挿入するのが十分に速い場合を除いて...しかし、テストする必要があります...
これは問題を探すための解決策のようです。
パフォーマンスのベンチマークを行いましたか?Oracleはそのままで十分高速ですか?トランザクション管理はOracleの動作方法に組み込まれており、それを回避しようとすると、自分で作業を作成しているように見えます。
問題があるかどうかを実際に知らなくても、トランザクション管理を問題として特定したようです。その後、テーブルに複数のライターがいるとどうなりますか?または、リーダーがライターをブロックしていますか?
PRAGMA AUTONOMOUS_TRANSACTION
これにより、周囲のトランザクションに影響を与えることなく、ログを記録してコミットすることができます。ロギングは、自律型トランザクションの非常に少数の受け入れ可能なユースケースの1つです。それはそれが言うことを行い、あなたがすでに参加しているかもしれないし、していないかもしれないトランザクションに影響を与えることなくその仕事をコミットすることができるpl/sql関数/プロシージャを書くことを可能にします。それは「自律的」です。
au・ton・o・mous1。(国または地域の)自治権を持っている。2.独立して行動する、またはそうする自由を持っている:「教育委員会の自律委員会」。
AUTONOMOUS_TRANSACTIONプラグマは、トランザクション内でのサブプログラムの動作方法を変更します。このプラグマでマークされたサブプログラムは、メイントランザクションでデータをコミットまたはロールバックすることなく、SQL操作を実行し、それらの操作をコミットまたはロールバックできます。
CREATE OR REPLACE FUNCTION FNC_LOG(p_log_text varchar2(4000))
RETURN NUMBER
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- Your brief code goes here (don't abuse the evil feature that is autonomous transactions).
END;
非常に高いパフォーマンスが必要な場合のもう1つのオプションは、OracleのTimesTenインメモリデータベースを検討することです。http ://www.oracle.com/technology/products/timesten/index.html