mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
rdb 批量并行提交
This commit is contained in:
@@ -23,11 +23,11 @@ canal.conf:
|
|||||||
# url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
|
# url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true
|
||||||
# username: root
|
# username: root
|
||||||
# password: 121212
|
# password: 121212
|
||||||
# canalInstances:
|
canalInstances:
|
||||||
# - instance: example
|
- instance: example
|
||||||
# groups:
|
groups:
|
||||||
# - outAdapters:
|
- outAdapters:
|
||||||
# - name: logger
|
- name: logger
|
||||||
# - name: rdb
|
# - name: rdb
|
||||||
# key: oracle1
|
# key: oracle1
|
||||||
# properties:
|
# properties:
|
||||||
@@ -35,6 +35,7 @@ canal.conf:
|
|||||||
# jdbc.url: jdbc:oracle:thin:@localhost:49161:XE
|
# jdbc.url: jdbc:oracle:thin:@localhost:49161:XE
|
||||||
# jdbc.username: mytest
|
# jdbc.username: mytest
|
||||||
# jdbc.password: m121212
|
# jdbc.password: m121212
|
||||||
|
# threads: 5
|
||||||
# - name: rdb
|
# - name: rdb
|
||||||
# key: postgres1
|
# key: postgres1
|
||||||
# properties:
|
# properties:
|
||||||
@@ -42,6 +43,7 @@ canal.conf:
|
|||||||
# jdbc.url: jdbc:postgresql://localhost:5432/postgres
|
# jdbc.url: jdbc:postgresql://localhost:5432/postgres
|
||||||
# jdbc.username: postgres
|
# jdbc.username: postgres
|
||||||
# jdbc.password: 121212
|
# jdbc.password: 121212
|
||||||
|
# threads: 5
|
||||||
# - name: hbase
|
# - name: hbase
|
||||||
# properties:
|
# properties:
|
||||||
# hbase.zookeeper.quorum: 127.0.0.1
|
# hbase.zookeeper.quorum: 127.0.0.1
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class RdbAdapter implements OuterAdapter {
|
|||||||
dataSource.setPassword(properties.get("jdbc.password"));
|
dataSource.setPassword(properties.get("jdbc.password"));
|
||||||
dataSource.setInitialSize(1);
|
dataSource.setInitialSize(1);
|
||||||
dataSource.setMinIdle(1);
|
dataSource.setMinIdle(1);
|
||||||
dataSource.setMaxActive(2);
|
dataSource.setMaxActive(20);
|
||||||
dataSource.setMaxWait(60000);
|
dataSource.setMaxWait(60000);
|
||||||
dataSource.setTimeBetweenEvictionRunsMillis(60000);
|
dataSource.setTimeBetweenEvictionRunsMillis(60000);
|
||||||
dataSource.setMinEvictableIdleTimeMillis(300000);
|
dataSource.setMinEvictableIdleTimeMillis(300000);
|
||||||
@@ -70,7 +70,12 @@ public class RdbAdapter implements OuterAdapter {
|
|||||||
logger.error("ERROR ## failed to initial datasource: " + properties.get("jdbc.url"), e);
|
logger.error("ERROR ## failed to initial datasource: " + properties.get("jdbc.url"), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
rdbSyncService = new RdbSyncService(dataSource);
|
String threads = properties.get("threads");
|
||||||
|
if (threads != null) {
|
||||||
|
rdbSyncService = new RdbSyncService(Integer.valueOf(threads), dataSource);
|
||||||
|
} else {
|
||||||
|
rdbSyncService = new RdbSyncService(null, dataSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -177,6 +182,9 @@ public class RdbAdapter implements OuterAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
if (rdbSyncService != null) {
|
||||||
|
rdbSyncService.close();
|
||||||
|
}
|
||||||
if (dataSource != null) {
|
if (dataSource != null) {
|
||||||
dataSource.close();
|
dataSource.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.rdb.service;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class BatchExecutor {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(BatchExecutor.class);
|
||||||
|
|
||||||
|
private Integer key;
|
||||||
|
private Connection conn;
|
||||||
|
private int commitSize = 3000;
|
||||||
|
private AtomicInteger idx = new AtomicInteger(0);
|
||||||
|
private ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||||
|
private Lock commitLock = new ReentrantLock();
|
||||||
|
private Condition condition = commitLock.newCondition();
|
||||||
|
|
||||||
|
public BatchExecutor(Integer key, Connection conn, Integer commitSize){
|
||||||
|
this.key = key;
|
||||||
|
this.conn = conn;
|
||||||
|
if (commitSize != null) {
|
||||||
|
this.commitSize = commitSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.conn.setAutoCommit(false);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConn() {
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setValue(List<Map<String, ?>> values, int type, Object value) {
|
||||||
|
Map<String, Object> valueItem = new HashMap<>();
|
||||||
|
valueItem.put("type", type);
|
||||||
|
valueItem.put("value", value);
|
||||||
|
values.add(valueItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(String sql, List<Map<String, ?>> values) {
|
||||||
|
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||||
|
int len = values.size();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int type = (Integer) values.get(i).get("type");
|
||||||
|
Object value = values.get(i).get("value");
|
||||||
|
RdbSyncService.setPStmt(type, pstmt, value, i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pstmt.execute();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
int i = idx.incrementAndGet();
|
||||||
|
|
||||||
|
// 批次的第一次执行设置延时
|
||||||
|
if (i == 1) {
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
commitLock.lock();
|
||||||
|
if (!condition.await(5, TimeUnit.SECONDS)) {
|
||||||
|
// 超时提交
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
} finally {
|
||||||
|
commitLock.unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == commitSize) {
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commit() {
|
||||||
|
try {
|
||||||
|
commitLock.lock();
|
||||||
|
conn.commit();
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Batch executor: " + key + " commit " + idx.get() + " rows");
|
||||||
|
}
|
||||||
|
condition.signal();
|
||||||
|
idx.set(0);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
commitLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (conn != null) {
|
||||||
|
try {
|
||||||
|
conn.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@@ -37,10 +37,30 @@ public class RdbSyncService {
|
|||||||
|
|
||||||
private final Map<String, Map<String, Integer>> COLUMNS_TYPE_CACHE = new ConcurrentHashMap<>();
|
private final Map<String, Map<String, Integer>> COLUMNS_TYPE_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private DataSource dataSource;
|
private BatchExecutor[] batchExecutors;
|
||||||
|
|
||||||
public RdbSyncService(DataSource dataSource){
|
private int threads = 1;
|
||||||
this.dataSource = dataSource;
|
|
||||||
|
private ExecutorService[] threadExecutors;
|
||||||
|
|
||||||
|
public RdbSyncService(Integer threads, DataSource dataSource){
|
||||||
|
try {
|
||||||
|
if (threads != null && threads > 1 && threads < 10) {
|
||||||
|
this.threads = threads;
|
||||||
|
}
|
||||||
|
batchExecutors = new BatchExecutor[this.threads];
|
||||||
|
for (int i = 0; i < this.threads; i++) {
|
||||||
|
Connection conn = dataSource.getConnection();
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
this.batchExecutors[i] = new BatchExecutor(i, conn, null);
|
||||||
|
}
|
||||||
|
threadExecutors = new ExecutorService[this.threads];
|
||||||
|
for (int i = 0; i < this.threads; i++) {
|
||||||
|
threadExecutors[i] = Executors.newFixedThreadPool(1);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sync(MappingConfig config, Dml dml) {
|
public void sync(MappingConfig config, Dml dml) {
|
||||||
@@ -69,7 +89,7 @@ public class RdbSyncService {
|
|||||||
* @param config 配置项
|
* @param config 配置项
|
||||||
* @param dml DML数据
|
* @param dml DML数据
|
||||||
*/
|
*/
|
||||||
private void insert(MappingConfig config, Dml dml) throws SQLException {
|
private void insert(MappingConfig config, Dml dml) {
|
||||||
List<Map<String, Object>> data = dml.getData();
|
List<Map<String, Object>> data = dml.getData();
|
||||||
if (data == null || data.isEmpty()) {
|
if (data == null || data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -77,12 +97,6 @@ public class RdbSyncService {
|
|||||||
|
|
||||||
DbMapping dbMapping = config.getDbMapping();
|
DbMapping dbMapping = config.getDbMapping();
|
||||||
|
|
||||||
int idx = 1;
|
|
||||||
boolean completed = false;
|
|
||||||
|
|
||||||
Connection conn = dataSource.getConnection();
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
try {
|
|
||||||
Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data.get(0));
|
Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data.get(0));
|
||||||
|
|
||||||
StringBuilder insertSql = new StringBuilder();
|
StringBuilder insertSql = new StringBuilder();
|
||||||
@@ -98,54 +112,37 @@ public class RdbSyncService {
|
|||||||
len = insertSql.length();
|
len = insertSql.length();
|
||||||
insertSql.delete(len - 1, len).append(")");
|
insertSql.delete(len - 1, len).append(")");
|
||||||
|
|
||||||
Map<String, Integer> ctype = getTargetColumnType(conn, config);
|
Map<String, Integer> ctype = getTargetColumnType(batchExecutors[0].getConn(), config);
|
||||||
|
|
||||||
PreparedStatement pstmt = conn.prepareStatement(insertSql.toString());
|
|
||||||
|
|
||||||
|
String sql = insertSql.toString();
|
||||||
for (Map<String, Object> d : data) {
|
for (Map<String, Object> d : data) {
|
||||||
pstmt.clearParameters();
|
int pkHash = pkHash(dbMapping, d, threads);
|
||||||
int i = 1;
|
ThreadPoolExecutor tpe = (ThreadPoolExecutor) threadExecutors[pkHash];
|
||||||
|
checkQueue(tpe);
|
||||||
|
tpe.submit(() -> {
|
||||||
|
try {
|
||||||
|
BatchExecutor batchExecutor = batchExecutors[pkHash];
|
||||||
|
List<Map<String, ?>> values = new ArrayList<>();
|
||||||
|
|
||||||
for (Map.Entry<String, String> entry : columnsMap.entrySet()) {
|
for (Map.Entry<String, String> entry : columnsMap.entrySet()) {
|
||||||
String targetClolumnName = entry.getKey();
|
String targetColumnName = entry.getKey();
|
||||||
String srcColumnName = entry.getValue();
|
String srcColumnName = entry.getValue();
|
||||||
if (srcColumnName == null) {
|
if (srcColumnName == null) {
|
||||||
srcColumnName = targetClolumnName;
|
srcColumnName = targetColumnName;
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer type = ctype.get(targetClolumnName.toLowerCase());
|
Integer type = ctype.get(targetColumnName.toLowerCase());
|
||||||
|
|
||||||
Object value = d.get(srcColumnName);
|
|
||||||
if (value != null) {
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
throw new RuntimeException("No column: " + targetClolumnName + " found in target db");
|
throw new RuntimeException("No column: " + targetColumnName + " found in target db");
|
||||||
}
|
}
|
||||||
|
Object value = d.get(srcColumnName);
|
||||||
setPStmt(type, pstmt, value, i);
|
BatchExecutor.setValue(values, type, value);
|
||||||
} else {
|
|
||||||
pstmt.setNull(i, type);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pstmt.execute();
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("Insert into target table, sql: {}", insertSql);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx % config.getDbMapping().getCommitBatch() == 0) {
|
|
||||||
conn.commit();
|
|
||||||
completed = true;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
if (!completed) {
|
|
||||||
conn.commit();
|
|
||||||
}
|
}
|
||||||
|
batchExecutor.execute(sql, values);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
conn.rollback();
|
}
|
||||||
} finally {
|
});
|
||||||
conn.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +152,7 @@ public class RdbSyncService {
|
|||||||
* @param config 配置项
|
* @param config 配置项
|
||||||
* @param dml DML数据
|
* @param dml DML数据
|
||||||
*/
|
*/
|
||||||
private void update(MappingConfig config, Dml dml) throws SQLException {
|
private void update(MappingConfig config, Dml dml) {
|
||||||
List<Map<String, Object>> data = dml.getData();
|
List<Map<String, Object>> data = dml.getData();
|
||||||
if (data == null || data.isEmpty()) {
|
if (data == null || data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -169,21 +166,24 @@ public class RdbSyncService {
|
|||||||
DbMapping dbMapping = config.getDbMapping();
|
DbMapping dbMapping = config.getDbMapping();
|
||||||
|
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
boolean completed = false;
|
|
||||||
|
|
||||||
Connection conn = dataSource.getConnection();
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data.get(0));
|
Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data.get(0));
|
||||||
|
|
||||||
Map<String, Integer> ctype = getTargetColumnType(conn, config);
|
Map<String, Integer> ctype = getTargetColumnType(batchExecutors[0].getConn(), config);
|
||||||
|
|
||||||
for (Map<String, Object> o : old) {
|
for (Map<String, Object> o : old) {
|
||||||
Map<String, Object> d = data.get(idx - 1);
|
Map<String, Object> d = data.get(idx - 1);
|
||||||
|
int pkHash = pkHash(dbMapping, d, threads);
|
||||||
|
|
||||||
|
ThreadPoolExecutor tpe = (ThreadPoolExecutor) threadExecutors[pkHash];
|
||||||
|
checkQueue(tpe);
|
||||||
|
tpe.submit(() -> {
|
||||||
|
try {
|
||||||
|
BatchExecutor batchExecutor = batchExecutors[pkHash];
|
||||||
|
|
||||||
StringBuilder updateSql = new StringBuilder();
|
StringBuilder updateSql = new StringBuilder();
|
||||||
updateSql.append("UPDATE ").append(dbMapping.getTargetTable()).append(" SET ");
|
updateSql.append("UPDATE ").append(dbMapping.getTargetTable()).append(" SET ");
|
||||||
Map<String, Object> values = new LinkedHashMap<>();
|
|
||||||
|
List<Map<String, ?>> values = new ArrayList<>();
|
||||||
for (String srcColumnName : o.keySet()) {
|
for (String srcColumnName : o.keySet()) {
|
||||||
List<String> targetColumnNames = new ArrayList<>();
|
List<String> targetColumnNames = new ArrayList<>();
|
||||||
columnsMap.forEach((targetColumn, srcColumn) -> {
|
columnsMap.forEach((targetColumn, srcColumn) -> {
|
||||||
@@ -192,10 +192,10 @@ public class RdbSyncService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!targetColumnNames.isEmpty()) {
|
if (!targetColumnNames.isEmpty()) {
|
||||||
|
|
||||||
for (String targetColumnName : targetColumnNames) {
|
for (String targetColumnName : targetColumnNames) {
|
||||||
updateSql.append(targetColumnName).append("=?, ");
|
updateSql.append(targetColumnName).append("=?, ");
|
||||||
values.put(targetColumnName, d.get(srcColumnName));
|
Integer type = ctype.get(targetColumnName.toLowerCase());
|
||||||
|
BatchExecutor.setValue(values, type, d.get(srcColumnName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,27 +203,18 @@ public class RdbSyncService {
|
|||||||
updateSql.delete(len - 2, len).append(" WHERE ");
|
updateSql.delete(len - 2, len).append(" WHERE ");
|
||||||
|
|
||||||
// 拼接主键
|
// 拼接主键
|
||||||
appendCondition(dbMapping, updateSql, values, d);
|
appendCondition(dbMapping, updateSql, ctype, values, d);
|
||||||
|
|
||||||
sqlExe(conn, updateSql.toString(), ctype, values);
|
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Update target table, sql: {}", updateSql);
|
logger.trace("Update target table, sql: {}", updateSql);
|
||||||
}
|
}
|
||||||
if (idx % config.getDbMapping().getCommitBatch() == 0) {
|
batchExecutor.execute(updateSql.toString(), values);
|
||||||
conn.commit();
|
|
||||||
completed = true;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
if (!completed) {
|
|
||||||
conn.commit();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
conn.rollback();
|
}
|
||||||
} finally {
|
});
|
||||||
conn.close();
|
|
||||||
|
idx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +225,7 @@ public class RdbSyncService {
|
|||||||
* @param dml
|
* @param dml
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
private void delete(MappingConfig config, Dml dml) throws SQLException {
|
private void delete(MappingConfig config, Dml dml) {
|
||||||
List<Map<String, Object>> data = dml.getData();
|
List<Map<String, Object>> data = dml.getData();
|
||||||
if (data == null || data.isEmpty()) {
|
if (data == null || data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -242,42 +233,32 @@ public class RdbSyncService {
|
|||||||
|
|
||||||
DbMapping dbMapping = config.getDbMapping();
|
DbMapping dbMapping = config.getDbMapping();
|
||||||
|
|
||||||
int idx = 1;
|
Map<String, Integer> ctype = getTargetColumnType(batchExecutors[0].getConn(), config);
|
||||||
boolean completed = false;
|
|
||||||
|
|
||||||
Connection conn = dataSource.getConnection();
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, Integer> ctype = getTargetColumnType(conn, config);
|
|
||||||
|
|
||||||
for (Map<String, Object> d : data) {
|
for (Map<String, Object> d : data) {
|
||||||
|
int pkHash = pkHash(dbMapping, d, threads);
|
||||||
|
|
||||||
|
ThreadPoolExecutor tpe = (ThreadPoolExecutor) threadExecutors[pkHash];
|
||||||
|
checkQueue(tpe);
|
||||||
|
tpe.submit(() -> {
|
||||||
|
try {
|
||||||
|
BatchExecutor batchExecutor = batchExecutors[pkHash];
|
||||||
StringBuilder sql = new StringBuilder();
|
StringBuilder sql = new StringBuilder();
|
||||||
sql.append("DELETE FROM ").append(dbMapping.getTargetTable()).append(" WHERE ");
|
sql.append("DELETE FROM ").append(dbMapping.getTargetTable()).append(" WHERE ");
|
||||||
|
|
||||||
Map<String, Object> values = new LinkedHashMap<>();
|
List<Map<String, ?>> values = new ArrayList<>();
|
||||||
// 拼接主键
|
|
||||||
appendCondition(dbMapping, sql, values, d);
|
|
||||||
|
|
||||||
sqlExe(conn, sql.toString(), ctype, values);
|
// 拼接主键
|
||||||
|
appendCondition(dbMapping, sql, ctype, values, d);
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Delete from target table, sql: {}", sql);
|
logger.trace("Delete from target table, sql: {}", sql);
|
||||||
}
|
}
|
||||||
if (idx % config.getDbMapping().getCommitBatch() == 0) {
|
batchExecutor.execute(sql.toString(), values);
|
||||||
conn.commit();
|
|
||||||
completed = true;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
if (!completed) {
|
|
||||||
conn.commit();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
conn.rollback();
|
}
|
||||||
} finally {
|
});
|
||||||
conn.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +307,9 @@ public class RdbSyncService {
|
|||||||
* @param i 索引号
|
* @param i 索引号
|
||||||
*/
|
*/
|
||||||
public static void setPStmt(int type, PreparedStatement pstmt, Object value, int i) throws SQLException {
|
public static void setPStmt(int type, PreparedStatement pstmt, Object value, int i) throws SQLException {
|
||||||
|
if (value == null) {
|
||||||
|
pstmt.setObject(i, type);
|
||||||
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Types.BIT:
|
case Types.BIT:
|
||||||
case Types.BOOLEAN:
|
case Types.BOOLEAN:
|
||||||
@@ -431,7 +415,7 @@ public class RdbSyncService {
|
|||||||
break;
|
break;
|
||||||
case Types.DATE:
|
case Types.DATE:
|
||||||
if (value instanceof java.util.Date) {
|
if (value instanceof java.util.Date) {
|
||||||
pstmt.setDate(i, new java.sql.Date(((java.util.Date) value).getTime()));
|
pstmt.setDate(i, new Date(((java.util.Date) value).getTime()));
|
||||||
} else if (value instanceof String) {
|
} else if (value instanceof String) {
|
||||||
String v = (String) value;
|
String v = (String) value;
|
||||||
if (!v.startsWith("0000-00-00")) {
|
if (!v.startsWith("0000-00-00")) {
|
||||||
@@ -447,7 +431,7 @@ public class RdbSyncService {
|
|||||||
break;
|
break;
|
||||||
case Types.TIME:
|
case Types.TIME:
|
||||||
if (value instanceof java.util.Date) {
|
if (value instanceof java.util.Date) {
|
||||||
pstmt.setTime(i, new java.sql.Time(((java.util.Date) value).getTime()));
|
pstmt.setTime(i, new Time(((java.util.Date) value).getTime()));
|
||||||
} else if (value instanceof String) {
|
} else if (value instanceof String) {
|
||||||
String v = (String) value;
|
String v = (String) value;
|
||||||
v = "T" + v;
|
v = "T" + v;
|
||||||
@@ -459,7 +443,7 @@ public class RdbSyncService {
|
|||||||
break;
|
break;
|
||||||
case Types.TIMESTAMP:
|
case Types.TIMESTAMP:
|
||||||
if (value instanceof java.util.Date) {
|
if (value instanceof java.util.Date) {
|
||||||
pstmt.setTimestamp(i, new java.sql.Timestamp(((java.util.Date) value).getTime()));
|
pstmt.setTimestamp(i, new Timestamp(((java.util.Date) value).getTime()));
|
||||||
} else if (value instanceof String) {
|
} else if (value instanceof String) {
|
||||||
String v = (String) value;
|
String v = (String) value;
|
||||||
if (!v.startsWith("0000-00-00")) {
|
if (!v.startsWith("0000-00-00")) {
|
||||||
@@ -481,8 +465,8 @@ public class RdbSyncService {
|
|||||||
/**
|
/**
|
||||||
* 拼接主键 where条件
|
* 拼接主键 where条件
|
||||||
*/
|
*/
|
||||||
private static void appendCondition(DbMapping dbMapping, StringBuilder sql, Map<String, Object> values,
|
private static void appendCondition(DbMapping dbMapping, StringBuilder sql, Map<String, Integer> ctype,
|
||||||
Map<String, Object> d) {
|
List<Map<String, ?>> values, Map<String, Object> d) {
|
||||||
// 拼接主键
|
// 拼接主键
|
||||||
for (Map.Entry<String, String> entry : dbMapping.getTargetPk().entrySet()) {
|
for (Map.Entry<String, String> entry : dbMapping.getTargetPk().entrySet()) {
|
||||||
String targetColumnName = entry.getKey();
|
String targetColumnName = entry.getKey();
|
||||||
@@ -491,28 +475,54 @@ public class RdbSyncService {
|
|||||||
srcColumnName = targetColumnName;
|
srcColumnName = targetColumnName;
|
||||||
}
|
}
|
||||||
sql.append(targetColumnName).append("=? AND ");
|
sql.append(targetColumnName).append("=? AND ");
|
||||||
values.put(targetColumnName, d.get(srcColumnName));
|
Integer type = ctype.get(targetColumnName.toLowerCase());
|
||||||
|
BatchExecutor.setValue(values, type, d.get(srcColumnName));
|
||||||
}
|
}
|
||||||
int len = sql.length();
|
int len = sql.length();
|
||||||
sql.delete(len - 4, len);
|
sql.delete(len - 4, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行sql
|
* 取主键hash
|
||||||
*/
|
*/
|
||||||
private static void sqlExe(Connection conn, String sql, Map<String, Integer> ctype, Map<String, Object> values) {
|
private static int pkHash(DbMapping dbMapping, Map<String, Object> d, int threads) {
|
||||||
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
int hash = 0;
|
||||||
int i = 1;
|
// 取主键
|
||||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
for (Map.Entry<String, String> entry : dbMapping.getTargetPk().entrySet()) {
|
||||||
String targetColumnName = entry.getKey();
|
String targetColumnName = entry.getKey();
|
||||||
Object value = entry.getValue();
|
String srcColumnName = entry.getValue();
|
||||||
Integer type = ctype.get(targetColumnName.toLowerCase());
|
if (srcColumnName == null) {
|
||||||
setPStmt(type, pstmt, value, i++);
|
srcColumnName = targetColumnName;
|
||||||
}
|
}
|
||||||
pstmt.execute();
|
Object value = d.get(srcColumnName);
|
||||||
} catch (SQLException e) {
|
if (value != null) {
|
||||||
logger.error(e.getMessage(), e);
|
hash += value.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash = Math.abs(hash) % threads;
|
||||||
|
return Math.abs(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
for (BatchExecutor batchExecutor : batchExecutors) {
|
||||||
|
batchExecutor.close();
|
||||||
|
}
|
||||||
|
for (ExecutorService executorService : threadExecutors) {
|
||||||
|
executorService.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkQueue(ThreadPoolExecutor tpe) {
|
||||||
|
// 防止队列过大
|
||||||
|
while (tpe.getQueue().size() > 10000) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(3000);
|
||||||
|
while (tpe.getQueue().size() > 5000) {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package com.alibaba.otter.canal.client.adapter.rdb.test;
|
|||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.math.BigDecimal;
|
import java.sql.Clob;
|
||||||
import java.sql.*;
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -34,20 +36,45 @@ public class DBTest {
|
|||||||
dataSource.init();
|
dataSource.init();
|
||||||
|
|
||||||
Connection conn = dataSource.getConnection();
|
Connection conn = dataSource.getConnection();
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
ResultSet rs = stmt.executeQuery("select * from user t where 1=2");
|
|
||||||
|
|
||||||
ResultSetMetaData rsm = rs.getMetaData();
|
conn.setAutoCommit(false);
|
||||||
int cnt = rsm.getColumnCount();
|
PreparedStatement pstmt = conn
|
||||||
for (int i = 1; i <= cnt; i++) {
|
.prepareStatement("insert into user (id,name,role_id,c_time,test1,test2) values (?,?,?,?,?,?)");
|
||||||
System.out.println(rsm.getColumnName(i) + " " + rsm.getColumnType(i));
|
|
||||||
|
java.util.Date now = new java.util.Date();
|
||||||
|
for (int i = 1; i <= 100000; i++) {
|
||||||
|
pstmt.clearParameters();
|
||||||
|
pstmt.setLong(1, (long) i);
|
||||||
|
pstmt.setString(2, "test_" + i);
|
||||||
|
pstmt.setLong(3, (long) i % 4 + 1);
|
||||||
|
pstmt.setDate(4, new java.sql.Date(now.getTime()));
|
||||||
|
pstmt.setString(5, "tttt");
|
||||||
|
pstmt.setBytes(6, null);
|
||||||
|
|
||||||
|
pstmt.execute();
|
||||||
|
if (i % 5000 == 0) {
|
||||||
|
conn.commit();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
conn.commit();
|
||||||
|
|
||||||
rs.close();
|
pstmt.close();
|
||||||
stmt.close();
|
|
||||||
|
// Statement stmt = conn.createStatement();
|
||||||
|
// ResultSet rs = stmt.executeQuery("select * from user t where 1=2");
|
||||||
|
//
|
||||||
|
// ResultSetMetaData rsm = rs.getMetaData();
|
||||||
|
// int cnt = rsm.getColumnCount();
|
||||||
|
// for (int i = 1; i <= cnt; i++) {
|
||||||
|
// System.out.println(rsm.getColumnName(i) + " " + rsm.getColumnType(i));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// rs.close();
|
||||||
|
// stmt.close();
|
||||||
|
|
||||||
// PreparedStatement pstmt = conn
|
// PreparedStatement pstmt = conn
|
||||||
// .prepareStatement("insert into tb_user (id,name,role_id,c_time,test1,test2) values (?,?,?,?,?,?)");
|
// .prepareStatement("insert into tb_user (id,name,role_id,c_time,test1,test2)
|
||||||
|
// values (?,?,?,?,?,?)");
|
||||||
// pstmt.setBigDecimal(1, new BigDecimal("5"));
|
// pstmt.setBigDecimal(1, new BigDecimal("5"));
|
||||||
// pstmt.setString(2, "test");
|
// pstmt.setString(2, "test");
|
||||||
// pstmt.setBigDecimal(3, new BigDecimal("1"));
|
// pstmt.setBigDecimal(3, new BigDecimal("1"));
|
||||||
|
|||||||
Reference in New Issue
Block a user