秒杀系统的核心挑战在于应对瞬时高并发流量,同时保证数据一致性和系统可用性。我们采用分层架构设计,将系统划分为四个关键层级:
小程序端作为用户入口,承担着流量控制和用户体验优化的重任。我们实现了以下关键功能:
javascript复制// 小程序端倒计时实现
updateCountdowns() {
const { activities, countdowns, currentTime } = this.data
const updatedCountdowns = { ...countdowns }
activities.forEach(activity => {
const startTime = new Date(activity.start_time).getTime()
const endTime = new Date(activity.end_time).getTime()
if (currentTime < startTime) {
updatedCountdowns[activity.id] = {
status: 'not_started',
time: startTime - currentTime
}
} else if (currentTime >= startTime && currentTime < endTime) {
updatedCountdowns[activity.id] = {
status: 'in_progress',
time: endTime - currentTime
}
} else {
updatedCountdowns[activity.id] = {
status: 'ended',
time: 0
}
}
})
this.setData({ countdowns: updatedCountdowns })
}
服务端采用微服务架构,核心模块包括:
关键设计原则:无状态服务设计,所有状态数据集中存储在Redis集群,确保服务实例可水平扩展
秒杀业务的核心流程包含以下关键步骤:
javascript复制// 秒杀核心服务实现
async executeSeckill(userId, activityId, quantity = 1) {
// 1. 参数校验
if (!userId || !activityId) throw new Error('参数错误')
// 2. 验证活动状态
const activity = await this.getActivityInfo(activityId)
if (!activity || activity.status !== 1) throw new Error('活动未开始或已结束')
// 3. 用户限购检查
const userPurchased = await redisManager.cluster.get(
`${redisManager.KEY_PREFIX.USER_LIMIT}${activityId}:${userId}`
)
if (userPurchased && parseInt(userPurchased) >= activity.limit_per_user) {
throw new Error('已达到限购数量')
}
// 4. 预扣库存
const stockResult = await redisManager.preDeductStock(activityId, quantity)
if (stockResult.err) throw new Error(stockResult.err)
// 5. 生成订单令牌
const orderToken = this.generateOrderToken(userId, activityId)
await redisManager.cluster.setex(
`${redisManager.KEY_PREFIX.ORDER_TOKEN}${orderToken}`,
300, // 5分钟有效期
JSON.stringify({ userId, activityId, quantity })
)
// 6. 记录用户购买
await redisManager.cluster.incrby(
`${redisManager.KEY_PREFIX.USER_LIMIT}${activityId}:${userId}`,
quantity
)
// 7. 异步创建订单
await this.seckillQueue.pushOrderTask({
orderToken, userId, activityId, quantity
})
return { success: true, orderToken }
}
库存管理是秒杀系统的核心难点,我们采用三级库存控制:
javascript复制// Redis库存预扣减Lua脚本
const preDeductScript = `
local stockKey = KEYS[1]
local availableKey = KEYS[2]
local quantity = tonumber(ARGV[1])
local available = tonumber(redis.call('GET', availableKey))
if not available or available < quantity then
return {err = '库存不足', available = 0}
end
redis.call('DECRBY', availableKey, quantity)
return {ok = '预扣成功', available = available - quantity}
`
分布式锁用于解决集群环境下的并发控制问题,关键特性包括:
javascript复制class RedisLock {
async acquire(lockKey, ttl = 10000) {
const lockValue = `${process.pid}:${Date.now()}`
const result = await this.redis.set(
lockKey, lockValue, 'PX', ttl, 'NX'
)
if (result === 'OK') return true
// 检查锁是否已过期但未被释放
const currentLock = await this.redis.get(lockKey)
if (!currentLock) return false
// 避免死锁,检查锁是否过期
const lockTimestamp = parseInt(currentLock.split(':')[1])
if (Date.now() > lockTimestamp + ttl) {
await this.release(lockKey)
return false
}
return false
}
}
消息队列实现秒杀请求的异步化处理,核心功能包括:
javascript复制class SeckillQueue {
async processWorker(processor, workerId) {
while (true) {
const taskData = await this.popOrderTask()
if (!taskData) {
await this.sleep(100)
continue
}
try {
const result = await processor(taskData)
if (result.success) {
await this.redis.lrem(this.processingKey, 0, taskData)
} else {
await this.handleFailedTask(taskData, result.error)
}
} catch (error) {
await this.handleFailedTask(taskData, error.message)
}
}
}
}
库存管理中的异常处理策略:
支付环节的异常处理机制:
javascript复制// 支付倒计时实现
startPaymentCountdown() {
this.setData({
timer: setInterval(() => {
let { countdown } = this.data
if (countdown <= 0) {
clearInterval(this.data.timer)
wx.showModal({
title: '提示',
content: '支付超时,订单已取消',
showCancel: false,
success: () => wx.navigateBack()
})
return
}
this.setData({ countdown: countdown - 1 })
}, 1000)
})
}
sql复制-- 订单表分表设计示例
CREATE TABLE `seckill_order_0` (
`id` varchar(32) NOT NULL COMMENT '订单号',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`activity_id` bigint(20) NOT NULL COMMENT '活动ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_activity` (`user_id`,`activity_id`),
KEY `idx_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
在实际部署中,我们通过压力测试验证系统性能,在4核8G的服务器配置下,单节点可支撑约5000 QPS的秒杀请求,整个集群可轻松应对10万级并发。关键是要确保Redis集群和数据库的性能瓶颈得到合理解决。