Oracle 大数据量update优化

一、基于ROWID改善update性能

oracle update 语句不支持象查新那样进行连接处理, 所以关联子表update 需要使用子查询,举个例子: 加入有一张员工考勤表, 一张员工打卡表,需要将打卡表的最早时间更新到员工表,那么应该这么写:

     update  <员工考勤表 t1> set  最早打卡时间 = (select min(打卡时间) from <员工打卡表 t2> where t1.员工编号 =t2.员工编号 and t1.时间 = t2.时间)

 这个语句在遍历update 员工考勤表时,  每条update 都会去执行子查询, 查找最早打卡时间, 所以执行效率很低, 所以当员工考勤表数据量很大时, 性能会成为瓶颈, 非常慢.

更好的一种方式, 是使用游标, 执行一次查询,取出员工的最早打卡时间, 以及考勤表的rowid, 然后循环update ,因为rowid 是硬地址, 定位相当快, 所以这个update 的性能是非常快, 可以提供性能几十倍不止, 下面是例子: 

declare

 cursor myCursor is   --声明游标myCursor

 select  A.ROWID rid, A.员工编号 no, A.时间 date, min(B.打卡时间) morningtime

  FROM 考勤表 A join 打开表 B on (A.员工编号 =B.员工编号 and A.时间=B.时间)

  WHERE <....>

   group by A.ROWID, 员工编号, 时间

  order by A.ROWID

V_COUNTER NUMBER;--声明一个number类型的变量

BEGIN

 V_VOUNTER:=0;--初始化变量值为0

 FOR row IN myCursor LOOP     --遍历游标

  UPDATE 考勤表 SET 打卡时间=row.morning WHERE ROWID = row.rid;

  V_COUNTER:=V_COUNTER+1;       --每次循环变量值+1

  IF(V_COUNTER>=1000) THEN

    COMMIT;

    V_COUNTER:=0;     --每更新1000行,V_COUNTER值为1000时候,就提交给数据库

    提交后将变量归零,继续下一个1000行更新

  END IF;

 END LOOP;

 COMMIT;

END;

二. ROWID的限制

oracle 中的ROWID是物理地址,看下面例子:

 select ROWID from Bruce_test where rownum<2;

ROWID 
------------------ ---------- 
AAABnlAAFAAAAAPAAA 
ROWID的格式如下:

数据对象编号        文件编号        块编号           行编号
OOOOOO             FFF                BBBBBB RRR

我们可以看出,从上面的rowid可以得知:
AAABnl 是数据对象编号
AAF是相关文件编号
AAAAAP是块编号
AAA 是行编号

 ROWID是表中行的唯一标识,是一个伪列.

注意ROWID有适用范围, 只能用在表:普通表,cluster table, partition table, subpartition table, index, index partitions and subpartitions(注意:不包含index-organized tables)

index-organized tables基于索引组织的表即IOT表,物理rowid是可能变化的,对于IOT表,Oracle以表的主键为基础引入UROWID(通用ROWID).

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot多线程向Oracle插入大数据可以通过使用JdbcTemplate的batchUpdate()方法和多线程来实现。下面是一个示例代码: ```java @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public void batchInsert(List<User> userList) throws InterruptedException { int batchSize = 1000; int threadCount = Runtime.getRuntime().availableProcessors(); ExecutorService executorService = Executors.newFixedThreadPool(threadCount); List<List<User>> splitList = ListUtils.splitList(userList, batchSize); CountDownLatch countDownLatch = new CountDownLatch(splitList.size()); for (List<User> list : splitList) { executorService.execute(() -> { try { batchInsert(list); } finally { countDownLatch.countDown(); } }); } countDownLatch.await(); executorService.shutdown(); } private void batchInsert(List<User> userList) { String sql = "INSERT INTO USER (ID, NAME, AGE) VALUES (USER_SEQ.NEXTVAL, ?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { User user = userList.get(i); ps.setString(1, user.getName()); ps.setInt(2, user.getAge()); } @Override public int getBatchSize() { return userList.size(); } }); } } ``` 这个示例中,我们先将数据按照固定大小分成多个批次,然后采用多线程的方式,每个线程执一个批次的插入操作。我们通过CountDownLatch来控制所有线程都执完毕后再退出程序。这样可以确保所有数据都被正确地插入到数据库中。 注意,这里的ListUtils.splitList()方法是一个自定义的分割List的工具类,具体实现可以参考Guava库中的Lists.partition()方法。另外,为了简化代码,这里省略了User实体类的定义。另外,我们在插入数据时使用了Oracle的序列生成器USER_SEQ,确保每次插入都有唯一的ID。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值