Designing a Scalable Coupon System for Rental Platforms: Architecture & Implementation
This article details the end‑to‑end design of a high‑performance coupon system for a rental platform, covering business flow, database schema, micro‑service interactions, caching strategies, state‑machine handling, and practical solutions for concurrency and consistency challenges.
Background
The platform is a rental listing service that provides merchants with listing and C‑side exposure capabilities. It needs a coupon system to support marketing activities during holidays, where merchants bind coupons to listings to offer rental discounts and attract tenants.
1. Business Flow
The overall process is illustrated in the diagram below. Merchants create activities on the B‑side, sign up for discount levels, and generate corresponding coupons. Listings are then bound to coupons, marking them with a coupon flag. On the C‑side, users can filter discounted listings and claim coupons. After claiming, users contact the merchant for on‑site inspection, negotiate, and finalize the lease using the coupon for price reduction.
2. Technical Design
The system is broken down into several modules, each with its own data model and processing logic.
2.1 Activity Management
2.1.1 Data Table
CREATE TABLE `t_activity` (
`activeId` bigint(20) NOT NULL COMMENT '活动ID',
`title` varchar(256) NOT NULL COMMENT '活动名称',
`applyStartTime` timestamp NULL DEFAULT NULL COMMENT '报名开始时间',
`applyEndTime` timestamp NULL DEFAULT NULL COMMENT '报名停止时间',
`activityStartTime` timestamp NULL DEFAULT NULL COMMENT '活动开始时间',
`activityEndTime` timestamp NULL DEFAULT NULL COMMENT '活动结束时间',
`cityIds` varchar(256) NOT NULL COMMENT '覆盖城市,多个逗号分隔',
`couponType` tinyint(4) NOT NULL DEFAULT '0' COMMENT '优惠类型,1 直减;2 折扣;3 免费住N天;4 免押金;5 特价房',
`lowerLimit` int NOT NULL DEFAULT 0 COMMENT '优惠数值下限',
`upperLimit` int NOT NULL DEFAULT 0 COMMENT '优惠数值上限',
`description` text COMMENT '活动描述',
`cubeType` smallint(6) NOT NULL DEFAULT '1001' COMMENT '活动类型',
`foreignId` bigint(20) NOT NULL DEFAULT '0' COMMENT '外部ID',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '活动状态',
`createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`recordStatus` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据状态',
PRIMARY KEY (`activeId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='活动信息表';2.1.2 Data Retrieval
Merchants query activities using the status and cityIds fields. The C‑side reads activity data through a proxy‑pattern cache layer that pushes active data into Redis.
2.1.3 Activity State Transition
A crontab job runs every minute to check time windows and update the status field, driving the activity lifecycle.
2.1.4 Caching Strategy
Read‑heavy queries are served from cache; if data is missing or requires real‑time freshness, the request falls back to the database.
2.2 Merchant Coupon Creation
2.2.1 Data Table
CREATE TABLE `t_couponmeta` (
`couponMetaId` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '券id',
`appId` int(11) NOT NULL DEFAULT '1' COMMENT '区分建立来源',
`activeId` bigint(20) NOT NULL DEFAULT '0' COMMENT '活动ID',
`companyId` bigint(20) NOT NULL COMMENT '公司编号',
`cityId` int(11) NOT NULL COMMENT '城市id',
`companyName` varchar(255) DEFAULT NULL COMMENT '公司名称',
`companyShortName` varchar(255) DEFAULT NULL COMMENT '公司简称',
`couponType` tinyint(4) NOT NULL COMMENT '优惠券类型',
`title` varchar(256) NOT NULL COMMENT '优惠券名称',
`directDiscount` int(11) NOT NULL DEFAULT '0' COMMENT '直减券优惠力度',
`discount` int(11) NOT NULL DEFAULT '0' COMMENT '折扣力度',
`freeLive` int(11) NOT NULL DEFAULT '0' COMMENT '免费住n天券',
`threshold` varchar(256) NOT NULL COMMENT '使用门槛',
`deduction` tinyint(4) NOT NULL DEFAULT '1' COMMENT '抵扣说明 1首月抵扣,2 平摊到月',
`totalAmount` int(11) NOT NULL DEFAULT '0' COMMENT '券总数',
`applyAmount` int(11) NOT NULL DEFAULT '0' COMMENT '已领取总数',
`activityStartTime` timestamp NULL DEFAULT NULL COMMENT '活动开始时间',
`activityEndTime` timestamp NULL DEFAULT NULL COMMENT '活动结束时间',
`startTime` timestamp NULL DEFAULT NULL COMMENT '券使用开始时间',
`expireTime` timestamp NULL DEFAULT NULL COMMENT '券使用结束时间',
`status` int(11) NOT NULL DEFAULT '10' COMMENT '10:新建未启用,20:已启用,30:过期, 40 已结束 50 已中止',
`expireType` tinyint(4) NOT NULL DEFAULT '1' COMMENT '类型:1固定有效期类型,2浮动有效期类型',
`validPeriod` tinyint(4) NOT NULL DEFAULT '0' COMMENT '浮动有效期(单位:天)',
`tenantRange` tinyint(1) NOT NULL DEFAULT '1' COMMENT '租客范围枚举值',
`customScope` varchar(256) NOT NULL DEFAULT '' COMMENT '自定义租客范围',
`comment` varchar(50) DEFAULT NULL COMMENT '备注',
`cubeType` smallint(6) NOT NULL DEFAULT '1001' COMMENT '活动类型',
`updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
`createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`recordStatus` tinyint(4) DEFAULT '0' COMMENT '状态 0默认 -1删除',
PRIMARY KEY (`couponMetaId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券表';2.2.2 Data Retrieval
Both B‑side and C‑side use a proxy‑pattern cache layer to serve high‑frequency reads, reducing database pressure.
2.2.3 State Transition
Coupon states follow the activity state. When an activity changes, a message is sent via MQ to asynchronously update related coupon states.
2.3 Coupon Binding to Listings
2.3.1 Data Table
CREATE TABLE `t_bindcoupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`couponMetaId` int(11) NOT NULL COMMENT '券id',
`companyId` bigint(20) NOT NULL COMMENT '公司编号',
`activityStatus` tinyint(4) NOT NULL COMMENT '状态 0 准备中 1 活动中 2 活动结束 券未失效 3 活动结束券失效',
`houseId` bigint(20) NOT NULL DEFAULT '0' COMMENT '房源id',
`recordStatus` tinyint(4) NOT NULL COMMENT '数据状态 0 有效,-1 失效',
`createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_companyId_couponMetaId` (`companyId`,`couponMetaId`),
KEY `idx_planeId` (`planeId`),
KEY `idx_houseid_activitystatus` (`houseId`,`activityStatus`)
) ENGINE=InnoDB AUTO_INCREMENT=17379 DEFAULT CHARSET=utf8 COMMENT='优惠券绑定范围表';The table records the many‑to‑many relationship between listings and coupons and limits the number of active coupons per listing. Distributed locks are used during binding to avoid concurrency issues.
2.4 C‑Side Coupon Claim
The claim process must prevent over‑claiming. It consists of three steps: request validation, Redis stock decrement, and a transactional MySQL record insertion.
2.4.1 Data Reading
Activity info, coupon info, and stock are all cached in Redis. A short‑lived local cache further reduces Redis latency.
2.4.2 Validation
Redis stores a Bloom filter keyed by coupon ID to quickly check if a user has already claimed. Coupon status is also validated from Redis. If stock data is missing, an async task re‑initializes it.
2.4.3 Stock Decrement
Redis performs atomic stock decrement; the DB update is off‑loaded to an asynchronous message queue to avoid locking hotspots. Edge cases such as Redis success but DB failure, Redis outage, or master‑slave inconsistency are handled with compensating tasks or alerts.
2.5 Coupon Redemption
A micro‑service provides APIs for coupon acquisition and state changes. When an order is placed, the coupon is locked; upon order completion it is marked used, otherwise it reverts to unused.
Future Optimization Directions
Shard user coupon data across databases to improve read/write throughput.
Deploy Redis as a sharded cluster for higher availability and capacity.
Split service clusters so each handles a subset of coupons, routing via a gateway.
Introduce jd‑hotkey to sync hot keys to local caches, reducing remote cache hits.
Use Canal to capture binlog changes, sync them to cache, and trigger downstream updates.
Conclusion
The coupon system is now complete, following the principle of "cache for read‑heavy, queue for write‑heavy". Multi‑level caching, proxy pattern, state and observer patterns, MQ‑driven async updates, and Redis‑based atomic stock handling together ensure consistency and scalability.
If you have better solutions, please share.
Source: https://juejin.cn/post/7160643319612047367
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
