十万火急,高分求Oracle批量插入数据方案

wahaha_1986 2013-05-21 09:17:10
在C++板块发了一帖,无人回复,希望在Oracle板块得到帮助。
大概描述下环境:

数据源----->C++服务器----->Oracle Database
数据源来自于第三方,先发送到服务器,然后服务器保存到数据库,由于数据源是一条一条地发送到服务器,而且发送的速度非常快,目前服务器每收到一条数据就调用一次存储过程保存数据库,这样导致服务器收到的数据来不及处理,造成数据丢失,所以考虑采用批量插入数据库。希望有经验的同志们提供下指导,十万火急!
...全文
214 点赞 收藏 16
写回复
16 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
cowboyhn 2013-05-24
这是PL/SQL文档上的批量处理例子,你可以看出批量跟单条的效率差别。 建存储过程也可以定义输入参数为数组类型,然后使用FORALL批量处理,但我不了解ADO如何传入数组参数。

DROP TABLE parts1;
CREATE TABLE parts1 (
  pnum INTEGER,
  pname VARCHAR2(15)
);
 
DROP TABLE parts2;
CREATE TABLE parts2 (
  pnum INTEGER,
  pname VARCHAR2(15)
);

DECLARE
  TYPE NumTab IS TABLE OF parts1.pnum%TYPE INDEX BY PLS_INTEGER;
  TYPE NameTab IS TABLE OF parts1.pname%TYPE INDEX BY PLS_INTEGER;
  pnums   NumTab;
  pnames  NameTab;
  iterations  CONSTANT PLS_INTEGER := 50000;
  t1  INTEGER;
  t2  INTEGER;
  t3  INTEGER;
BEGIN
  FOR j IN 1..iterations LOOP  -- populate collections
    pnums(j) := j;
    pnames(j) := 'Part No. ' || TO_CHAR(j);
  END LOOP;

  t1 := DBMS_UTILITY.get_time;

  FOR i IN 1..iterations LOOP
    INSERT INTO parts1 (pnum, pname)
    VALUES (pnums(i), pnames(i));
  END LOOP;

  t2 := DBMS_UTILITY.get_time;

  FORALL i IN 1..iterations
    INSERT INTO parts2 (pnum, pname)
    VALUES (pnums(i), pnames(i));

  t3 := DBMS_UTILITY.get_time;

  DBMS_OUTPUT.PUT_LINE('Execution Time (secs)');
  DBMS_OUTPUT.PUT_LINE('---------------------');
  DBMS_OUTPUT.PUT_LINE('FOR LOOP: ' || TO_CHAR((t2 - t1)/100));
  DBMS_OUTPUT.PUT_LINE('FORALL:   ' || TO_CHAR((t3 - t2)/100));
  COMMIT;
END;
/
回复
多壮志 2013-05-24
道理和吃饭一样,一粒粒吃,好像也很快,但都浪费时间在夹的过程,一次吞一口,不是快多了。 几个筷子都往嘴巴夹东西,看起来眼花缭乱,其实嘴巴根本来不及嚼(假设夹道嘴巴就一定要嚼的话)。
回复
多壮志 2013-05-24
不清楚,你的实时性要求非常高的具体所指,也不清楚你调用的过程是如何写的, 更不清楚你们的网络情况,数据库服务器的性能等等。 只能和楼上的一样意见,考虑批次处理吧! 既然非常快,缓冲下其实更快,而不是浪费时间在传输,以及连接上,重新包装服务器的过程,以便批量处理要传送的信息。 还有你的OCCI执行的时候,是否总是保持连接打开?是否可能存在执行一次连接一次情况。
回复
wahaha_1986 2013-05-22
引用 8 楼 cowboyhn 的回复:
[quote=引用 5 楼 wahaha_1986 的回复:] 恩,服务器是多线程的,数据发送到服务器,服务器是先存在队列中,然后用另外的线程去取出,放在数据库中,这时候是一条一条取出插入数据库,现在的问题是服务器收到的数据很快,导致队列很快就满了,然后造成数据丢失,然后才考虑批量插入,所以才寻找可行的批量插入方法。
1.对列满的时候要给对方返回错误,无法保证数据永远正常写入数据库。 2.你取出数据的时候,只要对列还有数据,就不停的读取出来存放到数组,直到一个批量数(如100条)。 3.然后以数组为输入参数批量插入到数据库。 PRO*C的一个例子:

char* dyn_statement = "INSERT INTO emp (ename) VALUES (:ename_arr)" ;
char ename_arr[3][6] = {Tom","Dick","Harry"} ;
short ename_ind[3] = {0,-1,0} ;
int ename_len = 6, ename_type = 97, cnt = 2 ;
int empno_arr[3] = {8001, 8002, 8003} ;
int empno_len = 4 ;
int empno_type = 3 ;
int array_size = 3 ;
EXEC SQL FOR :array_size ALLOCATE DESCRIPTOR 'in' ;
EXEC SQL SET DESCRIPTOR 'in' COUNT = :cnt ;
EXEC SQL SET DESCRIPTOR 'in' VALUE 1 TYPE = :ename_type, LENGTH = :ename_len ;
EXEC SQL SET DESCRIPTOR 'in' VALUE 2 TYPE = :empno_type, LENGTH = :empno_len ;
EXEC SQL FOR :array_size SET DESCRIPTOR 'in' VALUE 1
   DATA = :ename_arr, INDICATOR = :ename_ind ;
EXEC SQL FOR :array_size SET DESCRIPTOR 'in' VALUE 2
   DATA = :empno_arr ;
EXEC SQL PREPARE s FROM :dyn_statement ;
EXEC SQL FOR :array_size EXECUTE s USING DESCRIPTOR 'in' ;
[/quote] 谢谢你的提供的方法,解释下你提出的3点: 1.第三方只管发,不收,肯定是丢失数据越少越好,得所以才考验服务器插入数据库的能力。 2.服务器插入操作数据库是同步的,操作完一次,才会去队列中去下一个数据插入,我可以有多个线程去队列中取数据插入,请问下,你提供的这种批量方法可以达到提高插入效率吗?
回复
wahaha_1986 2013-05-22
目前存在的问题是,服务器队列满了,数据就会丢失,不是大忌,所以需要提高插入数据的效率。 目前在网上了解到的方法: 1.用insert into t_its_batchinsert_test(test_id,test_name) select 'S001','ccc' from dual union all select 'S002','ccc' from dual....实现批量插入数据库,但是这种批量插入的条数受Oracle所支持的SQL语句最大长度影响。而且不知道这种方式相比一条一条插入是否会提高插入效率? 2.将服务器队列中的数据先保存在文档中TXT或XML,然后另外开线程单独从文档中读取数据,写入磁盘速度不受数据库性能影响,但是数据的实时性变差了。 还有其他方法没?求大神指点!顶顶顶!
回复
cowboyhn 2013-05-22
引用 5 楼 wahaha_1986 的回复:
恩,服务器是多线程的,数据发送到服务器,服务器是先存在队列中,然后用另外的线程去取出,放在数据库中,这时候是一条一条取出插入数据库,现在的问题是服务器收到的数据很快,导致队列很快就满了,然后造成数据丢失,然后才考虑批量插入,所以才寻找可行的批量插入方法。
1.对列满的时候要给对方返回错误,无法保证数据永远正常写入数据库。 2.你取出数据的时候,只要对列还有数据,就不停的读取出来存放到数组,直到一个批量数(如100条)。 3.然后以数组为输入参数批量插入到数据库。 PRO*C的一个例子:

char* dyn_statement = "INSERT INTO emp (ename) VALUES (:ename_arr)" ;
char ename_arr[3][6] = {Tom","Dick","Harry"} ;
short ename_ind[3] = {0,-1,0} ;
int ename_len = 6, ename_type = 97, cnt = 2 ;
int empno_arr[3] = {8001, 8002, 8003} ;
int empno_len = 4 ;
int empno_type = 3 ;
int array_size = 3 ;
EXEC SQL FOR :array_size ALLOCATE DESCRIPTOR 'in' ;
EXEC SQL SET DESCRIPTOR 'in' COUNT = :cnt ;
EXEC SQL SET DESCRIPTOR 'in' VALUE 1 TYPE = :ename_type, LENGTH = :ename_len ;
EXEC SQL SET DESCRIPTOR 'in' VALUE 2 TYPE = :empno_type, LENGTH = :empno_len ;
EXEC SQL FOR :array_size SET DESCRIPTOR 'in' VALUE 1
   DATA = :ename_arr, INDICATOR = :ename_ind ;
EXEC SQL FOR :array_size SET DESCRIPTOR 'in' VALUE 2
   DATA = :empno_arr ;
EXEC SQL PREPARE s FROM :dyn_statement ;
EXEC SQL FOR :array_size EXECUTE s USING DESCRIPTOR 'in' ;
回复
wahaha_1986 2013-05-22
引用 4 楼 andy_linky 的回复:
估计用的MQ消息发送的吧。
恩,服务器用类似于MQ的东西缓存的消息。
回复
wahaha_1986 2013-05-22
引用 2 楼 u010412956 的回复:
数据源是一条一条地发送到服务器,这里是怎么发的
我数据一条一条上报给服务器很正常呀。
回复
wahaha_1986 2013-05-22
引用 3 楼 cowboyhn 的回复:
你的服务器需要分成多个进程(或线程)处理,其中一些进程负责接收数据,并存放在数据缓冲区,另外一个进程负责从缓冲区读取数据,然后使用数组变量批量插入到数据库。
恩,服务器是多线程的,数据发送到服务器,服务器是先存在队列中,然后用另外的线程去取出,放在数据库中,这时候是一条一条取出插入数据库,现在的问题是服务器收到的数据很快,导致队列很快就满了,然后造成数据丢失,然后才考虑批量插入,所以才寻找可行的批量插入方法。
回复
andy_linky 2013-05-22
估计用的MQ消息发送的吧。
回复
wahaha_1986 2013-05-22
有一个情况要说明下,服务器和数据库是在两台PC上的。
回复
wahaha_1986 2013-05-22
OCCI是什么东东?没听过,数据库方面的我不了解,服务器操作数据库使用的是WINDOWS 提供的ADO COM组件提供的接口,操作数据库建表,写存储过程什么的用的是pl/sql developer客户端。
回复
cowboyhn 2013-05-22
对于大数据量处理,批量操作会比单条操作效率高得多,你如果是使用OCCI,请参考OCCI文档的批量处理说明。 另外你在数据库生成个AWR报告,分析下数据库的问题。
回复
cowboyhn 2013-05-22
你的服务器需要分成多个进程(或线程)处理,其中一些进程负责接收数据,并存放在数据缓冲区,另外一个进程负责从缓冲区读取数据,然后使用数组变量批量插入到数据库。
回复
hh7yx 2013-05-21
数据源是一条一条地发送到服务器,这里是怎么发的
回复
wahaha_1986 2013-05-21
自己先顶一下,求指导。
回复
相关推荐
发帖
Oracle
创建于2007-09-28

1.6w+

社区成员

Oracle开发相关技术讨论
申请成为版主
帖子事件
创建了帖子
2013-05-21 09:17
社区公告
暂无公告