Backend Development 11 min read

Design and Implementation of an E‑commerce Coupon System

The article details a high‑concurrency e‑commerce coupon system that separates creation and distribution, uses Redis‑Lua atomic stock deduction with asynchronous replenishment and coupon generation, employs a lightweight distributed‑transaction table, and adds bucket and batch optimizations to safely handle tens of thousands of TPS.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
Design and Implementation of an E‑commerce Coupon System

Coupons are a common marketing tool in e‑commerce platforms. The article introduces the design and implementation of a coupon system that focuses on the creation and distribution capabilities, which are two of the four core functions (creation, distribution, usage, statistics).

Background : During large promotional periods, many e‑commerce platforms configure coupons of various categories and price ranges to attract users. The coupon system must support high‑concurrency scenarios and ensure inventory safety.

What is a coupon system? It consists of two main concepts: a *coupon batch* (a template for generating identical coupons) and a *coupon* (an individual instance generated from a batch, N:1 relationship).

Batch table core fields include batch ID, coupon name, type, stock quantity, discount rules, effective rules, daily/user limits, and usage rules.

Coupon table core fields include coupon ID (globally unique), batch ID, user ID, status, and context information.

Distribution challenges :

Stock management – preventing overselling while guaranteeing inventory safety.

Complex traffic spikes – supporting high‑concurrency and instantaneous traffic bursts.

The initial approach of using a relational database for stock deduction caused performance bottlenecks and crashes under high load. Simple caching introduced consistency problems (cache penetration, breakdown, avalanche).

Final solution :

Use Redis + Lua for atomic stock deduction, ensuring high‑concurrency safety.

Asynchronously replenish stock from the database to Redis when remaining stock falls below a threshold (M), adding N units each time.

Generate coupons asynchronously after successful stock deduction, avoiding synchronous database writes.

Handle distributed transaction issues with a local transaction table and best‑effort notification.

Key Lua script (kept unchanged):

--批次的HashKey
local stockKey = KEYS[1];
--Argv 参数
local stockId = ARGV[1];
local couponId = ARGV[2];
local uid = ARGV[3];
--该批次当天最大发放量
local maxByDay = ARGV[4];
-- 每人限领
local maxByUser = ARGV[5];
--当前时间Str
local crtDateStr = ARGV[6];
-- 每人每日限领
local dailyMaxByUser = ARGV[7];
stockId = tonumber(stockId);
maxByUser = tonumber(maxByUser);
maxByDay = tonumber(maxByDay);
dailyMaxByUser = tonumber(dailyMaxByUser);
if not stockKey then return '-4' end
if not stockId or not couponId or not uid or not maxByUser or not maxByDay or not crtDateStr or not dailyMaxByUser then return '-5' end
local leftAmountField = 'left_amount';
local res = redis.call('HMGET', stockKey, leftAmountField, crtDateStr);
local leftAmount = tonumber(res[1]);
local crtDispatchAmount = tonumber(res[2]);
local couponIdSetKey = stockKey .. ':coupon:zset';
local score = redis.call('ZSCORE', couponIdSetKey, couponId);
if score then return '-6' end
if not leftAmount or leftAmount <= 0 then return '-3' end
if crtDispatchAmount and crtDispatchAmount >= maxByDay then return '-1' end
local dailyUserAcquireNumKey = stockKey .. ':user:acquire:' .. crtDateStr;
if dailyMaxByUser > 0 then
local dailyUserAcquireNum = redis.call('HGET', dailyUserAcquireNumKey, uid);
dailyUserAcquireNum = tonumber(dailyUserAcquireNum);
if dailyUserAcquireNum and dailyUserAcquireNum >= dailyMaxByUser then return '-7' end
end
local userAcquireNumKey = stockKey .. ':user:acquire';
local usrAcquireNum = redis.call('HGET', userAcquireNumKey, uid);
usrAcquireNum = tonumber(usrAcquireNum);
if usrAcquireNum and usrAcquireNum >= maxByUser then return '-2' end
local leftAmountAfterOp = redis.call('HINCRBY', stockKey, leftAmountField, -1);
local crtDispatchAmountAfterOp = redis.call('HINCRBY', stockKey, crtDateStr, 1);
local usrAcquireNumAfterOp = redis.call('HINCRBY', userAcquireNumKey, uid, 1);
local dailyUserAcquireNumAfterOp = redis.call('HINCRBY', dailyUserAcquireNumKey, uid, 1);
redis.call('ZADD', couponIdSetKey, uid, couponId);
return '0|' .. leftAmountAfterOp .. '|' .. crtDispatchAmountAfterOp .. '|' .. usrAcquireNumAfterOp .. '|' .. dailyUserAcquireNumAfterOp

Additional optimization techniques discussed:

Resource bucket : Split stock into multiple buckets to reduce contention.

Aggregate deduction : Batch same‑batch requests and deduct stock in bulk.

Hotspot update : Use database‑level hotspot mitigation (refer to Tencent Cloud docs).

Performance testing shows the system can handle 10,000 TPS and can be horizontally scaled further.

Overall, the design combines Redis‑Lua atomic operations, asynchronous stock replenishment, and a lightweight distributed‑transaction mechanism to achieve high‑concurrency coupon distribution while maintaining data consistency.

e-commerceBackend Developmenthigh concurrencydistributed transactioncoupon systemRedis Lua
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.