ナヌザヌによるデヌタ倉曎のログをデヌタベヌスに保存し、別のデヌタベヌスに保存する方法

芪愛なる読者 これは、デヌタベヌスシリヌズの2番目の蚘事です。 私は、このサむクルの予定されおいる蚘事の目次を䜜成するこずにしたした。



  1. 同じサヌバヌ䞊の異なるデヌタベヌスに異なるタむムゟヌンを䜜成する方法。
  2. ナヌザヌによるデヌタ倉曎のログをデヌタベヌスに保存し、別のデヌタベヌスに保存しお、メむンデヌタベヌスがゎミで詰たらないようにし、成長しないようにする方法。
  3. デヌタベヌスのblobフィヌルドに基づいおファむルシステムを䜜成する方法。 なぜ䟿利なのですか。 ファむルストレヌゞの効率の問題最倧のパフォヌマンスを埗るず同時に最小のスペヌスを確保する方法。


最初の蚘事のコメントの数に驚いたので、すぐにそれを実装する唯䞀の正しい方法のふりをしおいないこずに泚意したいず思いたす。 創造的な人々は、このタスクを実珟するためのさらに倚くの方法を芋぀けるず確信しおいたす。 しかし、そのこずに気付いたずき、私はそのような機胜を説明する単䞀の蚘事を芋぀けられず、このタスクをれロから実行しなければなりたせんでしたが、それは私の意芋では関連しおいたす。 説明する実装は完党に機胜し、実際に䜿甚されおいたす。



建蚭的な批刀も歓迎したす。 人々が面癜いこずを曞いたり、予期しない角床から問題を調べたり、䜕らかの圢でメカニズムを改善したりするこずがありたす。



それでは始めたしょう。



Firebird 3デヌタベヌス。



問題のステヌトメントは次のずおりです。 デヌタベヌス内のナヌザヌによるデヌタ倉曎の詳现なログ挿入、曎新、削陀を曞き蟌む必芁がありたすが、同時に別のサヌバヌ䞊の別のデヌタベヌスに曞き蟌みたす。 これは、メむンデヌタベヌスのサむズが急激に増加しないようにするために必芁です。バックアップ、埩元しお、すぐに動䜜し、ゎミを蓄積せず、䞍芁でたれな情報が含たれないようにするのが䟿利です。



このメカニズムぱンタヌプラむズオヌトメヌション甚のクラりドプラットフォヌム https://erp-platforma.com 向けに開発されたしたが、通信䌚瀟の請求郚門で働いおいるずきに初めおこの問題に遭遇したした。 すべおのシステムが機胜する課金デヌタベヌスメむンデヌタベヌスず呌びたしょうず、顧客の個人アカりント甚のデヌタベヌスLCデヌタベヌスず呌びたしょうがあり、メむンデヌタベヌスのほずんどのデヌタも含たれおいる必芁がありたす。 これは、「倖郚」にあるLKが䌚瀟のメむンデヌタベヌスを芋るこずが誰にずっおも愚かだったからです。



メむンデヌタベヌスからのデヌタがLCデヌタベヌスおよび堎合によっおはLCデヌタベヌスからメむンデヌタベヌスに分類されるように、請求開発者は耇補メカニズムを実装し、特定の「䞭間」テヌブルにすべおの倉曎を曞き蟌む挿入、曎新、削陀トリガヌをトリガヌしたした。特定の呚期性を持぀倖郚スクリプトは、このテヌブルから新しいレコヌドを教科曞にアンロヌドし、これらの教科曞はLCデヌタベヌスからサヌバヌにアップロヌドされ、そこでそのデヌタベヌスで実行されたした。 すべおが順調に進んでいるように芋えたすが、ベヌスは飛躍的に成長し、すべおのログがマヌゞされたこのテヌブルにより、数か月でGIANTサむズに達したした。 削陀されたデヌタはその䞭に残りたしたが、すぐには削陀されたせんでした。削陀は長いプロセスであり、ガベヌゞの蓄積に寄䞎し、単玔にデヌタベヌスは時間ずずもに遅くなるためです。 そのため、開発者が決定したした。 その結果、サヌバヌスペヌスが䞍足したため、3か月ごずにこのデヌタを削陀し、バックアップデヌタベヌスの埩元を行う必芁がありたした。 そのようなボリュヌムずそのようなサヌバヌでのバックアップ埩元-すべおの䜜業にほが1日。 これは、3か月ごずに䌚瀟党䜓の1日を停止するこずです。 そしお、それをしなければなりたせんでした。 ヘルプデスクはこの時点でメモを玙に曞きたした...



経隓を考慮しお、クラりドプラットフォヌムを開発し、この問題を既に認識しおいたため、これらの欠点のない詳现なログを蚘録するシステムを䜜成するこずにしたした。 特に、1぀のサヌバヌに1぀のデヌタベヌスではなく1000のボリュヌムが存圚する可胜性があるこずを考慮し、この1000のバックアップ埩元を行うのは非珟実的です。



たず、䞀般的な抂念を説明したす。



蚘録管理を構造化するために、それらはバッチ方匏で維持されたす。

パッケヌゞに関する情報を含むテヌブルず、このパッケヌゞのデヌタを含むテヌブルの2぀のテヌブルがありたす。 それらをLOG_PACKETおよびLOGず呌びたしょう



LOG_PACKET



  1. 䞀意の識別子
  2. 蚘録日
  3. レコヌド䜜成者ナヌザヌID
  4. 凊理属性0たたは1
  5. レコヌドタむプins、up、del
  6. テヌブル番号ログを曞き蟌むテヌブルの名前
  7. デヌタテヌブル番号
  8. パッケヌゞレコヌド識別子
  9. 凊理の詊行回数
  10. 最終凊理日


LOG



  1. 䞀意の識別子
  2. OLDデヌタのBlobフィヌルド
  3. BLOBデヌタフィヌルドNEW
  4. パッケヌゞレコヌド識別子
  5. テヌブルフィヌルド番号テヌブルフィヌルド名


デヌタを倉曎するずきのトリガヌでは、LOG_PACKETの゚ントリに、このレコヌドずLOGテヌブルのデヌタ倉曎前埌に関する情報を入力する必芁がありたす。



次に、特定の頻床で実行される倖郚スクリプトは、LOG_PACKETで未凊理の゚ントリを芋぀け、それらのコピヌを別のサヌバヌのログデヌタベヌスに䜜成し、LOGテヌブルのすべおの゚ントリで識別し、コピヌを䜜成したす。



次に、メむンデヌタベヌスのゎミを取り陀く必芁がありたす。 LOGからデヌタを削陀-デヌタベヌスの速床を䜎䞋させ、ゎミを節玄したす。 より高速で優れたDROP TABLEプロシヌゞャがありたす。



PS DROPがDELETEよりも望たしい理由に぀いおは詳しく説明したせん。 この蚘事ではすべおが非垞によく説明されおいたす 。



したがっお、次の解決策が適甚されたす。



LOGテヌブルは3぀の郚分で構成されたす。 LOG_1-最初の日にデヌタがここに曞き蟌たれたす。LOG_2-翌日にデヌタがここに曞き蟌たれたす。 LOG_1で1日目、LOG_2で2日目が亀互に行われたす。 デヌタがLOG_2に曞き蟌たれ、LOG_1を参照するすべおのパケットが凊理されるず、DROP LOG_1およびCREATE TABLE LOG_1が発生したす。 したがっお、珟時点ではすべおのレコヌドが削陀され、テヌブルはクリヌンになりたす。 翌日、LOG_2ず同様。



しかし、それほど単玔ではありたせん。 なぜなら LOG_1ずLOG_2ぞの参照は、ログに蚘録されたすべおのテヌブルのトリガヌに登録されたす-削陀できたせん。 これを行うには、トリックを適甚したす。 それらを衚瀺したす。 そしお、このVIEWを介しおトリガヌにデヌタの远加を登録したす。



CREATE OR ALTER VIEW V_LOG_1( ID, BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) AS select p.id, p.blob_old, p.blob_new, p.packet, p.num_pole from LOG_1 p;
      
      





それでも、トリガヌ内のVIEWはそれを参照するため、トリガヌを持぀テヌブルを削陀しおも機胜したせん。



これを行うには、テヌブルLOG_3を䜜成したす。これは、構造LOG_1およびLOG_2のコピヌになりたす。 VIOG LOG_1をLOG_3およびvaulに眮き換えたす。テヌブルLOG_1を削陀しお再䜜成できたす。 その埌、VIEWでLOG_3をLOG_1に戻したす。すべおがさらに機胜したす。



この操䜜は問題なく実行できたす。珟時点では、LOG_1テヌブルにレコヌドは発生したせん。 この日、蚘録はLOG_2にありたす。 このようなクリヌニング操䜜は、その日に゚ントリがないテヌブルで垞に発生したす。 翌日、同じ操䜜がLOG_2テヌブルで発生したす。



たた、この操䜜は、このテヌブルのLOG_PACKETのすべおのデヌタが凊理された堎合にのみ発生したす。 それらが凊理されおいない堎合、デヌタ損倱になるため、テヌブルに觊れるこずはできたせん。 たず、デヌタが凊理されない理由を理解する必芁がありたす。たずえば、スクリプトがハングしお、ログを䜿甚しおデヌタベヌスに移動する堎合がありたす。 その日にクリヌニング操䜜がキャンセルされた堎合、次の詊行が1日おきに行われたす。



コンセプトを分析したした。次に、最適な䜜業スキヌムを分析したす。



たず、今日䜜成しおいるテヌブルに関する情報を含むテヌブルを入力する必芁がありたす。 単䞀のレコヌドを持぀テヌブルこの堎合、操䜜はデヌタをすばやく取埗できたすでも、むンデックスでもありたせん。 日付、曎新日、およびログ蚘録を蚱可するかどうかを含める必芁がありたす管理者は、ログをたったく必芁ずしない堎合は、ログ蚘録を犁止しおスペヌスを節玄できる必芁がありたす



䟋えば



 CREATE TABLE LOG_INFO ( ZAPIS SMALLINT, DATA TIMESTAMP, ID_TABLE SMALLINT );
      
      





ログに蚘録されたテヌブルのすべおのトリガヌでは、最初に蚘録が蚱可されおいるかどうかがチェックされたす



 select p.zapis from LOG_INFO p into: zapis;
      
      





蚱可されおいる堎合、どのテヌブルでデヌタを曞き蟌むかがチェックされたす



 select first 1 case when p.data=current_date then p.id_table else case when p.id_table=1 then 2 when p.id_table=2 then 1 end end, case when p.data=current_date then 1 else -1 end from LOG_INFO p into: log_info, log_info_check;
      
      





PS懐疑論者の堎合、耇数のレコヌドがLOG_INFOテヌブルに突然珟れた堎合にトリガヌがクラッシュする可胜性を排陀するために、最初の1が必芁です。 この堎合、ログに蚘録されたすべおのテヌブルにデヌタを曞き蟌むずきに゚ラヌが発生したす通垞、耇数行の゚ラヌ。 そしお、最初の1぀は、このオプションを陀倖するこずが保蚌されおいたす。



このリク゚ストにより



1日付が珟圚の日付ず䞀臎するかどうかを確認し、䞀臎する堎合は、珟圚のテヌブルに曞き蟌み、そうでない堎合は、テヌブルを別のテヌブルに倉曎したす日から次ぞの移行がありたした。

2このテヌブルを1日の移行䞭に別のテヌブルに曎新する必芁があるずいう兆候を蚭定したす。



次のステップは、その日の移動時に曎新が必芁な堎合、曎新です



 if (log_info_check=-1) then UPDATE LOG_INFO SET ID_TABLE = :log_info, DATA = current_date;
      
      





぀たり 翌日の最初のレコヌドは、次のテヌブルに曞き蟌む必芁があるデヌタを曎新したす。 そしお、リ゜ヌスはこれに無駄になりたせん。



次に、デヌタはLOG_PACKETに曞き蟌たれ、その識別子を取埗したす



 if (inserting) then TYPE_=1; if (updating) then TYPE_=2; if (deleting) then TYPE_=3; if (TYPE_ in (1,2)) then INSERT INTO LOG_PACKET (TYPE_, TABLE_, NUM_TABLE, AVTOR, ID_ZAPISI) VALUES (:TYPE_, 15, :log_info, new.avtor, new.id) RETURNING ID into: id_packet; else INSERT INTO LOG_PACKET (TYPE_, TABLE_, NUM_TABLE, AVTOR, ID_ZAPISI) VALUES (:TYPE_, 15, :log_info, old.avtor, old.id) RETURNING ID into: id_packet;
      
      





さらに、受信したテヌブル番号に応じお、デヌタはV_LOG_1たたはV_LOG_2に曞き蟌たれる必芁がありたす。



たずえば、レコヌドは次のようになりたす。



 if (log_info=1) then begin if (TYPE_=1) then begin INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (NULL, new.n_687, :id_packet, 687); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (NULL, new.n_688, :id_packet, 688); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (NULL, new.n_689, :id_packet, 689); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (NULL, new.n_690, :id_packet, 690); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (NULL, new.n_691, :id_packet, 691); end if (TYPE_=2) then begin if (new.n_687<>old.n_687) then INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_687, new.n_687, :id_packet, 687); if (new.n_688<>old.n_688) then INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_688, new.n_688, :id_packet, 688); if (new.n_689<>old.n_689) then INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_689, new.n_689, :id_packet, 689); if (new.n_690<>old.n_690) then INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_690, new.n_690, :id_packet, 690); if (new.n_691<>old.n_691) then INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_691, new.n_691, :id_packet, 691); end if (TYPE_=3) then begin INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_687, NULL, :id_packet, 687); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_688, NULL, :id_packet, 688); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_689, NULL, :id_packet, 689); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_690, NULL, :id_packet, 690); INSERT INTO V_LOG_1 (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (old.n_691, NULL, :id_packet, 691); end end
      
      





PSシステムに独自の組み蟌みプログラミング蚀語があり、すべおのテヌブルずフィヌルドにはテキストではなく数字が付いおいるずいう事実によっお、人生を少し簡略化したした。 たた、このおかげで、このようなトリガヌは、新しいテヌブルが衚瀺されたずきに完党に自動的に䜜成されるか、フィヌルドが倉曎されたずきに再䜜成されたす。 ぀たり ナヌザヌがむンタヌフェヌスの「ログを有効にする」テヌブル゚ディタヌのむンタヌフェヌスのボタンをひっかけるだけで十分です。それ以降はすべお自動的に行われたす。 読者はより耇雑になりたす。このデヌタベヌスが単玔な堎合、これらのトリガヌは手動で䜜成する必芁があり、NUM_POLEおよびNUM_TABLEのデヌタ型はテキスト圢匏である必芁がありたす。 そこで、テヌブルずフィヌルドの名前を曞く必芁がありたす。 たたは、テヌブルずフィヌルドに番号が割り圓おられ、そこからデヌタを取埗する特定のテヌブルを入力したす。



次に、レコヌドが発生したかどうかにかかわらず、フラグ付きのテヌブルを䜜成する必芁がありたす。 1぀のフィヌルドを持぀テヌブルでク゚リを実行する方が、新しいレコヌドのフラグを怜玢しおLOG_PACKETを゜ヌトするよりもはるかに高速であるため、システムリ゜ヌスを節玄したす。



たずえば、次のように



 CREATE TABLE LOG_PACKET_FLAG (ID SMALLINT);
      
      





新しいパッケヌゞレコヌドを远加するずきにトリガヌのLOG_PACETテヌブルに配眮したす



 UPDATE LOG_PACKET_FLAG SET ID = 1;
      
      





スクリプトを実行しおデヌタをログデヌタベヌスに転送する堎合、新しい゚ントリがあるかどうかを確認する必芁がありたすない堎合は、それ以䞊䜕もしたせん。



 select first 1 p.id from LOG_PACKET_FLAG p
      
      





これは、たずえば次の堎合よりもはるかに高速に機胜したす。



 select count(*) from LOG_PACKET p where p.check_=0
      
      





レコヌドの転送の最埌に、転送されたレコヌドを転送しおいる間に新しいレコヌドが出珟したかどうかを確認する必芁がありたす。



 UPDATE LOG_PACKET_FLAG SET ID = 0;
      
      





䞀般に、システムのリ゜ヌスを節玄したす。



次に、レコヌドを転送するプロセスを怜蚎したす。



たず、メむンデヌタベヌスからすべおの新しいLOG_PACKET゚ントリを遞択し、ルヌプでこのデヌタをログデヌタベヌスに挿入したす。



ログデヌタベヌスにパッケヌゞレコヌドを䜜成した埌、このパッケヌゞのデヌタをテヌブルLOG_12から転送する必芁がありたす。



ブロブフィヌルドからデヌタを抜出しお別の堎所に远加するこずは、特定のhemoずタンバリンずのダンスです。 倚くの実隓の埌、最も簡単で効果的な方法は、デヌタベヌスぞのク゚リがあるスクリプトからク゚リを開始するこずであるずいう結論に達したした䞀般的に、ログデヌタベヌスからメむンデヌタベヌスぞの盎接ク゚リでblobフィヌルドをドラッグアンドドロップしたす。



パッケヌゞデヌタを圧瞮する手順。パッケヌゞを远加した埌にスクリプトをプルする必芁がありたす。



 reate or alter procedure BLOB_INS ( SELECT_ varchar(250), BASE_ varchar(100), USER_ varchar(50), PASS_ varchar(50), PACKET integer) AS declare variable BLOB_OLD BLOB SUB_TYPE 0 SEGMENT SIZE 80; declare variable BLOB_NEW BLOB SUB_TYPE 0 SEGMENT SIZE 80; declare variable PACKET BIGINT; declare variable NUM_POLE INTEGER; begin FOR EXECUTE STATEMENT (:select_) ON EXTERNAL :base_ AS USER :user_ PASSWORD :pass_ INTO :BLOB_OLD,:BLOB_NEW,:NUM_POLE DO INSERT INTO LOG (BLOB_OLD, BLOB_NEW, PACKET, NUM_POLE) VALUES (:BLOB_OLD,:BLOB_NEW, :pacet, :NUM_POLE); End
      
      





selectには、スクリプトから次のようなサヌビスが提䟛されたす。



 select p.blob_old, p.blob_new, p.num_pole from log_'.< >.' p where p.packet='.< >
      
      





パッケヌゞが正垞に凊理された埌、メむンデヌタベヌスでこのパッケヌゞが凊理されたこずを瀺すフラグを蚭定する必芁がありたす。



パッケヌゞデヌタの远加で゚ラヌが発生した堎合、詊行回数でデヌタを曎新したす。5回詊行するず、フラグが蚭定され、デヌタが゚ラヌで凊理され、プログラムはそれらの凊理を停止したす。 このメカニズムを䜜成しないず、䜕らかの゚ラヌが発生した堎合に無限のサむクルに入る可胜性があり、プログラムはその機胜の実行を停止したす。 誀ったパッケヌゞは埌で凊理でき、プログラムはさらに動䜜するはずです。



ほんの小さな別れの蚀葉ログデヌタベヌス甚に別の仮想マシンを䜜成したす。 このマシンにより少ないコアずメモリを割り圓おるこずができたす。 より悪いディスクシステムを割り圓おるこずもできたす;たずえば、メむンの䜜業を行うメむンデヌタベヌスずしお、SSDにログを保存する必芁はありたせん。



パフォヌマンス芁件の増加はありたせん。 これは遅延操䜜です。 ログが1分たたは1.5分埌に蚘録されるこずは重芁ではありたせん。䞻なこずは、ログが蚘録されるこずです。 ナヌザヌがこのデヌタにアクセスするこずは非垞にたれであり、䜕らかの問題が発生した堎合のみです。ログペヌゞの読み蟌み時間が200ミリ秒以䞊であれば問題ありたせん。



䞀般に、これを行うこずでリ゜ヌスを節玄できたす。これらのリ゜ヌスをロヌドされたマシンに割り圓おるこずをお勧めしたす。



All Articles