论坛首页 Java企业应用论坛

向大数据数据库中插入值时,还要判断插入是否重复,然后插入。如何提高效率

浏览 77098 次
精华帖 (8) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (8)
作者 正文
   发表时间:2008-09-14  
我觉得这样的数据导入处理问题,没有必要自己写程序还要控制异常的;
最好用ETL,画好流程图,然后直接调用bat或者sh脚本,啥事都不用管,而且还能记录Log;
我用的是Kettle,最后的一步不是insert也不是update,而是insert/update;
我没搞过楼主说的那么大的数据量,我的就是几千条,但每条涉及16个表的插入和更新,速度还满意。


看了这么多回复,我比较看好 抛出异常的爱 和 wolfbrood提到的做法:
1) insert into temp table
2) 在temp table增加一个flag,并设置初始值: isRepeated=0
3) 然后select并把已经存在的标记改为isRepeated=1
4) 把isRepeated=0的记录insert正式表
5) 删除temp table中的isRepeated=1的记录

当然 也可以像tommy402 说的,可以借助于内存表或者缓存
1)insert之前,把已经存在的记录的关键字都提取到内存表中
2)
insert into a
select * from b
where b.id not exists (select 'x' from b where a.id = b.id)
0 请登录后投票
   发表时间:2008-09-14  
cuiyi.crazy 写道
我觉得这样的数据导入处理问题,没有必要自己写程序还要控制异常的;
最好用ETL,画好流程图,然后直接调用bat或者sh脚本,啥事都不用管,而且还能记录Log;
我用的是Kettle,最后的一步不是insert也不是update,而是insert/update;
我没搞过楼主说的那么大的数据量,我的就是几千条,但每条涉及16个表的插入和更新,速度还满意。


看了这么多回复,我比较看好 抛出异常的爱 和 wolfbrood提到的做法:
1) insert into temp table
2) 在temp table增加一个flag,并设置初始值: isRepeated=0
3) 然后select并把已经存在的标记改为isRepeated=1
4) 把isRepeated=0的记录insert正式表
5) 删除temp table中的isRepeated=1的记录

当然 也可以像tommy402 说的,可以借助于内存表或者缓存
1)insert之前,把已经存在的记录的关键字都提取到内存表中
2)
insert into a
select * from b
where b.id not exists (select 'x' from b where a.id = b.id)

目前我是采用了捕获异常,唯一索引的方案。
但好久都觉得ETl才更快,我知道ETL可以做很多事情,以前面试一家搞BI的,所以对ETL也了解过,
我用过微软的SSIS,指数初级阶段,对于更复杂的数据转换及其他控制我还不会。可目前恰恰正是我需要的。
我不是拿方案的人,只是建议性的私下摸索,最后还得上头说了算。不过还是学到了很多
0 请登录后投票
   发表时间:2008-09-14  
先说明我不是DBA,所以给的方案可能不一定符合数据库设计的美观。
我觉得如果有重复,那么重复的字段应该与业务有关
完全可以根据业务字段分区,如AB开头的数据放在Test_AB中,或者是用类HashCode的算法进行分类,然后估算规模,完全可以将一张表的数据控制在一个理想的范围。
如果数据增长超出预期,那么可以考虑迁移,重做数据库。(细节不讨论)
如果是我做的话,加个Lucene分段索引(只对指定字段索引),多了不好说,做个亿级的,速度应该没有大的问题。(如果再多,加几台机器分区处理了。)

其实最根本的东西,还是你的规模估算。
0 请登录后投票
   发表时间:2008-09-16  
	/**
	 * 分解采集的数据集,到两个参数组中去 这里要根据主表的ORGIN_ID去进行分析判断
	 * 
	 * @throws TaskAppConfigException
	 */
	private void partitionInsertAndUpdateList(Mapping mapping,
			List<FieldMapping> fieldMappings, List<Map> insertDatas,
			List<Map> updateDatas, List<Map> datas)
			throws TaskAppConfigException {
		LOG.info(new StringBuffer("★ 开始分析采集到的原始数据. . ."));
		long startTimes = System.currentTimeMillis();

		insertDatas.clear();
		updateDatas.clear();

		// TODO 这里fieldMappings.get(0)是个很大的bad smell
		String aliasA = mapping.getFieldMappingFieldAAlias();
		Table tableA = mapping.getTableByAlias(aliasA);
		String aliasB = fieldMappings.get(0).getAlias(
				FieldMapping.ATTR_KEY_FIELD_B);
		Table tableB = mapping.getTableByAlias(aliasB);

		int batchNum = 50;
		int startIndex = 1;
		DataPage batchPage = new DataPage(startIndex, datas.size(), batchNum);

		StringBuffer checkUniqueSqlA = new StringBuffer();
		checkUniqueSqlA.append("select nvl2(b.").append(
				tableB.getExtSysIdField().getName()).append(
				",'1','0') as IS$EXIST from table(str2varlist_order('");

		StringBuffer checkUniqueSqlB = new StringBuffer();

		Object extSysIdFieldValue = null;
		String extSysIdFieldName = aliasA + "$"
				+ tableA.getPrimaryKeyField().getName();
		Field extSysIdField = tableB.getExtSysIdField();
		Map dataRow = null;

		for (; batchPage.getPageIndex() <= batchPage.getTotalPages(); batchPage
				.setPageIndex(batchPage.getPageIndex() + 1)) {

			checkUniqueSqlB = new StringBuffer();

			for (int i = (int) batchPage.getStart(); i <= batchPage.getEnd(); i++) {
				dataRow = datas.get(i - 1);
				// 这里要做数据转换
				extSysIdFieldValue = dataRow.get(extSysIdFieldName);
				if (Field.FIELD_TYPE_NAME_STRING
						.equals(extSysIdField.getType())) {
					String tempString = null;
					if (extSysIdFieldValue instanceof Number) {
						tempString = ((Number) extSysIdFieldValue).toString();
						extSysIdFieldValue = tempString;
					}
				} else if (Field.FIELD_TYPE_NAME_NUMERIC.equals(extSysIdField
						.getType())) {
					if (extSysIdFieldValue instanceof String) {
						extSysIdFieldValue = Long
								.parseLong((String) extSysIdFieldValue);
					}
				}
				checkUniqueSqlB.append(extSysIdFieldValue).append(",");
			}

			checkUniqueSqlB.deleteCharAt(checkUniqueSqlB.length() - 1);
			checkUniqueSqlB.append("')) a, ").append(tableB.getName()).append(
					" b ");
			checkUniqueSqlB.append(" where a.var = b.").append(
					tableB.getExtSysIdField().getName()).append("(+)");

			if (tableB.getExtSysTypeField() != null) {
				checkUniqueSqlB.append(" and b.").append(
						tableB.getExtSysTypeField().getName()).append("(+)=")
						.append(tableA.getExtSysTypeValue());
			}

			String sql = checkUniqueSqlA.toString()
					+ checkUniqueSqlB.toString();
			List<Map> results = this.queryForList(sql);

			int i = (int) batchPage.getStart();
			for (Map aData : results) {
				String isExist = (String) aData.get("IS$EXIST");
				if (isExist.equals("0")) {
					insertDatas.add(datas.get(i - 1));
				} else if (isExist.equals("1")) {
					updateDatas.add(datas.get(i - 1));
				}
				i++;
			}
		}
		// mannul -DtaskID=ftrade$boss$task -DtaskType=DATE_RANGE
		LOG.info("★ 已经采集的原始数据分析完毕.【待UPDATE记录共: " + updateDatas.size() + " 条】"
				+ "【待INSERT记录共: " + insertDatas.size() + " 条】 【用时"
				+ (System.currentTimeMillis() - startTimes) + "毫秒】");
	}

 

0 请登录后投票
   发表时间:2008-09-16  
wl95421 写道
先说明我不是DBA,所以给的方案可能不一定符合数据库设计的美观。
我觉得如果有重复,那么重复的字段应该与业务有关
完全可以根据业务字段分区,如AB开头的数据放在Test_AB中,或者是用类HashCode的算法进行分类,然后估算规模,完全可以将一张表的数据控制在一个理想的范围。
如果数据增长超出预期,那么可以考虑迁移,重做数据库。(细节不讨论)
如果是我做的话,加个Lucene分段索引(只对指定字段索引),多了不好说,做个亿级的,速度应该没有大的问题。(如果再多,加几台机器分区处理了。)

其实最根本的东西,还是你的规模估算。



有这么复杂吗?这是一个简单的数学问题而不是一个复杂的工程问题
0 请登录后投票
   发表时间:2008-09-16  
楼上这么多讨论有些已经很接近正确的解决方案了:
1。首先导入数据需要经过过滤处理(数据少就DB外处理,数据多就临时表,这点异常的经验是对的)
2。唯一键是必要的,异常处理也是要的
0 请登录后投票
   发表时间:2008-09-16  
cuiyi.crazy 写道
我觉得这样的数据导入处理问题,没有必要自己写程序还要控制异常的;
最好用ETL,画好流程图,然后直接调用bat或者sh脚本,啥事都不用管,而且还能记录Log;
我用的是Kettle,最后的一步不是insert也不是update,而是insert/update;
我没搞过楼主说的那么大的数据量,我的就是几千条,但每条涉及16个表的插入和更新,速度还满意。


看了这么多回复,我比较看好 抛出异常的爱 和 wolfbrood提到的做法:
1) insert into temp table
2) 在temp table增加一个flag,并设置初始值: isRepeated=0
3) 然后select并把已经存在的标记改为isRepeated=1
4) 把isRepeated=0的记录insert正式表
5) 删除temp table中的isRepeated=1的记录

当然 也可以像tommy402 说的,可以借助于内存表或者缓存
1)insert之前,把已经存在的记录的关键字都提取到内存表中
2)
insert into a
select * from b
where b.id not exists (select 'x' from b where a.id = b.id)


如果是实时的这样还不如直接select count(*)  然后 insert 快

如果不实时怎么保证数据的一实时性,用户insert 以后,马上可以看到
0 请登录后投票
   发表时间:2008-09-16  
方法2:写trigger,在插入之前做一个查询,如果有就抛弃,没有就插入数据


这个方法比较好,很实用
0 请登录后投票
   发表时间:2008-09-16  
几十万的记录不是很大啊,你可以在你的客户端实现比较啊!
0 请登录后投票
   发表时间:2008-09-16  
tou3921 写道
cuiyi.crazy 写道
我觉得这样的数据导入处理问题,没有必要自己写程序还要控制异常的;
最好用ETL,画好流程图,然后直接调用bat或者sh脚本,啥事都不用管,而且还能记录Log;
我用的是Kettle,最后的一步不是insert也不是update,而是insert/update;
我没搞过楼主说的那么大的数据量,我的就是几千条,但每条涉及16个表的插入和更新,速度还满意。


看了这么多回复,我比较看好 抛出异常的爱 和 wolfbrood提到的做法:
1) insert into temp table
2) 在temp table增加一个flag,并设置初始值: isRepeated=0
3) 然后select并把已经存在的标记改为isRepeated=1
4) 把isRepeated=0的记录insert正式表
5) 删除temp table中的isRepeated=1的记录

当然 也可以像tommy402 说的,可以借助于内存表或者缓存
1)insert之前,把已经存在的记录的关键字都提取到内存表中
2)
insert into a
select * from b
where b.id not exists (select 'x' from b where a.id = b.id)

目前我是采用了捕获异常,唯一索引的方案。
但好久都觉得ETl才更快,我知道ETL可以做很多事情,以前面试一家搞BI的,所以对ETL也了解过,
我用过微软的SSIS,指数初级阶段,对于更复杂的数据转换及其他控制我还不会。可目前恰恰正是我需要的。
我不是拿方案的人,只是建议性的私下摸索,最后还得上头说了算。不过还是学到了很多



删除temp表的记录是否有隐患,删除的记录是否被真正删除掉,是否还要占空间,是否要定期做优化。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics