private boolean checkNeedDbForRowMode(Pipeline pipeline, EventData eventData) { // 获取数据表信息 DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, eventData.getTableId()); DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(), (DbMediaSource) dataMedia.getSource()); Table table = dbDialect.findTable(eventData.getSchemaName(), eventData.getTableName()); if (table.getColumnCount() == eventData.getColumns().size() + eventData.getKeys().size()) { return false; } else { return true; } }
@Override public void extract(DbBatch dbBatch) throws ExtractException { Assert.notNull(dbBatch); Assert.notNull(dbBatch.getRowBatch()); // 读取配置 Pipeline pipeline = getPipeline(dbBatch.getRowBatch().getIdentity().getPipelineId()); boolean mustDb = pipeline.getParameters().getSyncConsistency().isMedia(); boolean isRow = pipeline.getParameters().getSyncMode().isRow(); // 如果是行记录是必须进行数据库反查 // 读取一次配置 adjustPoolSize(pipeline.getParameters().getExtractPoolSize()); // 调整下线程池,Extractor会被池化处理 ExecutorCompletionService completionService = new ExecutorCompletionService(executor); // 进行并发提交 ExtractException exception = null; // 每个表进行处理 List<DataItem> items = new ArrayList<DataItem>(); List<Future> futures = new ArrayList<Future>(); List<EventData> eventDatas = dbBatch.getRowBatch().getDatas(); for (EventData eventData : eventDatas) { if (eventData.getEventType().isDdl()) { continue; } DataItem item = new DataItem(eventData); // 针对row模式,需要去检查一下当前是否已经包含row记录的所有字段,如果发现字段不足,则执行一次数据库查询 boolean flag = mustDb || (eventData.getSyncConsistency() != null && eventData.getSyncConsistency().isMedia()); // 增加一种case, 针对oracle erosa有时侯结果记录只有主键,没有变更字段,需要做一次反查 if (!flag && CollectionUtils.isEmpty(eventData.getUpdatedColumns())) { DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, eventData.getTableId()); if (dataMedia.getSource().getType().isOracle()) { flag |= true; eventData.setRemedy(true); // 针对这类数据,也统一视为补救的操作,可能erosa解析时反查数据库也不存在记录 } } if (isRow && !flag) { // 提前判断一次,避免进入多线程进行竞争 // 针对view视图的情况,会有后续再判断一次 flag = checkNeedDbForRowMode(pipeline, eventData); } if (flag && (eventData.getEventType().isInsert() || eventData.getEventType().isUpdate())) { // 判断是否需要反查 Future future = completionService.submit(new DatabaseExtractWorker(pipeline, item), null); // 提交进行并行查询 if (future.isDone()) { // 立即判断一次,因为使用了CallerRun可能当场跑出结果,针对有异常时快速响应,而不是等跑完所有的才抛异常 try { future.get(); } catch (InterruptedException e) { cancel(futures); // 取消完之后立马退出 throw new ExtractException(e); } catch (ExecutionException e) { cancel(futures); // 取消完之后立马退出 throw new ExtractException(e); } } futures.add(future); // 记录一下添加的任务 } items.add(item); // 按顺序添加 } // 开始处理结果 int index = 0; while (index < futures.size()) { // 循环处理发出去的所有任务 try { Future future = completionService.take(); // 它也可能被打断 future.get(); } catch (InterruptedException e) { exception = new ExtractException(e); break; // 如何一个future出现了异常,就退出 } catch (ExecutionException e) { exception = new ExtractException(e); break; // 如何一个future出现了异常,就退出 } index++; } if (index < futures.size()) { // 小于代表有错误,需要对未完成的记录进行cancel操作,对已完成的结果进行收集,做重复录入过滤记录 cancel(futures); throw exception; } else { // 全部成功分支, 构造返回结果也要保证原始的顺序 for (int i = 0; i < items.size(); i++) { DataItem item = items.get(i); if (item.filter) { // 忽略需要被过滤的数据,比如数据库反查时记录已经不存在 eventDatas.remove(item.getEventData()); } } } }
public void run() { try { MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipeline.getId())); Thread.currentThread() .setName(String.format(WORKER_NAME_FORMAT, pipeline.getId(), pipeline.getName())); // 获取数据表信息 DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, eventData.getTableId()); DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(), (DbMediaSource) dataMedia.getSource()); Table table = dbDialect.findTable(eventData.getSchemaName(), eventData.getTableName()); TableData keyTableData = buildTableData(table, eventData.getKeys()); // oracle类型特殊处理下 if (dbDialect instanceof OracleDialect) { keyTableData.columnTypes = getOraclePkTypes(table, keyTableData.columnNames); } boolean needAll = pipeline.getParameters().getSyncMode().isRow() || (eventData.getSyncMode() != null && eventData.getSyncMode().isRow()); // 增加一种case, 针对oracle erosa有时侯结果记录只有主键,没有变更字段,需要做一次反查,获取所有字段 needAll |= CollectionUtils.isEmpty(eventData.getUpdatedColumns()) && dataMedia.getSource().getType().isOracle(); List<DataMediaPair> mediaParis = ConfigHelper.findDataMediaPairByMediaId(pipeline, dataMedia.getId()); List<String> viewColumnNames = buildMaxColumnsFromColumnPairs(mediaParis, eventData.getKeys()); // TODO 后续版本测试下 // if (needAll) { // boolean needDb = checkNeedDbForRowMode(table, // viewColumnNames, eventData); // if (needAll && !needDb) {// 不需要进行反查 // item.setFilter(false); // return; // } // } // modified by ljh at 2012-11-04 // 反查数据时只反查带update=true标识的数据,因为update=false的记录可能只是进行filter需要用到的数据,不需要反查 TableData columnTableData = buildTableData(table, eventData.getUpdatedColumns(), needAll, viewColumnNames); if (columnTableData.columnNames.length == 0) { // 全主键,不需要进行反查 } else { List<String> newColumnValues = select( dbDialect, eventData.getSchemaName(), eventData.getTableName(), keyTableData, columnTableData); if (newColumnValues == null) { // miss from db // 设置为filter=true,可能存在丢数据的风险. // 比如针对源库发生主备切换,otter反查的是备库,查询不到对应的记录 // item.setFilter(true); // 针对需要自定义反查数据库的,允许忽略 // a. 自由门触发的数据,不存在时可以忽略 // b. 回环补救算法触发的数据,不存在时可以忽略 boolean needFilter = eventData.isRemedy() || pipeline.getParameters().getSkipNoRow(); item.setFilter(needFilter); // 判断主键是否有变更,如果变更了,就原样返回item int index = 0; for (EventColumn oldKey : eventData.getOldKeys()) { if (!oldKey.equals(eventData.getKeys().get(index))) { item.setFilter(false); break; } } } else { // 构造反查的返回结果 List<EventColumn> newEventColumns = new ArrayList<EventColumn>(); for (int i = 0; i < newColumnValues.size(); i++) { EventColumn column = new EventColumn(); column.setIndex(columnTableData.indexs[i]); column.setColumnName(columnTableData.columnNames[i]); column.setColumnType(columnTableData.columnTypes[i]); column.setNull(newColumnValues.get(i) == null); column.setColumnValue(newColumnValues.get(i)); column.setUpdate(true); newEventColumns.add(column); } // 处理下columns中不在反查字段内的字段列表 for (EventColumn column : eventData.getColumns()) { boolean override = false; for (EventColumn newEventColumn : newEventColumns) { if (StringUtils.equalsIgnoreCase( newEventColumn.getColumnName(), column.getColumnName())) { override = true; break; } } if (!override) { // 针对newcolumns不存在的记录进行添加 newEventColumns.add(column); } } Collections.sort(newEventColumns, new EventColumnIndexComparable()); // 重新排个序 eventData.setColumns(newEventColumns); } } } catch (InterruptedException e) { // ignore } finally { Thread.currentThread().setName(WORKER_NAME); MDC.remove(OtterConstants.splitPipelineLogFileKey); } }