`
josico
  • 浏览: 44012 次
社区版块
存档分类
最新评论

用sqlldr进行数据迁移

阅读更多

公司的业务场景如下:

某一个体系需要进行重构,其中包括表的重新设计和库表数据迁移。而这一块是我负责的,经历了从Java,到DML,到PLSQL的方案转移。之所以写这么多,是想记录我自己的探索过程,如果想看使用sqlldr进行数据迁移的直接步骤,直接跳到“方案3”的结尾处。

 

方案1:用Java进行数据迁移

最先开始选择用Java,是因为之前接触存储过程不是很多,用Java来写,操作对象集合,配置2个datasource,直接查询出来了再insert进行,比较方便。因为这个不是我们这次的重点,就不详叙。简单大致说一下,用的框架是jdbcTemplate,其中插入的时候需要注意的是,采用jdbcTemplate的batchInsert的批量提交方式,减少与数据库的交互次数,提供性能。去掉索引PK等越是后,效率大概是3w条/s。但有一个表特别大有将近2kw条记录,然后领导说,这个方案不行,让我用sqlldr。方案1流产

 

方案2:用DML语句进行数据迁移

由于没用过sqlldr啊,没办法,看想想能不能用其他的方式来做,既然公司的库都是在一个局域网,那我干嘛不直接用DBLINK来做呢?话不多说,我们上代码

-- 建立 DBLINK
create public database link my_dblink connect to old_username  identified by old_password
   using '(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.8.119)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME =old_service)
)
)';

好DB_LINK之后,我们直接以如下形式进行数据插入(实验证明,这也是最高效的

insert into XXX (select colnum_a,colnum_b from old_service.YYY@my_dblink) 

不要用下面这种形式,进行创建。因为这样创建,oracle会给你自动优化,你的字段类型本来想是定义为varchar2(255)的,说不定就只有varchar2(60)了,所以表还是创建好了再进行插入(PS.虽然这种方式比上面的方式快。一个是DML一个是DDL,效率肯定是不一样的)

create table XXX as (select colnum_a,colnum_b from old_service.YYY@my_dblink)

这种确确实实是最高效的,但这个方案出现了2个问题

1.有人说,你这个没法记录日志

2.领导坚持用sqlldr

没办法,那只能用sqlldr了

PS.这里我要很愤慨的说一句,数据迁移强烈建议大家用这种DML的方式进行数据插入。有人说没日志,下面的第三种方案是有日志了,但谁TM看呢,我去。第二,领导说用sqlldr,为什么?因为他觉得sqlldr快,既然他要的是快,你就告诉他,sqlldr不可能有这种DML的形式快(sqlldr慢不在于插入慢,在于前面的准备工作慢,后面我会细说sqlldr)

坑爹的,第二个方案也这样流产了

 

方案3:用sqlldr进行数据迁移

没办法,那就老老实实用sqlldr吧,不会就去学嘛。sqlldr在输入插入方面是特别快的,比insert快几个数量级,但为什么我说sqlldr的方案比第二个方案慢呢?因为sqlldr主要负责的是把数据导进去,那数据从哪来呢?

 

这里我简单说一下sqlldr吧

首先你有一个文件叫“user.csv”,内容是这样的

1,tom,23,11-AUG-14

2,jack,25,11-AUG-25

然后你有一个表USER,字段如下 id,name,age,created_time

然后用sqlldr就可以很轻松的把你文件里的数据导到表里面去,sqlldr来导,他怎么知道1对应id,tom对应name,又或者不是“tom,23”对应name呢?

 

执行sqlldr的时候,需要一个ctl文件,我们命名这个文件叫“user.ctl”,内容如下

LOAD DATA
INFILE '/home/oracle/data/user.csv'
APPEND INTO TABLE user
FIELDS TERMINATED BY ','
TRAILING NULLCOLS 
(id,name,age,created_time)

然后我们到oracle的bin目录下,确认找得到sqlldr这个命令,然后在这个目录下执行这个命令

-- silent=feedback,header表示去掉一些回显,如果不加的话,就一直在命令行打数据“插入XXX条”,这样会降低效率的
-- direct=true最好加上,这个是不考虑索引约束啊之类的,总之加了这个,数据会快很多(大概快一倍),不加他会插入的时候考虑一些别的因素,导致插入变慢。
sqlldr new_username/new_password /home/oracle/data/user.ctl silent=feedback,header direct=true

 

PS.大家插入数据的时候,最好还是只建表,不要加索引PK之类,弄好了再加,不然会特别耗时,虽然数据导完了再加索引,本身也是很耗时的- -!不过毕竟比一边插一边检查来得好一些

执行完后,就插入完毕了,去数据库检查一下数据,然后加上你的索引,唯一,PK等,这样就算是迁移完毕了。

 

是不是看上去像童话一样美好?

BUT!理论上的东西,放到现实场景下,就会出现各种问题,我这里还是想重申一个观点就是,HelloWorld确实很重要的,但你要是觉得会写HelloWorld,就能把实际案例上,工作中的问题解决了,那就大错特错了

 

首先我们来说一下用sqlldr会出现的坑

坑1:数据哪来呢?

我们这个demo里面是2条数据,都是我们手写的,但生产上,数据2条吗?2kw还差不多,那这2kw数据怎么来。目前我所知道的比较快捷的方法是用PL/SQL工具来导出是最快的,把你需要的select语句写好,然后放到PL/SQL中去执行,选择导出为CSV文件



这样导是很快的,2kw数据也用不了10分钟,但如果这么轻轻松松就搞定了,还叫坑吗?

不要急,我们一步步发现问题,一步步来解决问题

 

坑2:导出文件有问题?

我们先看看导出来的数据是什么样子的(这里我们就先只导了4条)

首先我们看到,用这个“导出为CSV文件”这个功能,导出来的会自带一条记录--“字段名称”,显然如果我们要用sqlldr来把这个“字段名称”插进去肯定是不行的,需要我们删掉第一行

(注:sqlldr可能有参数设定,不扫描第一行,又或者说这个“导出为CSV文件”功能,可能可以不导这个字段行。这2个都有可能,但笔者没试过,如果有试过的朋友,麻烦评论一下,也好帮助大家)

 

坑3:单元数据如何分隔

接着上面的坑,不管能不能让他不出现第一行,就算是出现,也可以手动删掉第一行,但问题大头不仅仅是在这第一行的问题上折腾。大家看一下 它的规则,他是用引号进行包裹,然后以逗号分隔,那你觉得某个表的memo字段(备注信息),可不可能出现引号和冒号的情况呢?答案是,肯定有!

 

鉴于对PL/SQL的”导出为CSV文件“功能不熟,我们只能另寻他路,既然摆脱工具的束缚,就不会出现上面所述的坑,既然这样那我们就自己来写数据

 

怎么写数据?答案是跑存储过程

我们也不卖关子了,现在一次性把所有可能遇到的坑全部说出来,再上代码

 

1.确保你的分隔符不会出现在数据信息中

比如一开始我想用”{“来做分隔符,结果我发现有这样的数据:”用户备注{用户明天来取货}“。然后我想用”|||“来做分隔符,没有人用这个吧,结果发现又有这样的数据”- -|||“

总结:选分隔符的时候,尽量选少重复的,然后对各个可能出现这个分隔符的做查询,有则修改,比如把所有的”|||“改成空字符等

 

2.去掉换行符

因为sqlldr默认是一行数据为一条记录,有的字段,他有回车换行符,这样导致你得到的数据会换行,这时候sqlldr会认为是2条记录,这样肯定是会插入失败的

 

3.时间问题

时间建议大家还是将date数据转为字符串,然后读取的时候,再按指定格式的字符串来转换,不然有时候是真的会出现不识别的情况的(测试环境直接拉下来的date可以用sqlldr,生产环境拉下来的date由于编码不一样,变成中文了,导致sqlldr不识别)

然后在ctl文件中,字段后面加上 "TO_DATE(:created_time,'yyyy-mm-dd hh24:mm:ss')",当然你写出来的date,也要是这种格式的

 

4.字段数据过长

如果不指定,sqlldr有一个默认最长字符的限制,sqlldr默认字符串的最大长度为255,多于255的会舍弃,但我们有的字段,长度确确实实是多于255,那怎么解决呢?

好解决,在ctl文件中,字段后面加上”char(2000)“,具体多少根据你的实际需求来

话不多说,上代码

 

写数据文件的存储过程函数

create or replace directory MY_DIR
  as '/home/oracle/data';

declare
  file_out  Utl_File.file_type;
  log_out   Utl_File.file_type;
  file_path varchar2(100);
  file_name varchar2(100);
  log_name  varchar2(100);
  count_sum number(10);
  date_start Date;
  date_end Date;

  cursor c is (
  select
   r.id ID,
   s.id UID_ORI,
   replace(replace(r.password,CHR(10),''),CHR(13),'') TX_PASSWORD,
   s.password LOGIN_PASSWORD,
   s.username SYSTEM_NAME,
   replace(replace(r.name,CHR(10),''),CHR(13),'') NAME,
   case
     when r.gender like '%男%' then
      0
     when r.gender = 'male' then
      0
     when r.gender like '%女%' then
      1
     when r.gender = 'female' then
      1
     else
      NULL
   end GENDER,
   r.gender GENDER_ORI,
   r.status_code STATUS_CODE,
   replace(replace(s.email_validate,CHR(10),''),CHR(13),'') EMAIL,
   substr(replace(replace(s.realname,CHR(10),''),CHR(13),''),1,20) NICKNAME,
   replace(replace(r.remark,CHR(10),''),CHR(13),'') REMARK,
   r.application_source_code FROM_SYSTEM,
   decode(r.created_by, NULL, 'Admin', replace(replace(r.created_by,CHR(10),''),CHR(13),'')) CREATED_BY,
   r.created_by created_by_ORI,
   to_char(decode(r.created_timestamp, NULL, sysdate, r.created_timestamp),'yyyy-mm-dd hh24:mi:ss') CREATED_TIME,
   r.created_timestamp created_timestamp_ORI,
   decode(r.updated_by, NULL, 'Admin', replace(replace(r.updated_by,CHR(10),''),CHR(13),'')) UPDATED_BY,
   r.updated_by updated_by_ORI,
   to_char(decode(r.updated_timestamp, NULL, sysdate, r.updated_timestamp),'yyyy-mm-dd hh24:mi:ss') UPDATED_TIME,
   r.updated_timestamp updated_timestamp_ORI
    from crm_customer r, hyt_user_asso o, bbs_members s
   where o.biostime_id = to_char(r.id)
     and o.mama100_username = s.username);
begin
  file_path := 'MY_DIR';
  file_name := 'cust_customer_2.csv';
  log_name  := 'cust_customer_2.log';
  file_out  := Utl_File.fopen(file_path, file_name, 'W');
  log_out   := Utl_File.fopen(file_path, log_name, 'W');
  count_sum := 0;
  date_start := sysdate;

  Utl_File.put_line(log_out,'=====================CUST_CUSTOMER_2开始,导出与BBS_MEMBERS相关联的记录=======================');
  for my_c in c loop
    Utl_File.put_line(file_out,my_c.ID||'|||'||my_c.TX_PASSWORD||'|||'||my_c.LOGIN_PASSWORD||'|||'||my_c.SYSTEM_NAME||'|||'||my_c.NAME||'|||'||my_c.GENDER||'|||'||my_c.STATUS_CODE||'|||'||my_c.EMAIL||'|||'||my_c.NICKNAME||'|||'||my_c.REMARK||'|||'||my_c.FROM_SYSTEM||'|||'||my_c.CREATED_BY||'|||'||my_c.CREATED_TIME||'|||'||my_c.UPDATED_BY||'|||'||my_c.UPDATED_TIME);
    count_sum := count_sum + 1;
  end loop;
  date_end := sysdate;
  Utl_File.put_line(log_out,'========CUST_CUSTOMER_2结束=========导出数据:'||count_sum||'==========耗时:'||(date_end-date_start)*24*60*60||'秒');
  Utl_File.fclose(file_out);
  Utl_File.fclose(log_out);
end;

 

sqlldr所需要的ctl文件内容

LOAD DATA
INFILE '/home/oracle/data/cust_customer_2.csv'
APPEND INTO TABLE cust_customer
FIELDS TERMINATED BY '|||'
TRAILING NULLCOLS 
(ID,TX_PASSWORD,LOGIN_PASSWORD,SYSTEM_NAME,NAME,GENDER,STATUS_CODE,EMAIL,NICKNAME,REMARK char(2000),FROM_SYSTEM,CREATED_BY,CREATED_TIME "TO_DATE(:created_time,'yyyy-mm-dd hh24:mm:ss')",UPDATED_BY,UPDATED_TIME "TO_DATE(:updated_time,'yyyy-mm-dd hh24:mm:ss')")

最后切换到oracle的bin目录,执行sqlldr命令

sqlldr new_username/new_password /home/oracle/data/cust_customer_2.ctl silent=feedback,header direct=true

 

经测试,2000w数据,写出来需要20分钟,导进去仅需要3~5分钟

 

总结就是,sqlldr在进行数据导入是非常迅速的,但很多时候问题会变成,如何得到数据文件,一般都是自己写出来,如果是通过工具生成,就得考虑我上面说的那几个坑了,我把生产环境的所有问题,尽可能的给大家罗列出来,还是那句话,HelloWorld是很重要的,但如果你要是觉得你会HelloWorld,其他的就七七八八差不多,那就大错特错了。记录自己,与君共勉,加油!!!

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 46.6 KB
  • 大小: 5.3 KB
分享到:
评论

相关推荐

    sqlldr导入数据

    在数据库管理和数据迁移等场景中,它具有高效、灵活的特点,可以处理各种格式的数据文件。本篇文章将深入探讨sqlldr的使用方法以及如何通过它来批量导入数据。 首先,我们要理解sqlldr的基本结构。它通过控制文件...

    OraLoader--用sqlldr的Oracle库表数据装载工具

    2. **数据迁移与同步**:在多个数据库间进行数据迁移,或者需要定期同步数据时,OraLoader是理想的选择。 3. **数据分析与测试**:在数据分析或系统测试中,导入样本数据或模拟生产环境时,OraLoader能提高效率。 ...

    关于 Oracle 的数据导入导出及 Sql Loader (sqlldr)

    Oracle的数据导入导出和Sql Loader是数据库管理的关键组成部分,它们提供了高效、灵活的数据迁移和加载方案。理解并熟练掌握这些工具的使用,对于数据库管理员和开发人员来说至关重要,可以极大地提高工作效率,保障...

    DB2到Oracle的数据迁移

    在进行DB2到Oracle的数据迁移过程中,需要注意以下几点: - **数据类型兼容性**:确保源数据库(DB2)和目标数据库(Oracle)中的数据类型兼容。例如,DB2中的`TIMESTAMP`类型可能需要映射为Oracle中的`DATE`类型。 ...

    oracle最强大的sqlldr上传工具

    SQL*Loader通常用于大规模的数据迁移、数据库初始化或者数据仓库的构建等场景。 1. SQL*Loader的工作原理: SQL*Loader通过读取控制文件(.ctl文件)来确定如何解析输入数据文件,并将其加载到数据库中。控制文件...

    浅谈ORACLE的SQLLDR

    - **数据迁移**:常用于将外部数据迁移到Oracle数据库中,尤其是在需要从非Oracle环境迁移数据时。 - **批量数据加载**:对于大量数据的批量导入特别有效,能够提高数据处理效率。 - **数据备份与恢复**:虽然主要...

    迁移mysql数据到oracle上

    本文主要介绍如何将MySQL数据库的数据迁移到Oracle数据库上,并提供详细的步骤。 #### 一、准备工作 1. **环境准备**: - **源数据库**:确保MySQL服务器已经安装并运行正常。 - **目标数据库**:确保Oracle...

    sqlldr导入csv文档

    在企业环境中,SQL*Loader常被用于数据迁移、数据导入和日常的数据维护任务。 #### 使用案例解析:导入CSV至Oracle表 在给定的信息中,一个具体的SQL*Loader命令行示例被展示出来,用于将位于`D:\csv\`目录下的`...

    instantclient_11_2-linux(sqlldr).zip

    - 数据迁移:利用sqlldr快速将大量数据导入到Oracle数据库中,适用于数据仓库构建、系统迁移等情况。 - 系统运维:tnsping帮助排查网络问题,确保数据库连接的稳定。 - 开发测试:在没有完整数据库环境的开发或测试...

    Sybase到Oracle数据迁移.docx

    在进行数据库迁移的过程中,从Sybase到Oracle的数据迁移是一个常见的任务,涉及到多个步骤和技术细节。以下是对这个过程的详细说明: 1. **Sybase数据导出到TXT文件**: Sybase使用BCP (Bulk Copy Program) 工具将...

    oracle_Sqlldr的使用

    Oracle SQL*Loader是Oracle数据库提供的一个用于将外部数据加载到Oracle表...对于数据库管理员和数据工程师来说,掌握SQL*Loader的使用是必须的,因为它在数据迁移、数据整合、数据备份和恢复等场景中发挥着重要作用。

    关于spool 和 sqlldr 的实例代码

    这样的流程对于数据迁移、备份和分析非常有帮助。 在提供的压缩包文件`spool和sqlldr实例`中,你可能找到了一个具体的Shell脚本,该脚本演示了如何结合使用这两个工具。这个脚本可能包含了以下几个部分: 1. 初始...

    Oracle Client sqlldr和ulus.msb文件

    它能够快速地将大量数据从文本文件导入到数据库表中,适用于大批量的数据迁移、初始化或更新任务。`sqlldr`具有以下特点和功能: 1. **高速导入**:通过直接路径加载方式,`sqlldr`绕过了数据库服务器的常规处理,...

    sqlldr自动生成控制文件导入到表

    SQL*Loader(简称sqlldr)是Oracle数据库提供的一款强大的数据加载工具,用于将...通过这种方式,sqlldr不仅可以用于数据导入,还可以作为一种有效的数据迁移和备份手段,尤其适用于需要定期同步或备份大量数据的场景。

    oracle linux 11gR2 x86-64 instant client with sqplus sqlldr imp exp工具

    Oracle Linux 11gR2 x86-64 Instant Client是...SQL*Plus、SQLLoader和Data Pump工具的组合使得开发、测试和数据迁移工作更加高效和灵活。无论你是开发者还是DBA,这个工具包都能帮助你轻松地与Oracle数据库进行交互。

    spool导出与sqlldr导入

    在Oracle数据库管理中,...通过这样的方式,你可以高效地处理大量数据的导入和导出,尤其在数据迁移、备份和分析场景中,spool和sqlldr都是非常有用的工具。记得在实际操作时根据自己的需求调整参数,并确保数据安全。

    定制化客户端,仅包含sqlplus、sqlldr、exp、imp

    exp(Export)可以将数据库的部分或全部对象(如表、视图、存储过程等)导出到一个二进制文件,方便备份或迁移数据。imp(Import)则用于将这些导出文件重新导入到数据库中,从而恢复数据或在另一数据库实例中使用。...

    数据库迁移后故障诊断优化案例

    - 在进行数据迁移时,采用了批量插入的方式,即从远端数据库读取数据并批量插入到目标端数据库中。 - 经过调查发现,问题的根本原因在于数据迁移过程中存在一定的网络延迟。 - 当数据从远端传输至目标端时,由于物理...

    Mysql迁移到Oracle就是这么简单

    MySQL和Oracle是两种广泛应用的DBMS,它们各有特点,但有时需要在两者之间进行数据迁移。本篇文章将深入探讨如何从MySQL迁移到Oracle,以及在过程中可能遇到的问题和解决方案。 首先,MySQL是一款开源、免费的DBMS...

Global site tag (gtag.js) - Google Analytics