大数据挑战
早上九点,我打开监控面板,看到红色的警告灯在疯狂闪烁。
click_logs 表突破 5000 万条记录了。磁盘占用飙到 40GB。我试着查询上周的数据统计,进度条慢悠悠地爬了 30 秒才返回结果。
我盯着屏幕,意识到一个残酷的事实:按照这个增长速度,三个月后数据库就要爆炸了。
当前数据量:
- 每天点击: 100 万次
- 每条日志: 500 字节
- 每天新增: 500MB
- 一年累积: 180GB
存储增长曲线:
Month 1: 15GB
Month 3: 45GB ← 查询开始变慢
Month 6: 90GB ← 数据库扛不住了
Month 12: 180GB ← 💥
我翻着 AWS 账单,看着 RDS 的存储成本一路飙升。我知道,必须做数据分层了。
那天晚上我先把统计链路画出来:跳转服务只负责产生点击事件,真正的大数据处理交给队列、实时聚合和离线分析。
第 4 版:异步统计链路
点击统计不能拖慢跳转,事件进入队列后再实时和离线消费。
跳转
事件
统计
存储
然后我把数据按照访问频率分成三类:
数据温度分布:
🔥 热数据(最近 7 天)
占比: 2% 访问频率: 80%
查询特点: 实时统计、活跃短链接监控
存储: Redis + MySQL
这些数据是用户最常看的——今天点击了多少、最近一周的趋势。
必须毫秒级响应,放在 Redis 缓存里。
🌡️ 温数据(7 天 ~ 30 天)
占比: 8% 访问频率: 15%
查询特点: 周报、月报查看
存储: MySQL
用户偶尔要看上个月的统计数据,但不会频繁访问。
放在 MySQL 里,用索引优化查询。
❄️ 冷数据(30 天以上)
占比: 90% 访问频率: 5%
查询特点: 历史数据分析、合规审计
存储: 对象存储(S3)
这些数据几乎没人看,但不能删——可能要做年度总结,
或者有人问"去年 12 月的点击量是多少"。
压缩后扔到 S3,成本只有 MySQL 的 1/10。
核心理念: 80% 的查询只看 2% 的数据。把冷数据搬走,热数据跑得飞快。
归档方案实现
我把冷数据归档做成每天凌晨的固定流程:
落地思路
- 归档脚本按时间边界分批捞数据:每次只取固定条数,捞完写进对象存储再删库里的对应记录。
- 分批是为了不让长事务把库锁住,也避免一次删几千万行把 undo 撑爆。
- 归档文件按日期命名并带批次号,恢复时能拼回完整的日粒度数据。
- 删库前先核对归档文件的记录数和字节数,对不上就停在这一批不删。
第一次运行时,我看着日志输出,心跳加速:
🔍 查找 2024-01-15 之前的数据...
📦 找到 42,356,789 条记录需要归档
📤 已归档 50,000/42,356,789 条记录
📤 已归档 100,000/42,356,789 条记录
...
✅ 归档完成! 删除了 42,356,789 条记录,释放了 20.2 GB 空间
MySQL 表从 5000 万条记录降到了 700 万条,查询速度从 30 秒降到了 0.3 秒。
快了 100 倍。
归档解决了存储问题,但 MySQL 表还有 700 万条记录。我学会了分区表——按日期分区,查询时只扫描相关分区。
数据设计要点
- 核心是在
click_logs_partitioned里保存业务事实,而不是把规则散落在应用逻辑里。- 分区表上的查询要看两件事:时间条件有没有让引擎裁掉无关分区,剩余分区里有没有命中索引。
- 分区裁剪失效时,执行计划里会看到扫过全部分区,扫描行数会随分区数线性放大。
- 关键字段包括
id、short_code、ip、country、city、device、browser、os,它们决定了后续查询和管理能力。
分区表的好处:
- 查询快: 只扫描相关分区
- 删除快: 删整个分区比逐行删除快 100 倍
- 维护方便: 可以单独优化某个分区
自动分区管理
分区维护也要流程化,重点是每月提前准备新分区:
落地思路
- 按创建时间分区,每月一个分区,查询带时间条件时只扫描命中的分区。
- 每月提前把新分区建好,避免数据写到没有分区的表上被拒。
- 过期分区整块删除,比逐行 DELETE 便宜得多,也不用长时间持锁。
- 分区管理要自动化:建分区、删过期分区、检查分区表是否健康,都放进定时任务。
冷数据搬到 S3 后,我做了个实验——看看不同压缩方式的效果。
落地思路
- 同一批归档数据分别用 Gzip 和 LZMA 压一遍,比较压缩后大小和压缩耗时两个指标。
- 压缩比高的算法不一定好用:压缩太慢会让归档窗口拉长,拖住当天的数据处理。
- 归档场景以压缩比优先,因为压缩只发生一次;在线传输场景则相反。
- 压缩后的文件仍要保留校验值,恢复前先验一遍,避免压缩损坏的数据被当成正常数据导入。
测试结果让我惊喜:
method original_size_kb compressed_size_kb compression_ratio space_saved
0 Gzip 1024.0 102.4 10.00% 90.0%
1 LZMA 1024.0 68.3 6.67% 93.3%
2 Zlib 1024.0 128.0 12.50% 87.5%
LZMA 压缩率最高,能节省 93% 的空间。但压缩速度慢,适合归档场景。Gzip 是折中方案,压缩率和速度都不错。
实际压缩效果
我统计了生产环境的压缩效果:
原始数据: 42,356,789 条 × 500 字节 = 20.2 GB
压缩后:
- Gzip: 2.0 GB (压缩率 10:1)
- LZMA: 1.4 GB (压缩率 14:1)
S3 存储成本:
- 标准: ¥0.18/GB/月
- 归档: ¥0.025/GB/月
月成本对比:
- MySQL 存储: ¥18/月 × 20GB = ¥360/月
- S3 归档(LZMA): ¥0.025 × 1.4GB = ¥0.035/月
节省了 99%! 💰
我把数据分层前后的成本做了个详细对比:
【方案 A: 全放 MySQL】
存储: 50GB × ¥1.5/GB/月 = ¥75/月
问题:
- 查询越来越慢
- 备份时间长
- 磁盘快满了
- 扩容成本高
月成本: ¥75 💸
【方案 B: 冷热分离】
热数据(MySQL): 7 天 × 500MB/天 = 3.5GB × ¥1.5 = ¥5.25/月
温数据(MySQL): 23 天 × 500MB/天 = 11.5GB × ¥1.5 = ¥17.25/月
冷数据(S3 归档): 335 天 × 500MB/天 × 10%压缩 = 16.75GB × ¥0.025 = ¥0.42/月
月成本: ¥22.92 💰
【节省】
¥75 - ¥22.92 = ¥52.08/月
一年节省: ¥625/年
而且查询速度快了 100 倍! ⚡
但成本不是唯一的考虑因素。数据分层还带来了:
- 查询速度: 热数据查询从 30 秒降到 0.3 秒
- 系统稳定性: MySQL 不再因为磁盘满而宕机
- 扩展性: S3 理论上可以存无限数据
- 合规性: 冷数据归档可以永久保存,满足审计要求
有一天,客户问我:“能查下去年 12 月的完整点击记录吗?”
我淡定地打开 S3 浏览器,找到对应的归档文件,下载、解压、导入临时表。
落地思路
- 恢复流程是:按日期找到对应的归档文件 → 下载 → 解压 → 导入一张临时表 → 用查询覆盖结果。
- 临时表只在恢复期间存在,恢复完成后删掉,不让它和在线表长期并存。
- 先按短码和时间范围在临时表里查出这段数据,再决定是否返回完整明细。
- 恢复是有成本的操作,大范围恢复要走异步任务并通知用户,而不是让接口一直等着。
30 秒后,我给客户发了完整的 Excel 表格。客户很惊讶:“这么快!”
我笑了笑,心里想:数据分层不只是省钱,更是让系统可持续。
数据问题搞定了,但我发现一个有趣的现象:
有些用户愿意付费来获取更详细的统计数据。比如:
- 某电商客户想知道:“哪个国家的点击量最高?”
- 某自媒体博主问:“我的粉丝在哪些城市最活跃?”
- 某广告代理商需要:“每小时点击趋势图”
这些查询都需要访问冷数据,但用户愿意为此付费。
我看着账本上的数字,脑子里冒出一个想法:是不是可以推出付费高级分析功能?
👉 下一章:自定义短链接 —— 用户可以自定义短链接后缀,但这个功能带来了新的技术挑战…
发现内容有问题?
告诉我们哪里需要改进。
