"金三银四"的招聘旺季即将到来,相信不少Java开发者已经开始摩拳擦掌准备面试。但根据我过去五年作为面试官的经验,超过70%的候选人在技术面中都会陷入以下典型困境:
最近我面试的一位3年经验的候选人就很有代表性:他简历上写着精通分布式系统,但在被问到"CAP理论中为什么只能三选二"时,回答竟是"因为字母C排在A和P前面"。这种对基础理论的误解在面试中绝非个例。
CAP理论看似简单,却是分布式系统的基石。很多开发者对它的理解存在三个典型误区:
实战案例:在设计分布式锁时,基于Redis的RedLock方案选择AP(可用性优先),而基于ZooKeeper的方案选择CP(一致性优先)。我曾遇到一个库存超卖案例,正是因为团队混用了这两种方案导致数据不一致。
当下主流方案各有适用场景:
| 方案 | 一致性强度 | 性能 | 复杂度 | 典型场景 |
|---|---|---|---|---|
| 2PC | 强一致 | 低 | 高 | 银行核心系统 |
| TCC | 最终一致 | 中 | 高 | 电商订单 |
| 本地消息表 | 最终一致 | 高 | 中 | 用户积分 |
| SAGA | 最终一致 | 高 | 高 | 长事务流程 |
| 最大努力通知 | 最弱一致 | 最高 | 低 | 短信通知 |
避坑指南:Seata框架的AT模式看似简单,但在分库分表环境下需要特别注意全局锁冲突问题。去年我们系统就曾因此出现死锁,最终通过引入重试机制和熔断策略解决。
案例:某社交平台的热搜榜使用zset存储,当突然出现热点事件时,Redis内存暴涨。我们通过分析发现是使用了默认的skiplist编码,改为调整zset-max-ziplist-entries参数后内存减少40%。
java复制// Redis Cluster与Codis对比
public class ClusterComparison {
// 数据迁移效率
void migrationSpeed() {
// Redis Cluster迁移slot时会影响请求
// Codis通过proxy层实现透明迁移
}
// 扩容便捷性
void scaling() {
// Redis Cluster需要resharding
// Codis支持动态增减节点
}
}
log.flush.interval.messages=10000而非实时刷盘compression.type=lz4可提升吞吐30%以上(topic+partition+offset)做唯一键fetch.min.bytes设置过大导致消费延迟session.timeout.ms=6s和heartbeat.interval.ms=2s平衡灵敏度和稳定性分层削峰架构:
库存扣减的原子性实现:
java复制// Redis+Lua实现原子扣减
String script =
"local stock = tonumber(redis.call('get', KEYS[1])) " +
"if stock <= 0 then return 0 end " +
"if stock >= tonumber(ARGV[1]) then " +
" return redis.call('decrby', KEYS[1], ARGV[1]) " +
"else return -1 end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("stock:"+itemId),
String.valueOf(num));
滑动窗口实现:
java复制// 基于Guava的RateLimiter改良版
class SlidingWindowLimiter {
private ConcurrentNavigableMap<Long, Integer> counters =
new ConcurrentSkipListMap<>();
private final int threshold;
public boolean tryAcquire() {
long now = System.currentTimeMillis();
counters.headMap(now - 1000).clear();
int sum = counters.values().stream().mapToInt(i->i).sum();
if(sum >= threshold) return false;
counters.merge(now, 1, Integer::sum);
return true;
}
}
压测经验:当QPS超过1万时,ConcurrentSkipListMap会成为瓶颈,可改用环形数组+原子变量实现,我在某金融项目中将限流性能从8000QPS提升到45000QPS。
联合索引避坑指南:
WHERE a>1 AND b=2 对索引(a,b)只能用到a列WHERE b=2 使用索引(a,b)会导致全索引扫描WHERE varchar_col=123 会使索引失效执行计划分析技巧:
sql复制EXPLAIN FORMAT=JSON
SELECT * FROM orders WHERE user_id=100 AND status='PAID'\G
-- 关注key_len值可判断实际使用的索引列数
-- "using_filesort":true表示需要额外排序
基因法分片:
java复制// 用户ID包含分片信息
long userId = ((shardId << 54) | (sequence & 0x3FFFFFFFFFFFFFL));
跨库查询方案对比:
策略模式在支付系统的实践:
java复制// 支付路由策略
public interface PaymentStrategy {
Result process(PaymentRequest request);
}
@Slf4j
public class AlipayStrategy implements PaymentStrategy {
@Override
public Result process(PaymentRequest request) {
// 支付宝特有风控逻辑
if(request.getAmount() > 50000) {
return Result.fail("单笔限额5万");
}
return callAlipayAPI(request);
}
}
// 策略上下文
public class PaymentContext {
private PaymentStrategy strategy;
public Result execute(PaymentRequest request) {
try {
return strategy.process(request);
} catch (Exception e) {
log.error("支付失败", e);
return switchToFallback(request);
}
}
}
Top K问题模板:
python复制# 海量数据求Top K通用解法
def top_k(nums, k):
heap = []
for num in nums:
if len(heap) < k:
heapq.heappush(heap, num)
elif num > heap[0]:
heapq.heappushpop(heap, num)
return heap
二叉树题型总结:
当被问到"你遇到的最大技术挑战"时,建议采用STAR-L模型:
薪资谈判三原则:
记得在某次跳槽时,我通过展示另一个offer最终将薪资谈高了15%。关键是要让企业看到你的不可替代性,而非单纯比较价格。