数据库的binlog也有删除策略,不可能永久保存所有的binlog。如何迁移binlog已经不存在的存量数据? otter针对这种场景需求设计了自由门模块。 详见otter中的自由门说明。 自由门的原理如下: a. 基于otter系统表retl_buffer,插入特定的数据,包含需要同步的表名,pk信息。 b. otter系统感知后会根据表名和pk提取对应的数据(整行记录),和正常的增量同步一起同步到目标库。 原先需要在每一个迁移的库所在实例建立retl.retl_buffer库表(存量数据迁移控制表)。当迁移的库比较多时,在多个实例上面分别建立retl库,不利于统一控制,同时给库表元数据管理带来一定的难度。为了后续DRC的统一快捷运维和减少运维成本,我们对自由门进行集中控制(不同实例上的数据迁移由同一个retl.retl_buffer库表控制)。通过在retl_buffer表上增加channel、pipeline两个字段,区分retl.retl_buffer库表中的数据属于不同的库表。然后在SelectTask阶段对数据进行分批整理 ,每批的管道改成同步管道信息。(统一控制相对单独控制存在一个风险点:如果同步的这批存量数据在Extract阶段后和Load阶段前存在源库数据对应记录的修改,同时修改的增量binlog又比存量同步的数据同步更快,存在数据老数据覆盖新数据的风险,不过这种场景概率极小)
//如果数据来自RETL库RETL_BUFFER表,将数据分批,每批的管道改成同步管道信息
if (StringUtils.equalsIgnoreCase(RETL_BUFFER, pipeline.getPairs().get(0).getSource().getName())
&& StringUtils.equalsIgnoreCase(RETL, pipeline.getPairs().get(0).getSource().getNamespace())) {
Long lastPipeLineId = null;
Long lastChannelId = null;
for (EventData data : eventData) {
// 获取每一条数据对应的pipeline
EventColumn pipelineColumn = getMatchColumn(data.getColumns(), PIPELINE_ID);
// 获取每一条数据对应的channelID
EventColumn channelColumn = getMatchColumn(data.getColumns(), CHANNEL_ID);
if(pipelineColumn == null || channelColumn == null){
logger.warn("data from RETL.RETL_BUFFER has no PIPELINE_ID OR CHANNEL_ID,the getKeys are {}",new Object[]{data.getKeys().toArray()});
continue;
}
Long pipeLineId = Long.valueOf(pipelineColumn.getColumnValue());
Long channelId = Long.valueOf(channelColumn.getColumnValue());
if (pipeLineId == null || channelId == null) {
continue;
}
//第一条数据,不发送
if (lastPipeLineId == null && lastChannelId == null) {
lastPipeLineId = pipeLineId;
lastChannelId = channelId;
rowBatch.merge(data);
continue;
}
//数据管道或通道有变化时,每个管道号数据作为一批发送
if (pipeLineId != lastPipeLineId || channelId != lastChannelId) {
// 构造唯一标识
Identity identity = new Identity();
identity.setChannelId(lastChannelId);
identity.setPipelineId(lastPipeLineId);
identity.setProcessId(etlEventData.getProcessId());
rowBatch.setIdentity(identity);
long nextNodeId = etlEventData.getNextNid();
List pipeKeys = rowDataPipeDelegate.put(new DbBatch(rowBatch),
nextNodeId);
etlEventData.setDesc(pipeKeys);
etlEventData.setNumber((long) rowBatch.getDatas().size());
etlEventData.setFirstTime(startTime); // 使用原始数据的第一条
etlEventData.setBatchId(message.getId());
if (profiling) {
Long profilingEndTime = System.currentTimeMillis();
stageAggregationCollector.push(pipelineId, StageType.SELECT,
new AggregationItem(profilingStartTime, profilingEndTime));
}
arbitrateEventService.selectEvent().single(etlEventData);
rowBatch = new RowBatch();
}
lastPipeLineId = pipeLineId;
lastChannelId = channelId;
rowBatch.merge(data);
}
if(rowBatch!=null && rowBatch.getDatas() != null && rowBatch.getDatas().size()>0){
// 构造唯一标识
Identity identity = new Identity();
identity.setChannelId(lastChannelId);
identity.setPipelineId(lastPipeLineId);
identity.setProcessId(etlEventData.getProcessId());
rowBatch.setIdentity(identity);
long nextNodeId = etlEventData.getNextNid();
List pipeKeys = rowDataPipeDelegate.put(new DbBatch(rowBatch),
nextNodeId);
etlEventData.setDesc(pipeKeys);
etlEventData.setNumber((long) rowBatch.getDatas().size());
etlEventData.setFirstTime(startTime); // 使用原始数据的第一条
etlEventData.setBatchId(message.getId());
if (profiling) {
Long profilingEndTime = System.currentTimeMillis();
stageAggregationCollector.push(pipelineId, StageType.SELECT,
new AggregationItem(profilingStartTime, profilingEndTime));
}
arbitrateEventService.selectEvent().single(etlEventData);
}
}
// 多态, 在JAVA中是这样用的, 其实在PHP当中可以自然消除, 因为参数是动态的, 你传什么过来都可以, 不限制类型, 直接调用类的方法
abstract class Tiger {
public abstract function climb();
}
class XTiger extends Tiger {
public function climb()
jQuery.extend({
handleError: function( s, xhr, status, e ) {
// If a local callback was specified, fire it
if ( s.error ) {
s.error.call( s.context || s, xhr, status, e );
}
always 总是
rice 水稻,米饭
before 在...之前
live 生活,居住
usual 通常的
early 早的
begin 开始
month 月份
year 年
last 最后的
east 东方的
high 高的
far 远的
window 窗户
world 世界
than 比...更
最近使用mybatis.3.1.0时无意中碰到一个问题:
The errors below were detected when validating the file "mybatis-3-mapper.dtd" via the file "account-mapper.xml". In most cases these errors can be d