当前位置: 首页 > article >正文

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

考虑到1.0的适用场景太过苛刻,一次只支持读取至多一个版本的脚本变化,想涉及多个脚本的连续读取就有困难,于是有了2.0。

该版本支持读取多个版本的sql脚本,并且如果某一脚本出现sql问题【如重复插入相同名称的字段】,则当前版本回滚,同时hd_version表不留痕,以便下次部署的时候可以再次插入。

2.0版本主要变动就是实现类: 

考虑到随着版本的变动,后续脚本文件维护的版本过多,不采用顺序读,改用二分查找
当然,也可以一个大版本对应一个SQL文件,也可以用倒序查找,都可以的...这里只是提供一个方案。用二分单纯看上了它的时间复杂度O(logN)。

方法讲解:

主要分为两个方法:

一个是通过二分寻找到最小【未执行过sql脚本的】版本号
一个是通过当前版本号以及sql脚本,去执行sql语句。

通过二分找到最小的版本号

/*** 二分与倒序查询的比较:* 二分可以在短的时间内,快速找到目标值,但是倒序排序,理论上还是O(N)的时间复杂度* 如果场景是,每个迭代只进行最后一至两个版本的sql脚本,那么倒序排序更好,二分的话,时间复杂度比较稳定*/@Override@Transactionalpublic 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;if( 0 == hdCommonDao.selectVersion(versionList.get(mid))){// 库中无对应版本号right = mid - 1;}else{// 库中存在对应版本号left = mid + 1;}}if(left == versionList.size()){return;}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();((HdSchemaExecutor)AopContext.currentProxy()).executeSqlScript(result, version);} else {log.info("next version not found:{}", nextVersion);LogUtil.info(version, "next version not found");}} else {// 没有下一个版本,提取剩余部分result = sqlScript.substring(index).trim();((HdSchemaExecutor)AopContext.currentProxy()).executeSqlScript(result, version);}left++;}log.info("auto deploying sql finished...");}

 写库方法

根据sql脚本以及对应的版本号完成写入功能。

    @Transactional(rollbackFor = Exception.class)public void executeSqlScript(String sqlScript, String version) throws Exception {String[] resultList = sqlScript.split(";");for (String line : resultList) {if (!line.toLowerCase().contains("drop") && !line.toLowerCase().contains("delete") && line.length() > 10 && !line.contains("--")) {// 开始执行插入操作try {hdCommonDao.updateSql(line.trim());log.info("version:{}, start sql script:{}", version, line.trim());LogUtil.info("version, sql script:", version, line.trim());} catch (Exception e) {log.info("version:{}, sql执行异常:{}", version, line.trim());LogUtil.info("sql执行异常", line.trim());throw new Exception("sql auto exception:"+ line.trim());}}}// 如果所有 SQL 语句都成功执行,插入版本记录HdVersionEntity entity = new HdVersionEntity();entity.setVersion(version);entity.setCreated(new Date());hdCommonDao.insertVersion(entity);}

细节说明

这里主要一个点,事务失效

首先,我们在方法A去调用方法B的时候,不是简单的在方法A上加@Transactional注解就可以的,需要两步:①在启动类上开启暴露代理的开关②在调用方法B的时候,改用代理对象去调用方法B【默认是this对象】

@Order(1)
@Component
@EnableAspectJAutoProxy(exposeProxy = true)
@Slf4j
public class HdSchemaExecutor implements ApplicationRunner

 注意这里需要通过代理对象调用方法B~

((HdSchemaExecutor)AopContext.currentProxy()).executeSqlScript(result, version);

如果你想在方法B完成手动throw错误,还需要在方法B上添加事务监听的范围。

@Transactional(rollbackFor = Exception.class)
public void executeSqlScript(String sqlScript, String version) throws Exception

写在最后

Mysql不支持DDL事务,只支持DML事务....

相关文章:

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

考虑到1.0的适用场景太过苛刻&#xff0c;一次只支持读取至多一个版本的脚本变化&#xff0c;想涉及多个脚本的连续读取就有困难&#xff0c;于是有了2.0。 该版本支持读取多个版本的sql脚本&#xff0c;并且如果某一脚本出现sql问题【如重复插入相同名称的字段】&#xff0c;…...

深度学习与大模型-张量

大家好&#xff01;今天我们来聊聊张量&#xff08;Tensor&#xff09;。别被这个词吓到&#xff0c;其实它没那么复杂。 什么是张量&#xff1f; 简单来说&#xff0c;张量就是一个多维数组。你可以把它看作是一个装数据的容器&#xff0c;数据的维度可以是一维、二维&#…...

DeepSeek+Maxkb+Ollama+Docker搭建一个AI问答系统

DeepSeekMaxkbOllamaDocker搭建一个AI问答系统 文章目录 DeepSeekMaxkbOllamaDocker搭建一个AI问答系统前言一、创建同一内网的网络二、拉取两个镜像三、启动Ollama以及调试Maxkb4.Maxkb创建一个应用并建立知识库5、应用效果总结 前言 我觉得只要是使用Docker技术&#xff0c;…...

江科大51单片机笔记【12】DS18B20温度传感器(上)

写在前言 此为博主自学江科大51单片机&#xff08;B站&#xff09;的笔记&#xff0c;方便后续重温知识 在后面的章节中&#xff0c;为了防止篇幅过长和易于查找&#xff0c;我把一个小节分成两部分来发&#xff0c;上章节主要是关于本节课的硬件介绍、电路图、原理图等理论…...

P8662 [蓝桥杯 2018 省 AB] 全球变暖--DFS

P8662 [蓝桥杯 2018 省 AB] 全球变暖--dfs 题目 解析讲下DFS代码 题目 解析 这道题的思路就是遍历所有岛屿&#xff0c;判断每一块陆地是否会沉没。对于这种图的遍历&#xff0c;我们首先应该想到DFS。 代码的注意思想就是&#xff0c;在主函数中遍历找出所有岛屿&#xff0c…...

【让POSTGRESQL支持MS SQLSERVER的 extension】 Babelfish for PostgreSQL介绍及源码安装

什么是 Babelfish for PostgreSQL? Babelfish for PostgreSQL(简称 Babelfish)是一个扩展(extension),使 PostgreSQL 兼容 Microsoft SQL Server(MSSQL),允许 MSSQL 客户端和应用程序直接连接到 PostgreSQL 数据库,而无需对 SQL 语法、T-SQL 存储过程、数据类型等进…...

Vue 侧边栏导航栏 el-menu单个item和多个item

在固钉的下面去写菜单导航栏。 <el-menu class"aside-menu" router :default-active"$route.path" :collapse"isCollapse" background-color"#131b27" text-color"#bfcbd9" active-text-color"#20a0ff" :defau…...

Unity Dots从入门到精通之 Prefab引用 转 实体引用

文章目录 前言安装 DOTS 包实体引用Authoring 前言 DOTS&#xff08;面向数据的技术堆栈&#xff09;是一套由 Unity 提供支持的技术&#xff0c;用于提供高性能游戏开发解决方案&#xff0c;特别适合需要处理大量数据的游戏&#xff0c;例如大型开放世界游戏。 本文讲解我在…...

无人机避障——XTDrone中运行VINS-Fusion+Ego-planner进行路径规划

本文聚焦于无人机避障技术领域的经典方案&#xff0c;重点探讨视觉双目VINS-Fusion建图与Ego-planner路径规划的组合应用。通过视觉双目VINS-Fusion实现精准的环境建图与自身定位&#xff0c;结合Ego-planner的高效路径规划能力&#xff0c;使无人机在复杂环境中实现自主避障飞…...

【沐渥科技】氮气柜日常如何维护?

氮气柜的维护是确保其长期稳定运行、延长使用寿命和保持环境控制精度的关键。以下是沐渥氮气柜的日常维护和定期保养指南&#xff1a; 一、日常维护 柜体清洁 定期用软布擦拭柜体表面和内部&#xff0c;避免灰尘堆积。避免使用腐蚀性清洁剂&#xff0c;防止损伤密封条或传感器。…...

MATLAB 控制系统设计与仿真 - 24

PID 控制器分析- 控制器的形式 连续控制器的结构&#xff1a; 为滤波时间常数&#xff0c;这类PID控制器在MATLAB系统控制工具箱称为并联PID控制器&#xff0c;可由MATLAB提供的pid函数直接输入&#xff0c;格式为&#xff1a; 其他类型的控制器也可以由该函数直接输入&#x…...

C# Excel开源操作库MiniExcel使用教程

简介 MiniExcel简单、高效避免OOM的.NET处理Excel查、写、填充数据工具。 目前主流框架大多需要将数据全载入到内存方便操作&#xff0c;但这会导致内存消耗问题&#xff0c;MiniExcel 尝试以 Stream 角度写底层算法逻辑&#xff0c;能让原本1000多MB占用降低到几MB&#xff0…...

linux(权限)

sudo 主要用来短暂的提权 权限 就是 >角色目标属性 这里面的角色就是---拥有者----所属组----other 所属组的目的&#xff1f; 更细化的管理 chmod 就是修改权限制 我们要是想要切换到体育的账号&#xff0c;我们可以去看一下有几个账号,我…...

paimon---同步mysql数据到paimon表中

1.1、mysql源表 CREATE TABLE mysql_orders (order_id varchar(100) NOT NULL,user_id varchar(100) DEFAULT NULL,amount decimal(10,2) DEFAULT NULL,update_time timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),PRIMARY KEY (order_i…...

《OpenCV》—— dlib(换脸操作)

文章目录 dlib换脸介绍仿射变换在 dlib 换脸中的应用 换脸操作 dlib换脸介绍 dlib 换脸是基于 dlib 库实现的一种人脸替换技术&#xff0c;以下是关于它的详细介绍&#xff1a; 原理 人脸检测&#xff1a;dlib 库中包含先进的人脸检测器&#xff0c;如基于 HOG&#xff08;方向…...

修改Flutter项目使用的JAVA版本

使用Android studio开发Flutter过程中&#xff0c;会默认使用Android studio自带的JDK。因为新版Android studio中的JDK版本过高&#xff0c;导致项目编译时总是无法完成&#xff0c;报【 unsupported class file major version 65】错误&#xff0c;如下&#xff1a; 解决这个…...

虚拟dom的diff中的双端比较算法

‌双端比较算法是Vue中用于高效比较新旧VNode子节点的一种策略‌。该算法的核心思想是&#xff0c;通过从新旧VNode子节点的两端开始比较&#xff0c;逐步向中间靠拢&#xff0c;以找到最小的差异并据此更新DOM。以下是双端比较算法的大致流程&#xff1a; ‌初始化指针‌&…...

# 如何确认elementary os (linux)使用的是Wayland而不是x11?

如何确认elementary os &#xff08;linux&#xff09;使用的是Wayland而不是x11&#xff1f; 文章目录 如何确认elementary os &#xff08;linux&#xff09;使用的是Wayland而不是x11&#xff1f;**方法 1&#xff1a;使用 loginctl 命令&#xff08;systemd 系统&#xff0…...

VMware安装Windows server 2016

1、新建虚拟机&#xff0c;选择自定义模式 2、选择兼容性 4、命名虚拟机 5、固件类型 EFI 虚拟磁盘类型&#xff0c;不同电脑推荐的类型不同&#xff0c;用默认的就行 删除声卡和打印机 检查网络配置 选择本地的Windows server 2016的系统镜像&#xff0c;系统镜像可以去Window…...

K8s 1.27.1 实战系列(十)PV PVC

一、核心概念与关系 ​1、PV(Persistent Volume)​ PV 是集群中的持久化存储资源,由管理员预先创建并配置,独立于 Pod 生命周期。它抽象了底层存储(如 NFS、云存储等),定义存储容量、访问模式(如 ReadWriteOnce)、回收策略(Retain/Delete/Recycle)等属性。例如,一…...

HippoRAG 2 原理精读

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 整体流程离线索引阶段在线检索和问答阶段 总结 整体流程 从上图可以看出&#xff0c;整个流程分为两个阶段 1、离线索引阶段 2、在线检索和问答阶段 离线索引阶段…...

三:FFMPEG拉流读取模块的讲解

FFMPEG拉流读取模块在远程监控项目最核心的作用是读取UVC摄像头传输的H264码流&#xff0c;并对其码流进行帧的提取&#xff0c;提取完成之后则把数据传输到VDEC解码模块进行解码。而在我们这个项目中&#xff0c;UVC推流的功能由FFMPEG的命令完成。 FFMPEG拉流读取模块的API…...

linux makefile tutorial

一个makefile的教程&#xff0c;几个小时就能看完&#xff0c;对makefile有个总体加细节的系统了解&#xff0c;非常不错&#xff1a; Learn Makefiles With the tastiest examples 中文翻译版&#xff1a; 起步 - Makefile 教程 (gavinliu6.github.io) gcc官网手册&#x…...

【从零开始学习计算机科学】操作系统(五)处理器调度

【从零开始学习计算机科学】操作系统(五)处理器调度 处理器调度一些简单的短程调度算法的思路先来先服务(First-Come-First-Served,FCFS)优先级调度及其变种最短作业优先调度算法(SJF)--非抢占式最短作业优先调度算法(SJF)--抢占式最高响应比优先调度算法轮转调度算法…...

视觉图像处理

在MATLAB中进行视觉图像处理仿真通常涉及图像增强、滤波、分割、特征提取等操作。以下是一个分步指南和示例代码,帮助您快速入门: 1. MATLAB图像处理基础步骤 1.1 读取和显示图像 % 读取图像(替换为实际文件路径) img = imread(lena.jpg); % 显示原图 figure; subplot(2…...

从零开始设计一个完整的网站:HTML、CSS、PHP、MySQL 和 JavaScript 实战教程

前言 本文将从实战角度出发&#xff0c;带你一步步设计一个完整的网站。我们将从 静态网页 开始&#xff0c;然后加入 动态功能&#xff08;使用 PHP&#xff09;&#xff0c;连接 数据库&#xff0c;最后加入 JavaScript 实现交互功能。通过这个教程&#xff0c;你将掌握一个…...

JavaScript(Web APIs)

这个阶段两天也能看完 目录 壹_DOM-获取元素 00、获取DOM元素&#xff08;根据CS选择器来获取DOM元素&#xff09; 01、修改元素内容 02、修改CSS 03、H5自定义属性 04、定时器 贰_DOM-事件基础 00、事件监听 01、事件类型 02、事件对象 03、环境对象 04、回调函数 叁_DOM-事…...

Global top sap abap 和deepseek对话,测试其abap推理能力

我提交给deepseek一段代码 FUNCTION zXXX_hr_pafm_pannnn_up. *"---------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" VALUE(IS_PRELP) TYPE PRELP OPTIONAL *" VALUE(IV…...

Android DUKPT - 3DES

一、DUKPT概述 DUKPT 即Derived Unique Key Per Transaction&#xff08;每个事务的派生唯一密钥&#xff09;。ANSI X9.24规范定义的密钥管理体系&#xff0c;主要用于对称密钥加密场景&#xff08;如MAC、PIN等敏感数据保护&#xff09;。通过动态生成唯一交易密钥&#xff…...

机器学习数学基础:45.多重响应分析

多重响应分析超详细教程&#xff1a;手把手教你分析多选题数据 一、深入理解多重响应分析的背景 问卷调查中&#xff0c;问题分为单选题与多选题&#xff1a; 单选题&#xff1a;如“你的性别&#xff1f;1.男 2.女”&#xff0c;答题者仅选一个选项&#xff0c;分析时直接统…...