秒杀业务开发java版

内容分享2周前发布
1 0 0

Java秒杀业务实现方案详解

秒杀业务java

我来为您详细介绍Java秒杀业务的实现方案。秒杀业务的核心在于解决高并发、超卖、性能瓶颈等问题。

一、秒杀系统架构设计

1.1 整体架构

text

用户请求 → 负载均衡 → 网关层 → 服务层 → 缓存层 → 数据库层
        ↓
      队列处理 ← 异步处理 ← 消息队列

二、核心代码实现

2.1 数据库设计

sql

CREATE TABLE `seckill_goods` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `goods_name` varchar(255) NOT NULL,
  `stock_count` int(11) NOT NULL,
  `start_time` datetime NOT NULL,
  `end_time` datetime NOT NULL,
  `version` int(11) DEFAULT '0',
  PRIMARY KEY (`id`)
);

CREATE TABLE `seckill_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `goods_id` bigint(20) NOT NULL,
  `order_id` varchar(32) NOT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_goods` (`user_id`,`goods_id`)
);

2.2 实体类

java

@Data
public class SeckillGoods {
    private Long id;
    private String goodsName;
    private Integer stockCount;
    private Date startTime;
    private Date endTime;
    private Integer version;
}

@Data
public class SeckillOrder {
    private Long id;
    private Long userId;
    private Long goodsId;
    private String orderId;
    private Date createTime;
}

2.3 核心Service实现

java

@Service
@Slf4j
public class SeckillService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    
    @Autowired
    private SeckillOrderMapper seckillOrderMapper;
    
    @Autowired
    private RedissonClient redissonClient;
    
    /**
     * 秒杀入口 - 使用Redis预减库存
     */
    public SeckillResult seckill(Long goodsId, Long userId) {
        // 1. 校验秒杀状态
        if (!checkSeckillStatus(goodsId)) {
            return SeckillResult.error("秒杀未开始或已结束");
        }
        
        // 2. 校验重复购买 - Redis set
        String key = "seckill_user:" + goodsId;
        Boolean isMember = redisTemplate.opsForSet().isMember(key, userId.toString());
        if (Boolean.TRUE.equals(isMember)) {
            return SeckillResult.error("请勿重复购买");
        }
        
        // 3. 预减库存 - Redis原子操作
        String stockKey = "seckill_stock:" + goodsId;
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        if (stock == null || stock < 0) {
            // 库存不足,恢复库存
            redisTemplate.opsForValue().increment(stockKey);
            return SeckillResult.error("商品已售罄");
        }
        
        // 4. 创建订单 - 异步处理
        SeckillMessage message = new SeckillMessage(userId, goodsId);
        sendSeckillMessage(message);
        
        return SeckillResult.success("秒杀成功,请等待订单生成");
    }
    
    /**
     * 异步创建订单
     */
    @Async
    public void handleSeckillOrder(SeckillMessage message) {
        Long userId = message.getUserId();
        Long goodsId = message.getGoodsId();
        
        // 分布式锁,防止重复消费
        RLock lock = redissonClient.getLock("seckill_order_lock:" + goodsId + ":" + userId);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                // 校验重复订单
                SeckillOrder existOrder = seckillOrderMapper.selectByUserAndGoods(userId, goodsId);
                if (existOrder != null) {
                    return;
                }
                
                // 数据库减库存 - 乐观锁
                int result = seckillGoodsMapper.reduceStockWithVersion(goodsId);
                if (result <= 0) {
                    // 减库存失败,恢复Redis库存
                    redisTemplate.opsForValue().increment("seckill_stock:" + goodsId);
                    return;
                }
                
                // 创建订单
                SeckillOrder order = new SeckillOrder();
                order.setUserId(userId);
                order.setGoodsId(goodsId);
                order.setOrderId(generateOrderId());
                order.setCreateTime(new Date());
                seckillOrderMapper.insert(order);
                
                // 记录已购买用户
                redisTemplate.opsForSet().add("seckill_user:" + goodsId, userId.toString());
            }
        } catch (InterruptedException e) {
            log.error("获取锁失败", e);
            Thread.currentThread().interrupt();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    
    private boolean checkSeckillStatus(Long goodsId) {
        // 从Redis或数据库检查秒杀时间
        // 实现略...
        return true;
    }
    
    private void sendSeckillMessage(SeckillMessage message) {
        // 发送到消息队列
        // 实现略...
    }
    
    private String generateOrderId() {
        return System.currentTimeMillis() + "" + (int)(Math.random() * 1000);
    }
}

2.4 Mapper层 – 乐观锁实现

java

@Mapper
public interface SeckillGoodsMapper {
    
    @Update("UPDATE seckill_goods SET stock_count = stock_count - 1, version = version + 1 " +
            "WHERE id = #{goodsId} AND stock_count > 0 AND version = #{version}")
    int reduceStockWithVersion(@Param("goodsId") Long goodsId, @Param("version") Integer version);
    
    @Select("SELECT * FROM seckill_goods WHERE id = #{goodsId}")
    SeckillGoods selectById(Long goodsId);
}

三、关键技术解决方案

3.1 库存超卖问题

解决方案:

Redis原子操作预减库存

数据库乐观锁

分布式锁

3.2 高性能实现

java

@Component
public class SeckillPreheat {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 秒杀开始前预热数据到Redis
     */
    public void preheatSeckillData(Long goodsId) {
        SeckillGoods goods = seckillGoodsMapper.selectById(goodsId);
        if (goods != null) {
            // 库存预热
            redisTemplate.opsForValue().set(
                "seckill_stock:" + goodsId, 
                goods.getStockCount()
            );
            
            // 商品信息预热
            redisTemplate.opsForValue().set(
                "seckill_goods:" + goodsId,
                goods
            );
        }
    }
}

3.3 限流防刷

java

@Component
public class RateLimiterService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 令牌桶限流
     */
    public boolean tryAcquire(String key, int capacity, int rate) {
        String redisKey = "rate_limit:" + key;
        long now = System.currentTimeMillis();
        
        RedisScript<Long> script = RedisScript.of(
            "local key = KEYS[1]
" +
            "local now = tonumber(ARGV[1])
" +
            "local capacity = tonumber(ARGV[2])
" +
            "local rate = tonumber(ARGV[3])
" +
            "local lastTime = redis.call('hget', key, 'lastTime')
" +
            "local tokens = redis.call('hget', key, 'tokens')
" +
            "
" +
            "if lastTime == false then
" +
            "    lastTime = now
" +
            "    tokens = capacity
" +
            "else
" +
            "    local elapsed = now - lastTime
" +
            "    local newTokens = math.floor(elapsed / 1000 * rate)
" +
            "    tokens = math.min(capacity, tokens + newTokens)
" +
            "    lastTime = now
" +
            "end
" +
            "
" +
            "if tokens >= 1 then
" +
            "    tokens = tokens - 1
" +
            "    redis.call('hset', key, 'lastTime', lastTime)
" +
            "    redis.call('hset', key, 'tokens', tokens)
" +
            "    redis.call('expire', key, math.ceil(capacity/rate) + 1)
" +
            "    return 1
" +
            "else
" +
            "    return 0
" +
            "end",
            Long.class
        );
        
        Long result = redisTemplate.execute(script, Collections.singletonList(redisKey), 
                                          now, capacity, rate);
        return result != null && result == 1;
    }
}

四、完整配置

4.1 Redis配置

java

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
            mapper.getPolymorphicTypeValidator(),
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        serializer.setObjectMapper(mapper);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

五、部署建议

前端优化:静态资源CDN、页面缓存、按钮防重复点击

网关层:Nginx负载均衡、限流配置

服务层:集群部署、服务降级

缓存层:Redis集群、持久化配置

数据库:主从复制、分库分表

这个方案能够支撑万级QPS的秒杀场景,通过多级防护确保系统的稳定性和数据的一致性。

页面缓存举例

页面缓存实现方案

一、前端页面缓存

1.1 HTML静态化 + CDN

html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Cache-Control" max-age="300">
    <title>秒杀活动页</title>
    <!-- CDN加速静态资源 -->
    <link href="https://cdn.example.com/css/seckill.css?v=1.0.0" rel="stylesheet">
    <script src="https://cdn.example.com/js/seckill.js?v=1.0.0"></script>
</head>
<body>
    <div>
        <div class="goods-info">
            <h2 data-goods-id="1001">iPhone 15 Pro 秒杀</h2>
            <p class="price">¥7999</p>
            <p class="stock">剩余库存: <span>0</span></p>
            <button class="btn-disabled">

1.2 服务端页面片段缓存

java

@Service
public class PageCacheService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final long PAGE_CACHE_EXPIRE = 30; // 30秒
    
    /**
     * 获取商品详情页缓存
     */
    public String getGoodsDetailPage(Long goodsId) {
        String cacheKey = "page:goods:detail:" + goodsId;
        
        // 1. 尝试从缓存获取
        String cachedPage = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cachedPage != null) {
            return cachedPage;
        }
        
        // 2. 缓存未命中,生成页面
        String html = generateGoodsDetailPage(goodsId);
        
        // 3. 异步更新缓存
        CompletableFuture.runAsync(() -> {
            redisTemplate.opsForValue().set(cacheKey, html, PAGE_CACHE_EXPIRE, TimeUnit.SECONDS);
        });
        
        return html;
    }
    
    /**
     * 生成商品详情页
     */
    private String generateGoodsDetailPage(Long goodsId) {
        SeckillGoods goods = seckillGoodsMapper.selectById(goodsId);
        
        return "<!DOCTYPE html>
" +
               "<html>
" +
               "<head>
" +
               "    <title>" + goods.getGoodsName() + "</title>
" +
               "</head>
" +
               "<body>
" +
               "    <div class='goods-detail'>
" +
               "        <h1>" + goods.getGoodsName() + "</h1>
" +
               "        <p class='price'>价格: ¥" + calculatePrice(goods) + "</p>
" +
               "        <p class='stock'>库存: " + goods.getStockCount() + "</p>
" +
               "        <div class='countdown' id='countdown'></div>
" +
               "    </div>
" +
               "</body>
" +
               "</html>";
    }
}

二、Spring Boot页面缓存实现

2.1 Controller层缓存

java

@Controller
public class SeckillPageController {
    
    @Autowired
    private PageCacheService pageCacheService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 商品详情页 - 带缓存
     */
    @GetMapping("/seckill/goods/{goodsId}")
    public String goodsDetail(@PathVariable Long goodsId, Model model) {
        // 检查页面缓存
        String cacheKey = "page:goods:detail:" + goodsId;
        String cachedHtml = (String) redisTemplate.opsForValue().get(cacheKey);
        
        if (cachedHtml != null) {
            // 直接返回缓存内容
            return cachedHtml;
        }
        
        // 获取商品信息
        SeckillGoods goods = getGoodsWithCache(goodsId);
        model.addAttribute("goods", goods);
        
        // 秒杀状态
        SeckillStatus status = getSeckillStatus(goodsId);
        model.addAttribute("status", status);
        
        // 异步更新页面缓存
        cacheGoodsPage(goodsId);
        
        return "seckill/goods_detail";
    }
    
    /**
     * 获取商品信息(带缓存)
     */
    private SeckillGoods getGoodsWithCache(Long goodsId) {
        String cacheKey = "goods:info:" + goodsId;
        SeckillGoods goods = (SeckillGoods) redisTemplate.opsForValue().get(cacheKey);
        
        if (goods == null) {
            goods = seckillGoodsMapper.selectById(goodsId);
            // 缓存5分钟
            redisTemplate.opsForValue().set(cacheKey, goods, 5, TimeUnit.MINUTES);
        }
        
        return goods;
    }
}

2.2 使用Spring Cache注解

java

@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(30)) // 30秒过期
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
    }
}

@Service
public class SeckillGoodsService {
    
    /**
     * 使用Spring Cache缓存商品信息
     */
    @Cacheable(value = "goods", key = "#goodsId")
    public SeckillGoods getGoodsById(Long goodsId) {
        return seckillGoodsMapper.selectById(goodsId);
    }
    
    /**
     * 缓存秒杀活动列表
     */
    @Cacheable(value = "seckillList", key = "'active_list'")
    public List<SeckillGoods> getActiveSeckillList() {
        Date now = new Date();
        return seckillGoodsMapper.selectActiveList(now);
    }
    
    /**
     * 更新时清除缓存
     */
    @CacheEvict(value = "goods", key = "#goodsId")
    public void updateGoods(SeckillGoods goods) {
        seckillGoodsMapper.updateById(goods);
    }
}

三、Nginx层面缓存配置

3.1 Nginx缓存配置

nginx

http {
    # 缓存路径和配置
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=seckill_cache:10m 
                     max_size=10g inactive=60m use_temp_path=off;
    
    upstream seckill_backend {
        server 127.0.0.1:8080 weight=1;
        server 127.0.0.1:8081 weight=1;
    }
    
    server {
        listen 80;
        server_name seckill.example.com;
        
        # 静态资源缓存
        location ~* .(html|css|js|png|jpg|jpeg|gif|ico)$ {
            expires 1h;
            add_header Cache-Control "public, immutable";
            root /var/www/seckill/static;
        }
        
        # 商品详情页缓存
        location ~ ^/seckill/goods/d+$ {
            proxy_pass http://seckill_backend;
            
            # 启用缓存
            proxy_cache seckill_cache;
            proxy_cache_key "$scheme$request_method$host$request_uri";
            proxy_cache_valid 200 10s;  # 200响应缓存10秒
            proxy_cache_valid 404 1m;   # 404响应缓存1分钟
            
            # 缓存相关头
            add_header X-Proxy-Cache $upstream_cache_status;
            add_header Cache-Control "public, max-age=10";
            
            # 多个请求同时访问时,只让一个请求回源
            proxy_cache_lock on;
            proxy_cache_lock_timeout 5s;
            
            # 缓存跳过条件(如带特定参数)
            proxy_cache_bypass $arg_nocache;
        }
        
        # 动态接口不缓存
        location /api/ {
            proxy_pass http://seckill_backend;
            add_header Cache-Control "no-cache, no-store, must-revalidate";
        }
    }
}

3.2 Nginx限流配置

nginx

http {
    limit_req_zone $binary_remote_addr zone=seckill_limit:10m rate=10r/s;
    
    server {
        location /seckill/goods/ {
            # 限流配置
            limit_req zone=seckill_limit burst=20 nodelay;
            
            proxy_pass http://seckill_backend;
            proxy_cache seckill_cache;
        }
    }
}

四、前端缓存策略

4.1 Service Worker缓存

javascript

// sw.js - Service Worker
const CACHE_NAME = 'seckill-v1';
const STATIC_URLS = [
    '/static/css/seckill.css',
    '/static/js/seckill.js',
    '/static/images/loading.gif'
];

// 安装阶段缓存静态资源
self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(cache => cache.addAll(STATIC_URLS))
    );
});

// 拦截请求
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request)
            .then(response => {
                // 返回缓存或网络请求
                return response || fetch(event.request);
            })
    );
});

4.2 本地存储缓存

javascript

class PageCache {
    constructor() {
        this.cacheKey = 'seckill_page_cache';
        this.expireTime = 5 * 60 * 1000; // 5分钟
    }
    
    // 缓存页面数据
    cachePageData(goodsId, data) {
        const cache = {
            data: data,
            timestamp: Date.now(),
            expire: this.expireTime
        };
        
        const key = `${this.cacheKey}_${goodsId}`;
        localStorage.setItem(key, JSON.stringify(cache));
    }
    
    // 获取缓存数据
    getCachedPageData(goodsId) {
        const key = `${this.cacheKey}_${goodsId}`;
        const cached = localStorage.getItem(key);
        
        if (!cached) return null;
        
        const cache = JSON.parse(cached);
        const isExpired = Date.now() - cache.timestamp > cache.expire;
        
        if (isExpired) {
            localStorage.removeItem(key);
            return null;
        }
        
        return cache.data;
    }
    
    // 清除缓存
    clearCache(goodsId) {
        const key = `${this.cacheKey}_${goodsId}`;
        localStorage.removeItem(key);
    }
}

// 使用示例
const pageCache = new PageCache();

async function loadGoodsDetail(goodsId) {
    // 先尝试从本地缓存获取
    let goodsData = pageCache.getCachedPageData(goodsId);
    
    if (!goodsData) {
        // 缓存未命中,从服务器获取
        goodsData = await fetchGoodsDetail(goodsId);
        // 更新缓存
        pageCache.cachePageData(goodsId, goodsData);
    }
    
    renderGoodsDetail(goodsData);
}

五、缓存更新策略

5.1 缓存失效和更新

java

@Service
public class CacheUpdateService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * 商品信息变更时清除相关缓存
     */
    @EventListener
    public void handleGoodsUpdate(GoodsUpdateEvent event) {
        Long goodsId = event.getGoodsId();
        
        // 清除Redis缓存
        String[] cacheKeys = {
            "page:goods:detail:" + goodsId,
            "goods:info:" + goodsId,
            "seckill:status:" + goodsId
        };
        
        redisTemplate.delete(Arrays.asList(cacheKeys));
        
        // 发布缓存更新事件
        eventPublisher.publishEvent(new CacheRefreshEvent(goodsId));
    }
    
    /**
     * 定时刷新缓存
     */
    @Scheduled(fixedRate = 30000) // 30秒刷新一次
    public void refreshHotGoodsCache() {
        List<Long> hotGoodsIds = getHotGoodsIds();
        
        for (Long goodsId : hotGoodsIds) {
            // 异步刷新缓存
            CompletableFuture.runAsync(() -> {
                refreshGoodsCache(goodsId);
            });
        }
    }
}

这样的页面缓存方案可以显著提升系统性能:

静态资源:CDN + 长期缓存

动态页面:Nginx缓存 + Redis缓存

API数据:Redis缓存 + 本地存储

缓存更新:事件驱动 + 定时刷新

网关层:Nginx负载均衡、限流配置

Nginx网关层配置详解

一、Nginx负载均衡配置

1.1 基础负载均衡配置

nginx

# nginx.conf
http {
    upstream seckill_backend {
        # 负载均衡策略
        # 1. 轮询 (默认)
        server 192.168.1.101:8080 weight=1;
        server 192.168.1.102:8080 weight=1;
        server 192.168.1.103:8080 weight=1;
        
        # 2. 最少连接数
        # least_conn;
        
        # 3. IP哈希
        # ip_hash;
        
        # 4. 响应时间优先
        # fair;
        
        # 健康检查
        check interval=3000 rise=2 fall=3 timeout=1000 type=http;
        check_http_send "HEAD /health HTTP/1.0

";
        check_http_expect_alive http_2xx http_3xx;
    }
    
    upstream seckill_api {
        # API服务集群
        server 192.168.1.111:8081 weight=2;  # 权重更高
        server 192.168.1.112:8081 weight=1;
        server 192.168.1.113:8081 weight=1;
        
        # 失败重试配置
        proxy_next_upstream error timeout http_500 http_502 http_503;
        proxy_next_upstream_tries 3;
        proxy_next_upstream_timeout 10s;
    }
    
    server {
        listen 80;
        server_name seckill.example.com;
        
        # 静态资源服务
        location /static/ {
            alias /var/www/seckill/static/;
            expires 1y;
            add_header Cache-Control "public, immutable";
            access_log off;
            
            # 防盗链
            valid_referers none blocked seckill.example.com *.example.com;
            if ($invalid_referer) {
                return 403;
            }
        }
        
        # 秒杀页面路由
        location /seckill/ {
            proxy_pass http://seckill_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            # 超时配置
            proxy_connect_timeout 3s;
            proxy_read_timeout 10s;
            proxy_send_timeout 10s;
            
            # 缓冲区优化
            proxy_buffering on;
            proxy_buffer_size 4k;
            proxy_buffers 8 4k;
            
            # 启用缓存
            proxy_cache seckill_cache;
            proxy_cache_valid 200 10s;
        }
        
        # API接口路由
        location /api/ {
            proxy_pass http://seckill_api;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # API特殊超时配置
            proxy_connect_timeout 2s;
            proxy_read_timeout 5s;
            proxy_send_timeout 5s;
            
            # 不缓存API响应
            proxy_no_cache 1;
            proxy_cache_bypass 1;
        }
        
        # 健康检查端点
        location /health {
            access_log off;
            return 200 "healthy
";
            add_header Content-Type text/plain;
        }
    }
}

1.2 高级负载均衡策略

nginx

http {
    # 一致性哈希负载均衡 - 提高缓存命中率
    upstream seckill_consistent {
        hash $request_uri consistent;
        
        server 192.168.1.101:8080;
        server 192.168.1.102:8080;
        server 192.168.1.103:8080;
        server 192.168.1.104:8080;
    }
    
    # 基于URI的负载均衡
    upstream seckill_goods {
        server 192.168.1.101:8080;
    }
    
    upstream seckill_order {
        server 192.168.1.102:8080;
    }
    
    upstream seckill_user {
        server 192.168.1.103:8080;
    }
    
    server {
        # 根据URI路径路由到不同后端
        location ~ ^/api/goods/ {
            proxy_pass http://seckill_goods;
        }
        
        location ~ ^/api/order/ {
            proxy_pass http://seckill_order;
        }
        
        location ~ ^/api/user/ {
            proxy_pass http://seckill_user;
        }
    }
}

二、Nginx限流配置

2.1 基础限流配置

nginx

http {
    # 限流区域配置
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=seckill_limit:10m rate=5r/s;
    limit_req_zone $server_name zone=global_limit:10m rate=1000r/s;
    
    # 连接数限制
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_conn_zone $server_name zone=server_conn:10m;
    
    server {
        listen 80;
        server_name seckill.example.com;
        
        # 全局连接数限制
        limit_conn server_conn 10000;
        
        # 静态资源 - 宽松限流
        location /static/ {
            limit_req zone=api_limit burst=50 nodelay;
            limit_conn conn_limit 100;
            # 静态资源处理...
        }
        
        # 商品查询API - 中等限流
        location /api/goods/ {
            limit_req zone=api_limit burst=20 nodelay;
            limit_conn conn_limit 50;
            limit_rate 500k;  # 带宽限制
            
            proxy_pass http://seckill_api;
            error_page 429 =200 /api/too_many_requests.json;
        }
        
        # 秒杀接口 - 严格限流
        location ~ ^/api/seckill/ {
            limit_req zone=seckill_limit burst=10 nodelay;
            limit_conn conn_limit 10;
            
            # 限流后返回特定JSON
            error_page 429 =200 /api/seckill_busy.json;
            
            proxy_pass http://seckill_api;
        }
        
        # 限流响应页面
        location /api/too_many_requests.json {
            add_header Content-Type application/json;
            return 200 '{"code":429,"message":"请求过于频繁,请稍后重试"}';
        }
        
        location /api/seckill_busy.json {
            add_header Content-Type application/json;
            return 200 '{"code":429,"message":"秒杀过于火爆,请稍后重试"}';
        }
    }
}

2.2 高级限流策略

nginx

http {
    # 基于地理位置的限流
    geo $limit_geo {
        default 1;
        192.168.0.0/24 0;  # 内网不限流
        10.0.0.0/8 0;      # 办公网络不限流
    }
    
    map $limit_geo $limit_key {
        0 "";              # 不限流
        1 $binary_remote_addr;  # 限流
    }
    
    limit_req_zone $limit_key zone=geo_limit:10m rate=5r/s;
    
    # 基于用户代理的限流
    map $http_user_agent $is_bot {
        default 0;
        "~*bot" 1;
        "~*crawler" 1;
        "~*spider" 1;
    }
    
    limit_req_zone $is_bot zone=bot_limit:1m rate=1r/m;
    
    server {
        # 地理位置限流
        location /api/ {
            limit_req zone=geo_limit burst=10 nodelay;
            
            # 机器人严格限流
            if ($is_bot) {
                limit_req zone=bot_limit burst=1 nodelay;
            }
            
            proxy_pass http://seckill_api;
        }
        
        # 白名单配置
        location /api/internal/ {
            allow 192.168.0.0/24;
            allow 10.0.0.0/8;
            deny all;
            
            # 内部接口不限流
            proxy_pass http://seckill_api;
        }
    }
}

三、安全防护配置

3.1 DDoS防护

nginx

http {
    # 请求频率限制
    limit_req_zone $binary_remote_addr zone=flood:10m rate=100r/s;
    
    # 连接数限制
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    server {
        # 全局防护
        limit_req zone=flood burst=200 nodelay;
        limit_conn addr 100;
        
        # 大文件上传限制
        client_max_body_size 10m;
        client_body_timeout 10s;
        
        # 隐藏Nginx版本信息
        server_tokens off;
        
        # 安全头设置
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
        
        # 限制请求方法
        if ($request_method !~ ^(GET|POST|HEAD)$) {
            return 405;
        }
        
        # 阻止常见攻击
        location ~* .(php|asp|aspx|jsp)$ {
            return 404;
        }
        
        # 阻止敏感文件访问
        location ~ /.(ht|git|svn) {
            return 404;
        }
    }
}

3.2 WAF功能配置

nginx

http {
    # 自定义WAF规则
    map $http_user_agent $bad_agent {
        default 0;
        "~*nmap" 1;
        "~*sqlmap" 1;
        "~*nikto" 1;
        "~*metasploit" 1;
    }
    
    map $args $sql_injection {
        default 0;
        "~*union.*select" 1;
        "~*drop.*table" 1;
        "~*insert.*into" 1;
        "~*sleep(.*)" 1;
    }
    
    server {
        # 阻止恶意User-Agent
        if ($bad_agent) {
            return 403;
        }
        
        # SQL注入检测
        if ($sql_injection) {
            return 403;
        }
        
        # XSS攻击检测
        if ($request_uri ~* "<script>") {
            return 403;
        }
        
        # 路径遍历检测
        if ($request_uri ~* "../") {
            return 403;
        }
    }
}

四、性能优化配置

4.1 缓存优化

nginx

http {
    # 缓存路径配置
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=seckill_cache:10m 
                     max_size=10g inactive=60m use_temp_path=off;
    
    proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=api_cache:5m 
                     max_size=1g inactive=5m use_temp_path=off;
    
    # 缓存key配置
    proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
    
    server {
        # 页面缓存
        location ~ ^/seckill/goods/d+$ {
            proxy_cache seckill_cache;
            proxy_cache_valid 200 10s;
            proxy_cache_valid 404 1m;
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
            proxy_cache_background_update on;
            proxy_cache_lock on;
            proxy_cache_lock_timeout 5s;
            
            add_header X-Cache-Status $upstream_cache_status;
            proxy_pass http://seckill_backend;
        }
        
        # API缓存 - 只缓存GET请求
        location /api/goods/ {
            proxy_cache api_cache;
            proxy_cache_methods GET;
            proxy_cache_valid 200 5s;
            proxy_cache_valid 404 30s;
            
            # 缓存条件
            proxy_cache_bypass $http_cache_control;
            proxy_no_cache $http_pragma $http_authorization;
            
            proxy_pass http://seckill_api;
        }
    }
}

4.2 连接优化

nginx

events {
    # 事件模型
    use epoll;
    
    # 连接数
    worker_connections 65535;
    
    # 多连接接受
    multi_accept on;
}

http {
    # 连接优化
    keepalive_timeout 65;
    keepalive_requests 1000;
    
    # 缓冲区优化
    client_body_buffer_size 16k;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 16k;
    
    # 文件传输优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    
    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
    
    server {
        listen 80 reuseport;  # 端口重用
        # ... 其他配置
    }
}

五、监控和日志配置

5.1 访问日志和监控

nginx

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'rt=$request_time uct="$upstream_connect_time" '
                    'uht="$upstream_header_time" urt="$upstream_response_time" '
                    'cache_status=$upstream_cache_status';
    
    log_format seckill_log '$remote_addr - $request_time - $status - $request_uri';
    
    # 状态监控
    server {
        listen 8080;
        server_name 127.0.0.1;
        
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
        }
        
        location /server_status {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
            allow 127.0.0.1;
            deny all;
        }
    }
    
    server {
        access_log /var/log/nginx/seckill_access.log seckill_log;
        error_log /var/log/nginx/seckill_error.log warn;
        
        # 秒杀特定日志
        location ~ ^/api/seckill/ {
            access_log /var/log/nginx/seckill_api.log main;
            # ... 其他配置
        }
    }
}

5.2 动态限流配置

nginx

http {
    # Lua脚本支持动态限流
    lua_shared_dict dynamic_limit 10m;
    
    init_by_lua_block {
        -- 初始化限流配置
        local limit_config = {
            ["/api/seckill/start"] = {rate = 100, burst = 50},
            ["/api/seckill/end"] = {rate = 50, burst = 20}
        }
        ngx.shared.dynamic_limit:set("config", cjson.encode(limit_config))
    }
    
    server {
        location /api/seckill/ {
            access_by_lua_file /etc/nginx/lua/dynamic_limit.lua;
            proxy_pass http://seckill_api;
        }
    }
}

这样的Nginx网关配置可以提供:

高性能负载均衡:多种策略支持

精细化限流:基于IP、路径、地理位置等

安全防护:DDoS防护、WAF功能

性能优化:缓存、连接优化

监控能力:详细日志和状态监控

集群部署

秒杀系统集群部署方案

一、整体集群架构

1.1 集群拓扑结构

text

用户请求 → CDN → 负载均衡层 (Nginx/LVS) → 网关层 → 业务集群 → 缓存集群 → 数据库集群
                    ↓                    ↓         ↓         ↓           ↓
                 Nginx集群          Spring Cloud  应用集群  Redis集群   MySQL集群
                    ↓                    ↓         ↓         ↓           ↓
                 Keepalived         Gateway集群  多节点部署 哨兵模式   主从复制

二、应用层集群部署

2.1 Spring Boot应用集群配置

2.1.1 应用配置文件

application-cluster.yml

yaml

server:
  port: 8080
  tomcat:
    max-threads: 1000
    min-spare-threads: 50
    max-connections: 10000

spring:
  application:
    name: seckill-service
  # 集群配置
  cloud:
    inetutils:
      preferred-networks: 192.168.1
    # 服务发现 - Nacos配置
    nacos:
      discovery:
        server-addr: 192.168.1.100:8848
        cluster-name: CLUSTER_A
        namespace: seckill-prod
  # Redis集群配置
  redis:
    cluster:
      nodes:
        - 192.168.1.101:6379
        - 192.168.1.102:6379
        - 192.168.1.103:6379
        - 192.168.1.104:6379
        - 192.168.1.105:6379
        - 192.168.1.106:6379
      max-redirects: 3
    lettuce:
      pool:
        max-active: 1000
        max-wait: 1000ms
        max-idle: 50
        min-idle: 10
  # 数据库集群配置
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          url: jdbc:mysql:loadbalance://192.168.1.110:3306,192.168.1.111:3306/seckill?loadBalanceAutoCommitStatementThreshold=5&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
          username: seckill_user
          password: ${DB_PASSWORD}
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql:loadbalance://192.168.1.112:3306,192.168.1.113:3306/seckill?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
          username: seckill_user
          password: ${DB_PASSWORD}
          driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis配置
mybatis-plus:
  configuration:
    cache-enabled: true
    lazy-loading-enabled: true
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      id-type: AUTO
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

# 集群部署配置
cluster:
  node-id: ${HOSTNAME:local-1}
  # 分布式锁配置
  distributed-lock:
    enabled: true
    type: redis
  # 会话配置
  session:
    store-type: redis
    timeout: 1800
2.1.2 服务注册发现配置

Nacos配置

java

@Configuration
@EnableDiscoveryClient
public class NacosConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate loadBalancedRestTemplate() {
        return new RestTemplate();
    }
}

// 服务间调用 - Feign客户端
@FeignClient(name = "seckill-service", path = "/api")
public interface SeckillServiceClient {
    
    @GetMapping("/goods/{goodsId}")
    ResponseEntity<GoodsInfo> getGoodsInfo(@PathVariable("goodsId") Long goodsId);
    
    @PostMapping("/seckill/{goodsId}")
    ResponseEntity<SeckillResult> executeSeckill(@PathVariable("goodsId") Long goodsId, 
                                               @RequestBody SeckillRequest request);
}

2.2 Docker容器化部署

2.2.1 Dockerfile

dockerfile

# 多阶段构建
FROM openjdk:8-jdk-alpine as builder
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests

# 运行阶段
FROM openjdk:8-jre-alpine
RUN apk add --no-cache tzdata && 
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && 
    echo "Asia/Shanghai" > /etc/timezone

# 创建应用用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# 安装监控组件
RUN apk add --no-cache curl

USER appuser
WORKDIR /app

# 复制jar包
COPY --from=builder /app/target/seckill-service-*.jar app.jar

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 
    CMD curl -f http://localhost:8080/actuator/health || exit 1

# JVM参数
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/logs/gc.log"

EXPOSE 8080

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar --spring.profiles.active=cluster"]
2.2.2 Docker Compose集群配置

docker-compose-cluster.yml

yaml

version: '3.8'

services:
  # Nacos服务注册中心
  nacos:
    image: nacos/nacos-server:2.0.3
    container_name: nacos-server
    environment:
      - MODE=cluster
      - PREFER_HOST_MODE=hostname
      - NACOS_SERVERS=nacos1:8848 nacos2:8848 nacos3:8848
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=mysql-master
      - MYSQL_SERVICE_DB_NAME=nacos
      - MYSQL_SERVICE_USER=nacos
      - MYSQL_SERVICE_PASSWORD=${NACOS_DB_PASSWORD}
    ports:
      - "8848:8848"
    networks:
      - seckill-cluster
    deploy:
      replicas: 1

  # 秒杀应用集群
  seckill-app:
    image: seckill-service:1.0.0
    deploy:
      replicas: 6
      resources:
        limits:
          memory: 2G
          cpus: '1.0'
        reservations:
          memory: 1G
          cpus: '0.5'
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      update_config:
        parallelism: 2
        delay: 10s
        order: start-first
    environment:
      - SPRING_PROFILES_ACTIVE=cluster
      - DB_PASSWORD=${DB_PASSWORD}
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - NACOS_SERVER_ADDR=nacos:8848
    configs:
      - source: app-config
        target: /app/config/application.yml
    networks:
      - seckill-cluster
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.seckill.rule=Host(`seckill.example.com`)"
      - "traefik.http.services.seckill.loadbalancer.sticky.cookie=true"

  # Redis集群
  redis-node-1:
    image: redis:6.2-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000
    ports:
      - "6379"
    networks:
      - seckill-cluster
    deploy:
      replicas: 1

  # MySQL集群
  mysql-master:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=seckill
      - MYSQL_USER=seckill_user
      - MYSQL_PASSWORD=${DB_PASSWORD}
    ports:
      - "3306"
    networks:
      - seckill-cluster
    volumes:
      - mysql-master-data:/var/lib/mysql
      - ./config/my.cnf:/etc/mysql/conf.d/my.cnf

configs:
  app-config:
    file: ./config/application-cluster.yml

networks:
  seckill-cluster:
    driver: overlay
    attachable: true

volumes:
  mysql-master-data:
  redis-data:

三、缓存集群部署

3.1 Redis集群配置

3.1.1 Redis集群部署脚本

redis-cluster-setup.sh

bash

#!/bin/bash

# Redis集群节点配置
REDIS_NODES=("192.168.1.101:6379" "192.168.1.102:6379" "192.168.1.103:6379" 
             "192.168.1.104:6379" "192.168.1.105:6379" "192.168.1.106:6379")

# 创建集群
redis-cli --cluster create ${REDIS_NODES[@]} --cluster-replicas 1 -a ${REDIS_PASSWORD}

# 检查集群状态
redis-cli -h 192.168.1.101 -p 6379 -a ${REDIS_PASSWORD} cluster nodes
3.1.2 Redis集群配置

redis.conf

conf

# 集群配置
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-require-full-coverage no

# 安全配置
requirepass ${REDIS_PASSWORD}
masterauth ${REDIS_PASSWORD}

# 性能配置
maxmemory 2gb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000

# 网络配置
bind 0.0.0.0
protected-mode no
port 6379

# 持久化配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

3.2 Redis集群Java配置

java

@Configuration
public class RedisClusterConfig {
    
    @Value("${spring.redis.cluster.nodes}")
    private String clusterNodes;
    
    @Value("${spring.redis.password}")
    private String password;
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisClusterConfiguration config = new RedisClusterConfiguration();
        
        // 解析集群节点
        String[] nodes = clusterNodes.split(",");
        for (String node : nodes) {
            String[] hostPort = node.split(":");
            config.addClusterNode(new RedisNode(hostPort[0], Integer.parseInt(hostPort[1])));
        }
        
        config.setPassword(RedisPassword.of(password));
        
        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                .commandTimeout(Duration.ofSeconds(2))
                .shutdownTimeout(Duration.ZERO)
                .build();
        
        return new LettuceConnectionFactory(config, clientConfig);
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory());
        
        // 序列化配置
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
            mapper.getPolymorphicTypeValidator(),
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        serializer.setObjectMapper(mapper);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

四、数据库集群部署

4.1 MySQL主从复制配置

4.1.1 主库配置 (my.cnf)

ini

[mysqld]
# 服务器ID,主库为1
server-id=1

# 二进制日志配置
log-bin=mysql-bin
binlog-format=ROW
expire_logs_days=7
sync_binlog=1
binlog_cache_size=1M

# 需要同步的数据库
binlog-do-db=seckill

# 从库配置
relay-log=mysql-relay-bin
log_slave_updates=1
read_only=0

# InnoDB配置
innodb_flush_log_at_trx_commit=1
innodb_buffer_pool_size=2G
4.1.2 从库配置

ini

[mysqld]
# 服务器ID,从库递增
server-id=2

# 二进制日志配置
log-bin=mysql-bin
binlog-format=ROW
relay-log=mysql-relay-bin
log_slave_updates=1
read_only=1

# 需要同步的数据库
replicate-do-db=seckill

4.2 数据库读写分离配置

java

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    public DataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource());
        targetDataSources.put("slave", slaveDataSource());
        
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        
        return dynamicDataSource;
    }
    
    @Bean
    public AbstractRoutingDataSource routingDataSource() {
        return new AbstractRoutingDataSource() {
            @Override
            protected Object determineCurrentLookupKey() {
                return DynamicDataSourceContextHolder.getDataSourceType();
            }
        };
    }
}

// 数据源上下文持有器
public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    
    public static void setDataSourceType(String dataSourceType) {
        CONTEXT_HOLDER.set(dataSourceType);
    }
    
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }
    
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

// AOP切面实现读写分离
@Aspect
@Component
public class DataSourceAspect {
    
    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Transactional transactional = method.getAnnotation(Transactional.class);
        
        if (transactional.readOnly()) {
            // 读操作使用从库
            DynamicDataSourceContextHolder.setDataSourceType("slave");
        } else {
            // 写操作使用主库
            DynamicDataSourceContextHolder.setDataSourceType("master");
        }
        
        try {
            return point.proceed();
        } finally {
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}

五、负载均衡和网关集群

5.1 Nginx负载均衡集群

nginx-cluster.conf

nginx

# 上游服务配置
upstream seckill_backend {
    # 一致性哈希,提高缓存命中率
    hash $request_uri consistent;
    
    # 应用服务器节点
    server 192.168.1.201:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.202:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.203:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.204:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.205:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.206:8080 weight=2 max_fails=3 fail_timeout=30s;
    
    # 健康检查
    check interval=5000 rise=2 fall=3 timeout=3000 type=http;
    check_http_send "HEAD /actuator/health HTTP/1.0

";
    check_http_expect_alive http_2xx http_3xx;
}

# 缓存服务负载均衡
upstream redis_cluster {
    server 192.168.1.101:6379;
    server 192.168.1.102:6379;
    server 192.168.1.103:6379;
    server 192.168.1.104:6379;
    server 192.168.1.105:6379;
    server 192.168.1.106:6379;
}

server {
    listen 80;
    server_name seckill.example.com;
    
    # 连接数限制
    limit_conn perip 100;
    limit_conn perserver 10000;
    
    # 请求频率限制
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    
    location / {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://seckill_backend;
        
        # 代理配置
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 超时配置
        proxy_connect_timeout 3s;
        proxy_read_timeout 10s;
        proxy_send_timeout 10s;
        
        # 启用缓存
        proxy_cache seckill_cache;
        proxy_cache_valid 200 10s;
    }
}

5.2 Spring Cloud Gateway集群

yaml

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: seckill-service
          uri: lb://seckill-service
          predicates:
            - Path=/api/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                redis-rate-limiter.requestedTokens: 1
            - name: CircuitBreaker
              args:
                name: seckillService
                fallbackUri: forward:/fallback
            - StripPrefix=1

六、监控和运维

6.1 集群监控配置

Prometheus配置

yaml

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'seckill-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: 
        - '192.168.1.201:8080'
        - '192.168.1.202:8080'
        - '192.168.1.203:8080'
        - '192.168.1.204:8080'
        - '192.168.1.205:8080'
        - '192.168.1.206:8080'
    labels:
      cluster: 'seckill-prod'

  - job_name: 'redis-cluster'
    static_configs:
      - targets:
        - '192.168.1.101:9121'
        - '192.168.1.102:9121'
        - '192.168.1.103:9121'

  - job_name: 'mysql-cluster'
    static_configs:
      - targets:
        - '192.168.1.110:9104'
        - '192.168.1.111:9104'

6.2 应用监控端点

java

@Configuration
@EnablePrometheusMetrics
@EnableScheduling
public class MonitorConfig {
    
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
        return registry -> registry.config().commonTags(
            "application", "seckill-service",
            "cluster", "CLUSTER_A",
            "instance", System.getenv("HOSTNAME")
        );
    }
}

// 自定义业务指标
@Component
public class SeckillMetrics {
    
    private final Counter seckillRequestCounter;
    private final Counter seckillSuccessCounter;
    private final Gauge stockGauge;
    
    public SeckillMetrics(MeterRegistry registry) {
        seckillRequestCounter = Counter.builder("seckill.requests.total")
            .description("Total seckill requests")
            .tag("type", "request")
            .register(registry);
            
        seckillSuccessCounter = Counter.builder("seckill.success.total")
            .description("Total successful seckills")
            .tag("type", "success")
            .register(registry);
    }
    
    public void recordSeckillRequest() {
        seckillRequestCounter.increment();
    }
    
    public void recordSeckillSuccess() {
        seckillSuccessCounter.increment();
    }
}

这样的集群部署方案可以提供:

高可用性:多节点冗余,故障自动转移

弹性扩展:根据负载动态调整节点数量

负载均衡:智能路由,避免单点过载

数据一致性:集群数据同步机制

监控运维:全面的监控和告警体系

分库分表

秒杀系统分库分表方案

一、分库分表架构设计

1.1 整体分片策略

text

应用层 → 分库分表中间件 → 物理数据库集群
                ↓
         订单表: user_id分片
         商品表: goods_id分片  
         用户表: user_id分片

二、ShardingSphere分库分表实现

2.1 Maven依赖配置

xml

<dependencies>
    <!-- ShardingSphere -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        <version>5.3.2</version>
    </dependency>
    
    <!-- 数据源 -->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
    
    <!-- 数据库连接 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

2.2 分库分表配置

application-sharding.yml

yaml

spring:
  shardingsphere:
    # 数据源配置
    datasource:
      names: ds0, ds1, ds2, ds3
      # 数据库0 - 主库
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.110:3306/seckill_0?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: seckill_user
        password: ${DB_PASSWORD}
        connection-timeout: 30000
        idle-timeout: 600000
        max-lifetime: 1800000
        maximum-pool-size: 20
        minimum-idle: 5
      # 数据库1
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.111:3306/seckill_1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: seckill_user
        password: ${DB_PASSWORD}
      # 数据库2
      ds2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.112:3306/seckill_2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: seckill_user
        password: ${DB_PASSWORD}
      # 数据库3
      ds3:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.113:3306/seckill_3?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: seckill_user
        password: ${DB_PASSWORD}
    
    # 分片规则
    rules:
      sharding:
        # 分片算法
        sharding-algorithms:
          # 订单表分库分表算法
          order-database-inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{user_id % 4}
          order-table-inline:
            type: INLINE
            props:
              algorithm-expression: seckill_order_$->{user_id % 16}
          
          # 商品表分表算法
          goods-table-inline:
            type: INLINE
            props:
              algorithm-expression: seckill_goods_$->{goods_id % 8}
          
          # 时间分片算法 - 按月分表
          order-time-inline:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd HH:mm:ss"
              datetime-lower: "2024-01-01 00:00:00"
              datetime-upper: "2024-12-31 23:59:59"
              sharding-suffix-pattern: "yyyyMM"
              datetime-interval-amount: 1
              datetime-interval-unit: "MONTHS"
        
        # 表规则
        tables:
          # 订单表分片规则 - 4库×16表=64张表
          seckill_order:
            actual-data-nodes: ds$->{0..3}.seckill_order_$->{0..15}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order-database-inline
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order-table-inline
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
            
          # 商品表分片规则 - 单库8表
          seckill_goods:
            actual-data-nodes: ds0.seckill_goods_$->{0..7}
            table-strategy:
              standard:
                sharding-column: goods_id
                sharding-algorithm-name: goods-table-inline
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
          
          # 用户表分片规则 - 4库分片
          user_info:
            actual-data-nodes: ds$->{0..3}.user_info
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order-database-inline
            key-generate-strategy:
              column: user_id
              key-generator-name: snowflake
        
        # 绑定表规则 - 优化关联查询
        binding-tables:
          - seckill_order, order_detail
        
        # 广播表 - 小表全库同步
        broadcast-tables:
          - region_info, system_config
    
    # 分布式序列算法
    key-generators:
      snowflake:
        type: SNOWFLAKE
        props:
          worker-id: 123
    
    # 属性配置
    props:
      sql-show: true
      sql-simple: true
      check-table-metadata-enabled: false

# MyBatis配置
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
  global-config:
    db-config:
      id-type: INPUT
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

三、数据库表结构设计

3.1 订单表分片设计

sql

-- 分库分表SQL生成脚本
-- 在每个数据库执行以下建表语句

-- 订单表 - 16张分表
CREATE TABLE `seckill_order_0` (
  `id` bigint(20) NOT NULL COMMENT '订单ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `goods_id` bigint(20) NOT NULL COMMENT '商品ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单号',
  `goods_name` varchar(255) NOT NULL COMMENT '商品名称',
  `quantity` int(11) NOT NULL DEFAULT '1' COMMENT '购买数量',
  `unit_price` decimal(10,2) NOT NULL COMMENT '单价',
  `total_amount` decimal(10,2) NOT NULL COMMENT '总金额',
  `order_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态:0-待支付,1-已支付,2-已取消',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_goods_id` (`goods_id`),
  KEY `idx_create_time` (`create_time`),
  KEY `idx_user_goods` (`user_id`,`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='秒杀订单表';

-- 为每个分表创建相同的结构
-- seckill_order_1 到 seckill_order_15

3.2 商品表分片设计

sql

-- 商品表 - 8张分表
CREATE TABLE `seckill_goods_0` (
  `id` bigint(20) NOT NULL COMMENT '商品ID',
  `goods_name` varchar(255) NOT NULL COMMENT '商品名称',
  `goods_title` varchar(255) NOT NULL COMMENT '商品标题',
  `goods_img` varchar(500) DEFAULT NULL COMMENT '商品图片',
  `goods_detail` text COMMENT '商品详情',
  `goods_price` decimal(10,2) NOT NULL COMMENT '商品价格',
  `seckill_price` decimal(10,2) NOT NULL COMMENT '秒杀价格',
  `stock_count` int(11) NOT NULL COMMENT '库存数量',
  `start_time` datetime NOT NULL COMMENT '开始时间',
  `end_time` datetime NOT NULL COMMENT '结束时间',
  `version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `deleted` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_start_time` (`start_time`),
  KEY `idx_end_time` (`end_time`),
  KEY `idx_time_range` (`start_time`,`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='秒杀商品表';

-- 为每个分表创建相同的结构
-- seckill_goods_1 到 seckill_goods_7

3.3 用户表设计

sql

-- 用户表 - 每个库都有相同的表结构
CREATE TABLE `user_info` (
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `phone` varchar(20) NOT NULL COMMENT '手机号',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `level` tinyint(4) NOT NULL DEFAULT '1' COMMENT '用户等级',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `deleted` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`user_id`),
  UNIQUE KEY `uk_username` (`username`),
  UNIQUE KEY `uk_phone` (`phone`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';

四、Java代码实现

4.1 实体类设计

java

@Data
@TableName("seckill_order")
public class SeckillOrder {
    @TableId(type = IdType.INPUT)
    private Long id;
    
    private Long userId;
    
    private Long goodsId;
    
    private String orderNo;
    
    private String goodsName;
    
    private Integer quantity;
    
    private BigDecimal unitPrice;
    
    private BigDecimal totalAmount;
    
    private Integer orderStatus;
    
    private Date createTime;
    
    private Date updateTime;
    
    private Integer deleted;
}

@Data
@TableName("seckill_goods")
public class SeckillGoods {
    @TableId(type = IdType.INPUT)
    private Long id;
    
    private String goodsName;
    
    private String goodsTitle;
    
    private String goodsImg;
    
    private String goodsDetail;
    
    private BigDecimal goodsPrice;
    
    private BigDecimal seckillPrice;
    
    private Integer stockCount;
    
    private Date startTime;
    
    private Date endTime;
    
    private Integer version;
    
    private Date createTime;
    
    private Date updateTime;
    
    private Integer deleted;
}

4.2 Mapper层实现

java

@Mapper
public interface SeckillOrderMapper extends BaseMapper<SeckillOrder> {
    
    /**
     * 根据用户ID和商品ID查询订单
     * 注意:这里需要指定分片键
     */
    @Select("SELECT * FROM seckill_order WHERE user_id = #{userId} AND goods_id = #{goodsId} AND deleted = 0")
    SeckillOrder selectByUserAndGoods(@Param("userId") Long userId, @Param("goodsId") Long goodsId);
    
    /**
     * 根据用户ID分页查询订单
     */
    @Select("SELECT * FROM seckill_order WHERE user_id = #{userId} AND deleted = 0 ORDER BY create_time DESC")
    List<SeckillOrder> selectByUserId(@Param("userId") Long userId);
    
    /**
     * 根据订单号查询
     */
    @Select("SELECT * FROM seckill_order WHERE order_no = #{orderNo} AND deleted = 0")
    SeckillOrder selectByOrderNo(@Param("orderNo") String orderNo);
}

@Mapper
public interface SeckillGoodsMapper extends BaseMapper<SeckillGoods> {
    
    /**
     * 扣减库存 - 带乐观锁
     */
    @Update("UPDATE seckill_goods SET stock_count = stock_count - 1, version = version + 1 " +
            "WHERE id = #{goodsId} AND stock_count > 0 AND version = #{version}")
    int reduceStockWithVersion(@Param("goodsId") Long goodsId, @Param("version") Integer version);
    
    /**
     * 查询秒杀商品列表
     */
    @Select("<script>" +
            "SELECT * FROM seckill_goods WHERE deleted = 0 " +
            "<if test='startTime != null'> AND start_time &lt;= #{startTime} </if>" +
            "<if test='endTime != null'> AND end_time &gt;= #{endTime} </if>" +
            "ORDER BY start_time DESC" +
            "</script>")
    List<SeckillGoods> selectSeckillGoodsList(@Param("startTime") Date startTime, 
                                             @Param("endTime") Date endTime);
}

4.3 Service层实现

java

@Service
@Slf4j
public class SeckillShardingService {
    
    @Autowired
    private SeckillOrderMapper seckillOrderMapper;
    
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    
    @Autowired
    private DistributedIdGenerator idGenerator;
    
    /**
     * 创建秒杀订单 - 分库分表
     */
    @Transactional(rollbackFor = Exception.class)
    public SeckillResult createSeckillOrder(Long userId, Long goodsId) {
        // 1. 查询商品信息
        SeckillGoods goods = seckillGoodsMapper.selectById(goodsId);
        if (goods == null) {
            return SeckillResult.error("商品不存在");
        }
        
        // 2. 检查库存
        if (goods.getStockCount() <= 0) {
            return SeckillResult.error("商品已售罄");
        }
        
        // 3. 检查重复购买
        SeckillOrder existOrder = seckillOrderMapper.selectByUserAndGoods(userId, goodsId);
        if (existOrder != null) {
            return SeckillResult.error("请勿重复购买");
        }
        
        // 4. 扣减库存
        int updateResult = seckillGoodsMapper.reduceStockWithVersion(goodsId, goods.getVersion());
        if (updateResult <= 0) {
            return SeckillResult.error("库存不足");
        }
        
        // 5. 创建订单
        SeckillOrder order = new SeckillOrder();
        order.setId(idGenerator.nextId());
        order.setUserId(userId);
        order.setGoodsId(goodsId);
        order.setOrderNo(generateOrderNo());
        order.setGoodsName(goods.getGoodsName());
        order.setQuantity(1);
        order.setUnitPrice(goods.getSeckillPrice());
        order.setTotalAmount(goods.getSeckillPrice());
        order.setOrderStatus(0);
        
        int insertResult = seckillOrderMapper.insert(order);
        if (insertResult <= 0) {
            throw new RuntimeException("创建订单失败");
        }
        
        return SeckillResult.success("秒杀成功", order.getOrderNo());
    }
    
    /**
     * 查询用户订单列表 - 支持分页
     */
    public PageResult<SeckillOrder> getUserOrders(Long userId, Integer page, Integer size) {
        Page<SeckillOrder> pageHelper = new Page<>(page, size);
        
        // 这里会自动路由到正确的分片
        LambdaQueryWrapper<SeckillOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SeckillOrder::getUserId, userId)
                   .eq(SeckillOrder::getDeleted, 0)
                   .orderByDesc(SeckillOrder::getCreateTime);
        
        IPage<SeckillOrder> orderPage = seckillOrderMapper.selectPage(pageHelper, queryWrapper);
        
        return new PageResult<>(
            orderPage.getRecords(),
            orderPage.getTotal(),
            page,
            size
        );
    }
    
    /**
     * 批量查询订单 - 使用Hint强制路由
     */
    public List<SeckillOrder> batchGetOrders(List<Long> orderIds) {
        // 对于跨分片的查询,需要分别查询每个分片
        List<SeckillOrder> result = new ArrayList<>();
        
        for (Long orderId : orderIds) {
            // 这里需要根据orderId计算出分片信息
            // 实际项目中可以使用ShardingSphere的HintManager
            SeckillOrder order = seckillOrderMapper.selectById(orderId);
            if (order != null) {
                result.add(order);
            }
        }
        
        return result;
    }
    
    private String generateOrderNo() {
        return "SO" + System.currentTimeMillis() + RandomUtil.randomNumbers(6);
    }
}

4.4 分布式ID生成器

java

@Component
public class DistributedIdGenerator {
    
    /**
     * 雪花算法ID生成
     */
    public long nextId() {
        // 使用ShardingSphere的雪花算法
        // 或者自定义实现
        return IdWorker.getId();
    }
    
    /**
     * 根据用户ID计算分片信息
     */
    public ShardInfo calculateShardInfo(Long userId) {
        int databaseShard = (int) (userId % 4);
        int tableShard = (int) (userId % 16);
        
        return new ShardInfo(databaseShard, tableShard);
    }
    
    @Data
    @AllArgsConstructor
    public static class ShardInfo {
        private int databaseShard;
        private int tableShard;
    }
}

五、数据迁移方案

5.1 历史数据迁移脚本

java

@Component
@Slf4j
public class DataMigrationService {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private DistributedIdGenerator idGenerator;
    
    /**
     * 订单数据迁移
     */
    @Async
    public void migrateOrderData() {
        log.info("开始迁移订单数据...");
        
        // 1. 从原单表查询数据
        String sourceSql = "SELECT * FROM seckill_order_old WHERE deleted = 0";
        
        try (Connection sourceConn = getSourceConnection();
             PreparedStatement pstmt = sourceConn.prepareStatement(sourceSql);
             ResultSet rs = pstmt.executeQuery()) {
            
            int batchCount = 0;
            int totalCount = 0;
            
            // 2. 批量插入到分片表
            while (rs.next()) {
                SeckillOrder order = convertToOrder(rs);
                
                // 根据用户ID路由到正确的分片
                insertToShardingTable(order);
                
                batchCount++;
                totalCount++;
                
                // 批量提交
                if (batchCount >= 1000) {
                    log.info("已迁移 {} 条订单数据", totalCount);
                    batchCount = 0;
                }
            }
            
            log.info("订单数据迁移完成,总计: {} 条", totalCount);
            
        } catch (Exception e) {
            log.error("订单数据迁移失败", e);
            throw new RuntimeException("数据迁移失败", e);
        }
    }
    
    private SeckillOrder convertToOrder(ResultSet rs) throws SQLException {
        SeckillOrder order = new SeckillOrder();
        order.setId(rs.getLong("id"));
        order.setUserId(rs.getLong("user_id"));
        order.setGoodsId(rs.getLong("goods_id"));
        order.setOrderNo(rs.getString("order_no"));
        // ... 其他字段赋值
        return order;
    }
    
    private void insertToShardingTable(SeckillOrder order) {
        // 使用ShardingSphere JDBC插入,会自动路由到正确的分片
        seckillOrderMapper.insert(order);
    }
}

5.2 数据校验工具

java

@Component
public class DataValidator {
    
    /**
     * 校验分片数据一致性
     */
    public boolean validateDataConsistency() {
        // 校验总数据量
        long sourceCount = getSourceDataCount();
        long shardingCount = getShardingDataCount();
        
        if (sourceCount != shardingCount) {
            log.error("数据量不一致: 源表={}, 分片表={}", sourceCount, shardingCount);
            return false;
        }
        
        // 抽样校验数据准确性
        return validateSampleData();
    }
    
    private boolean validateSampleData() {
        // 随机抽样1000条数据校验
        List<Long> sampleIds = getSampleOrderIds();
        
        for (Long orderId : sampleIds) {
            SeckillOrder sourceOrder = getSourceOrder(orderId);
            SeckillOrder shardingOrder = seckillOrderMapper.selectById(orderId);
            
            if (!compareOrders(sourceOrder, shardingOrder)) {
                log.error("数据不一致, orderId: {}", orderId);
                return false;
            }
        }
        
        return true;
    }
}

六、监控和运维

6.1 分片监控配置

yaml

spring:
  shardingsphere:
    # 监控配置
    metrics:
      enabled: true
      name: prometheus
    # 日志配置
    log:
      slow-sql-millis: 1000

6.2 自定义分片监控

java

@Component
public class ShardingMonitor {
    
    private final MeterRegistry meterRegistry;
    private final Counter slowSqlCounter;
    private final Gauge tableSizeGauge;
    
    public ShardingMonitor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        
        this.slowSqlCounter = Counter.builder("sharding.sql.slow")
            .description("Slow SQL queries in sharding tables")
            .register(meterRegistry);
            
        // 监控分表数据量
        this.tableSizeGauge = Gauge.builder("sharding.table.size")
            .description("Sharding table data size")
            .tag("table", "seckill_order")
            .register(meterRegistry);
    }
    
    /**
     * 记录慢查询
     */
    public void recordSlowQuery(String sql, long executionTime) {
        slowSqlCounter.increment();
        log.warn("慢SQL查询: {}, 执行时间: {}ms", sql, executionTime);
    }
    
    /**
     * 获取分表数据量统计
     */
    public Map<String, Long> getTableStatistics() {
        Map<String, Long> statistics = new HashMap<>();
        
        for (int i = 0; i < 16; i++) {
            String tableName = "seckill_order_" + i;
            long count = getTableCount(tableName);
            statistics.put(tableName, count);
        }
        
        return statistics;
    }
}

这样的分库分表方案能够:

水平扩展:支持海量数据存储

高性能:分散读写压力

高可用:多库冗余,故障隔离

易维护:标准化的分片策略和迁移方案

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...