首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >实时数据表到历史数据表的数据丢失.数据表

实时数据表到历史数据表的数据丢失.数据表
EN

Stack Overflow用户
提问于 2016-09-23 02:01:56
回答 1查看 99关注 0票数 2

对一个基于web的监测项目进行实时数据和历史数据的显示.有近16个样品频率为50 the的传感器。传感器的所有原始数据必须存储在数据库中,达到每秒近900个数据。这些数据必须保存至少三年。数据库是oracle 11g。

我的工作是为传感器硬件公司的工程师设计数据库结构,后者将编写数据采集程序并将数据存储到数据库中。

设计了实时数据表和历史数据表.实时数据从实时数据表读取,历史数据从历史数据表读取.

实数据表如下所示,它只存储了一分钟的数据.

代码语言:javascript
运行
复制
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;

历史数据表的结构与实数据相同,它由主键和分区组成。

代码语言:javascript
运行
复制
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);

选择间隔分区有两个原因:

  1. sql查询基于web客户端的时间记录,如 从ac_1 record_time >= to_timestamp('2016-08-01 :00:00:00“、‘yyyy-md-dh24:mi:ss’)和record_time <=to_timestamp('2016-08-01 00:30:00”、‘yyyy-md-dh24:mi:ss’)中选择record_time<=to_timestamp;
  2. 间隔间隔范围为天数。在对一天数据的测试中,每天430万个数据花费了将近40秒。

执行作业是为了每一分钟将真实数据传输到历史数据表。传输过程由oracle过程完成,传输时间由另一个table:real_data_top_backup_date记录。

代码语言:javascript
运行
复制
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;

并编写了模拟程序对传感器数据的采集和插入进行了仿真。

代码语言:javascript
运行
复制
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%的传感器数据。我认为传输数据的并行操作(插入数据和删除数据)会导致数据丢失。如何处理这个问题?

在这种情况下,数据库结构是否可行?数据库还有更好的设计吗?

EN

回答 1

Stack Overflow用户

发布于 2016-09-23 05:24:42

“近0.1%的传感器数据将丢失”

很有可能。默认情况下,Oracle在语句级别使用读提交隔离模型。隔离级别意味着由其他会话添加到表中的记录不会包含在过程插入的记录集中。但是,如果其他会话已经提交了这些行,则它们将位于delete语句的范围内。这种现象被称为“幻影阅读”。

因此关键是将记录插入到“实时”表中。在您的测试工具中,插入是以50 mod(time_index,50)=0的批次提交的。如果在copy_to_history_test()运行时在一个会话中发生提交,那么记录就会出现一个漏洞。也许您的过程在生产中会有所不同。

“如何处理这个问题?”

解决此问题的标准方法是使用SERIALIZABLE隔离级别。为运行copy_to_history_test()的会话设置此值意味着在事务持续时间内将使用相同的数据状态执行所有语句。如果只将记录插入到实时表中,则这种方法不会给您带来任何痛苦。(如果其他流程可以更新或删除这些记录,那么您就有了更大的架构问题。)

因此,您的程序现在应该如下所示:

代码语言:javascript
运行
复制
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;

注意,我还锁定了real_data_top_backup_date表。在多用户环境中,保留记录以进行更新以防止冲突导致事务失败是很好的做法。

文档非常详细地涵盖了隔离级别。了解更多信息

“数据库的另一个更好的设计吗?”

这取决于你想要实现什么。“实时”表的意义是什么?看来你只保存了一分钟的记录。因此,相关的问题是,为什么不直接插入到分区表中呢?有两个表来证明这么大的努力是有什么价值的呢?

“如果只将传感器数据插入历史数据表中,则无法保证实时图形显示,因为随着数据表的增长,检索数据的速度将变慢。”

你有没有做过任何基准来证明这一点?在主键上执行索引范围扫描时,选择一分钟的数据应该是相当稳定的。

无论如何,如果您的注意力集中在两个表结构上,我建议您使用insert ALL函数同时在两个表中插入记录:

代码语言:javascript
运行
复制
insert all
    into real_data values (....)
    into history_data values (....)
select ....

多表语法需要插入..。选择结构,但我们可以从双变量或任何适合您的用例中选择局部变量。了解更多信息

因为您已经有了history_data中的记录,所以可以从copy_to_history_test()中删除传输,只需从real_data表中删除。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39651437

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档