热数据直写MySQL并建联合索引;温数据按月分区迁移至归档库;冷数据导出CSV后转Parquet存对象存储;销毁前须标记、核对再物理删除。

热数据直接写入MySQL并加索引
新产生的业务数据(如订单、日志、用户行为)默认属于热数据,必须能毫秒级读写。直接用 PDO 或 mysqli 写入主库即可,但关键在索引设计:
• 单表超过 50 万行后,WHERE user_id = ? AND created_at > ? 这类查询必须有联合索引,顺序是 (user_id, created_at),不能反过来;
• 避免在 created_at 字段上单独建索引——热数据写多查多,单字段索引会拖慢写入且对范围查询帮助有限;
• 不要给热表加 JSON 字段存结构化内容,PHP 解析后再插入原子字段,否则后续无法高效过滤或排序。
温数据按月分区迁移到归档库
通常指 3–12 个月前的数据,查询频次下降但仍有报表、审计等需求。不要用 mysqldump + LOAD DATA 手动搬,容易锁表或丢数据:
• 使用 MySQL 原生 ALTER TABLE ... EXCHANGE PARTITION(需 MySQL 5.7+ 且表已按 created_at RANGE 分区);
• 若原表无分区,先创建带相同结构的归档表(引擎建议 MyISAM 或 Archive,节省空间),再用 INSERT INTO archive_table SELECT * FROM main_table WHERE created_at = '2023-01-01' 拆月迁移;
• 迁移后立即执行 DELETE FROM main_table WHERE ...,但务必在事务外分批删(每批 ≤ 1000 行),避免长事务阻塞热写;
• 归档库连接要独立配置,PHP 中用不同 $pdo_archive 实例,别复用主库连接。
冷数据压缩为 Parquet 存对象存储
1 年以上的数据基本只用于离线分析,MySQL 已不是合适载体。PHP 本身不直接生成 Parquet,但可调用外部工具链:
• 先用 SELECT ... INTO OUTFILE 导出为 CSV(注意用 FIELDS TERMINATED BY '\t' 避免逗号干扰);
• 调用 Python 脚本转换:python3 /path/to/csv2parquet.py --input /tmp/data_2022.tsv --output s3://my-bucket/cold/2022/,依赖 pyarrow 和 boto3;
• PHP 中用 proc_open() 启动该命令,捕获 STDERR 判断是否成功,失败时重试 2 次并记录 error_log;
• 对象存储路径必须含年份+月份(如 2022/06/),方便后续用 Presto 或 Trino 直接按分区扫描,避免全桶遍历。
销毁前必须验证归档完整性
删库跑路式销毁最危险。所谓“销毁”其实是三步:标记 → 核对 → 物理删除:
• 在原表加 archived_at 时间戳字段,迁移温/冷数据后更新该字段,而非直接 DELETE;
• 销毁脚本启动前,运行校验 SQL:SELECT COUNT(*) FROM main_table WHERE archived_at IS NOT NULL AND created_at ,再查归档库/对象存储对应时间段数据量是否一致;<br>• 只有校验通过且人工确认后,才执行最终 <code>DELETE 或 aws s3 rm s3://... --recursive;
• 所有销毁操作必须记入审计日志,包含操作人、时间、影响行数、校验哈希(如对归档 CSV 做 md5sum),这个哈希值要和销毁指令一起落库。
立即学习“PHP免费学习笔记(深入)”;
真正难的不是写几段迁移代码,而是让每个环节的边界清晰可测:热区不掺温数据,温区不漏冷数据,冷区文件不缺字段——一旦某个月份的 Parquet schema 少了一个字段,下游分析就全错,而这种问题往往三个月后才暴露。



















