在互聯網電商平臺上,秒殺活動往往會吸引大量用戶同時搶購,如何高效地處理高并發請求,保證用戶體驗,是一個重要的技術挑戰。本文將介紹如何基于 Redis 實現秒殺資格的判斷,提高并發性能。
基本思路
秒殺活動的核心流程可以歸納為以下幾個步驟:
- 檢查庫存是否足夠。
- 判斷用戶是否已經參加過此次秒殺。
- 扣減庫存。
- 記錄用戶的秒殺信息。
- 返回訂單 ID。
為了保證高并發情況下的執行效率和數據一致性,我們采用 Redis 來處理這些操作,并利用 Lua 腳本保證操作的原子性。
Lua 腳本實現
下面是一段 Lua 腳本,它能夠在 Redis 中執行上述的秒殺操作:
--- 秒殺資格判斷 Lua 腳本
--- 1. 參數列表
local voucherId = ARGV[1] -- 優惠券 ID
local userId = ARGV[2] -- 用戶 ID--- 2. 數據 key
local stockKey = 'seckill:stock:' .. voucherId -- 庫存 key
local orderKey = 'seckill:order:' .. voucherId -- 訂單 key--- 3. 腳本邏輯
--- 3.1. 判斷庫存是否足夠
if (tonumber(redis.call('get', stockKey)) <= 0) thenreturn 1
end
--- 3.2. 判斷用戶是否已經參與過秒殺
if (redis.call('sismember', orderKey, userId) == 1) thenreturn 2
end
--- 3.3. 扣減庫存
redis.call('incrby', stockKey, -1)
--- 3.4. 記錄用戶秒殺信息
redis.call('sadd', orderKey, userId)
return 0
Java 代碼實現
接下來展示如何在 Java 中調用上述 Lua 腳本,并處理返回結果:
@Override
public Result seckillVoucher(Long voucherId) {// 獲取當前用戶 IDLong userId = UserHolder.getUser().getId();// 1. 執行 Lua 腳本Long result = stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(), userId.toString());// 2. 判斷結果int r = result.intValue();if (r != 0) {// 2.1. 如果結果不為 0,說明沒有購買資格return Result.fail(r == 1 ? "庫存不足" : "不能重復下單");}// 2.2. 如果結果為 0,說明有購買資格,生成訂單 ID 并保存到阻塞隊列中long orderId = redisIdWorker.nextId("order");// TODO: 將訂單信息保存到數據庫或消息隊列中// 3. 返回訂單 IDreturn Result.ok(orderId);
}
代碼詳解
-
Lua 腳本部分:
- 定義兩個參數:優惠券 ID 和用戶 ID。
- 使用 Redis 的
get
命令獲取當前庫存,判斷庫存是否足夠。 - 使用 Redis 的
sismember
命令判斷用戶是否已經參與過秒殺活動。 - 如果庫存足夠且用戶沒有參與過,則使用
incrby
命令扣減庫存,使用sadd
命令記錄用戶信息。
-
Java 代碼部分:
- 獲取當前用戶 ID。
- 調用
stringRedisTemplate.execute
方法執行 Lua 腳本,并傳遞參數。 - 根據 Lua 腳本返回的結果判斷用戶是否有秒殺資格。
- 如果用戶有秒殺資格,則生成訂單 ID,并將訂單信息保存到數據庫或消息隊列中。
- 返回訂單 ID。
對比:
圖1是從數據庫查,圖2是基于Redis,可見平均值提高了很多。
總結
通過使用 Redis 和 Lua 腳本,可以高效地處理秒殺活動中的高并發請求,確保數據的準確性和一致性。這種方法不僅提高了系統的性能,還保證了用戶的秒殺體驗。希望本文對你理解和實現秒殺系統有所幫助。