分布式面试题
# 分布式面试题
# 🌐 分布式系统基础
# 1. 分布式系统特点
Q: 分布式系统面临的主要挑战?
A:
- 网络分区:节点间通信可能失败
- 节点故障:任何节点都可能宕机
- 并发控制:多节点同时访问共享资源
- 数据一致性:保证数据在各节点间的一致性
- 时钟同步:分布式环境下时钟难以精确同步
# 2. CAP定理详解
Q: 详细解释CAP定理及其实际应用
A: CAP定理指出分布式系统最多只能同时保证以下三项中的两项:
- 一致性(Consistency):所有节点在同一时间看到相同的数据
- 可用性(Availability):系统保持可操作状态
- 分区容错性(Partition Tolerance):系统在网络分区时仍能工作
实际选择:
// CP系统示例:强一致性系统
@Service
public class ConsistentService {
public void updateData(String key, String value) {
// 使用分布式锁确保一致性
DistributedLock lock = lockService.getLock(key);
try {
lock.lock();
// 更新所有副本
for (Node node : allNodes) {
node.update(key, value);
}
// 只有所有节点都成功才提交
} finally {
lock.unlock();
}
}
}
// AP系统示例:最终一致性系统
@Service
public class EventuallyConsistentService {
public void updateData(String key, String value) {
// 立即返回,异步同步
localNode.update(key, value);
// 异步同步到其他节点
asyncSyncService.syncToOtherNodes(key, value);
}
}
# 🔄 分布式一致性
# 1. 一致性模型
Q: 分布式系统中的一致性模型有哪些?
A:
- 强一致性:所有节点同时看到相同数据
- 弱一致性:不保证何时达到一致
- 最终一致性:保证最终会达到一致
- 因果一致性:有因果关系的操作保持顺序
- 会话一致性:同一会话内保持一致
# 2. 分布式事务
Q: 分布式事务的实现方案?
A:
1. 两阶段提交(2PC)
@Component
public class TwoPhaseCommitCoordinator {
public boolean executeTransaction(List<Participant> participants, Transaction tx) {
// Phase 1: Prepare
List<Boolean> prepareResults = new ArrayList<>();
for (Participant participant : participants) {
boolean canCommit = participant.prepare(tx);
prepareResults.add(canCommit);
if (!canCommit) {
// 如果任何参与者无法提交,则中止
abortTransaction(participants, tx);
return false;
}
}
// Phase 2: Commit
for (Participant participant : participants) {
participant.commit(tx);
}
return true;
}
private void abortTransaction(List<Participant> participants, Transaction tx) {
for (Participant participant : participants) {
participant.abort(tx);
}
}
}
2. TCC(Try-Confirm-Cancel)
@Service
public class AccountService {
// Try阶段:预留资源
@TccTry
public boolean tryDeduct(String accountId, BigDecimal amount, String txId) {
Account account = accountRepository.findById(accountId);
if (account.getBalance().compareTo(amount) >= 0) {
// 冻结金额
account.setFrozenAmount(account.getFrozenAmount().add(amount));
accountRepository.save(account);
return true;
}
return false;
}
// Confirm阶段:确认执行
@TccConfirm
public void confirmDeduct(String accountId, BigDecimal amount, String txId) {
Account account = accountRepository.findById(accountId);
account.setBalance(account.getBalance().subtract(amount));
account.setFrozenAmount(account.getFrozenAmount().subtract(amount));
accountRepository.save(account);
}
// Cancel阶段:取消执行
@TccCancel
public void cancelDeduct(String accountId, BigDecimal amount, String txId) {
Account account = accountRepository.findById(accountId);
account.setFrozenAmount(account.getFrozenAmount().subtract(amount));
accountRepository.save(account);
}
}
3. Saga模式
@Component
public class OrderSagaOrchestrator {
public void processOrder(Order order) {
SagaTransaction saga = SagaTransaction.builder()
.addStep("inventory", () -> inventoryService.reserve(order.getItems()),
() -> inventoryService.release(order.getItems()))
.addStep("payment", () -> paymentService.charge(order.getPayment()),
() -> paymentService.refund(order.getPayment()))
.addStep("shipping", () -> shippingService.ship(order),
() -> shippingService.cancel(order))
.build();
saga.execute();
}
}
# 🔐 分布式锁
# 1. 基于Redis的分布式锁
Q: 如何实现Redis分布式锁?
A:
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String LOCK_PREFIX = "distributed_lock:";
private static final int DEFAULT_EXPIRE_TIME = 30; // 秒
public boolean tryLock(String key, String value, int expireTime) {
String lockKey = LOCK_PREFIX + key;
// 使用SET NX EX命令,原子性操作
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(lockKey, value, expireTime, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
public boolean unlock(String key, String value) {
String lockKey = LOCK_PREFIX + key;
// 使用Lua脚本确保原子性
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey),
value
);
return result != null && result == 1L;
}
// 可重入锁实现
public boolean tryReentrantLock(String key, String threadId, int expireTime) {
String lockKey = LOCK_PREFIX + key;
String luaScript =
"local lockKey = KEYS[1] " +
"local threadId = ARGV[1] " +
"local expireTime = ARGV[2] " +
"local lockValue = redis.call('get', lockKey) " +
"if lockValue == false then " +
" redis.call('set', lockKey, threadId, 'EX', expireTime) " +
" return 1 " +
"elseif lockValue == threadId then " +
" redis.call('expire', lockKey, expireTime) " +
" return 1 " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey),
threadId, String.valueOf(expireTime)
);
return result != null && result == 1L;
}
}
# 2. 基于ZooKeeper的分布式锁
@Component
public class ZookeeperDistributedLock {
private CuratorFramework client;
private static final String LOCK_PATH = "/distributed_locks";
public InterProcessMutex createLock(String lockName) {
String lockPath = LOCK_PATH + "/" + lockName;
return new InterProcessMutex(client, lockPath);
}
public boolean tryLock(String lockName, long timeout, TimeUnit unit) {
InterProcessMutex lock = createLock(lockName);
try {
return lock.acquire(timeout, unit);
} catch (Exception e) {
log.error("Failed to acquire lock: {}", lockName, e);
return false;
}
}
public void unlock(InterProcessMutex lock) {
try {
lock.release();
} catch (Exception e) {
log.error("Failed to release lock", e);
}
}
}
# 📨 分布式消息队列
# 1. 消息队列的作用
Q: 消息队列在分布式系统中的作用?
A:
- 解耦:生产者和消费者解耦
- 异步:提高系统响应速度
- 削峰:处理突发流量
- 可靠性:消息持久化,保证不丢失
# 2. Kafka实现
// Kafka生产者
@Service
public class KafkaProducerService {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
public void sendMessage(String topic, Object message) {
kafkaTemplate.send(topic, message)
.addCallback(
result -> log.info("Message sent successfully: {}", result),
failure -> log.error("Failed to send message", failure)
);
}
// 事务消息
@Transactional
public void sendTransactionalMessage(String topic, Object message) {
kafkaTemplate.executeInTransaction(operations -> {
operations.send(topic, message);
return true;
});
}
}
// Kafka消费者
@Component
public class KafkaConsumerService {
@KafkaListener(topics = "order_events", groupId = "order_group")
public void handleOrderEvent(OrderEvent event) {
try {
processOrderEvent(event);
} catch (Exception e) {
log.error("Failed to process order event: {}", event, e);
// 发送到死信队列
sendToDeadLetterQueue(event);
}
}
private void processOrderEvent(OrderEvent event) {
switch (event.getType()) {
case ORDER_CREATED:
handleOrderCreated(event);
break;
case ORDER_PAID:
handleOrderPaid(event);
break;
case ORDER_SHIPPED:
handleOrderShipped(event);
break;
}
}
}
# 🎯 分布式ID生成
# 1. 雪花算法(Snowflake)
Q: 雪花算法的原理和实现?
A:
@Component
public class SnowflakeIdGenerator {
// 起始时间戳 (2020-01-01)
private static final long START_TIMESTAMP = 1577836800000L;
// 各部分位数
private static final long SEQUENCE_BITS = 12;
private static final long MACHINE_BITS = 10;
private static final long TIMESTAMP_BITS = 41;
// 各部分最大值
private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;
private static final long MAX_MACHINE_ID = (1L << MACHINE_BITS) - 1;
// 各部分位移
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
private final long machineId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long machineId) {
if (machineId > MAX_MACHINE_ID || machineId < 0) {
throw new IllegalArgumentException("Machine ID out of range");
}
this.machineId = machineId;
}
public synchronized long nextId() {
long currentTimestamp = System.currentTimeMillis();
// 时钟回拨检查
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards");
}
// 同一毫秒内
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
// 序列号用完,等待下一毫秒
currentTimestamp = waitNextMillis(currentTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = currentTimestamp;
// 组装ID
return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
| (machineId << MACHINE_SHIFT)
| sequence;
}
private long waitNextMillis(long currentTimestamp) {
while (currentTimestamp <= lastTimestamp) {
currentTimestamp = System.currentTimeMillis();
}
return currentTimestamp;
}
}
# 2. 基于数据库的ID生成
@Service
public class DatabaseIdGenerator {
@Autowired
private JdbcTemplate jdbcTemplate;
// 批量获取ID,减少数据库访问
public List<Long> getIdBatch(String bizType, int batchSize) {
String sql = "UPDATE id_generator SET current_id = current_id + ? WHERE biz_type = ?";
jdbcTemplate.update(sql, batchSize, bizType);
String selectSql = "SELECT current_id FROM id_generator WHERE biz_type = ?";
Long currentId = jdbcTemplate.queryForObject(selectSql, Long.class, bizType);
List<Long> ids = new ArrayList<>();
for (int i = 0; i < batchSize; i++) {
ids.add(currentId - batchSize + i + 1);
}
return ids;
}
}
# 🔍 分布式监控
# 1. 链路追踪
// 使用Spring Cloud Sleuth进行链路追踪
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
// 自动生成trace ID和span ID
Span span = tracer.nextSpan().name("create-order").start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
span.tag("order.type", request.getType());
span.tag("customer.id", request.getCustomerId());
Order order = orderService.createOrder(request);
span.tag("order.id", order.getId());
return ResponseEntity.ok(order);
} finally {
span.end();
}
}
}
@Service
public class OrderService {
@NewSpan("order-validation")
public void validateOrder(CreateOrderRequest request) {
// 验证逻辑
}
@NewSpan("inventory-check")
public boolean checkInventory(List<OrderItem> items) {
// 库存检查
return true;
}
}
# 2. 分布式配置中心
// 使用Spring Cloud Config
@RestController
@RefreshScope // 支持配置热更新
public class ConfigController {
@Value("${app.feature.enabled:false}")
private boolean featureEnabled;
@Value("${app.rate.limit:100}")
private int rateLimit;
@GetMapping("/config")
public Map<String, Object> getConfig() {
Map<String, Object> config = new HashMap<>();
config.put("featureEnabled", featureEnabled);
config.put("rateLimit", rateLimit);
return config;
}
}
// 配置变更监听
@Component
public class ConfigChangeListener {
@EventListener
public void handleConfigChange(RefreshRemoteApplicationEvent event) {
log.info("Configuration changed: {}", event.getDestinationService());
// 处理配置变更逻辑
}
}
# 💡 面试技巧
- 理论与实践结合:不仅要知道概念,还要能说出实际应用场景
- 权衡分析:分布式系统往往需要在一致性、可用性、性能之间做权衡
- 故障处理:重点关注系统如何处理各种故障情况
- 性能考虑:分析方案的性能特点和瓶颈
- 实际经验:结合自己的项目经验来回答问题
# 📚 推荐资源
- 《分布式系统概念与设计》
- 《大规模分布式存储系统》
- 《微服务架构设计模式》
- 《分布式系统原理与范型》