mysql历史数据自动归档

数据库运行一段时间后,因查询性能等因素需将在线数据挪至历史库。本文介绍自动归档思路,如在线库开启federated支持,创建相关数据库和表,编写存储过程并通过JOB调度,从库忽略特定库复制,最后用zabbix监控状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据库跑一段时间后,因为查询性能、磁盘容量,运维管理等方面的原因,需要将在线数据挪到历史库(不同的服务器)。如我们的在线订单只留3个月数据,3个月以前的就需要到历史库查了。

自动归档常见的方式有pt-archiver,但我还是觉得自己写存储过程更靠谱。。。

思路:

  • 在线库实例打开federated支持,创建数据库dborder(业务库), linkhis(归档用);
  • 历史库创建历史表dborderhis.myorder_tab_his;
  • 在linkhis库下创建federated表linkhis.myorder_tab_his,指向dborderhis.myorder_tab_his;
  • 在linkhis库下创建日志表archive_log,存储过程proc_archive,proc_archive_pkg,并通过JOB调度proc_archive_pkg;
  • 在线库的从库需要忽略linkhis的复制:replicate-ignore-db=linkhis,否则从库也会往这个历史库重复同步数据。

日志记录表archive_log

create table archive_log
(
  id bigint auto_increment PRIMARY key,
  tab_name           varchar(40),
  archive_date_begin datetime,
  archive_date_end   datetime,
  create_time        datetime default CURRENT_TIMESTAMP(),
  status             int(1),
  insert_rows        bigint(11),
  delete_rows        bigint(11),
  remark             varchar(1000)
)

存储过程:proc_archive

CREATE PROCEDURE proc_archive(in i_table_source varchar(40), 
                              in i_table_target varchar(40), 
                              in i_fieldname varchar(40), 
                              in i_keepdays int,
                              in i_archdays int,
                              in i_other_cond varchar(500))
begin
  /*
  入参:
  i_table_source:原表,含dbname
  i_table_target:federated表
  i_fieldname:时间字段
  i_keepdays:保留天数
  i_archdays:每次归档多少天数据
  i_other_cond:数据额外条件(如status in (2,3)不能归档,需要保留),无额外条件则输入'1=1'
  归档日志表archive_log.status字段含义:
  0:成功, 1:现有数据在保留天数内, 2:目标表含有待归档时间范围的数据, 
  3:插入数据和删除数据记录数不同, 4:SQL执行异常,具体错误见remark
  注意:
  有额外条件时,如果历史数据被修改,从不符合归档条件变成符合归档条件,
  因历史表中归档时间段内已经有之前归档的数据(@v_his_num_before>0),程序会退出,需手动处理
  */
  declare EXIT HANDLER for SQLWARNING,NOT FOUND,SQLEXCEPTION 
  begin 
      GET DIAGNOSTICS CONDITION 1 @p1=RETURNED_SQLSTATE,@p2= MESSAGE_TEXT;
      ROLLBACK;
      insert into archive_log(tab_name,archive_date_begin,archive_date_end,status,insert_rows,delete_rows,remark)
      values(i_table_source,@v_arch_begin,@v_arch_end,4,@v_his_num_after,@v_del_num,concat('error ',@p1,' - ',@p2));
  end;
  /* 获取在线表的最小日期 */
  set @mystmt = concat("select str_to_date(date_format(min(",i_fieldname,"),'%Y%m%d'),'%Y%m%d') into @v_arch_begin from ",i_table_source,' where ',i_other_cond);
  prepare stmt from @mystmt;
  execute stmt;
  deallocate prepare stmt;

  set @v_arch_end = date_add(@v_arch_begin,interval i_archdays day);

  set @mystmt = concat("select count(*) into @v_his_num_before from ",i_table_target," where ",i_fieldname," >= ? and ",i_fieldname," < ?");
  prepare stmt from @mystmt;
  execute stmt using @v_arch_begin,@v_arch_end;
  deallocate prepare stmt;
  /* 如果在线表的数据低于keepday范围,退出 */
  if timestampdiff(day,@v_arch_begin,now()) <= i_keepdays then
      insert into archive_log(tab_name,archive_date_begin,archive_date_end,status,insert_rows,delete_rows,remark)
      values(i_table_source,@v_arch_begin,@v_arch_end,1,0,0,concat('error, all data in keey days, min ',i_fieldname,': ',@v_arch_begin));
  end if;
  /* 如果历史表所在的日期区间有数据,退出(需要手动排查原因) */
  if @v_his_num_before <> 0 then
      insert into archive_log(tab_name,archive_date_begin,archive_date_end,status,insert_rows,delete_rows,remark)
      values(i_table_source,@v_arch_begin,@v_arch_end,2,0,0,concat('error, data exists,row num:',@v_his_num_before));
  end if;
  
  if (timestampdiff(day,@v_arch_begin,now()) > i_keepdays and @v_his_num_before = 0) then 
      set @mystmt = concat("insert into ",i_table_target," select * from ",i_table_source," where ",i_fieldname," >= ? and ",i_fieldname," < ? and ",i_other_cond);
      prepare stmt from @mystmt;
      execute stmt using @v_arch_begin,@v_arch_end;
      deallocate prepare stmt;
      /* 因为federated引擎不支持事务,数据insert后再select下记录数,与下面的delete记录数对比,相同则提交delete操作 */
      set @mystmt = concat("select count(*) into @v_his_num_after from ",i_table_target," where ",i_fieldname," >= ? and ",i_fieldname," < ?");
      prepare stmt from @mystmt;
      execute stmt using @v_arch_begin,@v_arch_end;
      deallocate prepare stmt;

      start transaction;

      set @mystmt = concat("delete from ",i_table_source," where ",i_fieldname," >= ? and ",i_fieldname," < ? and ",i_other_cond);
      prepare stmt from @mystmt;
      execute stmt using @v_arch_begin,@v_arch_end;
      set @v_del_num = row_count();
      deallocate prepare stmt;

      if @v_del_num = @v_his_num_after then
          commit;
          insert into archive_log(tab_name,archive_date_begin,archive_date_end,status,insert_rows,delete_rows,remark)
          values(i_table_source,@v_arch_begin,@v_arch_end,0,@v_his_num_after,@v_del_num,'success');
      else
          rollback;
          insert into archive_log(tab_name,archive_date_begin,archive_date_end,status,insert_rows,delete_rows,remark)
          values(i_table_source,@v_arch_begin,@v_arch_end,3,@v_his_num_after,@v_del_num,'rollback, inserted rows num not equal to deleted rows num');
      end if;
    end if;
        
end;

 

存储过程proc_archive_pkg

CREATE PROCEDURE `proc_archive_pkg`()
begin
call proc_archive(
                  'dborder.myorder_tab', -- tabel source
                  'myorder_tab_his',     -- table target
                  'create_time',         -- time field name
                  120,                   -- i_keepdays
                  1,                     -- i_archdays
                  '1=1'                  -- i_other_cond
                  );

end;

归档日志表记录

最后一步就是用zabbix监控这个表的状态了。

<think>我们正在讨论历史数据归档的方法和策略。根据引用内容,归档是将不常访问的数据从主数据库转移到其他存储系统的过程,具有释放空间、数据保护和合规性等优势。在MySQL、Java应用和OceanBase中都有相应的策略。在MySQL中,归档策略包括确定归档频率(如每月)、归档范围(如特定表)以及归档方式(如INSERTINTO...SELECT)。同时要确保归档期间数据库性能不受影响,可能使用低峰期执行。Java应用中的数据生命周期管理涉及定义数据的各个阶段(产生、使用、存储、归档、销毁),并通过编码实现自动归档。例如,使用定时任务(如Quartz)扫描旧数据,将其迁移到归档存储,并清理原数据库。OceanBase则利用其高压缩比存储和智能化工具,将冷数据归档到低成本存储(如机械盘集群),实现存储资源释放和成本降低。其方案经过大规模验证,支持自动均衡磁盘水位。基于这些,我们可以总结归档的核心步骤:识别冷数据、选择归档目标、设计迁移方法、确保一致性和访问性。同时要考虑业务影响、合规要求及后续管理。下面生成相关问题:1.如何识别数据库中的冷数据以进行归档?2.在MySQL中实施数据归档有哪些具体技术手段?3.Java应用程序如何实现自动数据归档流程?4.OceanBase的归档方案相比传统数据库有何优势?5.数据归档过程中如何保证业务连续性和数据一致性?</think>### 历史数据归档方法及策略详解 在数据库管理中,历史数据归档是通过**识别冷数据**、**安全迁移**和**存储优化**来提升系统性能的关键操作,核心策略如下: #### 一、归档核心方法 1. **数据分级策略** - **冷热分离**:将高频访问的热数据(如最近6个月订单)保留在主库,低频访问的冷数据(如3年前日志)迁移至归档- **分级标准**:基于$ \text{访问频率} = \frac{\text{查询次数}}{\text{时间窗口}} $ 公式量化冷热数据[^1] 2. **技术实现方式** ```sql /* MySQL 分区表归档示例 */ CREATE TABLE sales_archive LIKE sales; ALTER TABLE sales PARTITION BY RANGE(YEAR(order_date)) ( PARTITION p2023 VALUES LESS THAN (2024), PARTITION p_archive VALUES LESS THAN MAXVALUE ); /* 将2022年数据迁移至归档表 */ INSERT INTO sales_archive SELECT * FROM sales WHERE YEAR(order_date) < 2023; ``` **关键点**: - 使用`INSERT INTO ... SELECT`确保事务一致性 - 归档期避开业务高峰(如凌晨定时任务) 3. **存储优化策略** - **压缩技术**:OceanBase采用列式存储压缩,使归档数据体积减少$ 70\% \sim 85\% $[^4] - **分层存储**: ```mermaid graph LR A[主数据-SSD] -->|自动迁移| B[归档-高压缩盘] B -->|策略过期| C[对象存储-S3/OSS] ``` #### 二、企业级最佳实践 1. **生命周期管理(Java应用)** ```java // Spring Boot定时归档任务 @Scheduled(cron = "0 0 2 * * ?") // 每天2AM执行 public void archiveOldData() { LocalDate cutoff = LocalDate.now().minusYears(2); jdbcTemplate.update("INSERT INTO archive_table SELECT * FROM main_table WHERE create_date < ?", cutoff); jdbcTemplate.update("DELETE FROM main_table WHERE create_date < ?", cutoff); } ``` **注意事项**: - 批量处理时启用分页(`LIMIT 1000`)避免长事务[^2] - 归档前后校验$ \sum\text{主库数据量} + \sum\text{归档数据量} = \text{常量} $ 2. **合规性设计** | 要求 | 实现方案 | |--------------|-----------------------------| | GDPR删除权 | 归档库实现物理隔离删除 | | 金融业保留15年 | 多级存储策略 + 只读快照 | #### 三、效能验证指标 $$ \text{空间释放率} = \left(1 - \frac{\text{主库归档后大小}}{\text{主库原始大小}}\right) \times 100\% $$ 实际案例:某支付系统归档后查询性能提升$ 40\% $,存储成本下降$ 62\% $[^4] --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值