2016-09-23 16 views
2

リアルタイムのデータと履歴データは、Webベースの監視プロジェクトで表示されました。サンプル周波数50Hzのセンサーは16個近くあります。センサーの生データはすべてデータベースに保存されなければならず、これは1秒あたり900近くのデータに達します。また、データは少なくとも3年間保存しなければなりません。データベースはOracle 11gです。リアルタイムデータテーブルからヒストリデータテーブルへのデータ損失

私の仕事は、センサハードウェア会社のエンジニアのためのデータベース構造を設計し、データ収集プログラムを作成し、データをデータベースに保存することです。

リアルタイムデータテーブルと履歴データテーブルが設計されています。リアルタイムデータはリアルタイムデータテーブルから読み出され、履歴データは履歴データテーブルから読み出されます。

実データテーブルは、1分のデータしか格納していない次のようなものです。

    :主キーとパーティション

    Create Table history_data(
    record_time timestamp(3), 
    ac_1 Float, 
    ac_2 Float, 
    ac_3 Float, 
    ac_4 Float, 
    ac_5 Float, 
    ac_6 Float, 
    ac_7 Float, 
    ac_8 Float, 
    ac_9 Float, 
    ac_10 Float, 
    ac_11 Float, 
    ac_12 Float, 
    ac_13 Float, 
    ac_14 Float, 
    ac_15 Float, 
    ac_16 Float 
    ) 
    Tablespace data_test 
    PARTITION BY RANGE(record_time) 
    INTERVAL(numtodsinterval(1,'day')) 
    ( 
        PARTITION P1 VALUES LESS THAN (TO_DATE('2016-08-01', 'YYYY-MM-DD')) 
    ); 
    
    alter table history_data add constraint RECORD_DATE primary key (RECORD_TIME); 
    

    から成る

    Create Table real_data(
    record_time timestamp(3), 
    ac_1 Float, 
    ac_2 Float, 
    ac_3 Float, 
    ac_4 Float, 
    ac_5 Float, 
    ac_6 Float, 
    ac_7 Float, 
    ac_8 Float, 
    ac_9 Float, 
    ac_10 Float, 
    ac_11 Float, 
    ac_12 Float, 
    ac_13 Float, 
    ac_14 Float, 
    ac_15 Float, 
    ac_16 Float 
    ) 
    Tablespace data_test; 
    

    履歴データテーブルの構造は、実データと同じであるが、間隔パーティションは二つの理由のために選択されます

  1. SQLクエリーは、Webクライアントによる時刻レコードに基づいています(

    など)。

    ac_1からac_test ここで、record_time> = to_timestamp( '2016-08-01 00:00:00'、 'yyyy-mm-dd hh24:mi:ss') record_time < = to_timestamp( '2016-08-01 00 :30:00 '、' yyyy-mm-dd hh24:mi:ss ');

  2. 間隔パーティションは日からの範囲です。 1日のデータのテストでは、1日あたり430万件のデータで約40秒がかかりました。

実データと履歴データを1分ごとに転送するジョブが実行されます。転送プロセスはOracleプロシージャによって実行され、転送時間は別の表:real_data_top_backup_dateによって記録されます。

create or replace procedure copy_to_history_test is 
d_top_backup_date timestamp(3); 
begin 

select top_backup_date into d_top_backup_date from real_data_top_backup_date; 

Insert Into history_data Select * From real_data where record_time <d_top_backup_date; 

delete from real_data where record_time <d_top_backup_date; 

Update real_data_top_backup_date Set top_backup_date=(d_top_backup_date+1/24/60); 

commit; 

end copy_to_history_test; 

そして、センサデータの収集と挿入をシミュレートするシミュレーションプログラムが作成されます。

Declare 
time_index Number; 
start_time Timestamp(3); 
tmp_time Timestamp(3); 
tmp_value1 Float; 
tmp_value2 Float; 
tmp_value3 Float; 
tmp_value4 Float; 
tmp_value5 Float; 
tmp_value6 Float; 
tmp_value7 Float; 
tmp_value8 Float; 
tmp_value9 Float; 
tmp_value10 Float; 
tmp_value11 Float; 
tmp_value12 Float; 
tmp_value13 Float; 
tmp_value14 Float; 
tmp_value15 Float; 
tmp_value16 Float; 


Begin 

--initiaze the variable 
time_index:=0;  
SELECT to_timestamp('2016-08-01 00:00:00:000', 'yyyy-mm-dd h24:mi:ss:ff') Into start_time FROM DUAL; 

     While time_index<(50*60*60*24*7) 
     Loop 
     -- add 20 millionseconds 
     SELECT start_time+numtodsinterval((0.02*time_index),'SECOND') Into tmp_time FROM DUAL; 
     -- dbms_output.put_line(tmp_time); 
     -- create random number 
     select dbms_random.value Into tmp_value1 from dual ; 
     select dbms_random.value Into tmp_value2 from dual ; 
     select dbms_random.value Into tmp_value3 from dual ; 
     select dbms_random.value Into tmp_value4 from dual ; 
     select dbms_random.value Into tmp_value5 from dual ; 
     select dbms_random.value Into tmp_value6 from dual ; 
     select dbms_random.value Into tmp_value7 from dual ; 
     select dbms_random.value Into tmp_value8 from dual ; 
     select dbms_random.value Into tmp_value9 from dual ; 
     select dbms_random.value Into tmp_value10 from dual ; 
     select dbms_random.value Into tmp_value11 from dual ; 
     select dbms_random.value Into tmp_value12 from dual ; 
     select dbms_random.value Into tmp_value13 from dual ; 
     select dbms_random.value Into tmp_value14 from dual ; 
     select dbms_random.value Into tmp_value15 from dual ; 
     select dbms_random.value Into tmp_value16 from dual ; 
     --dbms_output.put_line(tmp_value); 

     -- Insert Into ac_data (sensor_id,data,record_time) Values(sensor_index,tmp_value,tmp_time); 
     Insert Into real_data Values(tmp_time,tmp_value1,tmp_value2,tmp_value3,tmp_value4,tmp_value5,tmp_value6,tmp_value7,tmp_value8,tmp_value9,tmp_value10,tmp_value11,tmp_value12,tmp_value13,tmp_value14,tmp_value15,tmp_value16); 
     if mod(time_index,50)=0 then 
     commit; 
     dbms_lock.sleep(1); 
     End If; 

     time_index:=time_index+1; 
     End Loop; 

-- dbms_output.put_line(c); 
    Exception 
    WHEN OTHERS THEN 
    log_write('insert data failure!'); 
End; 

問題は、転送データ処理中に0.1%程度の量のセンサデータが失われることです。私は、データの転送(データの挿入とデータの削除)が並行して行われると、データが失われると思います。どのように問題を扱う?

またこのシナリオでは、データベース構造は実現可能ですか?データベースのためのより良いデザインがありますか?

+0

データが失われていることをどのように知っていますか? –

+0

@EvgeniyK。私は1日4316850のセンサーデータがあることを発見しました、それは432000データで構成されます。 – skyspeed

答えて

0

非常に可能性が "ほぼ0.1%の量センサデータが失われます"。デフォルトでは、オラクル社では、ステートメント・レベルのRead Committed分離モデルを使用します。分離レベルは、他のセッションによってテーブルに追加されたレコードが、プロシージャによって挿入されたレコードのセットに含まれないことを意味します。ただし、他のセッションがdeleteステートメントの対象となる行をコミットしている場合に限ります。この現象を「ファントムリード」といいます。

したがって、重要な点は、レコードを「リアルタイム」テーブルに挿入することです。あなたのテストハーネスでは、挿入物は50のバッチでコミットされますmod(time_index,50)=0copy_to_history_test()が実行されている間にそのコミットが1つのセッションで発生した場合、レコードが転落する可能性のある穴があります。おそらくあなたのプロセスは生産で異なるでしょう。

「問題の対処方法」

この問題の標準的なアプローチは、SERIALIZABLE分離レベルを使用することです。 copy_to_history_test()を実行しているセッションでこれを設定すると、すべてのステートメントがトランザクションの継続中に同じ状態のデータを使用して実行されます。レコードがリアルタイムテーブルにのみ挿入されている場合、このアプローチはあなたに悲しみを与えてはいけません。 (他のプロセスでき更新またはその後、それらのレコードを削除した場合、あなたは大きなアーキテクチャの問題を抱えている。)

は、だからあなたの手順は次のようになります。私もreal_data_top_backup_dateテーブルをロックした

create or replace procedure copy_to_history_test is 
    d_top_backup_date timestamp(3); 
begin 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- isolate all these statements 

    select top_backup_date into d_top_backup_date from real_data_top_backup_date 
    FOR UPDATE OF top_backup_date; -- lock the table to any other session 
    Insert Into history_data Select * From real_data where record_time <d_top_backup_date; 
    delete from real_data where record_time <d_top_backup_date; 
    Update real_data_top_backup_date Set top_backup_date=(d_top_backup_date+1/24/60); 

    commit; -- reverts the isolation level 

end copy_to_history_test; 

注意を。マルチユーザー環境では、競合によるトランザクションの失敗を防ぐために更新用のレコードを予約することをお勧めします。

このドキュメントでは、隔離レベルについて詳しく説明しています。 Find out more

"データベースのデザインが優れていますか?"

これは、達成しようとしている内容によって異なります。 「リアルタイム」テーブルのポイントは何ですか?あなたは1分だけ記録を残しているようです。したがって、適切な質問は、なぜパーティション化されたテーブルに挿入しないのですか?このような多くの努力を正当化するために2つのテーブルを持つことから得られる価値はどれくらいですか?

"センサデータがヒストリデータテーブルに挿入されただけの場合、リアルタイムのグラフィック表示は保証されません。なぜなら、データテーブルの増加に伴いデータの取得が遅くなるからです。

これを証明するベンチマークはありますか? 1分分のデータを選択すると、主キーでインデックス範囲のスキャンを実行しているので、かなり安定しているはずです。あなたの心は、2つのテーブル構造に設定されている場合

とにかく、私はあなたがINSERTにすべての機能を使用して、同時に両方のテーブルにレコードを挿入勧め:

insert all 
    into real_data values (....) 
    into history_data values (....) 
select .... 

マルチテーブル構文はINSERTを必要とする... SELECT構造、 DUALやユースケースに合ったものからローカル変数を選択することができます。 Find out more

history_dataに既にレコードがあるので、copy_to_history_test()から転送を削除して、real_dataテーブルから削除してください。

+0

お返事ありがとうございます。私はあなたの提案に従っていくつかのテストを行います。 – skyspeed

+0

2つのテーブル(real_dataおよびhistory_dataテーブル)を設計する理由は、Webクライアントが毎秒リアルタイムのデータグラフィックを表示し、データベースを介して履歴データを照会する必要があるためです。センサデータがヒストリデータテーブルに挿入されただけの場合、リアルタイムのグラフィック表示は保証されません。なぜなら、データテーブルの増大に伴ってデータの取得がより遅くなるからです。したがって、2つのテーブル構造が設計されています。リアルタイムテーブルには1分のデータ(約54000データ)しか含まれていません。したがって、リアルタイムデータを取得することが保証されます。 – skyspeed

+0

ありがとう!私は1つのテーブルに挿入するだけでデータを取得する時間をテストしませんでした。私は、テーブルにインデックスが存在するため、挿入データがゆっくりとなり、検索データがゆっくりとなると思います。そして私はそれを試みます。さらに、このシナリオのすべての機能を挿入していただきありがとうございます。 – skyspeed

関連する問題