MySQL分区表从原理到实战:解决大表查询与归档难题
1. 一张不断膨胀的大表逼我认真认识分区表做数据库运维这些年我最怕听到的一句话不是“库挂了”而是“这张表已经几个亿了查一下慢得要死”。有个线上业务表存的是用户操作日志半年时间涨到 3 亿多行单表磁盘占用将近 60GB。SELECT 带时间范围倒是能用上索引但一旦涉及清理历史数据DELETE 一次跑十几分钟把主从延迟直接拉满DBA 群里一片哀嚎。当时第一个反应是上归档任务把三个月前的数据搬到历史库。但 DELETE 的代价摆在那里每删一行都要走事务、记 binlog、维护二级索引删 1000 万行的时间够我喝三杯咖啡。有人提议“直接 DROP 掉那些老分区数据”这句话点醒了我——业务表如果当初建成了分区表清理数据压根不用 DELETE一个 DROP PARTITION 就是秒级的事。MySQL 分区表Partitioning Table说白了就是逻辑上是一张表物理上拆成多个独立的存储片段。查询的时候优化器可以只扫描需要的那几个片段而不是整表撸一遍。官方文档叫 partition pruning分区裁剪记住这个词后面所有性能讨论都围着它转。这篇文章我会把分区表的原理、四种分区类型怎么选、分区键怎么定、实际运维怎么操作、以及哪些场景会被“分区表”三个字忽悠瘸一口气讲透。适合被大表折磨过、正在考虑给现有表做分区的同学也适合刚学 MySQL 想系统理解分区机制的初学者。1.1 先还原一个真实的表膨胀场景假设你手上有一张订单表CREATE TABLE order_record ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL, user_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL, create_time DATETIME NOT NULL, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_create_time (create_time) ) ENGINEInnoDB;业务跑了一年数据量破亿。这时候你发现三个问题第一索引越来越大。二级索引的 B 树体积跟着数据量膨胀内存 Buffer Pool 里能缓存的索引页占比越来越小随机 IO 变多查询响应时间从几十毫秒慢慢爬到几百毫秒。第二旧数据清理困难。产品说订单数据要保留两年但用户的日常查询基本只看最近三个月。你想把一年前的数据删掉DELETE 语句的 WHERE 条件如果命中 create_time即便有索引删除过程中索引也要同步更新且事务过大还会造成锁竞争、主从延迟。1500 万行 DELETE 下去从库延迟直接报了 800 秒。第三备份恢复的粒度太粗。整库备份 60GB恢复一次少说 40 分钟。可实际上你关心的活跃数据可能只有最近几个月的 10GB。分区表解决的就是这类问题。把表按 create_time 做 RANGE 分区每个月一个分区历史数据的清理变成ALTER TABLE order_record DROP PARTITION p202301;删一个分区等于删掉一个物理文件片段不产生大量行级操作主从延迟几乎为零备份也可以通过只备份活跃分区来缩小范围。这个收益是立竿见影的。1.2 分区表到底是什么一张表分成了 N 个物理小家伙在 MySQL 8.0 里InnoDB 引擎的分区表在存储引擎层仍然是多个独立的 B 树每个分区有自己独立的 .ibd 文件前提是 innodb_file_per_table 开启默认就是开启的。从客户端连接来看你操作的还是同一张表普通的 SELECT、INSERT、UPDATE、DELETE 语法完全不用改。每个分区有一个名字对应一段数据范围。比如按月份分了 12 个分区数据写入时存储引擎根据分区表达式自动决定这一行该进哪个分区。你在 SQL 里根本感知不到底层的路由逻辑感知不到不代表不重要——如果查询条件里不带分区键MySQL 可能把所有分区都扫一遍这就是“分区表反而更慢”的经典来源。打个比方分区表就像把一个超大型仓库按货架区域拆成 A 区、B 区、C 区每个区有独立的库管员。你找货时只要报上区域编号库管员直接去对应区域找如果不报编号那就得让所有区域的库管员一起翻一遍费力不讨好。分区键就是那个区域编号查询必须带上它收益才成立。2. 分区键没选好还不如不分区——最容易被忽视的根基我见过太多人兴致勃勃地把表改成分区表结果过了两周发现查询更慢了最后又花了一个通宵把分区拆掉。问题几乎都出在分区键上。分区键的选择不是“选一个字段这么简单”它直接决定两个硬性约束和一个核心性能假设。2.1 第一条铁律主键和唯一键必须包含分区键MySQL 对分区表有一个在别的数据库里不太常见的限制建分区表时表上的所有主键列和唯一键列都必须包含分区键。什么意思比如上面那张订单表主键是 id你想按 create_time 分区MySQL 直接报错ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the tables partitioning function因为主键在全局要求唯一性而分区表的数据分布在各分区文件中MySQL 需要保证主键索引和分区键能在同一个个分区内完成唯一性校验。如果主键只包含 id不包含 create_time那么插入时为了校验 id 不重复理论上得去所有分区查一遍代价不可控所以 MySQL 直接禁止。解决办法通常是两种把分区键加入主键做成联合主键PRIMARY KEY (id, create_time)或者取消主键只保留普通索引不推荐主键对 InnoDB 太重要了。我看到过不少线上表为了分区把主键语义强行改掉业务代码里拿 id 当唯一键用的逻辑全部推翻重来。这就是为什么我建议大家在表设计阶段就要想好分区需求等数据量上来再改会很痛。如果你真的要对已有主键表改分区第一步先检查所有唯一键。2.2 第二条铁律查询条件里不带分区键性能直接打回原形分区裁剪是分区表唯一的性能核心。所谓裁剪就是优化器根据 WHERE 条件中的分区键范围把不相关的分区直接跳过只扫描命中的分区。还是订单表的例子如果按 create_time 分区下面这条查询SELECT * FROM order_record WHERE create_time 2024-08-01 AND create_time 2024-09-01;MySQL 可以直接定位到 p202408 这一个分区扫描的数据量从数十 GB 缩小到几 GB效果立竿见影。但如果你按 user_id 查SELECT * FROM order_record WHERE user_id 123456;优化器无法从 create_time 上推断出该用户的数据分布在哪个分区只能全部 12 个分区挨个扫一遍。表面上你建了分区实际上查询很可能是全分区扫描再加上一次回表性能甚至不如普通单表加索引。第二个约束由此而来分区键必须是你最核心、最高频的那条查询路径上的过滤条件。对订单表来说绝大多数业务查询都要带 create_time 范围所以它是天然的分区键如果你的表平时主要按 user_id 查那分区键就应该选 user_id或者干脆别分区用普通索引更省事。2.3 分区键的另一个隐藏要求筛选性要好筛选性指的是分区键的取值离散程度。如果分区键只有“是/否”两个取值那么最多分成两个有意义的区其余全是摆设。RANGE 分区希望键值能自然分段比如时间、数值区间HASH 和 KEY 分区希望键值均匀散列避免数据倾斜。数据倾斜是个很隐蔽的问题。比如按 user_id 做 HASH 分区如果业务里 80% 的订单都来自几个大客户那么这几个值散列到的分区数据量会明显大于其他分区“木桶效应”会让查询性能被最热分区拖垮。分区表不是灵丹妙药它只是把大表切碎切完之后每片是否匀称取决于分区键和分区类型的配合。3. 四种分区类型各回各家——语法与适用场景对照MySQL 支持 RANGE、LIST、HASH、KEY 四种基础分区方式。虽然日常 90% 的场景都在用 RANGE但理解四种类型的差别仍然很重要选错了后面运维全是泪。3.1 RANGE 分区最常用的时间分区方案RANGE 分区按连续区间划分数据最经典的就是按时间分区CREATE TABLE order_record ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL, user_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL, create_time DATETIME NOT NULL, PRIMARY KEY (id, create_time), KEY idx_user_id (user_id) ) ENGINEInnoDB PARTITION BY RANGE (TO_DAYS(create_time)) ( PARTITION p202401 VALUES LESS THAN (TO_DAYS(2024-02-01)), PARTITION p202402 VALUES LESS THAN (TO_DAYS(2024-03-01)), PARTITION p202403 VALUES LESS THAN (TO_DAYS(2024-04-01)), PARTITION p202404 VALUES LESS THAN (TO_DAYS(2024-05-01)), PARTITION p_future VALUES LESS THAN MAXVALUE );分区边界是“小于”语义p202401存放 2024-01-01 零点到 2024-01-31 二十四点的数据2024-02-01零点这行会进p202402。p_future吸收所有超出已知范围的数据防止插入报错。这里的TO_DAYS()是一个时间处理函数返回天数数值。也可以用YEAR()做按年分区但粒度太粗月度分区是电商、日志类业务比较常见的折中。RANGE 分区非常适合时间范围查询 历史数据归档的场景。归档操作前面说了DORP PARTITION 秒删合并历史分区用REORGANIZE PARTITION把连续几个月的数据合并成一个大分区。3.2 LIST 分区适合枚举式分发LIST 分区按枚举值列表划分比如按地区、按业务类型CREATE TABLE user_login_log ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, login_channel TINYINT NOT NULL COMMENT 1:App 2:Web 3:小程序, login_time DATETIME NOT NULL, PRIMARY KEY (id, login_channel) ) ENGINEInnoDB PARTITION BY LIST (login_channel) ( PARTITION p_app VALUES IN (1), PARTITION p_web VALUES IN (2), PARTITION p_mini VALUES IN (3), PARTITION p_other VALUES IN (0, 4, 5) );LIST 分区的场景相对窄但有个典型优势可以按渠道单独做数据统计或清理。比如小程序渠道因为合规要求只保留 90 天其他渠道保留一年直接 TRUNCATE 对应分区就行比 DELETE 加条件高效得多。需要注意LIST 分区的分区键如果是 NULL且没有任何分区的 VALUES IN 列表包含 NULL插入时直接报错。这点和 RANGE 不一样RANGE 会把 NULL 丢进最小分区LIST 更严格。3.3 HASH 分区和 KEY 分区散列均匀但运维复杂HASH 分区对分区键取模把数据尽量均匀地打散到 N 个分区CREATE TABLE user_session ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, session_data TEXT, last_active_time DATETIME NOT NULL, PRIMARY KEY (id, user_id) ) ENGINEInnoDB PARTITION BY HASH (user_id) PARTITIONS 8;KEY 分区和 HASH 类似区别在于 KEY 可以指定多个列并且分区函数由 MySQL 内部哈希实现不要求只能是整数列PARTITION BY KEY (user_id, create_time) PARTITIONS 8;HASH 和 KEY 分区的核心价值是写入均匀分布对按分区键等值查询的小查询有一定帮助。但它的短板也很明显按时间范围归档数据时你无法把“某段时间”单独圈出来删掉因为同一时间段的数据可能散在 8 个分区里。想清理历史数据只能按普通 DELETE 的方式处理分区带来的归档优势就没了。所以我会说没有明确的数据生命周期管理需求时HASH 分区反而不如 RANGE 实用。3.4 四种分区方式怎么选一张对照表分区类型适用场景分区键建议归档删除查询裁剪效果RANGE时间范围、数值区间日期、自增 ID、金额区间极好DROP PARTITION范围查询收益明显LIST枚举值、业务分类渠道、地区、状态可以按枚举清理等值查询收益明显HASH键值均匀写入user_id、订单号较差无法按范围删等值查询有一定收益KEY类似 HASH支持多列多列组合较差等值查询有一定收益4. 从建表到归档一套完整可复制的分区表操作这一节我们把实际操作串一遍。考虑到前面说的主键限制如果你是从零开始设计建表时就要把分区键塞进主键或唯一键里。下面我以一个订单流水表为例走一遍建表、业务写入、分区维护、数据归档的完整流程。4.1 建表从零创建一个按月份分区的订单表CREATE TABLE trade_record ( id BIGINT NOT NULL AUTO_INCREMENT, trade_no VARCHAR(64) NOT NULL, user_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, trade_status TINYINT NOT NULL, create_time DATETIME NOT NULL, PRIMARY KEY (id, create_time), UNIQUE KEY uk_trade_no (trade_no, create_time), KEY idx_user_id (user_id) ) ENGINEInnoDB PARTITION BY RANGE (TO_DAYS(create_time)) ( PARTITION p202401 VALUES LESS THAN (TO_DAYS(2024-02-01)), PARTITION p202402 VALUES LESS THAN (TO_DAYS(2024-03-01)), PARTITION p202403 VALUES LESS THAN (TO_DAYS(2024-04-01)), PARTITION p202404 VALUES LESS THAN (TO_DAYS(2024-05-01)), PARTITION p_future VALUES LESS THAN MAXVALUE );注意两处设计细节一是唯一键uk_trade_no变成了(trade_no, create_time)联合唯一。这是为了满足“唯一键必须包含分区键”的硬性要求。trade_no 本身业务上就是唯一的加上 create_time 唯一性不变但业务代码里如果依赖 trade_no 做“唯一冲突检测”需要同步调整判断逻辑。二是保留p_future分区。一旦业务数据的时间超过了 2024-04-30比如 2024-05-02 的订单如果没有 MAXVALUE 分区就会直接插入失败。保留这个“兜底分区”可以有效避免线上故障代价是兜底分区里的数据在查询时裁剪不干净需要定期把它拆成新的月度分区。INSERT 语句和普通表完全一样MySQL 根据TO_DAYS(create_time)自动路由INSERT INTO trade_record (trade_no, user_id, amount, trade_status, create_time) VALUES (T20240115001, 10001, 299.00, 1, 2024-01-15 10:30:00);4.2 分区维护加分区、删分区、合并拆分当p_future里开始积累当月数据时你需要把下一个月独立拆出来ALTER TABLE trade_record REORGANIZE PARTITION p_future INTO ( PARTITION p202405 VALUES LESS THAN (TO_DAYS(2024-06-01)), PARTITION p_future VALUES LESS THAN MAXVALUE );这个操作会把 MAXVALUE 分区里的数据重新分发到新分区和剩下的兜底分区执行时间取决于p_future里积压的数据量。所以最佳实践是提前拆分比如每个月底就把下个月的分区建好别等数据真的涌进来再动刀。删除历史分区ALTER TABLE trade_record DROP PARTITION p202401;这一步物理删除对应分区文件内的所有数据释放磁盘空间速度远快于 DELETE。我见过有人用 DELETE 清一年前的数据跑了 1 个多小时换成 DROP PARTITION 不到 5 秒。只清空某个分区的数据但保留分区定义用ALTER TABLE trade_record TRUNCATE PARTITION p202402;把多个小分区合并成一个大分区比如把上半年的 6 个月分区合并成半年分区ALTER TABLE trade_record REORGANIZE PARTITION p202401, p202402, p202403, p202404, p202405, p202406 INTO ( PARTITION p2024_h1 VALUES LESS THAN (TO_DAYS(2024-07-01)) );这里有个小细节REORGANIZE PARTITION的目标分区范围必须和源分区的范围完全一致不能多也不能少否则 MySQL 报错。实际运维中我是写个存储过程按月自动生成下月分区避免手工维护。4.3 数据归档从 DELETE 到 DROP PARTITION 的质变分区表最有说服力的收益在归档场景。假设产品要求订单数据保留两年按月分区就有 24 个分区加一个兜底分区。到了 2026 年初2024 年 1 月的数据已经超期直接ALTER TABLE trade_record DROP PARTITION p202401;这个操作不会产生几百 MB 的 binlogDELETE 会产生每一行的 binlog 事件也不会触发二级索引的大规模更新对主从复制的影响几乎可以忽略。如果只是希望把旧数据搬走而不是删掉流程是先CREATE TABLE trade_record_202401 LIKE trade_record再ALTER TABLE trade_record_202401 REMOVE PARTITIONING去掉分区然后INSERT INTO trade_record_202401 SELECT * FROM trade_record PARTITION (p202401)最后 DROP 原分区。这样旧数据变成一张独立普通表可以单独备份或转移到低成本存储。5. 分区表的性能真相哪些收益被高估了分区表常被当作“性能银弹”但实际上它的收益面比很多人以为的要窄。我必须把这段话写明白分区表最大的价值是数据管理效率其次才是查询性能。如果你只是为了“查询快点”而不做数据生命周期管理分区表大概率会让你失望。5.1 分区裁剪性能收益的唯一来源分区裁剪是分区表性能提升的唯一机制没有之一。它发生在优化器阶段MySQL 根据 WHERE 条件里的分区键范围把不满足条件的分区直接排除只对剩余分区执行扫描。你可以用 EXPLAIN 验证裁剪效果EXPLAIN SELECT * FROM trade_record WHERE create_time 2024-03-01 AND create_time 2024-04-01;在 8.0 里EXPLAIN 输出会有一个partitions列上面这条语句应该显示p202403表示只扫描了这一个分区。如果显示p202401,p202402,p202403,p202404,p_future这种完整列表说明条件没有触发裁剪查询实际上是全分区扫描。一个值得注意的细节分区裁剪对函数包裹的列不生效。比如WHERE DATE(create_time) 2024-03-15这种写法因为函数把索引列变成了表达式优化器无法推导分区范围裁剪失效。正确的做法是写成范围条件WHERE create_time 2024-03-15 00:00:00 AND create_time 2024-03-16 00:00:005.2 什么时候分区表反而更慢分区表比普通表更慢常见原因有三个。第一查询条件不带分区键。前面已经反复强调这里用一个例子量化如果有 12 个分区每次查询理想扫描量是 1/12 的数据量但分区裁剪失效时扫描量是 100%还要加上分区的额外路由开销所以比普通表更慢。第二每个分区独立维护二级索引。假设分区表有 12 个分区每个分区内部都有一棵 idx_user_id 的 B 树。你在查询时即使只命中一个分区也要在这个分区的索引树上额外走一次定位如果用普通表只需要在一棵大索引树上定位。对单点查询来说分区表在索引层没有优势甚至因为区与区之间无法共享索引缓存冷分区频繁被访问时 Buffer Pool 命中率可能下降。第三统计信息不准导致执行计划跑偏。分区表在收集统计信息时是按分区收集的某些情况下优化器对分区行数的估算会偏差较大可能选错索引。这一点在 MySQL 8.0 里比 5.7 好一些但仍然不能掉以轻心。遇到“SQL 突然变慢”的排查记得先用 EXPLAIN 看partitions列和rows估算值。5.3 和索引的关系分区不能替代索引这是一个很常见的误解以为建了分区索引就可以不用建了。错得离谱。分区决定的是“数据存放的位置”索引决定的是“在分区内部怎么快速定位数据”。两者正交。比如前面订单表的idx_user_id它的作用是让你在某个分区内部能按 user_id 快速查。如果不建这个索引按 user_id 的查询在命中的分区里仍然要做全分区扫描。正确姿势是分区键解决范围裁剪二级索引解决分区内的精确查找两者配合才有最佳效果。不过二级索引也要注意选择性。分区表里如果二级索引的唯一性很差比如 status 字段只有 0/1 两个值索引扫描加回表的开销可能比全分区扫描更大MySQL 优化器一般会根据统计信息自己判断但你对这个逻辑要有数。6. 实践中踩过的坑NULL、主键限制与 ALTER TABLE 的代价最后这部分是我真实动手改分区表时踩过的坑。有些坑官方文档写得清楚但不到出问题时你根本不会注意到有些坑属于“文档说归说实际遇到才知道多痛”。6.1 NULL 值分区一个容易踩的边界RANGE 分区中如果分区键是 NULLMySQL 会把它放进最小分区。也就是说一行 create_time 为 NULL 的数据会被写到p202401最小边界里。这在语义上不算错但如果你的分区和业务强相关比如要对 p202401 做归档删除那这些 NULL 数据会被一并清理掉可能会造成业务数据丢失。解决办法在业务层保证分区键必填或者建表时给 create_time 加NOT NULL约束。最稳妥的方案是分区键字段一律NOT NULL从源头堵住。LIST 分区的 NULL 处理更严格NULL 不在任何 VALUES IN 列表里时插入直接报错。HASH 和 KEY 分区则把 NULL 当作 0 处理。对同一张表不同分区类型对 NULL 的容忍度不一样这个差异容易在新手手里变成线上事故。6.2 分区数量不是越多越好MySQL 8.0 中一张分区表最多支持 8192 个分区包含子分区。但这只是上限实际远不该用这么多。分区数过多会带来多重问题打开表时MySQL 需要初始化所有分区的文件句柄表分区过多会让SHOW TABLE STATUS、DDL 操作明显变慢每个分区都要独立收集统计信息ANALYZE TABLE的耗时随分区数线性增长如果每个分区数据量太小比如一个分区只有几千行分区裁剪省下的扫描开销还抵不过分区路由和文件切换的开销收益为负。我的经验值月度分区保留 24 个月加上 1 个兜底分区总共 25 个分区运维和性能比较均衡。如果数据量实在大到月度分区仍超过 20GB可以考虑按周分区甚至按天分区但随之而来的是更频繁的分区维护建议配合自动化脚本。6.3 ALTER TABLE 的隐性代价与在线 DDL对已有大表改成分区表不是一条 ALTER 语句瞬间完成的事。下面的操作ALTER TABLE order_record PARTITION BY RANGE (TO_DAYS(create_time)) (...);在 InnoDB 里通常会触发全表重建执行期间可能需要拷贝数据、重建所有索引一个 60GB 的表改分区跑几个小时很正常。虽然 8.0 支持在线 DDL 的 ALGORITHMINPLACE但分区变更的很多场景仍会退化为 COPY 算法表现是表被锁住业务读写被卡死。所以我的建议是彻底放弃“线上大表原地改分区”的想法。靠谱的路径是新建一张分区表通过INSERT INTO ... SELECT ...分批次迁移数据同时用触发器或应用双写保证增量数据同步最后在低峰期做表名切换。流程繁琐但可控。另一个容易被忽略的代价是分区表的ALTER TABLEDDL 在复制架构中会传递到从库如果从库性能较差一个加分区的操作也可能拖慢从库。所以重要分区的维护操作建议放到业务低峰期并且先在测试环境跑一遍确认耗时。6.4 UNIQUE KEY 的连带影响比你想的更隐蔽前面提到唯一键必须包含分区键这个限制还带了一个更隐蔽的问题唯一键的语义发生变化可能影响业务幂等逻辑。我遇到过一个实际案例业务方原来用trade_no做幂等重复提交订单时INSERT会因唯一键冲突而失败。改成联合唯一键(trade_no, create_time)之后同一毫秒内相同 trade_no 的两次请求如果 create_time 不同唯一约束就失效了线上出现重复订单。最后被迫在应用层加了分布式锁才解决。如果你正在设计分区表务必和业务开发对齐所有用到唯一键做幂等或去重的逻辑都要重新审视分区键加入后的语义变化。这一点文档不会提醒你但线上事故会。6.5 时区问题UNIX_TIMESTAMP 作分区键的坑有人喜欢用UNIX_TIMESTAMP(create_time)作为 RANGE 分区的表达式因为秒级时间戳方便按固定秒数切分。但有个隐蔽的坑UNIX_TIMESTAMP的返回值受 MySQL 会话时区影响。如果数据库时区从08:00调整成00:00同一个create_time算出的时间戳不同分区边界和实际数据分布可能错乱查询裁剪也可能失效。稳妥的做法是基于日期的TO_DAYS()或YEAR()它们直接解析日期时间值与时区无关。如果业务场景要求按时间戳粒度分区也要保证数据库时区规范统一且不随意变更。最后分享两个我常用的分区表运维小技巧第一个提前建分区别靠兜底分区扛。兜底分区MAXVALUE的目的是防止插入报错不是让你把数据堆在里面。我习惯在每个月的 25 号左右自动执行一个存储过程预创建下下个月的分区这样即使业务提前写入下月数据也有独立分区可以裁剪不会积压在兜底分区里影响性能。第二个监控里加上分区裁剪失效的 SQL 发现机制。在慢查询日志和监控报表里重点关注那些partitions列显示全分区扫描的语句。这类 SQL 往往是后来新增的业务查询开发没有意识到表是分区表、没带分区键。发现一条就推动业务优化一条长期下来分区表的红利才能真正兑现。分区表不是银弹它是数据管理思维下的产物。想清楚你的数据生命周期选对分区键和分区类型它能让大表运维轻松一个量级但如果只是为了“别人都分区了我也要分”那大概率是给自己制造新的麻烦。希望这篇文能帮你少走几步弯路。