2.4. blade执行脚本集成多发
2.4.1. blade执行脚本集成多发简介
在blade执行测试用例过程中多个信创库执行sql脚本存在以下问题:
1.操作繁琐重复:执行测试用例前,需针对 Oracle、dm、ocean_base_oracle 等不同数据库手动单独执行 SQL 脚本同步数据,存在大量重复劳动。
2.缺乏数据校验机制:没有统一的数据校验机制,无法快速排查不同数据库间的数据差异,影响测试效率和数据一致性保障。。
2.4.2. 优化后的整体流程图:
2.4.3. 提前在blade配置http脚本
项目设置-项目连接设置(开通权限),新增http_config类型
测试执行->测试环境(引用项目连接设置中的http_config),给测试环境添加执行机(执行机,配置执行机授权码,授权码来自项目设置)
测试脚本(http)引用项目连接的设置
测试用例中引用测试脚本(http)
调试测试用例,按照步骤,发起http(依赖执行机)
http api 参考下方接口内容
部分截图如下:
设置项目连接,新增http_config类型
![]()
设置测试环境,引用项目连接设置中的http_config, 设置http接口,参考下方接口内容,添加执行机
![]()
测试http脚本引用项目连接的设置
![]()
2.4.4. 服务部署
下载并解压uk_agent发布包
访问Hundsun Artifactory制品库指定地址,下载包含uk_agent压缩包,若存在访问问题,需联系制品库维护人员。具体见下方截图:
![]()
上传uk_agent到服务器(linux环境),将压缩包解压至uk_agent指定工作目录(示例路径:
/data/lightdb/xxxxxx/uk_agent),解压后需包含以下核心目录/文件:
config:配置目录,含数据源、多发参数等配置文件和待执行的sql脚本,具体参考下面说明
start.sh/stop.sh:服务启停脚本
uk_agent-25.3.1.jar:服务执行jar包,这里是25.3.1版本
配置目标库数据源 - 进入uk_agent配置目录:
uk_agent/config,编辑数据源配置文件application.properties,配置所有目标库数据源,示例如下:
# 第一个数据源配置(mysql的一个具体数据库) agent.datasource.sources[0].name=mysql # 数据库类型,不区分大小写, agent.datasource.sources[0].alias=mysql_xxxxxx # 具体数据库名称,对应下方请求中参数targetDbs内容 agent.datasource.sources[0].url=jdbc:mysql://10.20.30.199:2306/xxxxxx # 数据库url agent.datasource.sources[0].username=lightdb # 数据库用户名称 agent.datasource.sources[0].password=${password} # 数据库用户密码 agent.datasource.sources[0].driver-class-name=com.mysql.cj.jdbc.Driver # 数据库所需的驱动类,这里是mysql的驱动 agent.datasource.sources[0].initial-size=5 # 连接池初始大小 agent.datasource.sources[0].max-active=20 # 连接池最大活跃数 agent.datasource.sources[0].min-idle=5 # 连接池最小空闲数 agent.datasource.sources[0].max-wait=60000 # 最大等待时间(毫秒) agent.datasource.sources[0].validation-query=select 1 # 连接校验SQL # 第二个数据源配置(lightdb_mysql的一个具体数据库) agent.datasource.sources[1].name=LIGHTDB_MYSQL agent.datasource.sources[1].alias=lightdb_mysql_yyyyyy agent.datasource.sources[1].url=jdbc:postgresql://10.20.30.193:5440/yyyyyy?options=-c%20search_path=public,mysql,lt_catalog agent.datasource.sources[1].username=lightdb agent.datasource.sources[1].password=${password} agent.datasource.sources[1].driver-class-name=org.postgresql.Driver agent.datasource.sources[1].initial-size=5 agent.datasource.sources[1].max-active=20 agent.datasource.sources[1].min-idle=5 agent.datasource.sources[1].max-wait=10000 agent.datasource.sources[1].validation-query=select 1
配置多发关联参数
为确保服务能正常关联比对服务,需在
config目录下配置多发参数文件:#1. ``config/unisql.conf`` 文件,关键配置: unisql.lib.full-path=uk_agent/config/unisql.linux.x86_64.so # 统一SQL动态库路径,需与uk_agent部署环境匹配 unisql.skip=1 # 全局透传开关,按基础配置设置 #2. ``config/jrescloud.properties``文件,关键配置(确保数据可发送到比对服务进行比对): multi.run.what=2 # 执行多发,按基础配置设置 multi.sendCompareService=1 # 开启数据发送至比对服务 multi.compareServiceUrl=http://10.20.30.199:2043/em # 比对服务地址 multi.send.change.data.http.url=http://10.20.30.199:2043/em/compare/upload/change/data # 数据上传URL,与比对服务地址匹配
需在
config/scripts目录下,配置sql脚本(基于步骤2中具体数据库名称alias分层存储)
基于具体数据库名称的sql脚本分层目录如下:
config └── scripts ├── lightdb_mysql_yyyyyy │ └── xx │ └── yy │ └── test.sql └── mysql_xxxxxx └── xx └── yy └── test.sql
其中一个具体数据库例如mysql_xxxxxx的sql脚本test.sql内容示例:
-- 创建表 CREATE TABLE user_info ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ); -- 插入数据 INSERT INTO user_info (name) VALUES ('张三'); INSERT INTO user_info (name) VALUES ('李四'); INSERT INTO user_info (name) VALUES ('王五'); -- 更新数据 UPDATE user_info SET name = '李小四' WHERE id = 2; --查询数据 SELECT id, name FROM user_info ORDER BY id ASC LIMIT 2; -- 删除数据 DELETE FROM user_info WHERE id = 3; -- 删除表 DROP TABLE user_info;
oracle系(oracle,dm,gaussdb_oracle,ocean_base_oracle)支持plsql,sql脚本示例如下:
-- 这是文件级单行注释(会被跳过) prompt 开始执行PL/SQL块(带注释); -- 场景1:匿名PL/SQL块,含单行注释 begin -- 单行注释:声明变量 declare v_name varchar2(50); -- 用户名变量(行尾注释) v_age number; begin v_name := '张三--测试字符串'; -- 字符串内的--不是注释 v_age := 30; dbms_output.put_line('姓名:' || v_name || ', 年龄:' || v_age); end; -- 内层end end; / -- 场景2:存储过程(含注释) create or replace procedure test_procedure(p_id number) is v_desc varchar2(100); begin select 'ID=' || p_id || '--描述' into v_desc from dual; -- 字符串内的-- dbms_output.put_line('描述:' || v_desc); exception when no_data_found then dbms_output.put_line('无数据(注释:异常处理)'); end test_procedure; / -- 场景3:普通SQL(带行尾注释) insert into t_user (name, age) values ('李四', 25); -- 插入测试数据 commit;
2.4.5. 接口说明
2.4.5.1. 简介
http-api,接收测试用例数据同步请求,根据参数动态执行源库及指定目标信创库的脚本,sql执行变化发送到比对服务进行生成比对报告,并返回sql脚本执行结果
2.4.5.2. 接口请求地址
2.4.5.3. 请求方式
POST
2.4.5.4. Content-Type
application/json;charset=UTF-8
2.4.5.5. 接口请求参数
参数名 |
类型 |
是否必须 |
说明 |
取值范围 |
示例 |
|---|---|---|---|---|---|
scriptName |
String |
是 |
脚本相对目录和名称,如 “xxx/xxx/init.sql”(对应服务端脚本路径的子目录及文件名) |
||
sourceDb |
String |
是 |
源数据库类型,不区分大小写 |
取值范围[oracle,mysql] |
|
targetDbs |
Array<String> |
是 |
集合中每个元素是数据库的某个具体数据库或者schema的名称,对应数据源配置项agent.datasource.sources[i].alias, 也标识sql脚本目录中不同的数据库 |
示例[“dm_fund60x”,”ora_test”,”gaussdb_abc”] |
2.4.5.6. 接口响应参数
参数名 |
类型 |
是否必须 |
说明 |
取值范围 |
示例 |
status |
String |
是 |
整体执行状态 |
success:所有库执行成功,partial_success:部分库执行成功,failed:所有库执行失败 |
|
traceId |
String |
是 |
标识一次操作,sql脚本在源库和目标库执行 |
||
details |
Array<DetailInfo> |
是 |
各数据库执行详情数组,每个元素对应一个库的执行结果 |
DetailInfo对象结构如下:
参数名 |
类型 |
是否必须 |
说明 |
示例 |
|---|---|---|---|---|
name |
String |
是 |
数据库名称,对应的是入参中targetDbs这个数组中的具体数据库或者schema的名字 |
|
status |
String |
是 |
该库执行状态:success(成功)、failed(失败) |
|
error |
String |
否 |
可选字段,仅当 status=failed 时返回,描述错误原因(如 “表不存在”“权限不足” 等) |
|
sqlCount |
String |
是 |
执行的sql数量 |
|
executeDuration |
String |
是 |
执行sql总耗时,单位毫秒(ms) |
|
failedSqlDetails |
DailedSqlDetails |
否 |
可选对象,执行sql失败,打印行号,sql,失败原因 |
DailedSqlDetails对象结构如下:
参数名 |
类型 |
是否必须 |
说明 |
示例 |
|---|---|---|---|---|
lineNumber |
String |
是 |
sql行号 |
|
sql |
String |
是 |
失败SQL内容 |
|
error |
String |
否 |
失败原因 |
|
executeTime |
String |
是 |
执行耗时(毫秒) |
2.4.5.7. 请求和响应示例
请求示例1(成功)
{
"scriptName": "xx/yy/test.sql",
"sourceDb": "oracle",
"targetDbs": [
"lightdb_mysql_xxxxxx",
"mysql_xxxxxx"
]
}
响应示例1(成功)
{
"traceId": "uk_agent_b7569552c45945d2b5f2ec2d9607c8b1",
"status": "success",
"details": [
{
"name": "lightdb_mysql_liangdong30629",
"status": "success",
"error": null,
"sqlCount": 8,
"executeDuration": 5274,
"failedSqlDetails": null
},
{
"name": "mysql_liangdong30629",
"status": "success",
"error": null,
"sqlCount": 8,
"executeDuration": 5289,
"failedSqlDetails": null
}
]
}
请求示例2(失败)
{
"scriptName": "xx/yy/test2.sql",
"sourceDb": "mysql",
"targetDbs": [
"lightdb_mysql_liangdong30629",
"mysql_liangdong30629"
]
}
响应示例2(失败原因是脚本不存在)
{
"traceId": "uk_agent_ca7515c1a676443f938f43d436bfa4bb",
"status": "failed",
"details": [
{
"name": "lightdb_mysql_liangdong30629",
"status": "failed",
"error": "脚本文件不存在:C:\\Users\\T14s\\idea_workspace\\unisql_trunk_develop\\config\\scripts\\lightdb_mysql_liangdong30629\\xx/yy/test2.sql",
"sqlCount": 0,
"executeDuration": 0,
"failedSqlDetails": null
},
{
"name": "mysql_liangdong30629",
"status": "failed",
"error": "脚本文件不存在:C:\\Users\\T14s\\idea_workspace\\unisql_trunk_develop\\config\\scripts\\mysql_liangdong30629\\xx/yy/test2.sql",
"sqlCount": 0,
"executeDuration": 0,
"failedSqlDetails": null
}
]
}
请求示例3(失败)
{
"scriptName": "xx/yy/test.sql",
"sourceDb": "mysql",
"targetDbs": [
"lightdb_mysql_liangdong30629",
"mysql_liangdong30629"
]
}
响应示例3(失败,部分SQL执行失败,可查看失败的sql和失败原因)
{
"traceId": "uk_agent_a24d43bf64c443c2bdd10660a81edafb",
"status": "partial_success",
"details": [
{
"name": "lightdb_mysql_liangdong30629",
"status": "failed",
"error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
"sqlCount": 8,
"executeDuration": 5620,
"failedSqlDetails": [
{
"lineNumber": 22,
"sql": "SELECT id, name\nFROM user_info2\nORDER BY id ASC\nLIMIT 2;",
"error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_a24d43bf64c443c2bdd10660a81edafb] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 15:15:27\",\"bizSqlOperationType\":\"SELECT\",\"bizSqlSequence\":6,\"bizSqlTableName\":\"user_info2\",\"compareKind\":\"RAW_BIZ_QUERY\",\"compareReportGenerated\":false,\"errorReason\":\"多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL执行失败,traceId [uk_agent_a24d43bf64c443c2bdd10660a81edafb],业务SQL[SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;],异常信息[com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \\\"user_info2\\\" does not exist\\n 位置:22\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\\r\\n\\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\\r\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\r\\n]\",\"functionId\":\"\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQueryRowCount\":0,\"multiQuerySql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"state\":\"\",\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_a24d43bf64c443c2bdd10660a81edafb\",\"transformedBizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info2\" does not exist\n 位置:22\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
"executeTime": 620
}
]
},
{
"name": "mysql_liangdong30629",
"status": "success",
"error": null,
"sqlCount": 8,
"executeDuration": 5612,
"failedSqlDetails": null
}
]
}
请求示例4(失败)
{
"scriptName": "xx/yy/test.sql",
"sourceDb": "mysql",
"targetDbs": [
"lightdb_mysql_liangdong30629",
"mysql_liangdong30629"
]
}
响应示例4(失败,两个数据库均有sql执行失败,可查看失败的sql和失败原因)
{
"traceId": "uk_agent_3b2caececdf343f99c97f593861e8067",
"status": "failed",
"details": [
{
"name": "lightdb_mysql_liangdong30629",
"status": "failed",
"error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
"sqlCount": 8,
"executeDuration": 5576,
"failedSqlDetails": [
{
"lineNumber": 22,
"sql": "SELECT id, name\nFROM user_info2\nORDER BY id ASC\nLIMIT 2;",
"error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_3b2caececdf343f99c97f593861e8067] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:06:45\",\"bizSqlOperationType\":\"SELECT\",\"bizSqlSequence\":6,\"bizSqlTableName\":\"user_info2\",\"compareKind\":\"RAW_BIZ_QUERY\",\"compareReportGenerated\":false,\"errorReason\":\"多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL执行失败,traceId [uk_agent_3b2caececdf343f99c97f593861e8067],业务SQL[SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;],异常信息[com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \\\"user_info2\\\" does not exist\\n 位置:22\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\\r\\n\\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\\r\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\r\\n]\",\"functionId\":\"\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQueryRowCount\":0,\"multiQuerySql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"state\":\"BIZ_SQL_EXECUTE_FAIL\",\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_3b2caececdf343f99c97f593861e8067\",\"transformedBizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info2\" does not exist\n 位置:22\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
"executeTime": 561
}
]
},
{
"name": "mysql_liangdong30629",
"status": "failed",
"error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
"sqlCount": 8,
"executeDuration": 5565,
"failedSqlDetails": [
{
"lineNumber": 31,
"sql": "DELETE FROM user_info3\nWHERE id = 3;",
"error": "多发源端[MYSQL]到目标[MYSQL]业务SQL串行执行失败,traceId [uk_agent_3b2caececdf343f99c97f593861e8067] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:06:46\",\"bizSqlOperationType\":\"DELETE\",\"bizSqlSequence\":7,\"bizSqlTableName\":\"user_info3\",\"compareKind\":\"AFTER_QUERY\",\"compareReportGenerated\":false,\"functionId\":\"\",\"messageType\":\"http\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQuerySql\":\"SELECT * FROM user_info3 WHERE id=3 ORDER BY id\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":true,\"syncConnectionInfo\":{\"password\":\"lightdb123456\",\"url\":\"jdbc:mysql://10.20.30.199:2306/liangdong30629?sourceDialect=mysql&targetDialect=mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_3b2caececdf343f99c97f593861e8067\",\"transformedBizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [java.sql.SQLSyntaxErrorException: Table 'liangdong30629.user_info3' doesn't exist\r\n\tat com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121)\r\n\tat com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)\r\n\tat com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:770)\r\n\tat com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:653)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:855)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:564)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:514)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:381)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
"executeTime": 570
}
]
}
]
}
响应示例5(失败,两个数据库均有sql执行失败,其中一个具体数据库有多个sql执行失败,可查看失败的sql和失败原因)
{
"traceId": "uk_agent_729ea0c3a9334e1b9da0edb0fabac355",
"status": "failed",
"details": [
{
"name": "lightdb_mysql_liangdong30629",
"status": "failed",
"error": "部分SQL执行失败:总SQL数=8,成功数=6,失败数=2",
"sqlCount": 8,
"executeDuration": 5693,
"failedSqlDetails": [
{
"lineNumber": 22,
"sql": "SELECT id, name\nFROM user_info2\nORDER BY id ASC\nLIMIT 2;",
"error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:36:33\",\"bizSqlOperationType\":\"SELECT\",\"bizSqlSequence\":6,\"bizSqlTableName\":\"user_info2\",\"compareKind\":\"RAW_BIZ_QUERY\",\"compareReportGenerated\":false,\"errorReason\":\"多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355],业务SQL[SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;],异常信息[com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \\\"user_info2\\\" does not exist\\n 位置:22\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\\r\\n\\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\\r\\n\\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\\r\\n\\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\\r\\n\\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\\r\\n\\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\\r\\n\\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\\r\\n\\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\\r\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\r\\n]\",\"functionId\":\"\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQueryRowCount\":0,\"multiQuerySql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"state\":\"COMPARE_FAIL\",\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_729ea0c3a9334e1b9da0edb0fabac355\",\"transformedBizSql\":\"SELECT id, name\\nFROM user_info2\\nORDER BY id ASC\\nLIMIT 2;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info2\" does not exist\n 位置:22\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:818)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
"executeTime": 624
},
{
"lineNumber": 31,
"sql": "DELETE FROM user_info1\nWHERE id = 3;",
"error": "多发源端[MYSQL]到目标[LIGHTDB_MYSQL]业务SQL串行执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"bizSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:36:34\",\"bizSqlOperationType\":\"DELETE\",\"bizSqlSequence\":7,\"bizSqlTableName\":\"user_info1\",\"compareKind\":\"AFTER_QUERY\",\"compareReportGenerated\":false,\"functionId\":\"\",\"messageType\":\"http\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQuerySql\":\"SELECT * FROM user_info1 WHERE id=3 ORDER BY id\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":false,\"syncConnectionInfo\":{\"password\":\"lightdb@123\",\"url\":\"jdbc:postgresql://10.20.30.193:5440/liangdong30629?options=-c%20search_path=public,mysql,lt_catalog&sourceDialect=mysql&targetDialect=lightdb_mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"LIGHTDB_MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_729ea0c3a9334e1b9da0edb0fabac355\",\"transformedBizSql\":\"DELETE FROM user_info1\\nWHERE id = 3;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [com.tencentcloud.tdsql.pg.util.PSQLException: ERROR: relation \"user_info1\" does not exist\n 位置:13\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2678)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2368)\r\n\tat com.tencentcloud.tdsql.pg.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:358)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeInternal(PgStatement.java:513)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:431)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:348)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeCachedSql(PgStatement.java:334)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.executeWithFlags(PgStatement.java:310)\r\n\tat com.tencentcloud.tdsql.pg.jdbc.PgStatement.execute(PgStatement.java:305)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:855)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
"executeTime": 625
}
]
},
{
"name": "mysql_liangdong30629",
"status": "failed",
"error": "部分SQL执行失败:总SQL数=8,成功数=7,失败数=1",
"sqlCount": 8,
"executeDuration": 5917,
"failedSqlDetails": [
{
"lineNumber": 31,
"sql": "DELETE FROM user_info3\nWHERE id = 3;",
"error": "多发源端[MYSQL]到目标[MYSQL]业务SQL串行执行失败,traceId [uk_agent_729ea0c3a9334e1b9da0edb0fabac355] 多发消息 [{\"appName\":\"autoTest\",\"appType\":\"OTHER\",\"bizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"bizSqlAffectedRows\":0,\"bizSqlExecutionTime\":\"2025-11-10 16:36:34\",\"bizSqlOperationType\":\"DELETE\",\"bizSqlSequence\":7,\"bizSqlTableName\":\"user_info3\",\"compareKind\":\"AFTER_QUERY\",\"compareReportGenerated\":false,\"functionId\":\"\",\"messageType\":\"http\",\"multiAppHostIp\":\"10.188.129.216\",\"multiQueryResultSet\":{},\"multiQuerySql\":\"SELECT * FROM user_info3 WHERE id=3 ORDER BY id\",\"multiTargetDialects\":[\"LIGHTDB_MYSQL\",\"MYSQL\"],\"rawBizBindVarReplacedSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"realMultiplex\":false,\"schemaName\":\"liangdong30629\",\"sourceDialect\":\"MYSQL\",\"sourceMessage\":true,\"syncConnectionInfo\":{\"password\":\"lightdb123456\",\"url\":\"jdbc:mysql://10.20.30.199:2306/liangdong30629?sourceDialect=mysql&targetDialect=mysql&multiplexTargetDialects=lightdb_mysql,mysql\",\"username\":\"lightdb\"},\"targetDialect\":\"MYSQL\",\"tenantName\":\"autoTest\",\"traceId\":\"uk_agent_729ea0c3a9334e1b9da0edb0fabac355\",\"transformedBizSql\":\"DELETE FROM user_info3\\nWHERE id = 3;\",\"url\":\"xx/yy/test.sql\",\"username\":\"autoTest\"}] 错误信息 [java.sql.SQLSyntaxErrorException: Table 'liangdong30629.user_info3' doesn't exist\r\n\tat com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121)\r\n\tat com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)\r\n\tat com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:770)\r\n\tat com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:653)\r\n\tat com.hundsun.lightdb.unisql.model.MultiplexContext.execute(MultiplexContext.java:855)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:159)\r\n\tat com.hundsun.lightdb.unisql.proxy.jdbc.UnisqlStatement.execute(UnisqlStatement.java:100)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.executeSingleSql(DataSyncServiceImpl.java:682)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.processSingleTargetDbInternal(DataSyncServiceImpl.java:613)\r\n\tat com.hundsun.lightdb.ukagent.service.impl.DataSyncServiceImpl.lambda$createSingleDbTask$6(DataSyncServiceImpl.java:474)\r\n\tat java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)\r\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n]",
"executeTime": 861
}
]
}
]
}
2.4.6. 支持在比对服务页面查看具体报错信息
2.4.7. 常见问题
2.4.7.1. 解析sql脚本支持哪些sql
普通SQL(分号/斜杠结尾,非PL/SQL)
支持类型:DML(增删改查)、DDL(建表/索引/视图等)、DCL(授权/回收),需满足“分号结尾”或“独立斜杠结尾”。
注释兼容:自动过滤单行注释、多行注释、不误判字符串内的单行注释,见下方sql示例:
-- 分号结尾的普通SQL(支持) SELECT * FROM user_info WHERE name = '张三--测试'; -- 行尾注释 INSERT INTO dept (deptno, dname) VALUES (10, '研发部'); -- 独立斜杠结尾的普通SQL(支持,代码会移除结尾/) CREATE TABLE test (id INT); /
Oracle系PL/SQL块(自动移除结尾独立/)
支持的PL/SQL类型(通过正则识别): 匿名块(begin/declare … end;) ,存储过程/函数(create or replace procedure/function …) ,包/包体(create or replace package …)
关键特性:仅识别独立一行的/为结束符,自动移除该/,保留PL/SQL主体(适配JDBC执行)。
注释/嵌套兼容:支持PL/SQL内的单行/多行注释、嵌套begin-end块,注释中含end关键字不影响结束判断。
注意: plsql内嵌套多行注释,jdbc执行报错-- 处理前(带独立/结尾) begin declare v_name varchar2(50) := '张三'; begin /* 注释含end:end不算结束 */ dbms_output.put_line(v_name); end; -- 内层end end; / -- 处理后(自动移除/,保留主体) begin declare v_name varchar2(50) := '张三'; begin /* 注释含end:end不算结束 */ dbms_output.put_line(v_name); end; -- 内层end end;
Prompt提示语句(仅Oracle系支持)
识别以prompt开头的语句(忽略大小写),
注意: JDBC执行报错。PROMPT 开始执行初始化SQL... prompt 执行建表语句
2.4.7.2. 解析sql脚本不能处理哪些sql
非Oracle系数据库的PL/SQL/特有语法
限制原因:限定仅支持Oracle系数据库,其他数据库的PL/SQL或特有语法会被跳过或报错。
MySQL的存储过程(用DELIMITER $$切换结束符)
PostgreSQL的DO $$ … $$匿名块
未以“独立/”结尾的PL/SQL块
限制原因:判定PL/SQL结束的唯一条件是“独立一行的/”,若PL/SQL块仅以end;结尾(无/),会被当作“未结束SQL”存入,执行时可能因语法不完整报错。
-- 无/结尾的PL/SQL块(代码会视为未结束,存入后执行可能报错) begin dbms_output.put_line('无/结尾'); end;
非独立/结尾的PL/SQL块
限制原因:仅匹配严格独立一行,前后无空格/字符,若/前后有空格或其他字符,不会被识别为PL/SQL结束符。
begin dbms_output.put_line('非独立/'); end; / -- 末尾有空格
PL/SQL块内包含独立/(非结束符)
限制原因:若PL/SQL主体内有独立一行的/(如动态SQL、注释外的/),会被误判为PL/SQL结束符,导致后续内容被截断。
begin -- 主体内的独立/(会被误判为结束符,后续内容被丢弃) execute immediate 'select 1/2 from dual'; / dbms_output.put_line('被截断的内容'); -- 不会被执行 end; /
带绑定变量的SQL/PL/SQL
限制原因:未处理Oracle绑定变量(如`:v1`),JDBC执行时会因“未设置绑定变量值”报错。
SELECT * FROM user_info WHERE id = :v_id; -- 绑定变量:v_id未处理
其他数据库特有语法/结束符
不支持MySQL的DELIMITER、PostgreSQL的$$等特有结束符。
不支持非Oracle系的存储过程/函数语法。
2.4.7.3. 服务端执行过程中的限制
以下限制均基于实际执行流程(从请求校验→脚本解析→任务执行→结果处理):
源库类型固定限制:仅支持 MySQL 和 Oracle 两种源库类型(不区分大小写),其他数据库类型(如 PostgreSQL、DB2、SQL Server 等)直接判定为不支持,同步流程终止。
目标库配置强依赖限制:目标库必须在数据源配置文件application.properties定义别名(alias),未配置别名或别名重复(重复时保留原有配置)的目标库无法执行同步,会直接记录配置缺失错误。
sql脚本路径不可自定义限制:SQL 脚本存放路径固定为「用户工作目录/config/scripts/目标库别名/脚本名」。
sql脚本有效性强制校验限制:目标库对应的脚本文件必须存在且为有效文件(非目录),若脚本文件不存在、为空,或解析后无有效 SQL(全是注释/空行),会直接终止该目标库同步,抛出“脚本不存在”或“无有效 SQL”异常。
任务超时时间固定限制:并行执行,是所有任务的 “整体等待时间”(不管并行 / 串行,最多等30 分钟)。
并发同步无冲突控制限制:多目标库同步任务并行执行,无并发控制机制(如分布式锁、乐观锁、目标库级别的串行控制),同一目标库若被多次触发同步,可能导致数据一致性冲突。
SQL 执行方式安全与性能限制:每条 SQL 都会单独创建 JDBC 连接和 Statement。
事务支持缺失限制:SQL 执行默认自动提交,无事务管理机制(无手动提交、回滚逻辑),一条 SQL 执行失败不会影响其他 SQL 执行。
无自动重试机制限制:SQL 执行失败、任务超时、线程中断等场景下,无自动重试逻辑,失败后仅记录错误详情,需手动重新触发同步流程,不支持失败重试策略(如重试次数、重试间隔配置)。
动态数据源不支持限制:仅支持数据源配置文件application.properties中预定义的目标库,无法动态添加新的目标库(如通过接口传入临时数据源配置)进行同步。
批量 SQL 执行不支持限制:不支持 JDBC 批量执行,每条 SQL 单独执行。
sql语句中包含–注释,可能会引起jdbc执行报错:示例如下:
SELECT * FROM --users WHERE id = 1; --注释了原本的表名users,导致FROM子句后无表名,SQL 语法不完整。 SELECT * FROM users WHERE id = 1-- AND name = 'test'; --注释了第一行末尾,第二行的AND name = 'test'成为独立的无效语句(AND不能单独存在)。
2.4.8. 增加blade执行sql用例的标志位判断逻辑
2.4.8.1. 概述
站在服务uk_agent的角度
服务定位: 服务uk_agent作为blade与多环境后端服务的代理中间件。
核心功能: 统一登录、参数替换、清算标志位校验、结果聚合、可在比对服务查看比对报告。
核心价值: 简化多环境对接、减少重复开发、保障清算流程一致性。
2.4.8.2. 时序图
登录请求时序图
业务请求时序图
时序图进一步解释:
登录请求(/tafund/submitLogin)在业务请求之前
业务请求的请求参数operator_code和timeStamp来自登录请求返回结果中operator_code和timeStamp
2.4.8.3. 核心协议与使用指南
自定义请求头规范(Ta-Hosts、Expected、Ta-Timeout字段格式、必填性、示例)
参数名 |
是否必须 |
说明 |
示例 |
|---|---|---|---|
Ta-Hosts |
是 |
多环境后端服务地址,用逗号分隔,格式为`ip:port` |
10.20.154.49:8080,10.20.154.50:8080 |
Expected |
否 |
多个清算标志位表达式,多条件用逗号分隔,格式为`路径=值`(路径为业务数据的层级索引) |
data_0_rows_0_scheDealStatus=3,data_0_returnCode=0 |
Ta-Timeout |
否 |
多后端请求的总超时时间,单位ms(默认30000ms) |
-- 请求头示例
Ta-Hosts: 10.20.154.49:8080
Expected: data_0_rows_0_ErrorNo=0000
Ta-Timeout: 30000
-- Expected对应的响应体示例如下:
"data": [
{
"total": -1,
"totalCount": -1,
"rows": [
{
"ErrorInfo": "刷新完成",
"ErrorNo": "0000"
}
],
"returnCode": "0",
"errorNo": "0",
"errorInfo": ""
}
]
-- 解释一下,data_0_rows_0_ErrorNo 表示上面响应体中data集合第一个元素data_0的属性rows的集合的第一个元素data_0_rows_0的属性ErrorNo的值为0000
登录请求使用(请求路径、请求体参数、curl示例、成功/失败响应解读)
-- 多后端环境,示例,oracle环境10.20.162.155:8080, dm环境 10.20.198.56:8080 -- uk_agent服务 10.188.129.216:9091 -- 登录请求uri=/tafund/submitLogin -- 登录请求体参数operatorCode和password -- curl示例如下 curl -i -X POST http://10.188.129.216:9091/tafund/submitLogin -H "Ta-Hosts: 10.20.162.155:8080,10.20.198.56:8080" -H "Content-Type: application/x-www-form-urlencoded" -d "operatorCode=378F8B35904CF45EE61FBAB59AE66DCA&password=D5A47C2B6C3F61A2BB0C3515399A133D" -- 成功响应信息如下:, -- 只有请求头Ta-Hosts中第一个元素的主机信息的响应结果,这里与单环境的登录的请求结果保持一致。 -- 同时也可以让blade暂存响应结果中operator_code和timeStamp,用于后续其他业务请求中请求参数替换 { "authorization": [], "operator_code": "1DD1ADA68063874A33596F6C4B5E2242381E9A4A2FE12C5141F0FC598A9C7C456BEBF766B88E0AE82E2D2361BA5FC376CB6424CAC2C35134", "operator_name": "系统管理员", "org_code": "", "org_name": "", "return_code": "0", "return_info": "", "error_no": "", "error_info": "", "nick_name": "", "user_token": "", "user_type": "1", "istemporary": "0", "wan_ip": "", "lan_ip": "", "mac": "", "inst_id": "0", "certificate_type": "", "customer_id": "0", "realInst_id": "0", "lastLoginDateTime": "2025-12-22 16:37:18", "systemLatestVersion": "2", "currUserBranchCode": "总部机构", "sysInitDate": "2025年12月22日", "appCode": "", "menuCode": "", "path": "", "timeStamp": "1766392633852", "systemLatestVersions": [ "TA6.0-FUND.V202506.08.000", "TA6.0-PUB.V202506.10.000" ], "menuKindList": [ { "kindCode": "console-fund-ta-vue", "kindColor": "#2E81FF", "menuIcon": "icon-icon_menu_fund", "kindName": "自建" } ], "businTypes": [ { "businType": "*", "businCode": "all", "businName": "全部", "kindCode": "console-bizframe-vue", "baseUrl": "API_HOME", "isDefault": "1", "remark": " " }, { "businType": "*", "businCode": "all", "businName": "公共", "kindCode": "console-biz-common-vue", "baseUrl": "COMMON_API_HOME", "isDefault": "0", "remark": " " }, { "businType": "0", "businCode": "standard", "businName": "理财TA", "kindCode": "console-fina-ta-vue", "baseUrl": "FINATA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "1", "businCode": "ant", "businName": "蚂蚁TA", "kindCode": "console-ant-ta-vue", "baseUrl": "ANT_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "2", "businCode": "net", "businName": "腾讯TA", "kindCode": "console-net-ta-vue", "baseUrl": "NET_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "3", "businCode": "trust", "businName": "信托TA", "kindCode": "console-trust-ta-vue", "baseUrl": "TRUST_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "4", "businCode": "etf", "businName": "ETFTA", "kindCode": "console-etf-ta-vue", "baseUrl": "ETF_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "5", "businCode": "fund", "businName": "基金自TA", "kindCode": "console-fund-ta-vue", "baseUrl": "FUND_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "6", "businCode": "sub", "businName": "分TA", "kindCode": "console-sub-ta-vue", "baseUrl": "SUB_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "8", "businCode": "firs", "businName": "中债报备", "kindCode": "console-firs-ta-vue", "baseUrl": "FIRS_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "9", "businCode": "account", "businName": "账户中心", "kindCode": "console-account-ta-vue", "baseUrl": "ACCOUNT_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "A", "businCode": "aop", "businName": "AOP", "kindCode": "console-aop-vue", "baseUrl": "AOP_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "M", "businCode": "monitor", "businName": "监控", "kindCode": " ", "baseUrl": "MONITOR_TA_API_HOME", "isDefault": "1", "remark": " " }, { "businType": "I", "businCode": "web", "businName": "互联网TA", "kindCode": "console-web-ta-vue", "baseUrl": "WEB_TA_API_HOME", "isDefault": "1", "remark": " " } ], "licCustomerName": "恒生电子股份有限公司" }业务请求使用(请求路径、参数替换规则、清算标志位校验示例、curl示例)
## 1.请求头示例
### 请求头Expected参数示例,Expected: data_0_rows_0_ErrorNo=0000,注意看响应体中data集合第一个元素的rows属性对应的集合的第一个元素的属性ErrorNo值为0000
### 业务请求uri示例/tafund/fundCacheRefreshQuery/fundCacheRefreshQueryQuery
### 后端服务10.20.154.49:8080
### uk_agent服务10.188.129.216:9091
curl -i -X POST \
http://10.188.129.216:9091/tafund/fundCacheRefreshQuery/fundCacheRefreshQueryQuery \
-H "Ta-Hosts: 10.20.154.49:8080" \
-H "Expected: data_0_rows_0_ErrorNo=0000" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "operator_code=1DD1ADA68063874A33596F6C4B5E22420D921280C61A7D65707A6499FAD2228D13A41055CC4CED48393B207AAB1521E5FF63F2F4D0CE2842&refreshType=0,1,2,3,5&timeStamp=1763983025267"
## 2. 成功响应部分
### HTTP响应头
```
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 243
Date: Fri, 12 Dec 2025 06:29:40 GMT
```
### 格式化响应体(JSON)
{
"code": 0,
"msg": "success",
"traceId": "Trace-Id=uk_agent_29cc0dd5-d39b-4438-b784-9398f2157aa3",
"results": [
{
"data": [
{
"total": -1,
"totalCount": -1,
"rows": [
{
"ErrorInfo": "刷新完成",
"ErrorNo": "0000"
}
],
"returnCode": "0",
"errorNo": "0",
"errorInfo": ""
}
]
}
],
"ipHosts": [
"10.20.154.49:8080"
],
"failedHosts": []
}
-- 注意看响应体组成部分,顶层: code、msg、traceId、results、ipHosts、failedHosts,见下面响应格式详解
-- 响应中results集合中每个元素依次表示不同后端返回的响应,其中data集合第一个元素data_0的属性rows对应的集合中第一个元素data_0_rows_0的属性ErrorNo的值是0000与清算标志位预期值Expected: data_0_rows_0_ErrorNo=0000一致
2.4.8.4. 响应格式详解
响应字段说明(code、msg、traceId、results、ipHosts、failedHosts)
字段名 |
类型 |
必填项 |
说明 |
|---|---|---|---|
code |
int |
是 |
响应状态码: |
traceId |
String |
是 |
链路id,可以在比对服务查看本次多环境操作过程中sql引起的数据变化比对,前提多个后端服务接入统一sql和多发 |
msg |
String |
是 |
响应信息描述: |
results |
list |
是 |
多环境后端服务的响应结果集合,集合中每个元素对应每个后端服务返回的原始结果,与ipHosts集合中元素对应 |
ipHosts |
list |
是 |
本次uk_agent转发的全部的后端环境,举例 |
failedHosts |
list |
是 |
处理失败的主机列表:空数组表示所有主机处理成功,非空则包含失败的主机地址,比如不符合预期结果,预期值来自自定义请求头Expected内容 |
部分失败的响应体示例如下:
-- 失败响应示例1
{
"code": 1,
"msg": "部分或全部主机响应不符合预期",
"traceId": "Trace-Id=uk_agent_29cc0dd5-d39b-4438-b784-9398f2157aa3",
"results": [
{
"data": [
{
"total": -1,
"totalCount": -1,
"rows": [
{
"ErrorInfo": "刷新完成",
"ErrorNo": "0000"
}
],
"returnCode": "0",
"errorNo": "0",
"errorInfo": ""
}
]
},
{
"data": [
{
"returnCode": "-1",
"errorNo": "9999",
"total": 0,
"errorInfo": "节点【刷新JVM缓存-公共库】返回错误信息:节点【刷新JVM缓存-交易库】返回错误信息:片区1:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确,片区2:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确];errCode:[9999],errMsg:[节点【刷新JVM缓存-账户库】返回错误信息:片区1:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确,片区2:调用服务[ta-fund-parameter]出错,请检查服务是否已启动/服务名是否配置正确",
"rows": [],
"totalCount": 0
}
]
}
],
"ipHosts": [
"10.20.162.155:8080",
"10.20.198.56:8080"
],
"failedHosts": [
"10.20.198.56:8080"
]
}
-- 这里失败的主机是10.20.198.56:8080,返回的失败响应见results集合中第二个
-- 登录失败响应示例2
{
"code": 1,
"msg": "部分或全部主机登录失败",
"traceId": "Trace-Id=uk_agent_ede62f70-850d-4353-af9e-5150cab11399",
"results": [
{
"authorization": [],
"operator_code": "1DD1ADA68063874A33596F6C4B5E22420D921280C61A7D65FC6FDBD6ECF4DFF579650058DC0B5ED994B478D15B463E93D7CC0AE8BC2471D3",
"operator_name": "系统管理员",
"org_code": "",
"org_name": "",
"return_code": "0",
"return_info": "",
"error_no": "",
"error_info": "",
"nick_name": "",
"user_token": "",
"user_type": "1",
"istemporary": "0",
"wan_ip": "",
"lan_ip": "",
"mac": "",
"inst_id": "0",
"certificate_type": "",
"customer_id": "0",
"realInst_id": "0",
"lastLoginDateTime": "2025-12-22 16:12:18",
"systemLatestVersion": "2",
"currUserBranchCode": "总部机构",
"sysInitDate": "2025年12月22日",
"appCode": "",
"menuCode": "",
"path": "",
"timeStamp": "1766391134269",
"systemLatestVersions": [
"TA6.0-FUND.V202506.08.000",
"TA6.0-PUB.V202506.10.000"
],
"menuKindList": [
{
"kindCode": "console-fund-ta-vue",
"kindColor": "#2E81FF",
"menuIcon": "icon-icon_menu_fund",
"kindName": "自建"
}
],
"businTypes": [
{
"businType": "*",
"businCode": "all",
"businName": "全部",
"kindCode": "console-bizframe-vue",
"baseUrl": "API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "*",
"businCode": "all",
"businName": "公共",
"kindCode": "console-biz-common-vue",
"baseUrl": "COMMON_API_HOME",
"isDefault": "0",
"remark": " "
},
{
"businType": "0",
"businCode": "standard",
"businName": "理财TA",
"kindCode": "console-fina-ta-vue",
"baseUrl": "FINATA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "1",
"businCode": "ant",
"businName": "蚂蚁TA",
"kindCode": "console-ant-ta-vue",
"baseUrl": "ANT_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "2",
"businCode": "net",
"businName": "腾讯TA",
"kindCode": "console-net-ta-vue",
"baseUrl": "NET_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "3",
"businCode": "trust",
"businName": "信托TA",
"kindCode": "console-trust-ta-vue",
"baseUrl": "TRUST_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "4",
"businCode": "etf",
"businName": "ETFTA",
"kindCode": "console-etf-ta-vue",
"baseUrl": "ETF_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "5",
"businCode": "fund",
"businName": "基金自TA",
"kindCode": "console-fund-ta-vue",
"baseUrl": "FUND_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "6",
"businCode": "sub",
"businName": "分TA",
"kindCode": "console-sub-ta-vue",
"baseUrl": "SUB_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "8",
"businCode": "firs",
"businName": "中债报备",
"kindCode": "console-firs-ta-vue",
"baseUrl": "FIRS_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "9",
"businCode": "account",
"businName": "账户中心",
"kindCode": "console-account-ta-vue",
"baseUrl": "ACCOUNT_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "A",
"businCode": "aop",
"businName": "AOP",
"kindCode": "console-aop-vue",
"baseUrl": "AOP_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "M",
"businCode": "monitor",
"businName": "监控",
"kindCode": " ",
"baseUrl": "MONITOR_TA_API_HOME",
"isDefault": "1",
"remark": " "
},
{
"businType": "I",
"businCode": "web",
"businName": "互联网TA",
"kindCode": "console-web-ta-vue",
"baseUrl": "WEB_TA_API_HOME",
"isDefault": "1",
"remark": " "
}
],
"licCustomerName": "恒生电子股份有限公司"
},
{
"msg": "登录失败",
"reason": "I/O error on POST request for \"http://10.20.198.56:8081/tafund/submitLogin\": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect"
}
],
"ipHosts": [
"10.20.162.155:8080",
"10.20.198.56:8081"
],
"failedHosts": [
"10.20.198.56:8081"
]
}
-- 登录主机10.20.198.56:8081失败
2.4.8.5. 附录
响应码对照表(所有可能的code值及含义)
响应码 |
信息 |
原因 |
解决方案 |
|---|---|---|---|
0 |
success |
登录成功、业务请求成功(有标志位判断也是后端环境都符合预期(参考自定义请求头Expected)) |
|
1 |
未登录或登录缓存已过期,请先发送登录请求 |
业务请求时登录令牌缓存不存在/过期 |
blade侧立即重新发起登录请求 |
1 |
Ta-Hosts 格式不合法(需为ip:port,ip:port格式) |
Ta-Hosts 头非ip:port格式 |
在uk_agent侧封装请求参数检核工具类,发起请求前自动检核Ta-Hosts、Expected、请求体、必选参数的合法性,提前拦截错误 |
1 |
处理请求异常:内部服务错误 |
uk_agent自身线程池满或转发请求超时 |
可重启uk_agent服务临时恢复 |
1 |
部分或全部主机响应不符合预期 |
后端服务返回的响应与预期不符 |
排查后端服务出错原因 |
1 |
请求处理超时 |
uk_agent在一定时间内没有从后端服务获取响应结果 |
请求头Ta-Timeout增加uk_agent读取时间 |
1 |
请求处理异常:xxx |
uk_agent聚合多个后端服务返回的响应结果出现异常 |
根据请求处理异常:xxx 定位异常原因和解决方案 |
1 |
登录请求缺少operatorCode或password参数 |
登录请求(/tafund/submitLogin)缺少必要参数:operatorCode或password |
登录请求的请求参数增加必要参数operatorCode和password |
1 |
业务请求缺少operator_code参数 |
业务请求缺少operator_code参数 |
业务请求的请求参数增加必要参数operator_code,提示:参数operator_code来自登录请求返回结果 |
1 |
处理请求异常: xxx |
uk_agent在多环境请求处理流程异常 |
根据处理请求异常: xxx 定位异常原因和解决方案 |
2.4.9. 支持TA SQL语法标志位判断(校验多库SQL执行结果一致性)
2.4.9.1. 概述
复用原有标准接口 http://ip:port/api/exec/sql 无任何路径变更,在原有请求参数基础上 新增2个可选核心参数,实现对指定目标库执行校验SQL、并将SQL执行结果与预期值进行精准比对的能力。
核心价值:解决不同信创库执行相同SQL后,结果数据不一致/不符合预期 的验证诉求,自动化校验SQL执行结果,替代人工查询比对,提升多库兼容性测试效率,保障TA业务SQL执行结果的一致性。
核心能力说明
复用原有接口 http://ip:port/api/exec/sql :无需新增接口地址,保持调用方式、请求方式、Content-Type完全不变;
新增参数为 非必传项 :不传入则执行原有「脚本执行」逻辑,传入则执行「SQL结果校验」逻辑,两种模式互不影响;
一一对应校验规则:传入的校验SQL数组与预期结果数组,按 数组索引顺序严格一一匹配 校验;
多库并行校验:对 targetDbs 中配置的所有目标库,并行执行所有校验SQL并完成结果比对;
精准错误反馈:校验不通过时,明确返回「校验项索引、校验SQL、预期结果、实际结果」,快速定位不一致问题;
兼容原有返回结构:响应体的整体格式、字段含义、状态枚举值完全沿用原有规范,无适配成本。
2.4.9.2. 接口说明
接口请求地址(复用已有的接口)
请求方式
POST
Content-Type
application/json;charset=UTF-8
原有参数保持不变(必传)
参数名 |
类型 |
是否必须 |
说明 |
|---|---|---|---|
sourceDb |
String |
是 |
源数据库类型,不区分大小写,取值范围[oracle,mysql] |
targetDbs |
Array<String> |
是 |
集合中每个元素是数据库的具体别名,对应数据源配置项 agent.datasource.sources[i].alias |
新增扩展参数(可选,二选一执行逻辑)
说明:原参数 scriptName 为「脚本执行模式」核心参数,新增的 checkSql/expectedResult 为「SQL结果校验模式」核心参数,两种模式 互斥:
传 scriptName → 执行原有「多库执行指定SQL脚本」逻辑;
传 checkSql + expectedResult → 执行新增「多库执行校验SQL+结果比对」逻辑;
均不传/均传 → 接口返回参数错误,提示「请选择一种执行模式:脚本执行/结果校验」。
新增参数名 |
类型 |
是否必须 |
说明 |
|---|---|---|---|
checkSql |
Array<String> |
否 |
待执行的校验SQL数组,仅支持单行查询类SQL(SELECT) ,数组中每个元素为一条完整的校验SQL语句 |
expectedResult |
Array<Object> |
否 |
校验SQL对应的预期结果数组,数组长度必须与checkSql一致、索引一一对应;值类型支持字符串、数字,无结果时可传空字符串 “” |
新增参数约束规则
checkSql 与 expectedResult 必须 成对出现,有其一必有其二,缺一不可;
两个数组的 长度必须完全相同,否则直接判定参数非法;
checkSql 内仅支持 SELECT开头的单行查询SQL,不支持DML(增删改)、DDL(建表)、PL/SQL块、存储过程等;
每条校验SQL要求 返回且仅返回1行多列结果 (聚合查询最佳,如max/min/count/单行字段查询),若返回多行多列,取第一行作为实际结果参与比对;
expectedResult 的值类型会自动适配实际执行结果类型做 等值匹配,例如预期传数字 20100531,实际返回数值型结果也可精准匹配;
大小写兼容:数据库返回的字符串结果,比对时 不区分大小写 。
2.4.9.3. 时序图
2.4.9.4. 请求和响应示例
-- 示例1:部分SQL执行结果不符合预期(校验不通过,返回failed)
-- 请求参数
{
"checkSql": [
"select init_date from tbsysarg;",
"select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')"
],
"expectedResult": [
20100531,
"4"
],
"sourceDb": "oracle",
"targetDbs": [
"oracle_fund60pub",
"dm_fund60pub"
]
}
-- 响应参数
{
"traceId": "uk_agent_72370992605345b0bed8cf8ec31a5daa",
"status": "failed",
"details": [
{
"name": "oracle_fund60pub",
"status": "failed",
"error": "目标库oracle_fund60pub检查项校验不通过:\n【检查项索引1】\n- 校验SQL:select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')\n- 预期结果:4\n- 实际结果:3",
"sqlCount": 2,
"executeDuration": 73,
"failedSqlDetails": null
},
{
"name": "dm_fund60pub",
"status": "failed",
"error": "目标库dm_fund60pub检查项校验不通过:\n【检查项索引1】\n- 校验SQL:select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')\n- 预期结果:4\n- 实际结果:3",
"sqlCount": 2,
"executeDuration": 24,
"failedSqlDetails": null
}
]
}
-- 示例2:所有SQL执行结果符合预期(校验通过,返回success)
-- 请求参数
{
"checkSql": [
"select init_date from tbsysarg;",
"select max(t.sche_app_dealstatus) from tbscheduleappprocess t where t.sche_app_dealstatus not in ('Z')"
],
"expectedResult": [
20100531,
"3"
],
"sourceDb": "oracle",
"targetDbs": [
"oracle_fund60pub",
"dm_fund60pub"
]
}
-- 接口响应参数
{
"traceId": "uk_agent_473e72a694774859b8f01869ab9b79b9",
"status": "success",
"details": [
{
"name": "oracle_fund60pub",
"status": "success",
"error": null,
"sqlCount": 2,
"executeDuration": 63,
"failedSqlDetails": null
},
{
"name": "dm_fund60pub",
"status": "success",
"error": null,
"sqlCount": 2,
"executeDuration": 24,
"failedSqlDetails": null
}
]
}
响应字段补充说明(完全兼容原有规范)
本次增强 无任何响应体字段变更,所有返回字段含义、状态枚举值与原有接口完全一致,仅对 error 字段的内容做针对性补充,补充说明如下:
顶层核心状态 status 枚举(不变)
success :所有目标库的 全部校验SQL执行成功+结果比对一致;
failed :所有目标库校验失败,或存在SQL执行异常(如SQL语法错误、表不存在、权限不足)。
明细项 details 内字段补充说明
sqlCount :当前目标库执行的 校验SQL总数 (即 checkSql 数组的长度);
executeDuration :当前目标库执行 所有校验SQL的总耗时(毫秒);
error :
校验通过 → null;
校验不通过 → 格式化文本,清晰展示「目标库名称、校验项索引、校验SQL、预期结果、实际结果」;
SQL执行异常 → 原有报错逻辑,如「表不存在、SQL语法错误」等原生错误信息;
failedSqlDetails :
SQL结果校验模式下固定为null,该字段仅在「脚本执行模式」下返回具体失败SQL的行号/内容,两种模式互不干扰。
2.4.9.5. 注意事项
本能力仅支持 查询类SQL(SELECT),不支持执行任何修改类SQL,保障数据库数据安全;
校验SQL建议使用 聚合函数查询(max/min/count/avg)、单行查询 ,避免返回多行多列导致结果截取不准确;
预期结果的类型无需严格匹配,接口会自动做类型转换后比对(如数字 20100531 与字符串 “20100531” 判定为一致);
所有目标库的数据源配置,仍沿用原有 application.properties 中的配置,无需新增任何数据源配置;
执行结果的比对日志,可通过 traceId 在 比对服务页面 查看完整链路详情,与原有脚本执行链路日志一致;
性能说明:校验SQL为轻量级查询,多库并行执行,单条SQL执行耗时与直接在数据库客户端执行耗时基本一致。