自动同步多服务器下SQL脚本3.0

ops/2025/3/14 17:17:18/

由于上一版发现数据库【MySQL】不支持DML事务回滚,该迭代主要是去兼容这种问题。

数据表新增一个completed字段,用来表示当前版本下同步成功的个数。

数据表

sql">CREATE TABLE `auto_sql_version`  (`id` int NOT NULL AUTO_INCREMENT,`version` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '版本号',`created` datetime NULL DEFAULT NULL COMMENT '插入时间',`completed` int NULL DEFAULT NULL COMMENT '完成同步的sql脚本数',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `uniqueKey_version`(`version` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '数据版本' ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

run方法/主方法逻辑

 新增了一个判断,对于查到的版本号,需要额外判断completed字段是否为null,非null说明有无成功执行的sql,应该继续执行。

 @Overridepublic void run(ApplicationArguments args) throws Exception {if (!databaseAutoFillSwitch) {log.info("database auto fill switch is false,skip auto fill");return;}String basePath = "/dbVersion/MySQL.sql";InputStream inputStream = this.getClass().getResourceAsStream(basePath);String sqlScript = IoUtil.readUtf8(inputStream);if (null == inputStream) {log.info("inputStream is null");return;}inputStream.close();List<String> versionList = new ArrayList<>();String[] lines = sqlScript.split("\n");for (String line : lines) {if (line.toLowerCase().contains(PREFIX)) {versionList.add(line.substring(line.lastIndexOf("-") + 1).trim().toLowerCase());}}int left = 0, right = versionList.size() - 1;// 通过二分查找,最终得到的left,表示不在库中的最小版本号,如果left == list.size() 则还需要去查询库中是否真正存在while (left <= right) {int mid = left + (right - left) / 2;VersionVo versionVo = autoSqlFillDao.selectVersionAndCompleted(versionList.get(mid));if (versionVo == null || versionVo.getVersion() == null || versionVo.getCompleted() != null) {// 当前版本号为完成同步right = mid - 1;} else {// 当前版本号已完成同步left = mid + 1;}}if (left == versionList.size()) {log.info("no new sqlVersion found,finished auto sql fill");return;}//proxy = (AutoSqlFillImpl) AopContext.currentProxy();String result = "";// 现在开始,从left指针开始遍历所有的sql脚本while (left < versionList.size()) {// 得到版本号整串String latestVersion = versionList.get(left);// 写入数据库的版本号前缀【过滤掉无效字符,统一版本号】String version = latestVersion.substring(latestVersion.lastIndexOf("-") + 1).trim().toLowerCase();// 获取版本号在sql脚本中的位置int index = sqlScript.indexOf(latestVersion);if (index == -1) {log.info("current version exception:{}", version);LogUtil.info(version, "current version exception");return;}index += latestVersion.length();String nextVersion = "";if (left + 1 < versionList.size()) {nextVersion = versionList.get(left + 1);int nextIndex = sqlScript.indexOf(nextVersion);if (nextIndex != -1) {result = sqlScript.substring(index, nextIndex).trim();executeSqlScript(result, version);} else {log.info("should have next sqlVersion,but next version not found:{}", nextVersion);LogUtil.info(version, "should have next sqlVersion,but next version not found");}} else {// 没有下一个版本,提取剩余部分result = sqlScript.substring(index).trim();executeSqlScript(result, version);}left++;}log.info("auto deploying sql finished...");}

 service逻辑

public void executeSqlScript(String sqlScript, String version) {//查到的最小版本号,可能由于DDL语句执行失败,导致版本未同步完全,需要独立判断//这里加if else分支,保证只有第一次查库,后面循环不需要重复查库了if (!first) {VersionVo versionVo1 = autoSqlFillDao.selectVersionAndCompleted(version);//如果当前版本号存在未成功同步的表结构,则接着执行完if (Objects.nonNull(versionVo1) && versionVo1.getVersion() != null && versionVo1.getCompleted() != null) {int completed = versionVo1.getCompleted();readBySql(sqlScript, version, completed);}first = true;}else{readBySql(sqlScript, version, 0);}// 如果所有 SQL 语句都成功执行,插入版本记录AutoSqlVersionEntity entity = new AutoSqlVersionEntity();entity.setVersion(version);entity.setCreated(new Date());autoSqlFillDao.saveOrUpdateByVersion(entity);}

 具体的数据库逻辑

对于有执行失败的版本号,记录对应的completed字段。

 /*** 遍历sql并执行* @param sqlScript* @param version* @param completed*/public void readBySql(String sqlScript, String version, Integer completed) {String[] resultList = sqlScript.split(";");for (int i = completed; i < resultList.length; i++) {String line = resultList[i];if (!line.toLowerCase().contains("drop") && !line.toLowerCase().contains("delete") && line.length() > 10 && !line.contains("--")) {// 开始执行插入操作try {autoSqlFillDao.updateSql(line.trim());log.info("version:{}, start sql script:{}", version, line.trim());LogUtil.info("version, sql script:", version, line.trim());completed++;} catch (Exception e) {AutoSqlVersionEntity entity = new AutoSqlVersionEntity();entity.setVersion(version);entity.setCreated(new Date());entity.setCompleted(completed);autoSqlFillDao.saveOrUpdateByVersion(entity);log.info("version:{}, sql执行异常:{}", version, line.trim());LogUtil.info("sql执行异常", line.trim());StringBuilder stringBuilder = new StringBuilder("### 接口异常\n");stringBuilder.append("- **服务环境**: " + envValue + "\n");stringBuilder.append("- **异常信息**:" + e + "  \n");if (!"dev".equals(envValue)) {RobotClient.pushMarkDown("自动化执行sql脚本异常,请注意脚本格式是否正确", stringBuilder.toString());}throw new RuntimeException("sql auto exception:" + line.trim());}}}}


http://www.ppmy.cn/ops/165717.html

相关文章

⚡️Jolt -- 通过JSON配置来处理复杂数据转换的工具

简介&#xff1a;一个能够通过JSON配置&#xff08;特定的语法&#xff09;来处理复杂数据转换的工具。 比如将API响应转换为内部系统所需的格式&#xff0c;或者处理来自不同来源的数据结构差异。例如&#xff0c;将嵌套的JSON结构扁平化&#xff0c;或者重命名字段&#xff0…

vue3 中使用 Recorder 实现录音并上传,并用Go语言调取讯飞识别录音(Go语言)

录音并识别 效果图一、开启游览器录音权限二、前端代码三、Go代码,上传到讯飞识别录音返回到前端 效果图 recorder-core插件可以在网页中进行录音。录音文件(blob)并可以自定义上传&#xff0c;可以下载录音文件到本地,本文录音过程中会显示可视化波形&#xff0c;插件兼容PC端…

赛事|基于SprinBoot+vue的CSGO赛事管理系统(源码+数据库+文档)

CSGO赛事管理系统 目录 基于SprinBootvue的CSGO赛事管理系统 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3参赛战队功能模块 4合作方功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…

OBJ文件生成PCD文件(python 实现)

代码实现 将 .obj 文件转换为 .pcd&#xff08;点云数据&#xff09; 代码文件。 import open3d as o3d# 加载 .obj 文件 mesh o3d.io.read_triangle_mesh("bunny.obj")# 检查是否成功加载 if not mesh.has_vertices():print("无法加载 .obj 文件&#xff0c…

Manus:成为AI Agent领域的标杆

一、引言 官网&#xff1a;Manus 随着人工智能技术的飞速发展&#xff0c;AI Agent&#xff08;智能体&#xff09;作为人工智能领域的重要分支&#xff0c;正逐渐从概念走向现实&#xff0c;并在各行各业展现出巨大的应用潜力。在众多AI Agent产品中&#xff0c;Manus以其独…

C# net deepseek RAG AI开发 全流程 介绍

deepseek本地部署教程及net开发对接 步骤详解&#xff1a;安装教程及net开发对接全流程介绍 DeepSeekRAG 中的 RAG&#xff0c;全称是 Retrieval-Augmented Generation&#xff08;检索增强生成&#xff09;&#xff0c;是一种结合外部知识库检索与大模型生成能力的技术架构。其…

2025年03月11日Github流行趋势

项目名称&#xff1a;pydoll 项目地址url&#xff1a;https://github.com/thalissonvs/pydoll项目语言&#xff1a;Python历史star数&#xff1a;1372今日star数&#xff1a;148项目维护者&#xff1a;thalissonvs, apps/github-actions, LucasAlvws, CaioWzy, Patolox项目简介…

PHP语言的开源贡献

PHP语言的开源贡献及其影响 引言 在互联网技术飞速发展的今天&#xff0c;开源软件已经成为了软件开发的重要组成部分。它不仅改变了我们开发和使用软件的方式&#xff0c;更在促进技术共享、推动创新和降低开发成本等方面发挥了重要作用。而在众多的开源项目中&#xff0c;P…