一、 文章概述
在业务发展初期单表完全可以满足业务需求,在阿里巴巴开发手册也建议:单表行数超过500万行或者单表容量超过2GB才推荐进行分库分表,如果预计三年后数据量根本达不到这个级别,请不要在创建表时就分库分表。
但是随着业务的发展和深入,单表数据量不断增加,逐渐成为业务系统的瓶颈。这是为什么呢?
从宏观层面分析任何物体都必然有其物理极限。1965年英特尔创始人摩尔预测:集成电路上可容纳的元器件的数目,约每隔24个月增加一倍,性能提升一倍,即计算机性能每两年翻一番。
但是摩尔定律会有终点吗?有些科学家认为摩尔定律是有终点的:半导体芯片单位面积可集成的元件数量是有极限的,因为半导体芯片制程工艺的物理极限为2到3纳米。当然也有科学家不支持这种说法,但是我们可以从中看出物理极限是很难突破的,当单表数据量达到一定规模时必然也达到极限。
从细节层面分析我们将数据保存在数据库,实际上是保存在磁盘中,一次磁盘IO操作需要经历寻道、旋转延时、数据传输三个步骤,那么一次磁盘IO耗时公式如下:
单次IO时间 = 寻道时间 + 旋转延迟 + 传送时间
总体来说上述操作都较为耗时,速度和内存相比有着数量级的差距,当数据量过大磁盘这一瓶颈更加明显。那么应该怎么办?处理单表数据量过大有以下六字口诀:删、换、分、拆、异、热。
删是指删除历史数据并进行归档。换是指不要只使用数据库资源,有些数据可以存储至其它替代资源。分是指读写分离,增加多个读实例应对读多写少的互联网场景。拆是指分库分表,将数据分散至不同的库表中减轻压力。异指数据异构,将一份数据根据不同业务需求保存多份。热是指热点数据,这是一个非常值得注意的问题。
二、删
我们分析这样一个场景:消费者会经常查询一年之前的订单记录吗?答案是一般不会,或者说这种查询需求量很小。根据上述分析那么一年前的数据我们就没有必要放在单表这张业务主表,可以将一年前的数据迁移到历史归档表。
在查询历史数据表时,可以限制查询条件如必须选择日期范围,日期范围不能超过X个月等等从而减轻查询压力。
处理历史存量数据比较简单,因为存量数据一般是静态的,此时状态已经不再改变了。数据处理一般分为以下两个步骤:
(1) 迁移一年前数据至历史归档表
(2) 根据主键分批删除主表数据
不能一次性删除所有数据,因为数据量太大可能会引发超时,而是应该根据ID分批删除,例如每次删除500条数据。
第一步查询一年前主键最大值和最小值,这是我们需要删除的数据范围:
SELECT
MIN(id) AS minId,
MAX(id) AS maxId
FROM biz_table
WHERE create_time < DATE_SUB(now(),INTERVAL 1 YEAR)
第二步删除数据时不能一次性全部删掉,因为很可能会超时,我们可以通过代码动态更新endId进行批量删除:
DELETE FROM biz_table
WHERE id >= #{minId}
AND id <= #{maxId}
AND id <= #{endId}
LIMIT 500
三、换
换是指换一个存储介质,当然并不是说完全替换,而是用其它存储介质对数据库做一个补充。例如海量流水记录,这类数据量级是巨量的,根本不适合存储在MySQL数据库中,那么这些数据可以存在哪里呢?
现在互联网公司一般都具备与之规模相对应的大数据服务或者平台,那么作为业务开发者要善于应用公司大数据能力,减轻业务数据库压力。
3.1 消息队列
这些海量数据可以存储至Kafka,因为其本质上就是分布式的流数据存储系统。使用Kafka有如下优点:
第一个优点是Kafka社区活跃功能强大,已经成为了一种事实上的工业标准。大数据很多组件都提供了Kafka接入组件,经过生产验证并且对接成本较小,可以为下游业务提供更多选择。
第二个优点是Kafka具有消息队列本身的优点例如解耦、异步和削峰。
假设这些海量数据都已经存储在Kafka,现在我们希望这些数据可以产生业务价值,这涉及到两种数据分析任务:离线任务和实时任务。
离线任务对实时性要求不高,例如每天、每周、每月的数据报表统计分析,我们可以使用基于MapReduce数据仓库工具Hive进行报表统计。
实时任务对实时性要求高,例如根据用户相关行为推荐用户感兴趣的商品,提高用户购买体验和效率,可以使用Flink进行流处理分析。例如运营后台查询分析,可以将数据同步至ES进行检索。
还有一种分类方式是将任务分为批处理任务和流处理任务,我们可以这么理解:离线任务一般使用批