采用批量处理技术提高数据写入性能-【张永康】

摘要

                 在医院信息系统这类高并发的写入密集型应用中,提高数据写入的性能可以改善系统的整体性能,同时提高系统的并发能力。批量数据处理技术通过减少数据调用和提交的次数,极大的提高了写入性能。本文根据作者在医院信息系统开发中所实践的方法,通过实验对比,展示了批量处理技术带来的性能飞跃。

关键字:批量写入、性能、优化

一、  概述

                最近,在开发临床路径系统中,多次采用了批量数据处理技术。例如:在保存一批相同性质的数据时(例如:一个表格的多行数据),传统的方式就是每一行数据调用一次存储过程,通过循环调用来处理全部数据。而批量数据处理方式,则通过收集所有数据行,通过分隔符组合成一个字符串,只调用一次存储过程,通过一个参数传入所有数据,然后在存储过程中分解字符串,还原为多行数据,并且采用Oracle的批量数据提交语法(Forall)来进行数据的写入。

                经实际使用和试验表明,这种方式能够大大提高数据处理性能,具有较广的应用场合,值得我们在产品开发中推广使用。

二、  对比实验

                下面通过一个对比实验来展现批量处理带来的性能飞跃。

                假设:

                临床路径评估保存时,有一批评估指标相关的数据需要保存到数据库的相关表中,包括:指标名称,指标类型,指标结果。为了便于对比分析,我们假设有200条记录需要写入。

                采用传统方式,存储过程如下:

Create Or Replace Procedure Zl_病人路径评估_Insert_V1

(

  路径记录id_In 病人临床路径.Id%Type,

  阶段id_In     临床路径阶段.Id%Type,

  日期_In       病人路径评估.日期%Type,

  天数_In       病人路径评估.天数%Type,

  指标名称_In   病人路径指标.评估指标%Type,

  指标结果_In   病人路径指标.指标结果%Type,

  指标类型_In   病人路径指标.指标类型%Type

) Is

Begin

  --省略部分内容 

  Insert Into 病人路径指标

    (路径记录id, 阶段id, 日期, 天数, 评估类型, 评估指标, 指标结果, 指标类型)

  Values

    (路径记录id_In, 阶段id_In, 日期_In, 天数_In, 2, 指标名称_In, 指标结果_In, 指标类型_In);

  --省略部分内容

End Zl_病人路径评估_Insert_V1;

/

                200行记录,界面程序中就需要调用200次存储过程,分别传入每行数据的三个参数。

 

                如果采用批量数据处理方式,存储过程如下:

Create Or Replace Procedure Zl_病人路径评估_Insert_V2

(

  路径记录id_In 病人临床路径.Id%Type,

  阶段id_In     临床路径阶段.Id%Type,

  日期_In       病人路径评估.日期%Type,

  天数_In       病人路径评估.天数%Type,

  指标评估_In   Varchar2 --指标名称|指标结果|指标类型||...,末尾带||,允许为空

) Is

  v_Str   Varchar2(4000);

  v_Index Number;

  l_指标 t_Strlist := t_Strlist();

Begin

  --省略部分内容

  v_Str := 指标评估_In;

  Loop

    v_Index := Instr(v_Str, '||');

    Exit When(Nvl(v_Index, 0) = 0);

    l_指标.Extend;

    l_指标(l_指标.Count) := Substr(v_Str, 1, v_Index - 1); 

    v_Str := Substr(v_Str, v_Index + 2);

  End Loop;

 

  Forall I In 1 .. l_指标.Count 

    Insert Into 病人路径指标

      (路径记录id, 阶段id, 日期, 天数, 评估类型, 评估指标, 指标结果, 指标类型)

    Values

      (路径记录id_In, 阶段id_In, 日期_In, 天数_In, 2, Substr(l_指标(I), 1, Instr(l_指标(I), '|') - 1),

       Substr(l_指标(I), Instr(l_指标(I), '|') + 1, Instr(l_指标(I), '|', -1) - (Instr(l_指标(I), '|') + 1)),

       Substr(l_指标(I), Instr(l_指标(I), '|', -1) + 1));

  --省略部分内容

End Zl_病人路径评估_Insert_V2;

/

                说明:其中的t_Strlist 是一个预定义类型(单列记录集)

                                type t_StrList as Table of Varchar2(4000)

 

                这种方式,通过收集每行数据并以分隔符组合到一个变量中,然后把这200行数据传入,仅调用一次存储过程。在存储过程中,再通过分隔符把这个变量的内容分解成200行数据,然后采用Forall语法进行批量插入。

                如果实际开发过程中,多行数据组合后的字符串可能超过存储过程的字符参数的最大长度4000字节,则可以进行判断后,按字符串的最大长度进行组合,分多次调用存储过程。

 

                这个过程中,传入的字符串分解成行后,取每一行的字段的值时,采用了InstrSubstr,看起来比较复杂,而且在Forall语句中使用PLSQL函数,在8i版本时需要在SQL解析引擎和PLSQL解析引擎之间切换(9i以后已统一为一个解析引擎),所以,还有一种改良的方式,详见附后

                这种改进后的方式,先解析字符串的每一行,并将每一列的值放到不同的多行记录集中,然后Forall语句中直接取值,避免了函数的使用。

 

这三种方式分别插入相同的200行数据,试验结果如下:

 单位(毫秒)

第1次

第2次

第3次

第1种

515

421

468

第2种

32

47

109

第3种

15

16

31

                由此可见,批量处理方式与传统方式的写入速度是一个数量级的差别,要快30倍左右。并且,因为测试是在单机运行,如果是在网络环境下执行,加上网络往返的时间消耗,传统方式还会更慢。

 

 

 

 

 

通过以下的SQL查询写入前后的日志量,得到产生的日志大小。

Select a.Name, b.Value

From V$statname A, V$mystat B

Where a.Statistic# = b.Statistic# And Lower(a.Name) Like '%redo size%'

产生的日志数据对比:

                1种方式,产生155KB的日志;

                23种方式,仅产生32KB的日志;

                数据表明,又是一个数量级的差别。由于日志量的减少,可大大提高并发环境下的事务性能。

 

三、  分析与总结

                什么批量数据处理能够带来这么大的性能提升?根据实际结果的数据,再从理论方面进行分析,主要有三方面的原因。

1.       因为多次调用改为一次调用,减少了网络往返的时间消耗。

2.       由于多次调用需要多次进行SQL解析,虽然是软解析,但解析SQL是消耗CPU成本很高的操作。这一点是最主要的原因。

3.       采用forall方式批量处理数据时,Oracle采用了批量绑定和解析,并且所有数据作为一个事务原子,只产生一次回滚数据,所以,产生的日志量也很小。              

               

                批量数据处理还可用于数据转移,大表更新(更新字段值或类型),大表删除(按条件删除)等多种应用场合,提供高效的处理。

                所以,批量数据处理相对于传统单次处理方式,能够数十百甚至上百倍地提高数据处理性能,具有较广的应用场合,值得我们在产品开发中推广使用。

 

参考文献

Thomas Kyte . Oracle 9i 10g编程艺术(第9章第5小节):人民邮电出版社,2006.

               

 

 

附:

3种方式,改进后的批量处理过程

Create Or Replace Procedure Zl_病人路径评估_Insert_v3

(

  路径记录id_In 病人临床路径.Id%Type,

  阶段id_In     临床路径阶段.Id%Type,

  日期_In       病人路径评估.日期%Type,

  天数_In       病人路径评估.天数%Type,

  指标评估_In   Varchar2 --指标名称|指标结果|指标类型||...,末尾带||,允许为空

) Is

  v_Str   Varchar2(4000);

  v_Tmp   Varchar2(1000);

  v_Index Number;

  I       Number(5) := 1;

 

  l_指标名称 t_Strlist := t_Strlist();

  l_指标结果 t_Strlist := t_Strlist();

  l_指标类型 t_Numlist := t_Numlist();

Begin

  --省略部分内容

  v_Str := 指标评估_In;

  Loop

    v_Index := Instr(v_Str, '||');

    Exit When(Nvl(v_Index, 0) = 0);

    l_指标名称.Extend;

    l_指标结果.Extend;

    l_指标类型.Extend;

 

    v_Tmp := Substr(v_Str, 1, v_Index - 1);

    l_指标名称(I) := Substr(v_Tmp, 1, Instr(v_Tmp, '|') - 1);

    v_Tmp := Substr(v_Tmp, Instr(v_Tmp, '|') + 1);

    l_指标结果(I) := Trim(Substr(v_Tmp, 1, Instr(v_Tmp, '|') - 1));

    v_Tmp := Substr(v_Tmp, Instr(v_Tmp, '|') + 1);

    l_指标类型(I) := To_Number(v_Tmp);

 

    v_Str := Substr(v_Str, v_Index + 2);

    I     := I + 1;

  End Loop;

 

  Forall I In 1 .. l_指标名称.Count 

    Insert Into 病人路径指标

      (路径记录id, 阶段id, 日期, 天数, 评估类型, 评估指标, 指标结果, 指标类型)

    Values

      (路径记录id_In, 阶段id_In, 日期_In, 天数_In, 2, l_指标名称(I), l_指标结果(I), l_指标类型(I));

  --省略部分内容

End Zl_病人路径评估_Insert_v3;

/

 

                另外,随着数据行数的增大,例如:200条增加到2000条,性能差别将更加明显,主要是由于每次调用的SQL解析所消耗的CPU会大大增加,而批量处理时,增加很少。以前优化过的结帐处理程序后,由十几分钟或几十分钟缩短到几十秒或几分钟。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值