找回密码
 立即注册
首页 业界区 业界 如何设计一个扛住千万级流量的系统?

如何设计一个扛住千万级流量的系统?

骆贵 4 小时前
前言

大家好,我是苏三。
今天跟大家聊一下很多小伙伴都比较关心的话题:如何设计一个扛住千万级流量的系统?
这个话题无论在面试,还是在实际工作中,都非常常见。
今天这篇文章就专门跟大家聊聊这个问题,希望对你会有所帮助。
01 三个关键点

在深入技术细节前,我们先要明确设计千万级系统的核心目标。
记住这三个关键点:
1.png

1. 高性能:不是简单追求快,而是要在保证正确性的前提下,用有限的资源处理尽可能多的请求。我们的目标是核心接口P99响应时间低于100毫秒,单机QPS不低于5000。
2. 高可用:系统需要具备故障自愈能力。我们追求的是“两个9”打底,“三个9”起步,“四个9”努力的目标(即99.99%可用性,全年不可用时间不超过53分钟)。
3. 可扩展:系统要能随着业务增长而平滑扩展,且扩展成本要可控。这里包括水平扩展(加机器)和垂直扩展(优化单机性能)两个维度。
02 架构演进:从单体到千万级的四步走

让我们从一个最简单的电商系统开始,看看它是如何一步步演进到支撑千万级流量的。
阶段一:单机单体架构(日请求 /proc/sys/net/ipv4/conf/lo/arp_ignoreecho "2" > /proc/sys/net/ipv4/conf/lo/arp_announceecho "1" > /proc/sys/net/ipv4/conf/all/arp_ignoreecho "2" > /proc/sys/net/ipv4/conf/all/arp_announce# LVS Director配置ipvsadm -A -t 192.168.1.100:80 -s wrripvsadm -a -t 192.168.1.100:80 -r 192.168.1.10:8080 -g -w 1ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:8080 -g -w 2[/code]七层负载均衡(Nginx/API网关)
  1. // 最简单的Spring Boot单体应用
  2. @SpringBootApplication
  3. @RestController
  4. public class MonolithicApp {
  5.    
  6.     @Autowired
  7.     private ProductService productService;
  8.    
  9.     @Autowired
  10.     private OrderService orderService;
  11.    
  12.     @GetMapping("/product/{id}")
  13.     public Product getProduct(@PathVariable Long id) {
  14.         return productService.getById(id);
  15.     }
  16.    
  17.     @PostMapping("/order")
  18.     public Order createOrder(@RequestBody OrderRequest request) {
  19.         return orderService.createOrder(request);
  20.     }
  21.    
  22.     public static void main(String[] args) {
  23.         SpringApplication.run(MonolithicApp.class, args);
  24.     }
  25. }
复制代码
现代API网关(Spring Cloud Gateway)
  1. // 商品服务 - 独立部署
  2. @SpringBootApplication
  3. @RestController
  4. @RequestMapping("/product")
  5. public class ProductServiceApp {
  6.    
  7.     @GetMapping("/{id}")
  8.     public Product getProduct(@PathVariable Long id) {
  9.         // 直接查询数据库
  10.         return productRepository.findById(id).orElse(null);
  11.     }
  12. }
  13. // 订单服务 - 独立部署  
  14. @SpringBootApplication
  15. @RestController
  16. @RequestMapping("/order")
  17. public class OrderServiceApp {
  18.    
  19.     @PostMapping("/")
  20.     public Order createOrder(@RequestBody OrderRequest request) {
  21.         // 通过HTTP调用商品服务
  22.         Product product = restTemplate.getForObject(
  23.             "http://product-service/product/" + request.getProductId(),
  24.             Product.class
  25.         );
  26.         // 创建订单逻辑
  27.         return orderRepository.save(order);
  28.     }
  29. }
复制代码
04 缓存策略:性能加速的智能分层

缓存是提升系统性能最有效的手段之一。千万级系统需要设计智能的多级缓存策略。
四级缓存架构

2.png

1. 本地缓存(Caffeine)
  1. # Kubernetes部署配置文件示例
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   name: product-service
  6. spec:
  7.   replicas: 3  # 3个实例
  8.   selector:
  9.     matchLabels:
  10.       app: product-service
  11.   template:
  12.     metadata:
  13.       labels:
  14.         app: product-service
  15.     spec:
  16.       containers:
  17.       - name: product-service
  18.         image: product-service:latest
  19.         resources:
  20.           limits:
  21.             memory: "512Mi"
  22.             cpu: "500m"
  23.         readinessProbe:  # 就绪探针
  24.           httpGet:
  25.             path: /actuator/health
  26.             port: 8080
  27.           initialDelaySeconds: 30
  28.           periodSeconds: 10
  29.         livenessProbe:   # 存活探针
  30.           httpGet:
  31.             path: /actuator/health
  32.             port: 8080
  33.           initialDelaySeconds: 60
  34.           periodSeconds: 10
  35. ---
  36. apiVersion: v1
  37. kind: Service
  38. metadata:
  39.   name: product-service
  40. spec:
  41.   selector:
  42.     app: product-service
  43.   ports:
  44.   - port: 80
  45.     targetPort: 8080
  46.   type: ClusterIP
复制代码
2. 分布式缓存(Redis集群)
  1. # LVS DR模式配置示例
  2. # 真实服务器配置回环接口
  3. ifconfig lo:0 192.168.1.100 netmask 255.255.255.255 up
  4. route add -host 192.168.1.100 dev lo:0
  5. # 配置ARP抑制
  6. echo "1" > /proc/sys/net/ipv4/conf/lo/arp_ignore
  7. echo "2" > /proc/sys/net/ipv4/conf/lo/arp_announce
  8. echo "1" > /proc/sys/net/ipv4/conf/all/arp_ignore
  9. echo "2" > /proc/sys/net/ipv4/conf/all/arp_announce
  10. # LVS Director配置
  11. ipvsadm -A -t 192.168.1.100:80 -s wrr
  12. ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.10:8080 -g -w 1
  13. ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:8080 -g -w 2
复制代码
3. 缓存策略与问题解决
  1. # Nginx负载均衡配置
  2. upstream backend_servers {
  3.     # 加权轮询,配合健康检查
  4.     server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
  5.     server 192.168.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
  6.     server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
  7.    
  8.     # 会话保持(需要时开启)
  9.     # sticky cookie srv_id expires=1h domain=.example.com path=/;
  10.    
  11.     # 备份服务器
  12.     server 192.168.1.13:8080 backup;
  13. }
  14. server {
  15.     listen 80;
  16.     server_name api.example.com;
  17.    
  18.     # 限流配置
  19.     limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
  20.    
  21.     location /api/ {
  22.         # 应用限流
  23.         limit_req zone=api_limit burst=50 nodelay;
  24.         
  25.         # 连接超时设置
  26.         proxy_connect_timeout 3s;
  27.         proxy_read_timeout 10s;
  28.         proxy_send_timeout 10s;
  29.         
  30.         # 负载均衡
  31.         proxy_pass http://backend_servers;
  32.         
  33.         # 故障转移
  34.         proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
  35.         proxy_next_upstream_timeout 0;
  36.         proxy_next_upstream_tries 3;
  37.         
  38.         # 添加代理头
  39.         proxy_set_header Host $host;
  40.         proxy_set_header X-Real-IP $remote_addr;
  41.         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  42.     }
  43.    
  44.     # 健康检查端点
  45.     location /health {
  46.         access_log off;
  47.         return 200 "healthy\n";
  48.     }
  49. }
复制代码
05 数据库设计:从单机到分布式

数据库是系统的核心,也是千万级系统最难扩展的部分。
读写分离
  1. @Configuration
  2. public class GatewayConfig {
  3.    
  4.     @Bean
  5.     public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
  6.         return builder.routes()
  7.             .route("product_route", r -> r
  8.                 .path("/api/product/**")
  9.                 .filters(f -> f
  10.                     .requestRateLimiter(config -> config
  11.                         .setRateLimiter(redisRateLimiter())
  12.                         .setKeyResolver(ipKeyResolver()))
  13.                     .circuitBreaker(config -> config
  14.                         .setName("productCircuitBreaker")
  15.                         .setFallbackUri("forward:/fallback/product"))
  16.                     .rewritePath("/api/(?<segment>.*)", "/${segment}")
  17.                 )
  18.                 .uri("lb://product-service"))
  19.             .route("order_route", r -> r
  20.                 .path("/api/order/**")
  21.                 .filters(f -> f
  22.                     .requestRateLimiter(config -> config
  23.                         .setRateLimiter(redisRateLimiter())
  24.                         .setKeyResolver(userKeyResolver()))
  25.                     .retry(config -> config
  26.                         .setRetries(3)
  27.                         .setStatuses(HttpStatus.INTERNAL_SERVER_ERROR))
  28.                 )
  29.                 .uri("lb://order-service"))
  30.             .build();
  31.     }
  32.    
  33.     @Bean
  34.     public RedisRateLimiter redisRateLimiter() {
  35.         // 每秒10个请求,突发容量20
  36.         return new RedisRateLimiter(10, 20, 1);
  37.     }
  38.    
  39.     @Bean
  40.     public KeyResolver ipKeyResolver() {
  41.         return exchange -> Mono.just(
  42.             exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
  43.         );
  44.     }
  45. }
复制代码
分库分表
  1. @Component
  2. public class LocalCacheManager {
  3.    
  4.     // 一级缓存:Guava Cache(适合较小数据量)
  5.     private final Cache<String, Object> guavaCache = CacheBuilder.newBuilder()
  6.             .maximumSize(10000)
  7.             .expireAfterWrite(5, TimeUnit.MINUTES)
  8.             .recordStats()
  9.             .build();
  10.    
  11.     // 二级缓存:Caffeine(高性能,W-TinyLFU算法)
  12.     private final Cache<String, Object> caffeineCache = Caffeine.newBuilder()
  13.             .maximumSize(50000)
  14.             .expireAfterWrite(10, TimeUnit.MINUTES)
  15.             .refreshAfterWrite(1, TimeUnit.MINUTES)
  16.             .recordStats()
  17.             .build();
  18.    
  19.     // 热点数据特殊缓存(如秒杀商品)
  20.     private final Cache<String, Object> hotDataCache = Caffeine.newBuilder()
  21.             .maximumSize(1000)
  22.             .expireAfterWrite(30, TimeUnit.SECONDS) // 热点数据过期快
  23.             .recordStats()
  24.             .build();
  25.    
  26.     public <T> T getWithCache(String key, Class<T> clazz,
  27.                               Supplier<T> loader, CacheLevel level) {
  28.         switch (level) {
  29.             case LOCAL_HOT:
  30.                 return getFromHotCache(key, clazz, loader);
  31.             case LOCAL_NORMAL:
  32.                 return getFromLocalCache(key, clazz, loader);
  33.             case DISTRIBUTED:
  34.                 return getFromDistributedCache(key, clazz, loader);
  35.             default:
  36.                 return loader.get();
  37.         }
  38.     }
  39.    
  40.     private <T> T getFromLocalCache(String key, Class<T> clazz, Supplier<T> loader) {
  41.         try {
  42.             return (T) caffeineCache.get(key, k -> {
  43.                 T value = loader.get();
  44.                 if (value == null) {
  45.                     // 缓存空值,防止缓存穿透
  46.                     return new NullValue();
  47.                 }
  48.                 return value;
  49.             });
  50.         } catch (Exception e) {
  51.             // 本地缓存异常,降级直接加载
  52.             return loader.get();
  53.         }
  54.     }
  55. }
  56. // 使用示例
  57. @Service
  58. public class ProductService {
  59.    
  60.     @Autowired
  61.     private LocalCacheManager cacheManager;
  62.    
  63.     @Autowired
  64.     private RedisTemplate<String, Product> redisTemplate;
  65.    
  66.     public Product getProductWithCache(Long productId) {
  67.         String cacheKey = "product:" + productId;
  68.         
  69.         return cacheManager.getWithCache(cacheKey, Product.class, () -> {
  70.             // 先查Redis分布式缓存
  71.             Product product = redisTemplate.opsForValue().get(cacheKey);
  72.             if (product != null) {
  73.                 return product;
  74.             }
  75.             
  76.             // Redis没有,查数据库
  77.             product = productRepository.findById(productId).orElse(null);
  78.             if (product != null) {
  79.                 // 异步回写到Redis,不阻塞当前请求
  80.                 CompletableFuture.runAsync(() ->
  81.                     redisTemplate.opsForValue().set(cacheKey, product, 1, TimeUnit.HOURS)
  82.                 );
  83.             }
  84.             
  85.             return product;
  86.         }, CacheLevel.LOCAL_NORMAL);
  87.     }
  88. }
复制代码
数据库优化实战
  1. @Configuration
  2. public class RedisConfig {
  3.    
  4.     @Bean
  5.     public RedisConnectionFactory redisConnectionFactory() {
  6.         RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration();
  7.         
  8.         // 集群节点配置
  9.         clusterConfig.addClusterNode(new RedisNode("192.168.1.100", 6379));
  10.         clusterConfig.addClusterNode(new RedisNode("192.168.1.101", 6379));
  11.         clusterConfig.addClusterNode(new RedisNode("192.168.1.102", 6379));
  12.         
  13.         // 集群配置
  14.         clusterConfig.setMaxRedirects(3); // 最大重定向次数
  15.         
  16.         return new JedisConnectionFactory(clusterConfig);
  17.     }
  18.    
  19.     @Bean
  20.     public RedisTemplate<String, Object> redisTemplate() {
  21.         RedisTemplate<String, Object> template = new RedisTemplate<>();
  22.         template.setConnectionFactory(redisConnectionFactory());
  23.         
  24.         // 使用String序列化器
  25.         template.setKeySerializer(new StringRedisSerializer());
  26.         template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
  27.         
  28.         // 开启事务支持
  29.         template.setEnableTransactionSupport(true);
  30.         
  31.         return template;
  32.     }
  33.    
  34.     @Bean
  35.     public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
  36.         RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
  37.                 .entryTtl(Duration.ofMinutes(30)) // 默认过期时间
  38.                 .disableCachingNullValues() // 不缓存null值
  39.                 .serializeKeysWith(RedisSerializationContext.SerializationPair
  40.                         .fromSerializer(new StringRedisSerializer()))
  41.                 .serializeValuesWith(RedisSerializationContext.SerializationPair
  42.                         .fromSerializer(new GenericJackson2JsonRedisSerializer()));
  43.         
  44.         // 不同缓存区域的不同配置
  45.         Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
  46.         cacheConfigurations.put("product", config.entryTtl(Duration.ofHours(1)));
  47.         cacheConfigurations.put("user", config.entryTtl(Duration.ofDays(1)));
  48.         
  49.         return RedisCacheManager.builder(connectionFactory)
  50.                 .cacheDefaults(config)
  51.                 .withInitialCacheConfigurations(cacheConfigurations)
  52.                 .transactionAware()
  53.                 .build();
  54.     }
  55. }
复制代码
06 异步处理与消息队列

对于千万级系统,同步处理所有请求是不现实的。异步化是提高系统吞吐量的关键。
消息队列设计
  1. @Service
  2. public class CacheStrategyService {
  3.    
  4.     /**
  5.      * 防止缓存穿透:缓存空值
  6.      */
  7.     public Product getProductWithNullCache(Long productId) {
  8.         String cacheKey = "product:" + productId;
  9.         String nullCacheKey = "product_null:" + productId;
  10.         
  11.         // 先检查空值缓存
  12.         if (Boolean.TRUE.equals(redisTemplate.hasKey(nullCacheKey))) {
  13.             return null; // 知道是空值,直接返回,不查数据库
  14.         }
  15.         
  16.         Product product = redisTemplate.opsForValue().get(cacheKey);
  17.         if (product != null) {
  18.             return product;
  19.         }
  20.         
  21.         // 加分布式锁,防止缓存击穿
  22.         String lockKey = "lock:product:" + productId;
  23.         boolean locked = false;
  24.         try {
  25.             locked = tryLock(lockKey, 3, TimeUnit.SECONDS);
  26.             if (locked) {
  27.                 // 双重检查
  28.                 product = redisTemplate.opsForValue().get(cacheKey);
  29.                 if (product != null) {
  30.                     return product;
  31.                 }
  32.                
  33.                 // 查询数据库
  34.                 product = productRepository.findById(productId).orElse(null);
  35.                
  36.                 if (product == null) {
  37.                     // 缓存空值,过期时间短
  38.                     redisTemplate.opsForValue().set(nullCacheKey, "NULL", 5, TimeUnit.MINUTES);
  39.                 } else {
  40.                     // 缓存数据
  41.                     redisTemplate.opsForValue().set(cacheKey, product, 1, TimeUnit.HOURS);
  42.                 }
  43.                
  44.                 return product;
  45.             } else {
  46.                 // 未获取到锁,短暂等待后重试或返回降级数据
  47.                 Thread.sleep(100);
  48.                 return getProductWithNullCache(productId);
  49.             }
  50.         } catch (InterruptedException e) {
  51.             Thread.currentThread().interrupt();
  52.             return null;
  53.         } finally {
  54.             if (locked) {
  55.                 releaseLock(lockKey);
  56.             }
  57.         }
  58.     }
  59.    
  60.     /**
  61.      * 防止缓存雪崩:随机过期时间
  62.      */
  63.     public void setWithRandomExpire(String key, Object value, long baseExpire, TimeUnit unit) {
  64.         long expireTime = unit.toMillis(baseExpire);
  65.         
  66.         // 增加随机偏移量(±20%)
  67.         double randomFactor = 0.8 + Math.random() * 0.4; // 0.8 ~ 1.2
  68.         long actualExpire = (long) (expireTime * randomFactor);
  69.         
  70.         redisTemplate.opsForValue().set(
  71.             key, value, actualExpire, TimeUnit.MILLISECONDS
  72.         );
  73.     }
  74.    
  75.     /**
  76.      * 热点数据发现与自动缓存
  77.      */
  78.     @Scheduled(fixedDelay = 60000) // 每分钟执行一次
  79.     public void discoverHotData() {
  80.         // 从Redis统计访问频率
  81.         Set<String> hotKeys = findHotKeys();
  82.         
  83.         for (String key : hotKeys) {
  84.             // 将热点数据加载到本地缓存
  85.             Object value = redisTemplate.opsForValue().get(key);
  86.             if (value != null) {
  87.                 localCache.put(key, value);
  88.             }
  89.         }
  90.     }
  91. }
复制代码
异步处理模式
  1. @Configuration
  2. public class DataSourceConfig {
  3.    
  4.     @Bean
  5.     @Primary
  6.     public DataSource dataSource() {
  7.         // 主从数据源配置
  8.         Map<Object, Object> targetDataSources = new HashMap<>();
  9.         
  10.         // 主库
  11.         DataSource masterDataSource = createDataSource(
  12.             "jdbc:mysql://master-db:3306/db?useSSL=false",
  13.             "master_user",
  14.             "master_password"
  15.         );
  16.         targetDataSources.put(DataSourceType.MASTER, masterDataSource);
  17.         
  18.         // 从库1
  19.         DataSource slave1DataSource = createDataSource(
  20.             "jdbc:mysql://slave1-db:3306/db?useSSL=false",
  21.             "slave_user",
  22.             "slave_password"
  23.         );
  24.         targetDataSources.put("slave1", slave1DataSource);
  25.         
  26.         // 从库2
  27.         DataSource slave2DataSource = createDataSource(
  28.             "jdbc:mysql://slave2-db:3306/db?useSSL=false",
  29.             "slave_user",
  30.             "slave_password"
  31.         );
  32.         targetDataSources.put("slave2", slave2DataSource);
  33.         
  34.         // 动态数据源
  35.         DynamicDataSource dynamicDataSource = new DynamicDataSource();
  36.         dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
  37.         dynamicDataSource.setTargetDataSources(targetDataSources);
  38.         
  39.         return dynamicDataSource;
  40.     }
  41.    
  42.     @Bean
  43.     public AbstractRoutingDataSource routingDataSource() {
  44.         return new AbstractRoutingDataSource() {
  45.             @Override
  46.             protected Object determineCurrentLookupKey() {
  47.                 return DynamicDataSourceContextHolder.getDataSourceType();
  48.             }
  49.         };
  50.     }
  51.    
  52.     @Aspect
  53.     @Component
  54.     public class DataSourceAspect {
  55.         
  56.         @Before("@annotation(com.example.annotation.Master)")
  57.         public void setMasterDataSource() {
  58.             DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);
  59.         }
  60.         
  61.         @Before("@annotation(com.example.annotation.Slave)")
  62.         public void setSlaveDataSource() {
  63.             // 随机选择从库
  64.             String[] slaves = {"slave1", "slave2"};
  65.             String selectedSlave = slaves[ThreadLocalRandom.current().nextInt(slaves.length)];
  66.             DynamicDataSourceContextHolder.setDataSourceType(selectedSlave);
  67.         }
  68.         
  69.         @After("@annotation(com.example.annotation.Master) || " +
  70.                "@annotation(com.example.annotation.Slave)")
  71.         public void clearDataSource() {
  72.             DynamicDataSourceContextHolder.clearDataSourceType();
  73.         }
  74.     }
  75. }
  76. // 使用示例
  77. @Service
  78. public class OrderService {
  79.    
  80.     @Master // 使用主库
  81.     public void createOrder(Order order) {
  82.         orderRepository.save(order); // 写操作
  83.     }
  84.    
  85.     @Slave // 使用从库
  86.     public Order getOrder(Long orderId) {
  87.         return orderRepository.findById(orderId).orElse(null); // 读操作
  88.     }
  89. }
复制代码
07 监控与治理:系统的眼睛和大脑

没有监控的系统就像盲人开车。千万级系统需要完善的监控体系。
全链路监控
  1. // 分片策略:用户ID取模分片
  2. @Component
  3. public class UserShardingStrategy implements PreciseShardingAlgorithm<Long> {
  4.    
  5.     @Override
  6.     public String doSharding(Collection<String> availableTargetNames,
  7.                              PreciseShardingValue<Long> shardingValue) {
  8.         long userId = shardingValue.getValue();
  9.         
  10.         // 分片数
  11.         int shardCount = availableTargetNames.size();
  12.         
  13.         // 简单取模分片
  14.         long shardIndex = userId % shardCount;
  15.         
  16.         for (String tableName : availableTargetNames) {
  17.             if (tableName.endsWith("_" + shardIndex)) {
  18.                 return tableName;
  19.             }
  20.         }
  21.         
  22.         throw new UnsupportedOperationException("无法找到对应的分片表");
  23.     }
  24. }
  25. // 分库分表配置
  26. @Configuration
  27. public class ShardingConfig {
  28.    
  29.     @Bean
  30.     public DataSource shardingDataSource() throws SQLException {
  31.         // 数据源映射
  32.         Map<String, DataSource> dataSourceMap = new HashMap<>();
  33.         dataSourceMap.put("ds0", createDataSource("jdbc:mysql://db0:3306/db"));
  34.         dataSourceMap.put("ds1", createDataSource("jdbc:mysql://db1:3306/db"));
  35.         dataSourceMap.put("ds2", createDataSource("jdbc:mysql://db2:3306/db"));
  36.         
  37.         // 用户表分片规则
  38.         ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
  39.         
  40.         // 用户表分表规则
  41.         TableRuleConfiguration userTableRuleConfig = new TableRuleConfiguration("user", "ds${0..2}.user_${0..7}");
  42.         userTableRuleConfig.setDatabaseShardingStrategyConfig(
  43.             new InlineShardingStrategyConfiguration("id", "ds${id % 3}")
  44.         );
  45.         userTableRuleConfig.setTableShardingStrategyConfig(
  46.             new StandardShardingStrategyConfiguration("id", new UserShardingStrategy())
  47.         );
  48.         
  49.         // 订单表分表规则
  50.         TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration("order", "ds${0..2}.order_${0..15}");
  51.         orderTableRuleConfig.setDatabaseShardingStrategyConfig(
  52.             new InlineShardingStrategyConfiguration("user_id", "ds${user_id % 3}")
  53.         );
  54.         orderTableRuleConfig.setTableShardingStrategyConfig(
  55.             new InlineShardingStrategyConfiguration("order_id", "order_${order_id % 16}")
  56.         );
  57.         
  58.         shardingRuleConfig.getTableRuleConfigs().add(userTableRuleConfig);
  59.         shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
  60.         
  61.         // 创建ShardingSphere数据源
  62.         return ShardingSphereDataSourceFactory.createDataSource(
  63.             dataSourceMap, Collections.singleton(shardingRuleConfig), new Properties()
  64.         );
  65.     }
  66. }
复制代码
智能告警
  1. -- 1. 索引优化示例
  2. -- 错误的索引设计
  3. CREATE INDEX idx_user_email ON user(email); -- 过长的索引
  4. CREATE INDEX idx_user_status ON user(status); -- 低区分度的索引
  5. -- 正确的索引设计
  6. -- 联合索引,注意字段顺序
  7. CREATE INDEX idx_user_created_status ON user(created_at, status);
  8. -- 前缀索引,适合长字段
  9. CREATE INDEX idx_user_email_prefix ON user(email(20));
  10. -- 2. 慢查询优化示例
  11. -- 优化前:全表扫描
  12. EXPLAIN SELECT * FROM order WHERE YEAR(created_at) = 2024;
  13. -- 优化后:使用索引
  14. EXPLAIN SELECT * FROM order
  15. WHERE created_at >= '2024-01-01'
  16.   AND created_at < '2025-01-01';
  17. -- 3. 分页优化
  18. -- 传统分页(数据量大时慢)
  19. SELECT * FROM user ORDER BY id LIMIT 1000000, 20;
  20. -- 优化分页(使用覆盖索引)
  21. SELECT * FROM user
  22. WHERE id > (SELECT id FROM user ORDER BY id LIMIT 1000000, 1)
  23. ORDER BY id LIMIT 20;
  24. -- 4. 批量操作优化
  25. -- 批量插入
  26. INSERT INTO user (name, email) VALUES
  27. ('user1', 'user1@example.com'),
  28. ('user2', 'user2@example.com'),
  29. -- ... 1000条
  30. ('user1000', 'user1000@example.com');
  31. -- 批量更新(避免在循环中单条更新)
  32. UPDATE user SET status = 'active'
  33. WHERE id IN (1, 2, 3, ..., 1000);
复制代码
08 实战案例:秒杀系统设计

让我们用一个具体的例子来综合运用上述技术。
  1. @Configuration
  2. public class KafkaConfig {
  3.    
  4.     @Bean
  5.     public ProducerFactory<String, String> producerFactory() {
  6.         Map<String, Object> configProps = new HashMap<>();
  7.         configProps.put(
  8.             ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
  9.             "kafka1:9092,kafka2:9092,kafka3:9092"
  10.         );
  11.         configProps.put(
  12.             ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
  13.             StringSerializer.class
  14.         );
  15.         configProps.put(
  16.             ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
  17.             StringSerializer.class
  18.         );
  19.         // 高吞吐量配置
  20.         configProps.put(ProducerConfig.LINGER_MS_CONFIG, 20); // 批量发送延迟
  21.         configProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 32 * 1024); // 批量大小
  22.         configProps.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "lz4"); // 压缩
  23.         
  24.         // 高可靠性配置
  25.         configProps.put(ProducerConfig.ACKS_CONFIG, "all"); // 所有副本确认
  26.         configProps.put(ProducerConfig.RETRIES_CONFIG, 3); // 重试次数
  27.         configProps.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); // 幂等性
  28.         
  29.         return new DefaultKafkaProducerFactory<>(configProps);
  30.     }
  31.    
  32.     @Bean
  33.     public KafkaTemplate<String, String> kafkaTemplate() {
  34.         return new KafkaTemplate<>(producerFactory());
  35.     }
  36. }
  37. @Service
  38. public class OrderMessageService {
  39.    
  40.     @Autowired
  41.     private KafkaTemplate<String, String> kafkaTemplate;
  42.    
  43.     // 顺序消息发送
  44.     public void sendOrderEvent(OrderEvent event) {
  45.         // 使用订单ID作为key,保证同一订单的消息顺序
  46.         String key = String.valueOf(event.getOrderId());
  47.         String topic = "order-events";
  48.         
  49.         kafkaTemplate.send(topic, key, JsonUtils.toJson(event))
  50.             .addCallback(
  51.                 result -> log.info("消息发送成功: {}", event),
  52.                 ex -> {
  53.                     log.error("消息发送失败: {}", event, ex);
  54.                     // 失败处理:记录到数据库,定时重试
  55.                     saveFailedMessage(event, ex.getMessage());
  56.                 }
  57.             );
  58.     }
  59.    
  60.     // 批量消息处理
  61.     @KafkaListener(topics = "order-events",
  62.                    containerFactory = "batchFactory")
  63.     public void processOrderEvents(List<ConsumerRecord<String, String>> records) {
  64.         List<OrderEvent> events = new ArrayList<>();
  65.         
  66.         for (ConsumerRecord<String, String> record : records) {
  67.             try {
  68.                 OrderEvent event = JsonUtils.fromJson(record.value(), OrderEvent.class);
  69.                 events.add(event);
  70.             } catch (Exception e) {
  71.                 log.error("消息解析失败: {}", record.value(), e);
  72.             }
  73.         }
  74.         
  75.         if (!events.isEmpty()) {
  76.             // 批量处理
  77.             batchProcessOrders(events);
  78.         }
  79.     }
  80.    
  81.     @Bean
  82.     public ConcurrentKafkaListenerContainerFactory<String, String> batchFactory() {
  83.         ConcurrentKafkaListenerContainerFactory<String, String> factory =
  84.             new ConcurrentKafkaListenerContainerFactory<>();
  85.         factory.setConsumerFactory(consumerFactory());
  86.         factory.setBatchListener(true); // 开启批量监听
  87.         factory.getContainerProperties().setPollTimeout(3000);
  88.         return factory;
  89.     }
  90. }
复制代码
总结

设计一个扛住千万级流量的系统,不是简单堆砌技术组件,而是构建一个有机的、能自我调节的生态系统
通过本文的分享,我想你已经看到了一个完整的高并发系统架构图景。
让我最后总结一下关键点:

  • 架构是演进而来的:不要一开始就追求完美架构,而是随着业务增长不断演进。从小而美的单体开始,逐步拆分、优化、扩展。
  • 缓存是性能的银弹:但要用得聪明。多级缓存、智能淘汰、防止穿透/击穿/雪崩,每个细节都影响巨大。
  • 数据库是瓶颈所在:读写分离、分库分表、索引优化、SQL调优,这些基本功比任何炫酷的技术都重要。
  • 异步是扩展的关键:能异步的绝不同步,能批处理的绝不单条。消息队列、事件驱动、反应式编程,让系统松耦合、高内聚。
  • 监控是系统的眼睛:没有监控的系统就是在裸奔。全链路追踪、指标监控、日志聚合、智能告警,缺一不可。
  • 容错比优化更重要:系统一定会出问题,关键是如何快速发现、快速恢复、快速止损。熔断、降级、限流、重试,这些机制是系统的保险绳。
真正的架构大师,不是掌握了多少技术框架,而是能在业务需求、技术实现、团队能力、资源约束之间找到最佳平衡点。
更多内容推荐:

  • Java常见面试题及答案
  • JVM面试题及答案
  • SpringBoot项目实战

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册