本章节以简化的业务场景来介绍物流快递的最佳实践。
- 本例中简化的业务场景:仅包括邮件状态表用于实时监控和更新邮件包裹状态,包裹从订单、收单、装车、投递等分为不同的状态。
- 业务要求:每个机构实时统计当前不同状态的邮件数量。
- 业务性能要求:
- 实时统计新增数据,且要求一秒以内返回结果(即RT<1s)。
- 查询并发量:QPS>300 。
表的逻辑设计—邮件状态表(t_fact_mail_status)
要求:
- 单日数据量约一亿记录。
- 有15万机构或快递员,每个机构的邮件量为100到3000不等。
- 存储30天的数据。
为满足高QPS,从设计上采用大宽表、冗余字段,并且避免表关联。
字段名 | 字段类型 | 说明 |
---|---|---|
mail_id | varchar | 邮件订单唯一码 |
scan_timestamp | timestamp | 收件时间 |
biz_date | bigint | 日期(二级分区) |
dlv_person_name | varchar | 投递员名称 |
rg_code | varchar | 机构编码 |
rg_name | varchar | 机构名称 |
receiver_name | varchar | 收件人名 |
receiver_phone | varchar | 收件人电话 |
receiver_addr 收件人地址 | varchar | 收件人地址 |
product_no 产品编码 | varchar | 产品编码 |
mag_no | varchar | 包裹编号 |
op_1_timestamp | bigint | 状态1操作时间 |
op_2_timestamp | bigint | 状态2操作时间 |
op_3_timestamp | bigint | 状态3操作时间 |
op_4_timestamp | bigint | 状态4操作时间 |
op_5_timestamp | bigint | 状态5操作时间 |
表的物理设计
创建邮件状态表 t_fact_mail_status ,需要规划表的一级分区、二级分区。业务查询主要按机构进行查询,QPS要求高。综上所述, 我们选择按rg_code进行一级分区。
CREATE TABLE t_fact_mail_status (
mail_id varchar COMMENT '',
scan_timestamp timestamp COMMENT '',
biz_date bigint COMMENT '',
rg_code varchar COMMENT '',
rg_name varchar COMMENT '',
dlv_person_name varchar COMMENT '',
receiver_name varchar COMMENT '',
receiver_phone varchar COMMENT '',
receiver_addr varchar COMMENT '',
product_no varchar COMMENT '',
mag_no varchar COMMENT '',
op_1_timestamp bigint COMMENT '',
op_2_timestamp bigint COMMENT '',
op_3_timestamp bigint COMMENT '',
op_4_timestamp bigint COMMENT '',
op_5_timestamp bigint COMMENT '',
PRIMARY KEY (mail_id,rg_code,biz_date)
)
PARTITION BY HASH KEY (rg_code) PARTITION NUM 128
SUBPARTITION BY LIST KEY (biz_date)
SUBPARTITION OPTIONS (available_partition_num = 30)
TABLEGROUP ads_demo
OPTIONS (UPDATETYPE='realtime')
COMMENT '';
查询SQL
要求:
- 查询当天某机构号
- 不同状态的邮件数据量
- 核查需要操作的邮件量
- RT<1s 并且高QPS
综上所述,须利用分区裁剪,使单个查询在不同的节点上运行。SQL语句示例如下:
select sum(case when t.op_1_timestamp >= 20171128000000 and t.
op_1_timestamp <= 20171128235900
and(( t.op_2_timestamp is null or t.op_2_timestamp >
20171128235900 or t.op_2_timestamp < 20171128000000)
and( t.op_3_timestamp is null or t.op_3_timestamp >
20171128235900 or t.op_3_timestamp < 20171128000000)
and( t.op_4_timestamp is null or t.op_4_timestamp >
20171128235900 or t.op_4_timestamp < 20171128000000))
then 1 else 0 end ) as cn
from t_fact_mail_status t
where t.rg_code = '21111101' and t.biz_date = 20171128;