在后端岗位面试中,MySQL分库分表是检验候选人数据处理功底的高频考点。它不只是会写几条SQL,更要求理解数据拆分背后的权衡、分布式环境下的事务与查询限制。下面整理出几个几乎必问的经典问题,并给出可落地的回答思路。

一、什么情况下才需要做分库分表
面试官通常不会直接让你讲概念,而是抛出一个场景:单表数据量到了多少才拆分。一般来说,当单表行数超过千万级、磁盘占用超过百GB,且日常查询出现明显延迟,索引优化和读写分离已经扛不住时,才考虑分表。分库则更多是为了突破单机连接数、CPU或IO瓶颈。
需要注意的是,分库分表不是银弹。它引入了跨节点查询、分布式事务等复杂度。如果在数据量还很小的时候就过早拆分,反而会让系统难以维护。面试中强调“先单库单表,按需演进”的思路,会比直接背阈值更得分。
二、分片键怎么选,哈希还是范围
分片键(sharding key)决定了数据落到哪个库哪张表。最常见的两种策略是哈希分片和范围分片。哈希分片通过对分片键取模,让数据均匀分布,适合按用户ID查询的场景;范围分片按时间或主键区间切分,方便清理冷数据,但容易产生写入热点。
下面是一段简单的哈希分片路由示例:
// 根据用户ID计算分表下标,假设分成8张表
public static int getTableIndex(long userId, int tableCount) {
// 取绝对值后取模,避免负数
return (int)(Math.abs(userId) % tableCount);
}
// 实际拼接表名
public static String getTableName(long userId) {
int index = getTableIndex(userId, 8);
return "t_order_" + index;
}
面试时如果能指出哈希分片在扩容时需要进行数据迁移(如从8张表扩到16张表,大部分数据要重算落点),而一致性哈希可缓解该问题,会显得思考更完整。范围分片虽然扩容简单,但要警惕某段时间集中写入同一分片造成的性能倾斜。
三、跨分片查询和分布式事务怎么处理
拆分后最头疼的是跨分片操作。比如既要按用户查订单,又要按商家统计,若分片键是用户ID,商家维度查询就只能扫所有表。面试中常被问到如何解决,答案一般是引入异构索引表或借助ES等外部索引。
分布式事务方面,单库内可用本地事务,跨库则常用最大努力通知、TCC或基于消息队列的最终一致性。下面用消息队列保证最终一致性的伪代码说明:
# 扣减库存并发送消息,由订单服务消费
def create_order(user_id, product_id):
# 1. 本地事务:写订单表(分片键user_id)
order_id = db.execute("insert into t_order_xxx ...")
# 2. 发送事务消息
mq.send("order_created", {"order_id": order_id, "product_id": product_id})
return order_id
# 库存服务消费
def on_order_created(msg):
# 幂等扣减库存
stock_db.execute("update t_stock set count=count-1 where product_id=?", msg["product_id"])
要提醒的是,两阶段提交(2PC)在数据库层面能保证强一致,但锁资源时间长,高并发下不建议作为首选。面试中对比强一致与最终一致性的适用场景,能体现架构权衡能力。
四、全局唯一主键如何生成
分表后自增主键会冲突,这是必考题。常用方案有雪花算法、UUID、号段模式。雪花算法生成64位Long型ID,包含时间戳和机器位,性能高但依赖时钟;UUID简单但太长且无序,影响索引;号段模式由中心服务批量发号,实现简单。
一段雪花算法核心逻辑如下:
// 简化版雪花算法,仅示意位运算
public class Snowflake {
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public synchronized long nextId() {
long ts = System.currentTimeMillis();
if (ts == lastTimestamp) {
sequence = (sequence + 1) & 0xfff;
} else {
sequence = 0L;
}
lastTimestamp = ts;
// 移位拼接:时间戳、机器ID、序列号
return ((ts - 1600000000000L) << 22) | (workerId << 12) | sequence;
}
}
面试补充一句“若机器时钟回拨可能导致ID重复,需做时钟监控或等待”就能展示细节把控。此外,也可以说在数据库中间件如ShardingSphere中,已内置分布式主键生成,不必重复造轮子。
五、扩容和迁移有哪些坑
当数据继续增长,原分片数不够,就要扩容。哈希取模扩容几乎要迁移全部数据,因此提前规划分片数很重要。一种做法是初期就用一致性哈希,节点增减只影响邻近数据。
迁移过程还要考虑双写和灰度:旧表和新表同时写,验证无误再切读流量。用表格对比常见扩容方式:
| 方式 | 数据迁移量 | 复杂度 | 适用场景 |
|---|---|---|---|
| 取模翻倍 | 约50% | 中 | 初期规划翻倍扩容 |
| 一致性哈希 | 局部 | 高 | 节点频繁变动 |
| 异构冗余 | 无停服 | 高 | 在线迁移核心业务 |
最后,面试官可能让你聊分库分表带来的运维变化,比如监控每个分片水位、慢SQL定位更麻烦。回答时若能结合具体中间件(如MyCat、ShardingSphere)说明配置和路由规则,会比纯理论更扎实。