20行try-except废掉重试机制?异常处理陷阱与重构指南
2026/11/3 17:50:40
票务预约系统的核心在于处理高并发请求和保证数据一致性。一个典型的系统架构包含以下层次:
关键设计原则:系统需要遵循CAP理论中的CP特性(一致性和分区容错性),在分布式环境下确保票务数据的准确性和系统可用性。
票务系统的核心数据模型包含以下主要实体:
CREATE TABLE events ( event_id INT PRIMARY KEY AUTO_INCREMENT, event_name VARCHAR(100) NOT NULL, start_time DATETIME NOT NULL, end_time DATETIME NOT NULL, venue_id INT NOT NULL, total_seats INT NOT NULL, available_seats INT NOT NULL, price DECIMAL(10,2) NOT NULL ); CREATE TABLE orders ( order_id VARCHAR(32) PRIMARY KEY, user_id INT NOT NULL, event_id INT NOT NULL, seat_count INT NOT NULL, order_time DATETIME NOT NULL, status TINYINT NOT NULL COMMENT '0-待支付 1-已支付 2-已取消', FOREIGN KEY (event_id) REFERENCES events(event_id) );实际项目中还需要考虑:
| 方案 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 悲观锁 | SELECT FOR UPDATE | 强一致性 | 性能差,易死锁 |
| 乐观锁 | 版本号控制 | 并发度高 | 需处理重试逻辑 |
| Redis原子操作 | DECR/LUA脚本 | 性能最佳 | 需维护缓存一致性 |
推荐采用Redis+LUA脚本实现原子扣减:
-- ticket_reserve.lua local key = KEYS[1] local requested = tonumber(ARGV[1]) local remaining = tonumber(redis.call('GET', key)) if remaining >= requested then redis.call('DECRBY', key, requested) return 1 -- 成功 else return 0 -- 库存不足 end使用Redisson实现分布式锁:
RLock lock = redissonClient.getLock("event_lock_" + eventId); try { boolean acquired = lock.tryLock(3, 10, TimeUnit.SECONDS); if (acquired) { // 执行库存扣减和订单创建 } } finally { lock.unlock(); }# MySQL每日全量备份 mysqldump -u root -p ticket_db | gzip > /backup/ticket_$(date +%F).sql.gz # Redis RDB+AOF持久化 save 900 1 save 300 10 save 60 10000 appendonly yesspring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.idle-timeout=30000java -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 ...防刷机制:
数据安全:
支付安全:
// 支付签名验证示例 String sign = MD5Util.md5(orderId + amount + timestamp + SECRET_KEY); if (!sign.equals(requestSign)) { throw new SecurityException("签名验证失败"); }# prometheus.yml 配置示例 scrape_configs: - job_name: 'ticket-service' metrics_path: '/actuator/prometheus' static_configs: - targets: ['service1:8080', 'service2:8080']关键监控项:
# Logstash配置示例 input { file { path => "/var/log/ticket/*.log" start_position => "beginning" } } filter { grok { match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:msg}" } } } output { elasticsearch { hosts => ["elasticsearch:9200"] index => "ticket-logs-%{+YYYY.MM.dd}" } }FROM openjdk:11-jre COPY target/ticket-service.jar /app/ EXPOSE 8080 ENTRYPOINT ["java","-jar","/app/ticket-service.jar"]apiVersion: apps/v1 kind: Deployment metadata: name: ticket-service spec: replicas: 3 selector: matchLabels: app: ticket template: spec: containers: - name: ticket image: registry.example.com/ticket:1.0.0 resources: limits: cpu: "2" memory: 2Gi# 使用Nginx实现金丝雀发布 split_clients $remote_addr $canary { 5% canary; 95% production; }在真实项目部署中,我们还需要考虑: