(这是2009年写的东西了,在网上看到有人对数据库批量操作的‘速度’比较关注,于是就把这篇老文章整理了一下)

一、环境及前提

在244上(一台稍好一些的机器,做了RAID,机械硬盘,Raid几忘了),对eprk_person_flow表进行复制、更新,验证在Oracle数据库中操纵数据的各种方法的速度。共35,629,784条记录。

二、复制:直接建表

create /*+parallel*/table tt1 as select /*+parallel(pf, 4)*/ * from eprk_person_flow pf;

结果:
第一次:126.297秒,约每秒写入28.2W条数据。
第二次:149.109秒,约每秒写入23.9W条数据。

drop table tt1 purge;

三、复制:使用BULK COLLECT批量处理

create table tt1 as select * from eprk_person_flow where 1 = 2;

--   以下代码还有一个可以参考的地方:没有根据表结构声明太多的类型及变量。
declare
cursor cur_temp is
select /*+parallel(pf, 4)*/* from eprk_person_flow pf; type t_person_flow is tableof cur_temp%rowtype; -- 注意:这句很关键,直接引用上面的游标来定义类型 v_person_flow t_person_flow; begin
open cur_temp; loop
fetch cur_temp bulk collect into v_person_flow
limit 1000; forall i in 1..v_person_flow.last
insert /*+append nologging*/ into tt1 values v_person_flow(i);-- 注意:这里values后没有扩号 exit when cur_temp%notfound; end loop; closecur_temp; end; drop tablett1;

结果:

第一次:768.828秒。约每秒写入4.6W条数。

第二次:505.953秒。约每秒写入7.0W条数据。(加hint,把limit从200改为1000)如果在目标表上建立索引后再插入,哪怕只建立一个主键约束,耗时也将*2还多。

在复制数据时,DDL对比DML语句,在速度上还有很有优势的。

四、更新

直接更新10%、20%、50%、100%的数据。

/*
建表并建立索引
*/
create /*+parallel*/table tt1 as select /*+parallel(pf, 4)*/ * from eprk_person_flow pf; -- 147s alter table tt1
add constraint PK_tt1_PERSON_FLOW primary key (SERIAL_NUMBER) using index
tablespace NNC_INDEX01 pctfree 10
initrans 2
maxtrans 255 storage
(
initial 64M minextents 1
maxextents unlimited
);-- 102s create index I_TT1_PERSON_FLOW_TT on TT1 (PK_PERSON_ACCOUNT, PK_TRADETYPE)
tablespace NNC_INDEX01 pctfree 10 initrans 2
maxtrans 255 storage
(
initial 128M
minextents 1
maxextents unlimited
);-- 200s create index I_TT1_PERSON_FLOW_VN on TT1 (VOUCHER_NUMBER) tablespace NNC_INDEX01
pctfree 10 initrans 2
maxtrans 255
storage (
initial 128M minextents 1
maxextents unlimited
);-- 88s -- 更新数据(10%)
declare
i integer; -- 从tt1表中,取10%的数据
cursor cur_temp is
select serial_number from tt1
where rownum < cast((35629784 / 10) as integer);
type t_serial_number is tableof tt1.serial_number%type index by pls_integer; v_serial_number t_serial_number;
begin
open cur_temp; loop
fetch cur_temp bulk collect into v_serial_number limit 1000; forall i in 1..v_serial_number.count update /*+nologging*/ tt1
set occur_cash = 1, occur_unit = 1, cash_balance=1 , balance= 1 where serial_number = v_serial_number(i); exit when cur_temp%notfound;
end loop; closecur_temp; end;

更新10%的数据,共更新356W数据,耗时86.515秒,平均约每秒更新4.1W条。
更新20%的数据,共更新712W数据,耗时161.485秒,平均约每秒更新4 .4W条。
更新50%的数据,共更新1781W数据,耗时427.766秒,平均约每秒更新4.1W条。
更新100%的数据,共更新3562W数据,耗时1118.063秒,平均约每秒更新3.2W条。

在更新过程中,游标只读取1个字段,更新4个字段,由于该表有3个索引,虽然更新的字段都不在索引上,发现写入的数据量是读取的数据量 的20倍以上。(数据库归档日志打开)
以上只测试单表,并且顺序读取数据,然后根据唯一索引进行更新,如果有多表关联等要求,情况会更复杂。所以实际过程中,速度应该达不到4W/秒。

drop table tt1;

五、更新:使用中间表进行更新,并通过判断更新的数据量,进行对应的DDL操作。

-- 更新10%的数据。
-- 建立临时表
create global temporary table temp_tt1 as select * from tt1 where 1 = 2; -- 0.2s
-- 插入数据
insert into temp_tt1
select * from tt1
where rownum < cast((35629784 / 10) as integer);-- 12s
-- 更新临时表中的数据
update /*+parallel(temp_tt1, 4) nologging */ temp_tt1
set occur_cash = 1, occur_unit = 1, cash_balance=1 , balance= 1;-- 60s 分支一:
-- 删除源表中的数据
delete from tt1 t1 where exists (select 1 from temp_tt1 temp1 where t1.serial_number = temp1.serial_number);-- 163s
-- 把临时表中的数据插入到源表中
insert /*+parallel(tt1, 4)*/ into tt1 select /*+parallel(temp_tt1, 4)*/* from temp_tt1;--240s
分支二:
-- 使用临时表数据更新目标表
update tt1 t1 set (occur_cash, occur_unit, cash_balance, balance)
= (
select occur_cash, occur_unit, cash_balance, balance from temp_tt1 temp
where t1.serial_number = temp.serial_number
)
where exists ( select 1
from temp_tt1 temp
where t1.serial_number = temp.serial_number
);-- 约30分钟未完成,取消。
用上述方式更新时,硬盘IO没有规律,与其他方式相比,大部分时间都几乎“没有”IO,极慢。
-- 改为以下方式:
update /*+parallel(t1, 4) nologging*/ tt1 t1 set (occur_cash, occur_unit, cash_balance, balance)
= (
select /*+parallel(temp, 4)*/occur_cash, occur_unit, cash_balance, balance
from temp_tt1 temp
where t1.serial_number = temp.serial_number
)
where exists (
select /*+parallel(temp, 4)*/1
from temp_tt1 temp
where t1.serial_number = temp.serial_number
);-- 无效果,用大表(即使是临时表,本例有365W数据)更新更大的表时,直接用SQL实现在性能上是无法接受的。 commit;
drop table temp_tt1;

六、由此想到的:

create table XXX as select .. from ...比插入要快,由此,系统中的临时表如果用create替代insert的话,速度应该有提高。

数据对象的存储及使用设计,应严格根据实际的业务场景来定义,在建立诸如个人流水表之类的大表前,必须对表类型、各种存储结构及参数、是否及如何分区、索引的数量及字段(应根据实际查询要求建立索引)等进行详细的分析。

sql处理的速度还是有上限的,受制于应用环境。

另外,大数据量的处理,还是要考虑拆分为小任务、小事务进行。否则即使数据库有那么大的回滚段,一但后续出错,回滚的时间也等不起,给系统造成的压力也耗不起。

进行上述测试验证,只是好奇而已。

验证Oracle处理速度的更多相关文章

  1. 触发器 'SA.U_USER_INFO_TRG' 无效且未通过重新验证--Oracle序列

    程序开发时报错:触发器 'SA.U_USER_INFO_TRG' 无效且未通过重新验证打开触发器的定义,执行其中的语句,发现序列 U_USER_INFO_SEQ 未定义.什么是序列呢?序列相当于sql ...

  2. 验证Oracle收集统计信息参数granularity数据分析的力度

    最近在学习Oracle的统计信息这一块,收集统计信息的方法如下: DBMS_STATS.GATHER_TABLE_STATS ( ownname VARCHAR2, ---所有者名字 tabname ...

  3. Raid信息丢失数据恢复及oracle数据库恢复验证方案

    早些时候,有个客户14块盘的磁盘阵列出现故障,需要恢复的数据是oracle数据库,客户在寻求数据恢复技术支持,要求我提供详细的数据恢复方案,以下是提供给客户的详细数据恢复解决方案,本方案包含Raid数 ...

  4. oracle dump数据库

    最近正在看老白的<DBA的思想天空>,了解数据块结构,想通过dump data block验证oracle对于行尾的NULL,是不占用存储空间的. 我们先来看一下怎样dump数据块: 1. ...

  5. PL/SQL Developer连接本地Oracle 11g 64位数据库

    转摘:http://www.cnblogs.com/ymj126/p/3712727.html 用于学习,笔记,以备后用. 1.登录PL/SQL Developer 这里省略Oracle数据库和PL/ ...

  6. 如何查看Oracle客户端版本

    在实际工作中,总会遇到一些需要查看.验证ORACLE客户端版本的问题,因为一台服务器可能装了多个Oracle客户端版本:也有可能你需要知道安装的版本是32位还是64位的.如何查看Oracle客户端(O ...

  7. 安装oracle

    1.安装vnc  yum install tigervnc tigervnc-server 2.vncserver启动 3.安装依赖库 yum install -y compat-libstdc*   ...

  8. PL/SQL Developer连接本地64位Oracle数据库

    1.安装oracle Clinet 首先到Oracle官网上去下载一个Oracle 11g Client(我的是11g的oracle),不过需要先申请一个Oracle 帐号,才能下载. 目前下载地址: ...

  9. pl/sql developer 连接本地ORACLE 11g 64位数据库

    1.登录PL/SQL Developer 这里省略Oracle数据库和PL/SQL Developer的安装步骤,注意在安装PL/SQL Developer软件时,不要安装在Program Files ...

随机推荐

  1. linux上搭建ftp

    linux上搭建ftp 重要 解决如何搭建ftp         解决用户指定访问其根目录         解决访问ftp超时连接         解决ftp主动连接.被动连接的问题 1.安装ftp ...

  2. JavaScript 版数据结构与算法(三)链表

    今天,我们要讲的是数据结构与算法中的链表. 链表简介 链表是什么?链表是一种动态的数据结构,这意味着我们可以任意增删元素,它会按需扩容.为何要使用链表?下面列举一些链表的用途: 因为数组的存储有缺陷: ...

  3. 使用DapperSimpleCRUD对Repository层进行封装

    通过前面的两篇文章使用Dapper操作Mysql数据库与使用Dapper进行参数化查询,大致介绍了Dapper的一些基本操作和简单使用,在实际的使用当中,我们可以把项目简单的分为MVC+Service ...

  4. 学习笔记之CSS样式(选择器背景字体边框绝/相对、固定位置and分层流等)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. 创建静态库Static Library(Framework库原理相似)

    在项目开发的过程中,经常使用静态库文件.例如两个公司之间业务交流,不可能把源代码都发送给另一个公司,这时候将私密内容打包成静态库,别人只能调用接口,而不能知道其中实现的细节. 简介: 库是一些没有ma ...

  6. Python学习笔记(十四)

    Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...

  7. STM32 AD采样电压计算公式

    在使用STM32的ADC进行检测电压时必须回涉及到电压值的计算,为了更高效率的获取电压,现在有以下三种方法: 你得到的结果是你当前AD引脚上的电压值相对于3.3V和4096转换成的数字.假如你得到的A ...

  8. C# 根据路径删除文件或文件夹

    如何根据路径删除文件或文件夹? 1.首先我们要判断路径是文件或者是文件夹 那么我们可以通过 FileAttributes attr = File.GetAttributes(path); 来得到路径的 ...

  9. JS难点--面向对象(继承)

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Consolas; color: #a5b2b9 } 继承 让一个对象拥有另一个对象的属性或者 ...

  10. 自学Java HashMap源码

    自学Java HashMap源码 参考:http://zhangshixi.iteye.com/blog/672697 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提 ...