门户网站建设的好处,郑州网站推广方案,加强制度建设 信息公开 网站 专栏,个人注册公司网站空间架构设计第七讲#xff1a;数据巡检系统之daily线上表结构自动化比对 本文是架构设计第七讲#xff0c;数据巡检系统之daily线上表结构自动化比对#xff0c;避免正式环境与测试环境数据库/表、列结构不一致带来问题。 文章目录 架构设计第七讲#xff1a;数据巡…架构设计第七讲数据巡检系统之daily线上表结构自动化比对 本文是架构设计第七讲数据巡检系统之daily线上表结构自动化比对避免正式环境与测试环境数据库/表、列结构不一致带来问题。 文章目录 架构设计第七讲数据巡检系统之daily线上表结构自动化比对1、背景2、存在问题的场景3、技术方案3.1、页面如下3.2、整体流程图3.3、数据获取3.4、数据比对3.5、数据表巡检信息推送3.6、dbChange 4、问题记录Action1为了实现daily环境线上环境数据表比对需要解决这两问题Action2SpringBoot使用多数据源导致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 然后排查下代码中需要做兼容的地方 问题3finance_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、数据获取
卡点1daily环境与线上环境网络不通
解法将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 ThreadLocalDataBaseType TYPE new ThreadLocalDataBaseType();// 往当前线程里设置数据源类型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)PrimaryConfigurationProperties(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) {MapObject, 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
表1table差异巡检表
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、问题记录
问题1dbName没有值 问题2表结构修改
/*** 创建人*/
private Long createUser;/*** 更新人*/
private Long updateUser;/*** 创建时间*/
private Date createTime;
/*** 更新时间*/
private Date updateTime;问题3钉钉机器人的流控
send too fast, exceed 20 times per minute每分钟最多 20 条 会限流10分钟 推送消息体过大 单条消息最长 2000 字节
问题4sql解析失败 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实例、线上实例
Action2SpringBoot使用多数据源导致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中如果在之后设置则不会注入。