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

news2024/11/30 18:46:17

架构设计第七讲:数据巡检系统之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_invalidstate
          on 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_invalidstate
        on 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.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    first:
      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 {

    @Override
    protected 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.coloradmin.cn/o/1051735.html

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

相关文章

36 二叉树中序遍历

二叉树中序遍历 题解1 递归题解2 迭代 给定一个二叉树的根节点 root &#xff0c;返回它的 中序 遍历 。 提示&#xff1a; 树中节点数目在范围 [0, 100] 内-100 < Node.val < 100 进阶: 递归算法很简单&#xff0c;你可以通过迭代算法完成吗&#xff1f; 题解1 递归…

Python绘图系统23:导入多个坐标轴的数据

文章目录 单轴导入多轴导入多文件导入合并导入源代码 Python绘图系统&#xff1a; 前置源码&#xff1a; Python打造动态绘图系统&#x1f4c8;一 三维绘图系统 &#x1f4c8;二 多图绘制系统&#x1f4c8;三 坐 标 轴 定 制&#x1f4c8;四 定制绘图风格 &#x1f4c8;五 数据…

第一章 数据可视化和matplotlib

Python数据可视化 第一章 数据可视化和matplotlib 1.数据可视化概述 1.1.什么是数据可视化 ​ 数据可视化旨在借助图形化的手段&#xff0c;将一组数据以图形的形式表示&#xff0c;并利用数据分析和开发工具发现其中未知信息的处理过程。 ​ 数据可视化发展历史 ​ 可视化…

Spring IOC容器实例化Bean整体流程图

SpringBean实例化的基本流程-CSDN博客 Spring容器中的BeanDefinitionReader读取器&#xff0c;读取xml配置文件&#xff0c;解析每一个bean标签&#xff0c;将bean标签中信息封装到BeanDefinition对象中&#xff0c;该对象的集合存储到BeanDefinitionMap中&#xff0c;然后Spri…

Cocos Creator3.8 实战问题(三)去除scrollview背景色和label 对齐方式设置无效问题

1、 scrollview 默认背景是白色的&#xff0c; 我们不想要 scrollview 默认的背景颜色&#xff0c;怎么办&#xff1f; 设置 scrollview的color为透明吗&#xff1f; 不对&#xff0c;这会导致 view节点完全透明。 解决方法&#xff1a;直接删除scrollview 的Spritre frame就…

船用低速发动机缸压在线监测系统

LabVIEW开发船用低速发动机缸压在线监测系统 船用发动机结构复杂&#xff0c;部件相互连接&#xff0c;运行环境恶劣&#xff0c;使其更容易发生故障。如果船用发动机发生故障或工作状态不佳&#xff0c;将增加造成经济损失和威胁船舶安全的机。为了减少故障的发生&#xff0c…

【Kettle】Kettle部署与运行

一、部署 1.安装java 此处安装openjdk1.8&#xff0c;可用yum、apt、源码等方式安装&#xff0c;具体安装方式略。 2.安装kettle 1.下载 https://www.hitachivantara.com/en-us/products/pentaho-platform/data-integration-analytics/pentaho-community-edition.html 此…

Unity中UI Shader遮罩RectMask2D

文章目录 前言一、需要定义一个变体UNITY_UI_CLIP_RECTUNITY_UI_CLIP_RECT 二、需要申明一个_ClipRect,这是使用上面这个变体需要使用的&#xff0c;这个属性并没有在Properties声明1、现在我们用简单的代码测试一下 _ClipRect 的使用然后我们基于以上的基础&#xff0c;让 内层…

二、局域网联机

目录 1.下载资源包 2.配置NetworkManager 3.编写测试UI 1.下载资源包 2.配置NetworkManager &#xff08;1&#xff09;在Assets/Prefabs下创建Network Prefabs List 相应设置如下&#xff1a; &#xff08;2&#xff09; 创建空物体“NetworkManager”并挂载NetworkMan…

MySQL数据库——索引(5)-索引使用(上),验证索引效率、最左前缀法则、范围查询、索引失效情况、SQL提示

目录 索引使用 验证索引效率 最左前缀法则 范围查询 索引失效情况 索引列运算 字符串不加引号 模糊查询 or连接条件 数据分布影响 SQL提示 use index ignore index force index 索引使用&#xff08;上&#xff09; 验证索引效率 在讲解索引的使用原则之前&…

c语言常用语法,长时间不用容易忘。

关键字 auto 声明自动变量const 定义常量&#xff0c;如果一个变量被 const 修饰&#xff0c;那么它的值就不能再被改变extern 声明变量或函数是在其它文件或本文件的其他位置定义register 声明寄存器变量signed 声明有符号类型变量或函数static 声明静态变量&#xff0c;修饰…

毅速课堂:3D打印随形水路在小零件注塑中优势明显

小零件注塑中的冷却不均匀问题常常导致烧焦现象的发生。这主要是因为传统机加工方法无法制造出足够细小的水路&#xff0c;以适应小零件的复杂形状。而3D打印技术的引入&#xff0c;尤其是随形水路的设计&#xff0c;为解决这一问题提供了新的解决方案。 3D打印随形水路技术的优…

3.canvas绘制基本图形——圆弧

圆弧是圆的组成部分&#xff0c;一个圆可以看成多个圆弧或者一个圆弧闭环组合而成。因此我们画圆弧的时候往往把他看成一个残缺的圆 arc arc是最基础最简单的圆弧绘制方法。 他有六个参数 参数一跟参数二表示圆的圆心坐标 参数三是圆的半径 参数四是圆开始的角度 参数五是圆结…

No module named ipykernel解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

HTML的相关知识

1.什么是HTML&#xff1f;基本语法 HTML: Hyper Text Markup Language &#xff08;超文本标记语言&#xff09; 超文本&#xff1f;超级文本&#xff0c;例如流媒体&#xff0c;声音、视频、图片等。 标记语言&#xff1f;这种语言是由大量的标签组成。HTML标签参考手…

单目标应用:基于沙丁鱼优化算法(Sardine optimization algorithm,SOA)的微电网优化调度MATLAB

一、沙丁鱼优化算法 沙丁鱼优化算法(Sardine optimization algorithm,SOA)由Zhang HongGuang等人于2023年提出&#xff0c;该算法模拟沙丁鱼的生存策略&#xff0c;具有搜索能力强&#xff0c;求解精度高等特点。 沙丁鱼主要以浮游生物为食&#xff0c;这些生物包括细菌、腔肠…

SpringBoot整合RabbitMQ实现延迟队列功能

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

AOP 编程

目录 ​编辑一、AOP 编程 1、AOP 概念 2、AOP 编程的开发步骤 3、切面的名词解释 二、AOP 的底层实现原理 1、核心问题 2、动态代理类的创建 &#xff08;1&#xff09;JDK 的动态代理创建 &#xff08;2&#xff09;CGlib 的动态代理 &#xff08;3&#xff09;总结…

[红明谷CTF 2021]write_shell %09绕过过滤空格 ``执行

目录 1.正常短标签 2.短标签配合内联执行 看看代码 <?php error_reporting(0); highlight_file(__FILE__); function check($input){if(preg_match("/| |_|php|;|~|\\^|\\|eval|{|}/i",$input)){ 过滤了 木马类型的东西// if(preg_match("/| |_||php/&quo…

设计模式5、原型模式 Prototype

解释说明&#xff1a;使用原型实例指定待创建对象的类型&#xff0c;并且通过复制这个原型阿里创建型的对象 UML 结构图&#xff1a; 抽象原型&#xff08;Prototype&#xff09;&#xff1a;规定了具体原型对象必须实现的clone()方法 具体原型&#xff08;ConcretePrototype&…