分布式面试题

# 分布式面试题

# 🌐 分布式系统基础

# 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());
        // 处理配置变更逻辑
    }
}

# 💡 面试技巧

  1. 理论与实践结合:不仅要知道概念,还要能说出实际应用场景
  2. 权衡分析:分布式系统往往需要在一致性、可用性、性能之间做权衡
  3. 故障处理:重点关注系统如何处理各种故障情况
  4. 性能考虑:分析方案的性能特点和瓶颈
  5. 实际经验:结合自己的项目经验来回答问题

# 📚 推荐资源

  • 《分布式系统概念与设计》
  • 《大规模分布式存储系统》
  • 《微服务架构设计模式》
  • 《分布式系统原理与范型》