My Octopress Blog

A blogging framework for hackers.

1亿条数据模拟

在oracle中插入
使用pl/sql developer数据生成器

参考http://wenku.baidu.com/link?url=ewPnBsCVyDNPG0vJFZHHbNyLMXnKw1eM9-CFoIzY9qS4adhUCjar8DfTB1AP0qjHzMQf5FqBNPmruhJkLv1qh5euDOMuS6IuSWtgE4eIorO&qq-pf-to=pcqq.c2c

报错ORA-01653

表空间扩展失败,参考http://www.2cto.com/database/201201/116522.html 解决方法

alter tablespace USERS
add datafile '/u01/app/oracle/oradata/XE/users2.dbf' size 10240M;
报错ORA-12953

超过了允许的最大数据库大小。原因是oracle11g xe版本只支持11g大小用户空间。 解决方法,暂无。

在mongodb中插入

编写程序模拟数据,采用insert batch操作,每10000条数据一插。对于某些可能有意义的字段采用了枚举,枚举数目为10个。

报错

第一次在2363万条数据时报错

Exception in thread "main" org.springframework.dao.DataAccessResourceFailureException: Operation on server 114.212.189.143:27017 failed; nested exception is com.mongodb.MongoException$Network: Operation on server 114.212.189.143:27017 failed

第二次在7000多万条时报错。查看数据库,发现只有4000多万数据。我当时怀疑可能mongodb的insert操作是异步的,导致了大量insert堆积,最后丢失数据。 我决定再插一次,如果还不成功,就分多次插。然而这次容器崩溃了。

容器崩溃

shard1和router崩溃了,重启无效。删除容器后新建也无效。 一开始我怀疑是镜像坏掉了,于是使用袁忠良配过的另一个镜像,那个镜像需要手动启动数据库。手动启动的时候报错journal大小不足3g,无法启动数据库。于是查看,发现服务器存储空间已经耗竭。 解决方法,暂无,目前先插入5000万条数据。

远程mongodb的运行效果

未分片的运行耗时37秒,分2片的运行耗时39秒,对比oracle最好的数据是63秒。

在服务器上的mongodb分片

在服务器上的mongodb分片

建立mongodb容器
sudo docker run --name mongodbRouter -d -p 27017:27019 mongo
sudo docker run --name mongodbShard1 -d  mongo
sudo docker run --name mongodbShard2 -d  mongo
获得容器ip

分别为172.17.0.15,172.17.0.16,172.17.0.17

启动mongodb
mongos --port 27019 --configdb 172.17.0.15:27017
mongo --port 27019
sh.addShard("172.17.0.16:27017")
sh.addShard("172.17.0.17:27017")
sh.enableSharding("fitetl")
sh.shardCollection("fitetl.YJBZ_00701_B0349H245010001", {"NUM":1})

从oracle到mongodb的数据迁移

使用spoon进行数据迁移

使用etl工具进行数据迁移。关于spoon的用法,首先搭建如图的结构。表输入为oracle数据库表,mongoDB output为mongoDB数据库的输出。 image 然后分别编辑输入输出,编辑oracle数据库: image 编辑mongoDB数据库: image

报错

运行到84万多条数据的时候就报错了: image 一开始我以为是mongodb的cursor关闭了,因为看文档说mongodb的cursor默认十分钟关闭,而spoon迁移大概10分钟的时候停了。因为spoon没有改相关项的选项,所以后来打算自己写程序,然而自己写程序后,发现用不到mongodb的cursor。而且也在83万多数据的时候出问题,卡住不动了。 后来可能的原因是数据一次读取过多,因为2次出错读取的数据差不多多(83万和84万),所以下一步会尝试分多次读取数据,比如50万数据一读。 最后找到问题了,是mongodb32位存储空间不足,解决方法是在远程服务器上运行64位mongodb。迁移用时约38分钟。

应用数据库都在远程的profile

把客户端也放到远程上了。单位是秒。测到的最小值是63秒。 测试了一些数据,列代表所少条数据分一线程,行代表一次读多少数据

500 1k 2k 5k 10k 20k 50k 100k
500 79 *** *** *** *** *** *** ***
1k 67 *** *** *** *** *** ***
2k 67 *** *** *** *** ***
5k 72 63 *** *** *** ***
10k 75 66 69 66 63 *** *** ***
20k 66 65 *** ***
50k 64 68 ***
100k 64 77

具体数据: 500一线程500分一段 共耗时79秒 之前的3步各耗时0.826秒,60.961秒,16.697秒

1000一线程1000分一段 共耗时67秒 之前的3步各耗时0.825秒,53.675秒,10.948秒

2000一线程2000分一段 共耗时67秒 之前的3步各耗时1.014秒,52.836秒,11.675秒

5000一线程2000分一段 共耗时72秒 之前的3步各耗时0.8秒,59.145秒,10.829秒

5000一线程5000分一段 共耗时63秒 之前的3步各耗时0.814秒,45.701秒,16.189秒

10000一线程500分一段 总耗时75秒 之前的3步各耗时0.817秒,61.304秒,11.118秒

10000一线程1000分一段 总耗时66秒 之前的3步各耗时0.840秒,53.079秒,11.118秒

10000一线程2000分一段 总耗时69秒 之前的3步各耗时0.949秒,50.165秒,16.248秒

10000一线程5000分一段 总耗时66秒 之前的3步各耗时0.815秒,47.927秒,16.156秒

10000一线程10000分一段 共耗时63秒 之前的3步各耗时0.824秒,44.952秒,16.265秒

20000一线程2000分一段 总耗时66秒 之前的3步各耗时0.809秒,50.084秒,11.225秒

20000一线程20000分一段 总耗时65秒 之前的3步各耗时0.824秒,45.867秒,16.228秒

50000一线程2000分一段 总耗时64秒 之前的3步各耗时0.807秒,50.227秒,10.895秒

50000一线程50000分一段 共耗时68秒 之前的3步各耗时1.007秒,49.458秒,16.054秒

100000一线程2000分一段 总耗时64秒 之前的3步各耗时1.130秒,50.450秒,10.815秒

100000一线程100000分一段 共耗时77秒 之前的3步各耗时1.472秒,65.458秒,8.054秒

应用在本地数据库在远程的profile

采用插装log输出

在sql语句和报文生成的前后插入了计时语句,log编写参考了http://blog.csdn.net/seven_cm/article/details/26849821 ,这是某次操作的结果总结。 总共耗时386秒 其中超过1秒的操作 1、 对YJBZ_00701_B0349H245010001计数。由于表超过3M行,总共耗时8.630秒。 2、 查询遍历YJBZ_00701_B0349H245010001内容。每10000条数据新建一个线程查,线程上查找大约花费几百毫秒,报文生成花费几毫秒,总共300多个线程,一共花费262.266秒。 3、 将报文合并,总共花费110.731秒 三项加起来384.587秒,占总时间的99.6%

采用visualvm

没有成功。试了几次,每次中途就没响应了。 image 这是失去响应之前的截图 第一位是org.quartz.simpl.SimpleThreadPool$WorkerThread.run() 第二位是org.quartz.core.QuartzSchedulerThread.run() 第三位是org.apache.log4j.Category.callAppenders(org.apache.log4j.spi.LoggingEvent) 第四位是org.hibernate.loader.Loader.doQuery(org.hibernate.engine.SessionImplementor, org.hibernate.engine.QueryParameters, boolean) 从第五位开始数值就很小了。

在远程服务器上运行exportData

将数据库放到服务器上
事先的工作

已有一个docker容器oracle11g xe,22端口映射到32772,1521端口映射到32771,8080端口映射到32770。

putty

登录114.212.189.143 port32772

sqlplus sys/oracle as sysdba
create user east identified by east;
create user etl identified by etl;
grant dba to east;
grant dba to etl;
shutdown immediate
STARTUP MOUNT
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
ALTER DATABASE CHARACTER SET INTERNAL_USE ZHS16GBK;
SHUTDOWN IMMEDIATE
STARTUP
psftp

open 114.212.189.143 32772

put 路径\east.dmp
put 路径\etl.dmp
将应用放到服务器上
事先的工作

已有一个docker容器java8,id是fbbf… 将exportData打成jar包,取名ccc.jar

psftp
open 114.212.189.143 22
put ccc.jar
putty

登录114.212.189.143 port22

sudo docker exec -ti fbbf /bin/bash
java -jar ccc.jar
应用卡住,然后报错
org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/beans/spring-beans-2.0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.

参考http://blog.csdn.net/bluishglc/article/details/7596118 ,spring xml配置文件中指定的xsd文件读取不到了,原因是reference中不同jar包对spring-beans-2.0.xsd的多次定义。 spring在加载xsd文件时总是先试图在本地查找xsd文件(spring的jar包中已经包含了所有版本的xsd文件),如果没有找到,才会转向去URL指定的路径下载。 dwr.jar中无用的xsd定义覆盖了有用的定义,导致需要上网加载xsd文件。先前本机由于联网,该问题没有暴露,容器没有网络连接,所以报错。 解决方法,删除dwr.jar包。目前exportData正常运行,对以后可能的影响未知。

exportData使用的sql查询

com/fitech/model/etl/service/hsyh/ETLConductExportService.java:56

from EtlTaskInfo e where e.taskId= 61

表ETL_TASK_INFO

com/fitech/model/etl/service/hsyh/ETLConductExportService.java:59

select config_value from east.sys_config where config_key='need_hand_export'

表SYS_CONFIG

com/fitech/model/etl/service/hsyh/ETLConductExportService.java:68

select base_rpt_id,model_name,main_type,rpt_en_name from east.base_rpt_info where rpt_name='交易流水'

表BASE_RPT_INFO

com/fitech/model/etl/service/hsyh/ETLConductExportService.java:73

select count(0) from east.YJBZ_00701_B0349H245010001

表YJBZ_00701_B0349H245010001

com/fitech/model/etl/service/hsyh/ETLConductExportService.java:78

delete from east.YJBZ_ETL_CHECK where TASK_NAME='交易流水' and TERM='20141231'

表YJBZ_ETL_CHECK

com/fitech/model/etl/service/hsyh/ETLConductExportService.java:108

insert into east.convert_log(log_name,log_message,log_time,log_term) values (?,?,?,?)

表CONVERT_LOG

com/fitech/model/etl/service/hsyh/FitechRpt/service/impl/RptOprServiceImpl.java:248

SELECT FIELD_EN_NAME,FIELD_TYPE FROM east.BASE_FIELD_INFO FIELD_INFO WHERE FIELD_INFO.BASE_RPT_ID = 'yjbz_00701' ORDER BY FIELD_INFO.ORDER_ID ASC

表BASE_FIELD_INFO

com/fitech/model/etl/service/hsyh/FitechRpt/service/impl/RptOprServiceImpl.java:269

select count(1) from east.YJBZ_00701_B0349H245010001

表YJBZ_00701_B0349H245010001

com\fitech\model\etl\service\hsyh\FitechRpt\util\RptUtil.java:438

select HXJYLSH,ZJYLSH,BCXH,JYRQ,YXJGDM,NBJGH,JRXKZH,MXKMBH,JYSJ,JZRQ,JZSJ,JYJGMC,JYZH,JYHM,JYXTMC,DFXH,DFJGMC,DFZH,DFHM,JYJE,ZHYE,JDBZ,XZBZ,BZ,YWLX,JYLX,JYQD,JYJZMC,JYJZH,CZGYH,GYLSH,FHGYH,ZY,ZPZZL,ZPZH,FPZZL,FPZH,CBMBZ,SJC,ZHBZ,KXHBZ,CJRQ
from YJBZ_00701_B0349H245010001
where  num>=begIndex and num <+(begIndex+count)

表YJBZ_00701_B0349H245010001

如何运行应用

在sql命令行中

创建2个新用户,east和etl密码是east和etl

create user east identified by east;
create user etl identified by etl;

赋予dba权限

grant dba to east;
grant dba to etl;
在命令行中

导入数据

imp east/east@localhost/xe fromuser=east touser=east log=no file=(dmp的路径)/east.dmp
imp etl/etl@localhost/xe fromuser=etl touser=etl  log=no file=(dmp的路径)/etl.dmp
报错:ora-12899

在插入数据库的时候报错

IMP-00019: 由于 ORACLE 错误 12899 而拒绝行
IMP-00003: 遇到 ORACLE 错误 12899
ORA-12899: 列 "JACKEYJ"."JK_REGISTER"."OPNAME" 的值太大 (实际值: 21, 最大值: 20)

原因是数据库的字库问题,更换字库后解决,参考http://blog.chinaunix.net/uid-10951-id-1762312.html ,在sql命令行中输入

SHUTDOWN IMMEDIATE 
STARTUP MOUNT
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
ALTER DATABASE CHARACTER SET INTERNAL_USE ZHS16GBK;
SHUTDOWN IMMEDIATE
STARTUP

重新插入即可

在fitetl程序中

修改com.fitech.model.etl. service.hsyh.FitechRpt.etl.sysLog.properties 修改db_url=east/east@xe 修改server_ip=localhost 修改server_port=1521

修改applicationContext.xml image image 共有2段,将192.168.0.89:1521:orcl修改为localhost:1521:xe

修改com.fitech.model.etl.service.hsyh.ExportData.java 此为主方法, 修改任务id,修改为vo.setTaskId(61); 修改日期,修改为vo.setTaskTerm(“2014-12-31”);

运行后无报文生成

原因是base_rpt_info表中有一行多余数据。打开east.base_rpt_info这张表,找到rpt_name为交易流水的行,有2行,把其中base_rpt_id为yjbz_00701的那行,也就是上面一行,删了。

运行结果

成功,大约19分钟。

应用执行流程

1、\fitetl\src\com\fitech\model\etl\service\hsyh\ExportData.java image 程序入口,ETLConductExportService处理主要内容。

2、\fitetl\src\com\fitech\model\etl\service\hsyh\ETLConductExportService.java image 主要功能在方法writerUploadFile中实现,由于采用oracle数据库,调用createRptFile生成报文。

3、\fitetl\src\com\fitech\model\etl\service\hsyh\FitechRpt\service\impl\RptOprServiceImpl.java image 全部报文,调用方法creRptFileAll

image 调用方法creRpfFileSgl

image image image image 每10000行创建一个新的线程,线程RptExpOprThread用来生成报文。

4、\fitech\model\etl\service\hsyh\FitechRpt\util\RptExpOprThread.java image 调用方法createSplitRpt

5、\fitetl\src\com\fitech\model\etl\service\hsyh\FitechRpt\util\RptUtil.java image 调用方法createRptFile

image image 生成报文文件