本文目录导读:

我来详细说明脚本归档分离历史数据的几种常见方法和实现方案。
按时间归档分离
1 Bash脚本示例(按日期归档)
#!/bin/bash
# 历史数据归档脚本
# 功能:按月份归档日志/数据文件
# 配置
DATA_DIR="/data/logs"
ARCHIVE_DIR="/data/archives"
RETENTION_DAYS=90 # 保留最近90天的在线数据
# 创建归档函数
archive_data() {
local source_dir=$1
local archive_name=$2
# 创建归档目录
mkdir -p "${ARCHIVE_DIR}/${archive_name}"
# 移动过期数据到归档
find "${source_dir}" -type f -mtime +${RETENTION_DAYS} -exec mv {} "${ARCHIVE_DIR}/${archive_name}/" \;
# 压缩归档
tar -czf "${ARCHIVE_DIR}/${archive_name}.tar.gz" -C "${ARCHIVE_DIR}" "${archive_name}"
# 清理未压缩的目录
rm -rf "${ARCHIVE_DIR}/${archive_name}"
}
# 按月归档
monthly_archive() {
local current_date=$(date +%Y%m)
local last_month=$(date -d "1 month ago" +%Y%m)
echo "开始归档 ${last_month} 月份的数据..."
archive_data "${DATA_DIR}" "archive_${last_month}"
}
# 执行归档
monthly_archive
echo "归档完成!"
2 Python脚本示例(灵活的时间窗口)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import shutil
import gzip
from datetime import datetime, timedelta
import logging
class DataArchiver:
"""数据归档分离器"""
def __init__(self, config):
self.source_dir = config.get('source_dir', '/data/source')
self.archive_dir = config.get('archive_dir', '/data/archive')
self.interval_days = config.get('interval_days', 30)
self.file_pattern = config.get('file_pattern', '*.log')
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
self.logger = logging.getLogger(__name__)
def archive_by_date(self, cutoff_date=None):
"""按日期归档历史数据"""
if not cutoff_date:
cutoff_date = datetime.now() - timedelta(days=self.interval_days)
archived_count = 0
for root, dirs, files in os.walk(self.source_dir):
for file in files:
file_path = os.path.join(root, file)
file_mtime = datetime.fromtimestamp(os.path.getmtime(file_path))
# 判断是否需要归档
if file_mtime < cutoff_date:
try:
self._move_and_compress(file_path, file_mtime)
archived_count += 1
except Exception as e:
self.logger.error(f"归档失败: {file_path} - {e}")
self.logger.info(f"完成归档: {archived_count} 个文件")
return archived_count
def _move_and_compress(self, file_path, file_date):
"""移动并压缩文件"""
# 创建按年月组织的归档目录
archive_subdir = os.path.join(
self.archive_dir,
str(file_date.year),
f"{file_date.month:02d}"
)
os.makedirs(archive_subdir, exist_ok=True)
# 生成归档文件名
filename = os.path.basename(file_path)
archive_path = os.path.join(archive_subdir, filename)
# 移动文件
shutil.move(file_path, archive_path)
# 压缩文件(可选)
self._compress_file(archive_path)
def _compress_file(self, file_path):
"""压缩单个文件"""
try:
with open(file_path, 'rb') as f_in:
with gzip.open(f"{file_path}.gz", 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
os.remove(file_path) # 删除原始文件
except Exception:
pass # 压缩失败不影响主流程
def list_archives(self):
"""列出所有归档"""
for root, dirs, files in os.walk(self.archive_dir):
for file in files:
print(os.path.join(root, file))
# 使用示例
if __name__ == "__main__":
config = {
'source_dir': '/var/log/app',
'archive_dir': '/data/archives/app_logs',
'interval_days': 30, # 归档30天前的数据
'file_pattern': '*.log'
}
archiver = DataArchiver(config)
# 执行归档
archiver.archive_by_date()
# 查看归档列表
archiver.list_archives()
数据库数据归档
1 MySQL历史数据归档脚本
-- 创建归档存储过程
DELIMITER //
CREATE PROCEDURE archive_historical_data(
IN archive_date DATE,
IN archive_table VARCHAR(100)
)
BEGIN
DECLARE archive_sql VARCHAR(500);
DECLARE delete_sql VARCHAR(500);
DECLARE table_name VARCHAR(200);
-- 创建归档表
SET table_name = CONCAT(archive_table, '_archive_', DATE_FORMAT(archive_date, '%Y%m'));
-- 创建新表存储归档数据
SET archive_sql = CONCAT(
'CREATE TABLE IF NOT EXISTS ', table_name,
' LIKE ', archive_table
);
PREPARE stmt FROM archive_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 插入历史数据
SET archive_sql = CONCAT(
'INSERT INTO ', table_name,
' SELECT * FROM ', archive_table,
' WHERE DATE(created_at) < \'', archive_date, '\''
);
PREPARE stmt FROM archive_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 删除原始表中的历史数据
SET delete_sql = CONCAT(
'DELETE FROM ', archive_table,
' WHERE DATE(created_at) < \'', archive_date, '\''
);
PREPARE stmt FROM delete_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT CONCAT('归档完成,共迁移 ', ROW_COUNT(), ' 条记录') AS result;
END//
DELIMITER ;
-- 调用存储过程
CALL archive_historical_data('2024-01-01', 'orders');
2 MongoDB时间序列归档
#!/usr/bin/env python3
from pymongo import MongoClient
from datetime import datetime, timedelta
import pickle
import gzip
class MongoDBArchiver:
"""MongoDB历史数据归档"""
def __init__(self, connection_string, database):
self.client = MongoClient(connection_string)
self.db = self.client[database]
def archive_collection(self, collection_name, archive_before, archive_format='file'):
"""归档历史集合"""
collection = self.db[collection_name]
# 查询历史数据
query = {
'timestamp': {'$lt': archive_before}
}
# 批量处理
batch_size = 1000
cursor = collection.find(query).batch_size(batch_size)
archived_docs = []
total_archived = 0
for doc in cursor:
archived_docs.append(doc)
if len(archived_docs) >= batch_size:
total_archived += self._save_batch(archived_docs, collection_name)
archived_docs = []
# 处理剩余的
if archived_docs:
total_archived += self._save_batch(archived_docs, collection_name)
# 删除源数据
if total_archived > 0:
collection.delete_many(query)
return total_archived
def _save_batch(self, docs, collection_name):
"""保存批次数据到归档文件"""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
archive_file = f"archive_{collection_name}_{timestamp}.pkl.gz"
with gzip.open(archive_file, 'wb') as f:
pickle.dump(docs, f)
return len(docs)
# 使用示例
archiver = MongoDBArchiver(
'mongodb://localhost:27017/',
'production_db'
)
# 归档30天前的数据
archive_date = datetime.now() - timedelta(days=30)
count = archiver.archive_collection('sensor_data', archive_date)
print(f"归档了 {count} 条记录")
文件系统层级归档
1 按目录结构归档
#!/bin/bash
# 分层次归档脚本
# 将 /data/projects 下90天未修改的文件归档
ARCHIVE_ROOT="/mnt/archive"
DATA_ROOT="/data/projects"
DAYS_OLD=90
# 归档函数
archive_directory() {
local project_name=$1
local project_path="${DATA_ROOT}/${project_name}"
if [ ! -d "$project_path" ]; then
return 1
fi
# 按项目创建归档
archive_file="${ARCHIVE_ROOT}/${project_name}_$(date +%Y%m%d).tar.gz"
# 查找并归档过期文件
find "$project_path" -type f -mtime +${DAYS_OLD} | \
tar -czf "$archive_file" -T - 2>/dev/null
# 删除已归档的文件
find "$project_path" -type f -mtime +${DAYS_OLD} -delete
echo "项目 ${project_name} 归档完成"
}
# 获取所有项目目录
for project in $(ls -d ${DATA_ROOT}/*/ 2>/dev/null); do
project_name=$(basename "$project")
archive_directory "$project_name"
done
智能归档脚本(带配置和管理)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import os
import shutil
import hashlib
from datetime import datetime, timedelta
class SmartArchiver:
"""智能归档管理器"""
def __init__(self, config_file='archiver_config.json'):
with open(config_file, 'r') as f:
self.config = json.load(f)
self.manifest_file = os.path.join(
self.config.get('archive_root', '/data/archive'),
'manifest.json'
)
self.manifest = self._load_manifest()
def _load_manifest(self):
"""加载归档清单"""
if os.path.exists(self.manifest_file):
with open(self.manifest_file, 'r') as f:
return json.load(f)
return {'archives': [], 'last_archive': None}
def _save_manifest(self):
"""保存归档清单"""
with open(self.manifest_file, 'w') as f:
json.dump(self.manifest, f, indent=2, default=str)
def smart_archive(self, source_path, rules=None):
"""智能归档"""
if not rules:
rules = self.config.get('default_rules', {})
for item in os.listdir(source_path):
item_path = os.path.join(source_path, item)
if os.path.isfile(item_path):
self._archive_file(item_path, rules)
elif os.path.isdir(item_path):
self._archive_directory(item_path, rules)
self._save_manifest()
def _archive_file(self, file_path, rules):
"""归档单个文件"""
file_stat = os.stat(file_path)
file_age = (datetime.now() - datetime.fromtimestamp(file_stat.st_mtime)).days
min_age = rules.get('min_age_days', 30)
if file_age < min_age:
return # 文件不够老,跳过
# 生成归档路径
relative_path = os.path.relpath(file_path, self.config.get('root_path', '/'))
archive_path = os.path.join(
self.config.get('archive_root', '/data/archive'),
relative_path
)
# 创建归档目录
os.makedirs(os.path.dirname(archive_path), exist_ok=True)
# 移动文件
shutil.move(file_path, archive_path)
# 记录归档信息
archive_info = {
'original_path': file_path,
'archive_path': archive_path,
'timestamp': datetime.now().isoformat(),
'file_hash': self._get_file_hash(archive_path),
'file_size': os.path.getsize(archive_path)
}
self.manifest['archives'].append(archive_info)
def _get_file_hash(self, file_path):
"""计算文件哈希"""
hasher = hashlib.sha256()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
hasher.update(chunk)
return hasher.hexdigest()
def restore_archive(self, archive_id=None, target_path=None):
"""恢复归档"""
if archive_id:
archives = [a for a in self.manifest['archives']
if a.get('id') == archive_id]
else:
archives = self.manifest['archives']
restored_count = 0
for archive in archives:
if target_path:
restore_path = os.path.join(target_path,
os.path.basename(archive['original_path']))
else:
restore_path = archive['original_path']
shutil.copy2(archive['archive_path'], restore_path)
restored_count += 1
return restored_count
# 使用示例
if __name__ == "__main__":
archiver = SmartArchiver('archiver_config.json')
# 执行智能归档
archiver.smart_archive('/var/log/applications')
# 查看归档统计
manifest = archiver.manifest
print(f"总归档文件数: {len(manifest['archives'])}")
print(f"最后归档时间: {manifest['last_archive']}")
最佳实践建议
1 归档策略选择
# 推荐的归档策略配置
archive_strategies = {
'hot_data': {
'retention_days': 30,
'storage': 'SSD',
'compression': False
},
'warm_data': {
'retention_days': 90,
'storage': 'HDD',
'compression': True,
'format': 'gzip'
},
'cold_data': {
'retention_days': 365,
'storage': 'Archive',
'compression': True,
'format': 'zstd',
'encryption': True
},
'frozen_data': {
'retention': 'permanent',
'storage': 'Tape/Cloud',
'compression': True,
'checksum': True
}
}
2 自动化部署(crontab)
# 每天凌晨2点执行归档 0 2 * * * /usr/local/bin/data_archiver.py --config /etc/archiver.conf # 每周日进行完整性检查 0 3 * * 0 /usr/local/bin/archiver_audit.py --verify # 每月1号清理过期归档 0 4 1 * * /usr/local/bin/cleanup_old_archives.sh --older-than 365
这些脚本可以根据你的具体需求进行调整,关键是要考虑:
- 归档频率(实时、每日、每周、每月)
- 数据保留策略(保留期限、版本控制)
- 存储位置(本地、网络存储、云存储)
- 压缩和加密需求
- 审计和恢复能力
选择最适合你业务场景的方案进行实现。