MaxCompute限制单张表最多60,000个分区。本文说明该限制的原因,并提供三种方案帮助合理规划分区策略、降低现有表的分区数量。
限制原因
表分区功能,目的是根据业务字段组织数据,并在查询中通过分区过滤表中数据。但是在大数据场景下,分区过多会带来以下问题:
数据倾斜:分区列取值不均匀,导致数据分布不平衡。多级分区的总数是各级取值的笛卡尔积,长尾分区的元数据开销可能超过实际数据量。
管理成本高:设置、维护大量的分区比较复杂。冷热标记、自动过期、备份恢复、跨集群复制等操作都以分区为粒度,分区过细会显著增加管理负担。
元数据瓶颈:分区元信息集中存储在元数据服务中,分区数过多容易成为性能瓶颈。
为了保障稳定性和高效使用,MaxCompute设置了单表六万分区的限制。
使用方法
因此,推荐在分区下构建聚簇分桶 、重排数据以及设置索引等,把数据的元信息分散到需要访问数据的链路中,提高稳定性和计算效率。
分区结合聚簇分桶可以提供更细粒度、更高效的索引能力。
如果原业务的分区数过多,减少分区数同时要获得好的过滤效果,可以采用分区聚簇组合方案替代全分区方案。
如果单纯限制分区数,避免稳定性风险,可以合并历史分区,减少分区数量。
如果保持分区不变,仍要查询分区总数超过六万的数据,可以将历史分区通过clone归档为历史表,然后用视图union多张历史表的方式实现,但此种情况下扫描数据过多,有稳定性风险。
下面介绍详细的使用方法。
分区表示例
假设有如下两张分区表,如果分区数达到几万,对该表的读写操作性能都会有很大影响。
以天、小时、region、终端类型分为四个分区
CREATE TABLE IF NOT EXISTS test01 ( user_id STRING COMMENT '用户ID', action STRING COMMENT '行为:点击、购买、浏览等', view_time BIGINT COMMENT '时间戳' ) PARTITIONED BY ( dt STRING COMMENT '日期,格式: yyyy-MM-dd', hour STRING COMMENT '小时,00-23', region STRING COMMENT '地区:如 beijing、shanghai', type STRING COMMENT '终端品牌:如 huawei、xiaomi、apple' );以天、小时、region、uid分为四个分区
CREATE TABLE IF NOT EXISTS test02 ( action STRING COMMENT '行为:点击、购买、浏览等', view_time BIGINT COMMENT '时间戳', type STRING COMMENT '终端品牌:如 huawei、xiaomi、apple' ) PARTITIONED BY ( dt STRING COMMENT '日期,格式: yyyy-MM-dd', hour STRING COMMENT '小时,00-23', region STRING COMMENT '地区:如 beijing、shanghai', user_id STRING COMMENT '用户ID' );
方案一:使用分区聚簇组合替代全分区
创建分区表时设置分桶属性,对数据进行哈希散列后,将数据均匀分布到固定数量的Bucket中。可参考Hash Clustering。
使用方式:建表时加上聚簇参数。
示例
对于上述test02表,原始分区是
dt、hour、region、user_id,其中user_id存在许多不重复的值,可以将user_id改为clustered key。CREATE TABLE IF NOT EXISTS test02_bucket ( user_id STRING COMMENT '用户ID', action STRING COMMENT '行为:点击、购买、浏览等', view_time BIGINT COMMENT '时间戳', type STRING COMMENT '终端品牌:如 huawei、xiaomi、apple' ) PARTITIONED BY ( dt STRING COMMENT '日期,格式: yyyy-MM-dd', hour STRING COMMENT '小时,00-23', region STRING COMMENT '地区:如 beijing、shanghai' ) CLUSTERED BY (user_id) SORTED by (user_id) INTO 128 BUCKETS;使用建议
选择高基数且均匀分布的字段,比如
user_id。分桶数量一般设置为 2 的幂次方(便于哈希计算),如 64、128、256。
单个桶存储量建议在500MB ~ 1GB之间。
哈希桶的数目必须提供,由数据量大小来决定。
当分桶key不均衡时,可使用Range Clustering。
Range Clustering作为一种新的数据切分方式,提供了一个全局有序的数据分布,一是可以避免Hash Clustering可能造成的数据倾斜问题;二是在数据有序分布的前提下,创建两级索引(Index),支持对Clustering Key的区域查询以及多键的组合查询等场景。
使用方式
和Hash Clustering类似,比较大的区别是RANGE关键字以及Bucket数目对于Range Clustering是可以省略的。
CREATE TABLE IF NOT EXISTS test02_range_bucket ( user_id STRING COMMENT '用户ID', action STRING COMMENT '行为:点击、购买、浏览等', view_time BIGINT COMMENT '时间戳', type STRING COMMENT '终端品牌:如 huawei、xiaomi、apple' ) PARTITIONED BY ( dt STRING COMMENT '日期,格式: yyyy-MM-dd', hour STRING COMMENT '小时,00-23', region STRING COMMENT '地区:如 beijing、shanghai' ) RANGE CLUSTERED BY (user_id) SORTED by (user_id) INTO 128 BUCKETS;
方案二:合并历史分区,减少分区数量
将高频细粒度分区(如按小时)的历史数据,合并为低频大分区(统一按天),从而减少总分区数。这种情况下,对历史分区的查询条件要扩大到更高一级,否则用合并掉的分区条件查不到数据。
规划哪些属于历史分区。比如test01表:2024-04-15这天的数据为历史分区,那么将2024-04-15这天的所有分区的数据都合并到dt='2024-04-15',hour='04',region='beijing',type='meizu'这一个分区。近期业务数据仍按小时分区。具体操作如下:
执行合并分区的命令,详情可参考分区操作。
-- 可以先列出当天有多少分区 SHOW PARTITIONS test01; -- 合并当天的多个分区,到指定的一个分区。 ALTER TABLE test01 MERGE IF EXISTS PARTITION(dt='2024-04-15',hour='04',region='beijing',type='meizu'), PARTITION(dt='2024-04-15',hour='05',region='nanjing',type='oneplus'), PARTITION(dt='2024-04-15',hour='08',region='hangzhou',type='huawei'), PARTITION(dt='2024-04-15',hour='16',region='zhangjiakou',type='vivo'), PARTITION(dt='2024-04-15',hour='19',region='zhangjiakou',type='apple'), PARTITION(dt='2024-04-15',hour='20',region='zhangjiakou',type='vivo') OVERWRITE PARTITION(dt='2024-04-15',hour='04',region='beijing',type='meizu') purge;删除源表中已经合并的分区,可通过指定筛选条件的方式删除分区,详情可参考删除分区。
ALTER TABLE test01 DROP IF EXISTS PARTITION(dt='2024-04-15', hour RLIKE '^(0[0-35-9]|[12][0-9])$', region LIKE '%', type LIKE '%');
方案三:使用CLONE归档历史分区
将历史数据从当前主表迁出,形成独立的历史表,主表只保留近期活跃数据。
规划哪些属于历史分区。例如,test01表2025-10-01这天的数据为历史分区,那么把2025-10-01这天前的数据都迁移到新表中。具体操作如下:
创建新分区表作为历史表,以CLONE TABLE的方式将历史分区数据拆分到历史表中,详情可参考CLONE TABLE。
-- 创建新分区表作为历史表,以时间命名。 CREATE TABLE IF NOT EXISTS test01_20251001 ( user_id STRING COMMENT '用户ID', action STRING COMMENT '行为:点击、购买、浏览等', view_time BIGINT COMMENT '时间戳' ) PARTITIONED BY ( dt STRING COMMENT '日期,格式: yyyy-MM-dd', hour STRING COMMENT '小时,00-23', region STRING COMMENT '地区:如 beijing、shanghai', type STRING COMMENT '终端品牌:如 huawei、xiaomi、apple' ); -- 通过clone命令复制历史分区数据。 CLONE TABLE test01 PARTITION (dt='2025-10-01',hour='00',region='shenzhen',type='realme'), PARTITION (dt='2025-10-01',hour='01',region='beijing',type='realme') TO test01_20251001;删除源表中的历史数据,可通过指定筛选条件的方式删除分区,详情可参考删除分区。
ALTER TABLE test01 DROP IF EXISTS PARTITION(dt='2025-10-01',hour LIKE '%',region LIKE '%',type LIKE '%');如果后续需要查询所有数据,可以进一步构建基于所有历史表的union视图。
CREATE VIEW IF NOT EXISTS test01_view ( user_id ,action ,view_time,dt,hour,region,type) AS SELECT * FROM test01 UNION ALL SELECT * FROM test01_20251001; -- 查询视图 SET odps.sql.allow.fullscan=true; SELECT * FROM test01_view WHERE dt='2025-10-01' OR dt='2024-04-15';