fixes issue #4388 , support mariadb 10.x log_bin_compress

This commit is contained in:
jianghang.loujh
2023-09-01 13:42:49 +08:00
parent 252ca0279f
commit f13cc4ee9f
23 changed files with 673 additions and 343 deletions

View File

@@ -114,17 +114,26 @@ public class LocalBinLogConnection implements ErosaConnection {
continue;
}
checkServerId(event);
if (!func.sink(event)) {
needContinue = false;
break;
List<LogEvent> iterateEvents = decoder.processIterateDecode(event, context);
if (!iterateEvents.isEmpty()) {
// 处理compress event
for(LogEvent itEvent : iterateEvents) {
if (!func.sink(event)) {
needContinue = false;
break;
}
}
} else {
if (!func.sink(event)) {
needContinue = false;
break;
}
}
}
fetcher.close(); // 关闭上一个文件
parserFinish(current.getName());
if (needContinue) {// 读取下一个
File nextFile;
if (needWait) {
nextFile = binlogs.waitForNextFile(current);

View File

@@ -213,8 +213,18 @@ public class MysqlConnection implements ErosaConnection {
throw new CanalParseException("parse failed");
}
if (!func.sink(event)) {
break;
List<LogEvent> iterateEvents = decoder.processIterateDecode(event, context);
if (!iterateEvents.isEmpty()) {
// 处理compress event
for(LogEvent itEvent : iterateEvents) {
if (!func.sink(event)) {
break;
}
}
} else {
if (!func.sink(event)) {
break;
}
}
}
}

View File

@@ -1,11 +1,14 @@
package com.alibaba.otter.canal.parse.inbound.mysql;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
import org.apache.commons.compress.utils.Lists;
import com.alibaba.otter.canal.common.AbstractCanalLifeCycle;
import com.alibaba.otter.canal.common.utils.NamedThreadFactory;
import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet;
@@ -16,27 +19,12 @@ import com.alibaba.otter.canal.parse.inbound.MultiStageCoprocessor;
import com.alibaba.otter.canal.parse.inbound.TableMeta;
import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvert;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.lmax.disruptor.BatchEventProcessor;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.ExceptionHandler;
import com.lmax.disruptor.InsufficientCapacityException;
import com.lmax.disruptor.LifecycleAware;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.Sequence;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.*;
import com.taobao.tddl.dbsync.binlog.LogBuffer;
import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent;
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.FormatDescriptionLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.*;
/**
* 针对解析器提供一个多阶段协同的处理
@@ -271,48 +259,71 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
}
int eventType = logEvent.getHeader().getType();
TableMeta tableMeta = null;
boolean needDmlParse = false;
switch (eventType) {
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
if (!filterDmlInsert) {
tableMeta = logEventConvert.parseRowsEventForTableMeta((WriteRowsLogEvent) logEvent);
needDmlParse = true;
}
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
if (!filterDmlUpdate) {
tableMeta = logEventConvert.parseRowsEventForTableMeta((UpdateRowsLogEvent) logEvent);
needDmlParse = true;
}
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
if (!filterDmlDelete) {
tableMeta = logEventConvert.parseRowsEventForTableMeta((DeleteRowsLogEvent) logEvent);
needDmlParse = true;
}
break;
case LogEvent.ROWS_QUERY_LOG_EVENT:
needDmlParse = true;
break;
default:
CanalEntry.Entry entry = logEventConvert.parse(event.getEvent(), false);
event.setEntry(entry);
}
boolean needIterate = false;
// 记录一下DML的表结构
event.setNeedDmlParse(needDmlParse);
event.setTable(tableMeta);
if (eventType == LogEvent.TRANSACTION_PAYLOAD_EVENT) {
// https://github.com/alibaba/canal/issues/4388
List<LogEvent> deLogEvents = decoder.processIterateDecode(logEvent, context);
List<TableMeta> tableMetas = Lists.newArrayList();
event.setNeedIterate(true);
for (LogEvent deLogEvent : deLogEvents) {
TableMeta table = processEvent(deLogEvent, event);
tableMetas.add(table);
}
event.setIterateEvents(deLogEvents);
event.setIterateTables(tableMetas);
} else {
TableMeta table = processEvent(logEvent, event);
event.setTable(table);
}
} catch (Throwable e) {
exception = new CanalParseException(e);
throw exception;
}
}
private TableMeta processEvent(LogEvent logEvent, MessageEvent event) {
TableMeta tableMeta = null;
boolean needDmlParse = false;
int eventType = logEvent.getHeader().getType();
switch (eventType) {
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
if (!filterDmlInsert) {
tableMeta = logEventConvert.parseRowsEventForTableMeta((WriteRowsLogEvent) logEvent);
needDmlParse = true;
}
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
if (!filterDmlUpdate) {
tableMeta = logEventConvert.parseRowsEventForTableMeta((UpdateRowsLogEvent) logEvent);
needDmlParse = true;
}
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
if (!filterDmlDelete) {
tableMeta = logEventConvert.parseRowsEventForTableMeta((DeleteRowsLogEvent) logEvent);
needDmlParse = true;
}
break;
case LogEvent.ROWS_QUERY_LOG_EVENT:
needDmlParse = true;
break;
default:
CanalEntry.Entry entry = logEventConvert.parse(event.getEvent(), false);
event.setEntry(entry);
}
// 记录一下DML的表结构
if (needDmlParse && !event.isNeedDmlParse()) {
event.setNeedDmlParse(true);
}
return tableMeta;
}
@Override
public void onStart() {
@@ -330,18 +341,21 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
public void onEvent(MessageEvent event) throws Exception {
try {
if (event.isNeedDmlParse()) {
int eventType = event.getEvent().getHeader().getType();
CanalEntry.Entry entry = null;
switch (eventType) {
case LogEvent.ROWS_QUERY_LOG_EVENT:
entry = logEventConvert.parse(event.getEvent(), false);
break;
default:
// 单独解析dml事件
entry = logEventConvert.parseRowsEvent((RowsLogEvent) event.getEvent(), event.getTable());
if (event.isNeedIterate()) {
// compress binlog
List<CanalEntry.Entry> entrys = Lists.newArrayList();
for (int index = 0; index < event.getIterateEvents().size(); index++) {
CanalEntry.Entry entry = processEvent(event.getIterateEvents().get(index),
event.getIterateTables().get(index));
if (entry != null) {
entrys.add(entry);
}
}
event.setIterateEntrys(entrys);
} else {
CanalEntry.Entry entry = processEvent(event.getEvent(), event.getTable());
event.setEntry(entry);
}
event.setEntry(entry);
}
} catch (Throwable e) {
exception = new CanalParseException(e);
@@ -349,6 +363,29 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
}
}
private CanalEntry.Entry processEvent(LogEvent logEvent, TableMeta table) {
int eventType = logEvent.getHeader().getType();
CanalEntry.Entry entry = null;
switch (eventType) {
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
// 单独解析dml事件
entry = logEventConvert.parseRowsEvent((RowsLogEvent) logEvent, table);
break;
default:
// 如果出现compress binlog,会出现其他的event type类型
entry = logEventConvert.parse(logEvent, false);
break;
}
return entry;
}
@Override
public void onStart() {
@@ -364,8 +401,15 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
public void onEvent(MessageEvent event, long sequence, boolean endOfBatch) throws Exception {
try {
if (event.getEntry() != null) {
transactionBuffer.add(event.getEntry());
if (event.isNeedIterate()) {
// compress binlog
for (CanalEntry.Entry entry : event.getIterateEntrys()) {
transactionBuffer.add(entry);
}
} else {
if (event.getEntry() != null) {
transactionBuffer.add(event.getEntry());
}
}
LogEvent logEvent = event.getEvent();
@@ -380,7 +424,11 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
event.setEvent(null);
event.setTable(null);
event.setEntry(null);
// clear compress binlog events
event.setNeedDmlParse(false);
event.setIterateEntrys(null);
event.setIterateTables(null);
event.setIterateEvents(null);
} catch (Throwable e) {
exception = new CanalParseException(e);
throw exception;
@@ -405,6 +453,11 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
private boolean needDmlParse = false;
private TableMeta table;
private LogEvent event;
private boolean needIterate = false;
// compress binlog
private List<LogEvent> iterateEvents;
private List<TableMeta> iterateTables;
private List<CanalEntry.Entry> iterateEntrys;
public LogBuffer getBuffer() {
return buffer;
@@ -446,6 +499,37 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
this.table = table;
}
public boolean isNeedIterate() {
return needIterate;
}
public void setNeedIterate(boolean needIterate) {
this.needIterate = needIterate;
}
public List<LogEvent> getIterateEvents() {
return iterateEvents;
}
public List<TableMeta> getIterateTables() {
return iterateTables;
}
public void setIterateEvents(List<LogEvent> iterateEvents) {
this.iterateEvents = iterateEvents;
}
public void setIterateTables(List<TableMeta> iterateTables) {
this.iterateTables = iterateTables;
}
public List<CanalEntry.Entry> getIterateEntrys() {
return iterateEntrys;
}
public void setIterateEntrys(List<CanalEntry.Entry> iterateEntrys) {
this.iterateEntrys = iterateEntrys;
}
}
static class SimpleFatalExceptionHandler implements ExceptionHandler {

View File

@@ -32,19 +32,10 @@ import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.DirectLogFetcher;
import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent;
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.FormatDescriptionLogEvent;
import com.taobao.tddl.dbsync.binlog.event.QueryLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RotateLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer;
import com.taobao.tddl.dbsync.binlog.event.RowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsQueryLogEvent;
import com.taobao.tddl.dbsync.binlog.event.TableMapLogEvent;
import com.taobao.tddl.dbsync.binlog.event.*;
import com.taobao.tddl.dbsync.binlog.event.TableMapLogEvent.ColumnInfo;
import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.XidLogEvent;
import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent;
import com.taobao.tddl.dbsync.binlog.event.mariadb.BinlogCheckPointLogEvent;
@Ignore
public class DirectLogFetcherTest {
@@ -63,7 +54,7 @@ public class DirectLogFetcherTest {
updateSettings(connector);
loadBinlogChecksum(connector);
sendRegisterSlave(connector, 3);
sendBinlogDump(connector, "mysql-bin.000001", 4L, 3);
sendBinlogDump(connector, "mysql-bin.000002", 4L, 3);
fetcher.start(connector.getChannel());
@@ -78,45 +69,9 @@ public class DirectLogFetcherTest {
if (event == null) {
throw new RuntimeException("parse failed");
}
int eventType = event.getHeader().getType();
switch (eventType) {
case LogEvent.ROTATE_EVENT:
// binlogFileName = ((RotateLogEvent)
// event).getFilename();
System.out.println(((RotateLogEvent) event).getFilename());
break;
case LogEvent.TABLE_MAP_EVENT:
parseTableMapEvent((TableMapLogEvent) event);
break;
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
parseRowsEvent((WriteRowsLogEvent) event);
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
parseRowsEvent((UpdateRowsLogEvent) event);
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
parseRowsEvent((DeleteRowsLogEvent) event);
break;
case LogEvent.QUERY_EVENT:
parseQueryEvent((QueryLogEvent) event);
break;
case LogEvent.ROWS_QUERY_LOG_EVENT:
parseRowsQueryEvent((RowsQueryLogEvent) event);
break;
case LogEvent.ANNOTATE_ROWS_EVENT:
break;
case LogEvent.XID_EVENT:
break;
default:
break;
}
processEvent(event, decoder, context);
}
} catch (Exception e) {
} catch (Throwable e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} finally {
@@ -129,6 +84,56 @@ public class DirectLogFetcherTest {
}
private void processEvent(LogEvent event, LogDecoder decoder, LogContext context) throws Throwable {
int eventType = event.getHeader().getType();
switch (eventType) {
case LogEvent.ROTATE_EVENT:
// binlogFileName = ((RotateLogEvent)
// event).getFilename();
System.out.println("RotateLogEvent : " + ((RotateLogEvent) event).getFilename());
break;
case LogEvent.BINLOG_CHECKPOINT_EVENT:
// binlogFileName = ((BinlogCheckPointLogEvent)
// event).getFilename();
System.out.println("BinlogCheckPointLogEvent : " + ((BinlogCheckPointLogEvent) event).getFilename());
break;
case LogEvent.TABLE_MAP_EVENT:
parseTableMapEvent((TableMapLogEvent) event);
break;
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
parseRowsEvent((WriteRowsLogEvent) event);
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
parseRowsEvent((UpdateRowsLogEvent) event);
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
parseRowsEvent((DeleteRowsLogEvent) event);
break;
case LogEvent.QUERY_EVENT:
parseQueryEvent((QueryLogEvent) event);
break;
case LogEvent.ROWS_QUERY_LOG_EVENT:
parseRowsQueryEvent((RowsQueryLogEvent) event);
break;
case LogEvent.ANNOTATE_ROWS_EVENT:
break;
case LogEvent.XID_EVENT:
break;
case LogEvent.TRANSACTION_PAYLOAD_EVENT:
List<LogEvent> events = decoder.processIterateDecode(event, context);
for (LogEvent deEvent : events) {
processEvent(deEvent, decoder, context);
}
break;
default:
break;
}
}
private void sendRegisterSlave(MysqlConnector connector, int slaveId) throws IOException {
RegisterSlaveCommandPacket cmd = new RegisterSlaveCommandPacket();
cmd.reportHost = connector.getAddress().getAddress().getHostAddress();

View File

@@ -22,12 +22,12 @@ public class MysqlBinlogDumpPerformanceTest {
public static void main(String args[]) {
final MysqlEventParser controller = new MysqlEventParser();
final EntryPosition startPosition = new EntryPosition("mysql-bin.000007", 89796293L, 100L);
final EntryPosition startPosition = new EntryPosition("binlog.000002", 4L, 100L);
controller.setConnectionCharset("UTF-8");
controller.setSlaveId(3344L);
controller.setDetectingEnable(false);
controller.setFilterQueryDml(true);
controller.setMasterInfo(new AuthenticationInfo(new InetSocketAddress("100.81.154.142", 3306), "canal", "canal"));
controller.setMasterInfo(new AuthenticationInfo(new InetSocketAddress("127.0.0.1", 3306), "canal", "canal"));
controller.setMasterPosition(startPosition);
controller.setEnableTsdb(false);
controller.setDestination("example");

View File

@@ -5,10 +5,13 @@ import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.BitSet;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Ignore;
import com.alibaba.otter.canal.parse.driver.mysql.MysqlConnector;
import com.alibaba.otter.canal.parse.driver.mysql.MysqlUpdateExecutor;
import com.alibaba.otter.canal.parse.driver.mysql.packets.HeaderPacket;
@@ -19,14 +22,8 @@ import com.taobao.tddl.dbsync.binlog.LogBuffer;
import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent;
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer;
import com.taobao.tddl.dbsync.binlog.event.RowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.TableMapLogEvent;
import com.taobao.tddl.dbsync.binlog.event.*;
import com.taobao.tddl.dbsync.binlog.event.TableMapLogEvent.ColumnInfo;
import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
import org.junit.Ignore;
@Ignore
public class MysqlBinlogParsePerformanceTest {
@@ -44,7 +41,7 @@ public class MysqlBinlogParsePerformanceTest {
Thread thread = new Thread(() -> {
try {
consumer(buffer);
} catch (IOException | InterruptedException e) {
} catch (Throwable e) {
e.printStackTrace();
}
});
@@ -59,7 +56,7 @@ public class MysqlBinlogParsePerformanceTest {
}
}
public static void consumer(BlockingQueue<LogBuffer> buffer) throws IOException, InterruptedException {
public static void consumer(BlockingQueue<LogBuffer> buffer) throws Throwable {
LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT);
LogContext context = new LogContext();
@@ -70,33 +67,7 @@ public class MysqlBinlogParsePerformanceTest {
while (true) {
LogEvent event = null;
event = decoder.decode(buffer.take(), context);
int eventType = event.getHeader().getType();
switch (eventType) {
case LogEvent.ROTATE_EVENT:
break;
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
parseRowsEvent((WriteRowsLogEvent) event, sum);
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
parseRowsEvent((UpdateRowsLogEvent) event, sum);
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
parseRowsEvent((DeleteRowsLogEvent) event, sum);
break;
case LogEvent.XID_EVENT:
sum.incrementAndGet();
break;
case LogEvent.QUERY_EVENT:
sum.incrementAndGet();
break;
default:
break;
}
processEvent(event, decoder, context, sum);
long current = sum.get();
if (current - last >= 100000) {
end = System.currentTimeMillis();
@@ -108,6 +79,41 @@ public class MysqlBinlogParsePerformanceTest {
}
}
private static void processEvent(LogEvent event, LogDecoder decoder, LogContext context, AtomicLong sum) throws Throwable {
int eventType = event.getHeader().getType();
switch (eventType) {
case LogEvent.ROTATE_EVENT:
break;
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
parseRowsEvent((WriteRowsLogEvent) event, sum);
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.PARTIAL_UPDATE_ROWS_EVENT:
case LogEvent.UPDATE_ROWS_EVENT:
parseRowsEvent((UpdateRowsLogEvent) event, sum);
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
parseRowsEvent((DeleteRowsLogEvent) event, sum);
break;
case LogEvent.XID_EVENT:
sum.incrementAndGet();
break;
case LogEvent.QUERY_EVENT:
sum.incrementAndGet();
break;
case LogEvent.TRANSACTION_PAYLOAD_EVENT:
List<LogEvent> events = decoder.processIterateDecode(event, context);
for (LogEvent deEvent : events) {
processEvent(deEvent, decoder, context, sum);
}
break;
default:
break;
}
}
private static void sendBinlogDump(MysqlConnector connector, String binlogfilename, Long binlogPosition, int slaveId)
throws IOException {
BinlogDumpCommandPacket binlogDumpCmd = new BinlogDumpCommandPacket();