架构设计第七讲:数据巡检系统之daily线上表结构自动化比对

架构设计第七讲:数据巡检系统之daily&线上表结构自动化比对

本文是架构设计第七讲,数据巡检系统之daily&线上表结构自动化比对,避免正式环境与测试环境数据库/表、列结构不一致带来问题。

文章目录

  • 架构设计第七讲:数据巡检系统之daily&线上表结构自动化比对
    • 1、背景
    • 2、存在问题的场景
    • 3、技术方案
      • 3.1、页面如下
      • 3.2、整体流程图
      • 3.3、数据获取
      • 3.4、数据比对
      • 3.5、数据表巡检信息推送
      • 3.6、dbChange
    • 4、问题记录
    • Action1:为了实现daily环境线上环境数据表比对,需要解决这两问题
    • Action2:SpringBoot使用多数据源导致MyBatis分页插件无效

1、背景

daily与线上表结构,索引不一致场景梳理

巡检:巡检业务差异表,表名不一致,已经修改

案例:

alter table finance_sub_order_info modify total_receive decimal(19,3) default 0.000 not null comment '待删除';
alter table finance_sub_order_info modify total_pay decimal(19,3) default 0.000 not null comment '待删除';
alter table finance_sub_order_info modify total_cost decimal(19,3) default 0.000 not null comment '待删除';alter table finance_fare_info modify img text null comment '附件图片';
alter table finance_bill drop column bill_amount;alter table finance_sub_order_settlement drop column price;
alter table finance_sub_order_settlement drop column business_types;
alter table finance_sub_order_settlement drop column invalid_state;
alter table finance_sub_order_settlement drop column record_user;
alter table finance_sub_order_settlement drop column can_settlement;
alter table finance_sub_order_settlement drop column create_user;

车队财务:

finance_sub_order_info

  • 问题1:三个字段待删除
    • total_receive decimal(19,3) NOT NULL DEFAULT ‘0.000’ COMMENT ‘总应收’,
    • total_pay decimal(19,3) NOT NULL DEFAULT ‘0.000’ COMMENT ‘总应付’,
    • total_cost decimal(19,3) NOT NULL DEFAULT ‘0.000’ COMMENT ‘总成本’,
  • 先将这几个字段设置为待删除

finance_fare_info

  • 问题1:

  • 在这里插入图片描述

  • 问题2:将AttachmentId设置为必填,默认值为0

    • 然后排查下代码中需要做兼容的地方
  • 问题3:finance_fare_info表

    • confirm_time            datetime           null comment '确认时间',
      confirm_user            bigint                null comment '确认人',
      
    • 这两字段在线上已经被删除了,但是daily环境还存在,需要排查

    • 新功能

  • 问题4:发票号码

    • invoice_code   varchar(128)      null comment '发票号码数组,以逗号分割',
      
    • 这字段在线上已经被删除了,但是daily环境还存在,需要排查

    • 新功能

  • 问题5:协作状态

    • team_state              int         default 0                    not null comment '协作状态 0非协作费用 1协作费用',
      
    • 这字段在线上已经被删除了,但是daily环境还存在,需要排查

    • 新功能

finance_bill

  • 问题1:账单总额字段,在线上存在,但是在daily环境不存在

    • bill_amount decimal(19,3) NOT NULL DEFAULT '0.000' COMMENT '账单金额',
    • 应该被删除
  • 问题2:这几个字段在daily存在

    • settlement_owned_type      tinyint        default 0                 not null comment '1 自营  2-外协',
      settlement_entity_id       bigint         default 0                 not null comment '结算实体id',
      settlement_entity_classify tinyint        default 0                 not null comment '结算实体类型,1-司机 2-企业id 3-车队id ',
      

finance_sub_order_settlement

  • 问题1:费用合计字段,在线上存在,但是在daily环境不存在

    • price decimal(19,3) NOT NULL DEFAULT '0.000' COMMENT '费用合计'
  • 问题2:以下5字段,在线上存在,但是在daily环境不存在

    • business_types varchar(60) null comment ‘业务类型,送重、门到门、提重、送空、提空、带货、运费’,
    • invalid_state int(10) default 0 not null comment ‘是否作废或删除,0:正常订单,1:作废订单,2:删除订单’,
    • record_user bigint null comment ‘录单员’,
    • can_settlement tinyint null comment ‘是否可结算’,
    • create_user bigint not null comment ‘创建人’,
    • 需要删除这批数据 已经上线的功能
  • 问题3:索引不一致

    • -- daily
      create index idx_tenantid_settlementtype_invalidstateon falcon_convoy.finance_sub_order_settlement (tenant_id, settlement_type);-- 线上
      create index idx_tenantid_settlementtype_invalidstate on falcon_convoy.finance_sub_order_settlement (tenant_id, settlement_type, invalid_state);-- todo 索引名称需要修改
      

2、存在问题的场景

场景1:索引冲突

两索引tenantId字段重合了,下面这个索引做删除处理

在这里插入图片描述

场景2:索引不一致

  • -- daily
    create index idx_tenantid_settlementtype_invalidstateon falcon_convoy.finance_sub_order_settlement (tenant_id, settlement_type);-- 线上
    create index idx_tenantid_settlementtype_invalidstate on falcon_convoy.finance_sub_order_settlement (tenant_id, settlement_type, invalid_state);
    

场景3:某些字段在线上存在,但是在daily环境不存在

场景4:某些字段在线上已经被删除了,但是daily环境还存在

3、技术方案

3.1、页面如下

在这里插入图片描述

3.2、整体流程图

在这里插入图片描述

目标:避免正式环境与测试环境数据库/表、列结构不一致带来问题。

  • 检测daily环境和线上环境表结构是否一致,不一致的数据记录起来,并推送钉钉告警

步骤1:数据获取

  • 上游:线上环境库+表

  • 下游:daily环境库+表

  • 频率:一周两次即可

步骤2:数据比对

  • 1、线上存在,daily不存在,场景可能是daily环境发生了不兼容的升级改造,消息推送即可;
  • 2、线上不存在,daily存在,场景可能是daily在新增了表,可以将表名存放到redis中,7天后,线上还不存在该表,消息推送;
  • 3、都存在,但是不一致,场景是索引遗漏、comment该了、字段名改了、字段类型改了,立即消息推送。

步骤3:差错处理

  • 不一致的数据记录起来,并推送钉钉告警(对接钉钉机器人)

3.3、数据获取

卡点1:daily环境与线上环境网络不通

  • 解法:将ecs部署到control区

卡点2:多数据源配置,application.yml文件中配置

 datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcefirst:url: ${huxun.datasource.url}username: ${huxun.datasource.username}password: ${huxun.datasource.password}second:url: ${huxun.datasource.daily.url}username: ${huxun.datasource.daily.username}password: ${huxun.datasource.daily.password}

多数据源具体实现:

1、定义一个动态数据源: 继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法

public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();return dataBaseType;}
}

2、创建一个切换数据源类型的类

public class DataSourceType {public enum DataBaseType {//默认数据库FIRST,SECOND;}// 使用ThreadLocal保证线程安全private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();// 往当前线程里设置数据源类型public static void setDataBaseType(DataBaseType dataBaseType) {if (dataBaseType == null) {throw new NullPointerException();}System.out.println("[将当前数据源改为]:" + dataBaseType);TYPE.set(dataBaseType);}// 获取数据源类型public static DataBaseType getDataBaseType() {DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.FIRST : TYPE.get();System.out.println("[获取当前数据源的类型为]:" + dataBaseType);return dataBaseType;}// 清空数据类型(清理时机不好掌控,且目前ThreadLocal只存在一个值,不清理也没影响)public static void clearDataBaseType() {TYPE.remove();}
}

3、定义多个数据源: 将定义好的多个数据源放在动态数据源中。

@Configuration
@MapperScan(basePackages = {"com.huxun.inspection.mapper"}, sqlSessionFactoryRef = "SqlSessionFactory")
public class DruidConfig {@Bean(name = "firstDataSource")@Primary@ConfigurationProperties(prefix = "spring.datasource.first")public DataSource firstDataSource(){return DruidDataSourceBuilder.create().build();}@Bean(name = "secondDataSource")@ConfigurationProperties(prefix = "spring.datasource.second")public DataSource secondDataSource(){return DruidDataSourceBuilder.create().build();}@Bean(name = "dynamicDataSource")public DynamicDataSource DataSource(@Qualifier("firstDataSource") DataSource test1DataSource,@Qualifier("secondDataSource") DataSource test2DataSource) {Map<Object, Object> targetDataSource = new HashMap<>();targetDataSource.put(DataSourceType.DataBaseType.FIRST, test1DataSource);targetDataSource.put(DataSourceType.DataBaseType.SECOND, test2DataSource);DynamicDataSource dataSource = new DynamicDataSource();dataSource.setTargetDataSources(targetDataSource);dataSource.setDefaultTargetDataSource(test1DataSource);return dataSource;}@Bean(name = "SqlSessionFactory")public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dynamicDataSource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));return bean.getObject();}
}

4、定义AOP: 用于切换不同业务数据库的入口。

@Aspect
@Component
public class DataSourceAspect {@Before("execution(* com.huxun.inspection.mapper..Daily*.*(..))")public void setDataSource2test01() {System.err.println("读取第二个数据源");DataSourceType.setDataBaseType(DataSourceType.DataBaseType.SECOND);}@Before("execution(* com.huxun.inspection.mapper..*.*(..)) && !execution(* com.huxun.inspection.mapper..Daily*.*(..))")public void setDataSource2test02() {System.err.println("读取第一个数据源");DataSourceType.setDataBaseType(DataSourceType.DataBaseType.FIRST);}
}

整体目录如图:

在这里插入图片描述

需要权限,能读取information_schema.TABLES 数据

在这里插入图片描述

定时任务执行时机:每周三和周五(发版后的第一天)
在这里插入图片描述

3.4、数据比对

逻辑如下:

  • 1、线上存在,daily不存在,场景可能是daily环境发生了不兼容的升级改造,消息推送即可;

  • 2、线上不存在,daily存在,场景可能是daily在新增了表,可以将表名存放到redis中,7天后,线上还不存在该表,消息推送;

  • 3、都存在,但是不一致,场景是索引遗漏、comment该了、字段名改了、字段类型改了,立即消息推送。

3.5、数据表巡检信息推送

  • 业务类型:%s 数据不一致,请及时处理
  • 表名:%s
  • 负责人:%s
  • %s 上下游数据不一致,请及时处理
  • 差异类别(0-create、1-update、2-delete):%s
  • 批次id:%s

3.6、dbChange

表1:table差异巡检表

CREATE TABLE IF NOT EXISTS `table_diff_inspection`(`id`           bigint 				    unsigned auto_increment comment '主键id' primary key,`biz_id`       bigint             not null comment '业务id',`batch_id`     bigint      				not	null comment '批次id',`status`       tinyint(1)         default 0  not null comment '状态,0-待确认,1-确认',`key_field_json` longtext         not null comment '业务关键字段数据',`diff_type`      tinyint          null comment '差异类别 (0-create、1-update、2-delete)',`db_name`    varchar(50)          not null COMMENT '库名',`group_name`    varchar(50)            not null COMMENT '处理人',`create_user`   bigint            not null comment '创建人',`update_user`   bigint            null comment '更新人',`create_time` datetime default CURRENT_TIMESTAMP not null comment '创建时间',`update_time`   datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
) DEFAULT CHARACTER SET = utf8mb4 COMMENT = 'table差异巡检表';
create index idx_batchId_bizIds on falcon_inspection.falcon_table_diff (batch_id, biz_id);

4、问题记录

问题1:dbName没有值

在这里插入图片描述

问题2:表结构修改

/*** 创建人*/
private Long createUser;/*** 更新人*/
private Long updateUser;/*** 创建时间*/
private Date createTime;
/*** 更新时间*/
private Date updateTime;

问题3:钉钉机器人的流控

  1. send too fast, exceed 20 times per minute:每分钟最多 20 条
    1. 会限流10分钟
  2. 推送消息体过大 单条消息最长 2000 字节

问题4:sql解析失败
param:insert ignore into falcon_convoy.tp_4740783_ogt_finance_fare_**info** (id, tenant_id, sub_order_id, sub_order_carrier_id, sub_order_settlement_id, fare_item_id, bill_no, settlement_type, settlement_id, creator_type, price, tax_rate, currency, img, attachment_id, remark, confirm_state, confirm_no, confirm_user, confirm_remark, confirm_time, collate_state, invoice_state, invoice_user, invoice_code, invoice_time, verify_state, verify_user, verify_time, team_fare_state, team_state, deleted, create_user, update_user, create_time, update_time) select id, tenant_id, sub_order_id, sub_order_carrier_id, sub_order_settlement_id, fare_item_id, bill_no, settlement_type, settlement_id, creator_type, price, tax_rate, currency, img, attachment_id, remark, confirm_state, confirm_no, confirm_user, confirm_remark, confirm_time, collate_state, invoice_state, invoice_user, invoice_code, invoice_time, verify_state, verify_user, verify_time, team_fare_state, team_state, deleted, create_user, update_user, create_time, update_time from falcon_convoy.finance_fare_**info** force index (primary) where id > $0 and (id < $1 or id = $2) lock in share mode

Action1:为了实现daily环境线上环境数据表比对,需要解决这两问题

1、daily环境与线上环境网络不通:需要在一个环境中,既访问线上环境db,又访问daily环境db

  • 即 将ecs部署到control区

2、现在线上各个库使用各自的账号密码:能不能提供一个只读权限的账号,能访问线上db实例 全部的库

  • 这样多数据源只用连两就行:daily实例、线上实例

Action2:SpringBoot使用多数据源导致MyBatis分页插件无效

背景

现象是gateway 网关 报错 FluxOnAssembly$OnAssemblyException,经过排查,发现是分页查询时返回了1000多条数据,导致数据量超出了网关限制,从而抛错。打断点发现MyBatis分页插件无效,MyBatis分页拦截器断点无法进入。

情景

1、使用Springboot

2、自定义sqlSession(多数据源)

解决方法

1、检查分页插件类上是否加注解 @Component ✅

2、在SqlSessionFactoryConfig类注入拦截器 ✅

3、sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageInterceptor});

注意:设置plugins时必须在sqlSessionFactoryBean.getObject()之前。SqlSessionFactory在生成的时候就会获取plugins,并设置到Configuration中,如果在之后设置则不会注入。

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/91077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

193419-86-2,用于蛋白电泳检测的Fluorescein o-acrylate

产品简介&#xff1a;Fluorescein o-acrylate 中FITC具有荧光素衍生物的普遍特性&#xff0c;FITC也经常被用于蛋白电泳检测和荧光能量激发转移测试。 荧光染料及其荧光标记技术一直是生物领域常用的产品和技术&#xff0c;荧光物质是指具有共轭双键体系化学结构的化合物&…

LabVIEW开发实时自动化多物镜云计算全玻片成像装置

LabVIEW开发实时自动化多物镜云计算全玻片成像装置 数字病理学领域正在迅速发展&#xff0c;这主要是由于计算机处理能力、数据传输速度、软件创新和云存储解决方案方面的技术进步。因此&#xff0c;病理科室不仅将数字成像用于图像存档等简单任务&#xff0c;还用于远程病理学…

monkeyrunner录制脚本和回放

Monkeyrunner关于使用录制、生成脚本、编译脚本及执行脚本。 首先在计算机上下载和安装SDK、python 2.将recorder.py文件放置SDK文件夹里tools文件夹下 3.USB连接手机&#xff0c;手机端&#xff0c;开启USB调试&#xff0c;并在计算机DOS中输入adb devices命令&#xff0c;查看…

K8S-CNI

CNI的设计思想即为:Kubernetes在启动Pod的pause容器之后&#xff0c;直接调用CNI网络插件&#xff0c;从而实现为Pod内部应用容器月在的Network Namespace配置符合预期的网络信息。 这里面需要特别关注两个方面:Container必须有自己的网络命名空间的环境&#xff0c;也就是end…

go mod tidy 报错:x509: certificate signed by unknown authority 最佳实践

最近在docker中运行了一个ubuntu20的系统&#xff0c;在上面运行golang程序&#xff0c;使用go mod tidy后报错&#xff1a; tls: failed to verify certificate: x509: certificate signed by unknown authority 如&#xff1a; go: finding module for package google.gol…

Java:使用 Graphics2D 类来绘制图像

目录 过程介绍创建一个 BufferedImage 对象创建一个 Graphics2D 对象绘制字符和干扰线将生成的图像保存到文件 示例代码 过程介绍 创建一个 BufferedImage 对象 首先创建一个 BufferedImage 对象来表示图像 创建一个 Graphics2D 对象 然后使用 createGraphics() 方法创建一…

XXE 漏洞及案例实战

文章目录 XXE 漏洞1. 基础概念1.1 XML基础概念1.2 XML与HTML的主要差异1.3 xml示例 2. 演示案例2.1 pikachu靶场XML2.1.1 文件读取2.1.2 内网探针或者攻击内网应用&#xff08;触发漏洞地址&#xff09;2.1.4 RCE2.1.5 引入外部实体DTD2.1.6 无回显读取文件 3. XXE 绕过3.1 dat…

网络爬虫--伪装浏览器

从用户请求的Headers反反爬 在访问某些网站的时候&#xff0c;网站通常会用判断访问是否带有头文件来鉴别该访问是否为爬虫&#xff0c;用来作为反爬取的一种策略。很多网站都会对Headers的User-Agent进行检测&#xff0c;还有一部分网站会对Referer进行检测&#xff08;一些资…

Go语言strings标准库

strings包 参考资料 常用函数 函数功能备注EqualFold(s, t string) bool判断两个utf-8编码字符串&#xff08;将unicode大写、小写、标题三种格式字符视为相同&#xff09;是否相同。HasPrefix(s, prefix string) bool判断s是否有前缀字符串prefixHasSuffix(s, suffix strin…

【Leetcode】 501. 二叉搜索树中的众数

给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 如果树中有不止一个众数&#xff0c;可以按 任意顺序 返回。 假定 BST 满足如下定义&#xf…

【图论C++】树的重心——教父POJ 3107(链式前向星的使用)

》》》算法竞赛 /*** file * author jUicE_g2R(qq:3406291309)————彬(bin-必应)* 一个某双流一大学通信与信息专业大二在读 * * brief 一直在竞赛算法学习的路上* * copyright 2023.9* COPYRIGHT 原创技术笔记&#xff1a;转载…

K8S:pod控制器详解

文章目录 一.pod控制器的基础&#xff11;.pod概念及分类&#xff12;.什么是Pod控制器及其功用&#xff13;.pod控制器有多种类型&#xff08;1&#xff09;ReplicaSet&#xff08;2&#xff09;Deployment&#xff08;3&#xff09;DaemonSet&#xff08;4&#xff09;Statef…

css调整字体间距 以及让倾斜字体

调整字体间距 .element {letter-spacing: 2px; /* 调整为适当的值 */ }倾斜字体1 .element {font-style: italic; }请注意&#xff0c;不是所有的字体都有斜体样式可用。如果字体本身没有斜体版本&#xff0c;则可能无法实现完全的斜体效果。 倾斜字体2 <span class"…

python time和datetime的常用转换处理

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备好了&#xff0c;直接在文末名片自取就可 一、time 1、…

第一部分:HTML5

目录 一&#xff1a;网页 1.1&#xff1a;什么是网页&#xff1f; 1.2&#xff1a;什么是HTML&#xff1f; 1.3&#xff1a;网页的形成 二&#xff1a;常用浏览器 三&#xff1a;Web标准 3.1&#xff1a;为什么需要Web标准&#xff1f; 3.2&#xff1a;Web标准的构成 四&a…

自动群发节日祝福,1 行 Python 代码搞定,小白可用

想了解更多精彩内容&#xff0c;快来关注程序员晚枫 大家节日快乐&#xff0c;这里是程序员晚枫&#xff0c;小红薯也叫这个名字。 今天给大家分享一个实用功能&#xff1a;自动群发祝福消息。 我相信社会人都体会过&#xff0c;过年过节给别人群发祝福消息的无奈&#xff0…

Cloudflare进阶技巧:缓存利用最大化

1. 引言 cloudflare我想你应该知道是什么&#xff0c;一家真正意义上免费无限量的CDN&#xff0c;至今未曾有哥们喷它的。当然&#xff0c;在国内的速度确实比较一般&#xff0c;不过这也不能怪它。 CDN最大的特色&#xff0c;我想就是它的缓存功能&#xff0c;达到防攻击&am…

C#解析JSON详解

C#解析Json详解 文章目录 C#解析Json详解什么是Json&#xff1f;Json的特点 常用的Json库Json.NET (Newtonsoft.Json)System.Text.Json 实例序列化反序列化 总结 什么是Json&#xff1f; JSON的全称是JavaScript Object Notation&#xff0c;是一种轻量级的数据交换格式&#…

抖音短视频seo矩阵系统源代码开发系统架构及功能解析

短视频seo源码&#xff0c;短视频seo矩阵系统底层框架上支持了从ai视频混剪&#xff0c;视频批量原创产出&#xff0c;云存储批量视频制作&#xff0c;账号矩阵&#xff0c;视频一键分发&#xff0c;站内实现关键词、短视频批量搜索排名&#xff0c;数据统计分类多功能细节深度…

Package vips was not found in the pkg-config search path的解决方案

出现该问题是因为pkg-config未安装或未成功设置环境变量。 下文是centos下的操作。 前提 先安装C编译环境&#xff1a; yum -y install gcc-c 否则会报错configure: error: no acceptable C compiler found in $PATH 成功后gcc -v会显示版本信息。 下载&安装 pkg-config 传…