건축설계7강: 일일&온라인 테이블 구조 데이터 검사 시스템의 자동 비교

건축설계7강: 일일&온라인 테이블 구조 데이터 검사 시스템의 자동 비교

본 글은 정형환경과 테스트 환경 간의 데이터베이스/테이블 및 컬럼 구조의 불일치로 인해 발생하는 문제를 방지하기 위한 아키텍처 설계, 데이터 검사 시스템의 일일 및 온라인 테이블 구조 자동 비교에 관한 일곱 번째 강의입니다.

1. 배경

일일 및 온라인 테이블 구조, 인덱스 불일치 시나리오 정리

검사 : 검사 업무 차이 테이블, 테이블 이름이 일치하지 않아 수정되었습니다.

사례:

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십진수(19,3) NOT NULL DEFAULT '0.000' COMMENT '미수금 총액',
    • total_pay십진수(19,3) NOT NULL DEFAULT '0.000' COMMENT '지불총액',
    • total_cost십진수(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 '确认人',
      
    • 이 두 필드는 온라인에서 삭제되었지만 일상적인 환경은 여전히 ​​존재하며 확인이 필요합니다.

    • 새로운 기능

  • 질문 4: 송장 번호

    • invoice_code   varchar(128)      null comment '发票号码数组,以逗号分割',
      
    • 이 필드는 온라인에서는 삭제되었으나, 일상 환경에는 여전히 존재하므로 확인이 필요합니다.

    • 새로운 기능

  • 질문 5: 협업 상태

    • team_state              int         default 0                    not null comment '协作状态 0非协作费用 1协作费用',
      
    • 이 필드는 온라인에서는 삭제되었으나, 일상 환경에는 여전히 존재하므로 확인이 필요합니다.

    • 새로운 기능

금융_청구서

  • 질문 1: 총 청구서 필드는 온라인에는 존재하지만 일상 환경에는 존재하지 않습니다.

    • bill_amount decimal(19,3) NOT NULL DEFAULT '0.000' COMMENT '账单金额',
    • 삭제해야한다
  • 질문 2: 이 필드는 매일에 존재합니다.

    • 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: 총 비용 필드는 온라인에는 존재하지만 일상 환경에는 존재하지 않습니다.

    • price decimal(19,3) NOT NULL DEFAULT '0.000' COMMENT '费用合计'
  • 질문 2: 다음 5개 필드는 온라인에는 존재하지만 일상 환경에는 존재하지 않습니다.

    • business_types varchar(60) null comment '업종, 중량물 배송, Door to Door, 중량물 배송, 빈 배송, 빈 배송, 물품 운반, 화물',
    • valid_state int(10) default 0 not null comment '무효 또는 삭제 여부, 0: 정상 주문, 1: 주문 무효, 2: 주문 삭제',
    • Record_user bigint null 설명 '레코더',
    • can_settlementtinyint null 코멘트 '정산 가능 여부',
    • create_user bigint는 null 주석 '작성자'가 아닙니다.
    • 이 데이터 배치를 삭제해야 합니다. 기능이 이미 온라인 상태입니다.
  • 문제 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: 인덱스 충돌

두 인덱스의 테넌트 ID 필드가 중복되며, 다음 인덱스가 삭제됩니다.

여기에 이미지 설명을 삽입하세요.

시나리오 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: 일부 필드가 온라인에 존재하지만 일상 환경에는 존재하지 않습니다.

시나리오 4: 일부 필드가 온라인에서 삭제되었지만 일상적인 환경은 여전히 ​​존재합니다.

3. 기술 솔루션

3.1 페이지는 다음과 같습니다

여기에 이미지 설명을 삽입하세요.

3.2 전체 흐름도

여기에 이미지 설명을 삽입하세요.

목표 : 정형환경과 테스트 환경의 데이터베이스/테이블 및 컬럼 구조의 불일치로 인해 발생하는 문제를 방지합니다.

  • 일상 환경과 온라인 환경 테이블 구조의 일관성 여부를 감지하고, 불일치하는 데이터를 기록하며, DingTalk 알림을 푸시합니다.

1단계: 데이터 수집

  • 업스트림: 온라인 환경 라이브러리 + 테이블

  • 하류: 일일 환경 라이브러리 + 테이블

  • 빈도: 일주일에 두 번이면 충분합니다.

2단계: 데이터 비교

  • 1. 온라인에는 존재하지만 데일리에는 존재하지 않는 경우, 데일리 환경이 비호환적으로 업그레이드되어 메시지가 푸시될 수 있는 시나리오가 있을 수 있습니다.
  • 2. 온라인에는 존재하지 않지만 Daily에는 존재합니다. Daily가 새 테이블을 추가하고 Redis에 테이블 이름을 저장할 수 있는 시나리오가 있을 수 있습니다. 7일 후에 테이블은 아직 온라인에 존재하지 않으며 메시지가 푸시됩니다.
  • 3. 모두 존재하지만 일관성이 없습니다.인덱스가 누락되고, 주석이 만료되고, 필드 이름이 변경되고, 필드 유형이 변경되고, 메시지가 즉시 푸시되는 시나리오입니다.

3단계: 오류 처리

  • 일치하지 않는 데이터가 기록되고 DingTalk 알림이 푸시됩니다. ( DingTalk 로봇과 도킹 )

3.3 데이터 수집

걸림돌 1: 일상 환경과 온라인 환경 사이에 네트워크 연결이 없습니다.

  • 해결 방법: EC를 제어 영역에 배포

막힌 지점 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 추상 클래스를 상속하고determinCurrentLookupKey() 메서드를 재정의합니다.

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);
    }
}

전체 카탈로그는 다음과 같습니다.

여기에 이미지 설명을 삽입하세요.

TABLESinformation_schema.data를 읽을 수 있는 권한이 필요합니다.

여기에 이미지 설명을 삽입하세요.

예정된 작업 실행 시간 : 매주 수요일, 금요일 (해제 후 첫 번째 날)
여기에 이미지 설명을 삽입하세요.

3.4 데이터 비교

논리는 다음과 같습니다.

  • 1. 온라인에는 존재하지만 데일리에는 존재하지 않는 경우, 데일리 환경이 비호환적으로 업그레이드되어 메시지가 푸시될 수 있는 시나리오가 있을 수 있습니다.

  • 2. 온라인에는 존재하지 않지만 Daily에는 존재합니다. Daily가 새 테이블을 추가하고 Redis에 테이블 이름을 저장할 수 있는 시나리오가 있을 수 있습니다. 7일 후에 테이블은 아직 온라인에 존재하지 않으며 메시지가 푸시됩니다.

  • 3. 모두 존재하지만 일관성이 없습니다.인덱스가 누락되고, 주석이 만료되고, 필드 이름이 변경되고, 필드 유형이 변경되고, 메시지가 즉시 푸시되는 시나리오입니다.

3.5.데이터 테이블 검사 정보 푸시

  • 비즈니스 유형: %s 데이터가 일관성이 없습니다. 제때 처리해 주세요.
  • 테이블 이름: %s
  • 담당자: %s
  • %s 업스트림 및 다운스트림 데이터가 일치하지 않습니다. 제때에 처리하십시오.
  • 차이 범주(0-생성, 1-업데이트, 2-삭제): %s
  • 배치 ID: %s

3.6, DB변경

표 1: 테이블 차이 검사 테이블

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: DingTalk 로봇의 흐름 제어

  1. 너무 빠르게 전송, 분당 20회 초과: 분당 최대 20개 메시지
    1. 흐름은 10분 동안 제한됩니다.
  2. 푸시 메시지 본문이 너무 큽니다. 단일 메시지의 최대 길이는 2000바이트입니다.

질문 4: SQL 구문 분석 실패 매개 변수
: 무시 삽입 합니다 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, , , , , , , , , , , , , , , , , ) 선택 , , , , , , , , , , , , , _currencyimgattachment_idremarkconfirm_stateconfirm_noconfirm_userconfirm_remarkconfirm_timecollate_stateinvoice_stateinvoice_userinvoice_codeinvoice_timeverify_stateverify_userverify_timeteam_fare_stateteam_statedeletedcreate_userupdate_usercreate_timeupdate_timeidtenant_idsub_order_idsub_order_carrier_idsub_order_settlement_idfare_item_idbill_nosettlement_typesettlement_idcreator_typepricetax_ratecurrencyimgattachment_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에서 falcon_convoy. finance_fare_**info**강제 인덱스( primary), 여기서 id> $0 및 ( id< $1 또는 id= $2) 공유 모드 잠금

조치 1: 일상 환경의 온라인 환경 데이터 테이블 비교를 달성하려면 이 두 가지 문제를 해결해야 합니다.

1. 일상환경과 온라인 환경이 연결되어 있지 않습니다 . : 온라인 환경 DB와 일상 환경 DB를 하나의 환경에서 모두 접속 해야 합니다 .

  • 제어 영역에 EC를 배포하려고 합니다.

2. 이제 각 온라인 라이브러리는 자체 계정과 비밀번호를 사용합니다 . 온라인 db 인스턴스의 모든 라이브러리에 액세스할 수 있는 읽기 전용 권한이 있는 계정을 제공할 수 있습니까?

  • 이러한 여러 데이터 소스의 경우 일일 인스턴스와 온라인 인스턴스라는 두 개의 연속적인 예만 필요합니다.

조치 2: SpringBoot는 여러 데이터 소스를 사용하므로 MyBatis 페이징 플러그인이 유효하지 않게 됩니다.

배경

해당 현상은 게이트웨이에서 오류를 보고하는 현상인데 FluxOnAssembly$OnAssemblyException, 조사 결과 페이징 쿼리 중 1,000개 이상의 데이터가 반환되어 데이터량이 게이트웨이 한도를 초과해 오류가 발생한 것으로 확인됐다. 중단점은 MyBatis 페이징 플러그인이 유효하지 않으며 MyBatis 페이징 인터셉터 중단점을 입력할 수 없음을 발견했습니다.

장면

1. 스프링부트 사용

2. sqlSession 사용자 정의(다중 데이터 소스)

해결책

1. 페이징 플러그인 클래스에 @Component 주석이 추가되었는지 확인하세요 ✅

2. SqlSessionFactoryConfig 클래스에 인터셉터 삽입✅

삼,sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageInterceptor});

참고: 플러그인을 설정할 때 sqlSessionFactoryBean.getObject() 이전이어야 합니다. SqlSessionFactory는 플러그인이 생성될 때 플러그인을 얻어서 구성에서 설정합니다. 나중에 설정하면 삽입되지 않습니다.

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

Supongo que te gusta

Origin blog.csdn.net/qq_28959087/article/details/132365073
Recomendado
Clasificación