Backend Development 7 min read

Preventing Product Overselling in High‑Concurrency E‑Commerce Systems

To prevent overselling during flash sales, the article explains how non‑atomic database updates cause negative stock and presents solutions such as optimistic DB locking, Redis Lua atomic deductions, Redisson distributed locks, transactional message queues, and pre‑deduction with rate limiting, recommending a combined approach that achieved 120 000 QPS with zero oversell.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Preventing Product Overselling in High‑Concurrency E‑Commerce Systems

Introduction

During a flash‑sale the distributed system was overwhelmed, causing the stock count to become negative and leading to overselling.

Why overselling happens

Database non‑atomic check‑update

Concurrent requests read the same stock value (>0) and then update it, so the check and update are not atomic.

Root cause: the query and update are separate statements, allowing multiple transactions to pass the stock>0 condition.

Solutions

1. Database optimistic lock

Use a version column to ensure only one transaction updates the row.

public boolean deductStock(Long productId) {
    Product product = productDao.selectForUpdate(productId);
    if (product.getStock() <= 0) return false;
    int affected = productDao.updateWithVersion(
        productId,
        product.getVersion(),
        product.getStock() - 1
    );
    return affected > 0;
}

Advantages: no extra middleware; simple to implement.

Disadvantages: high DB load under heavy concurrency; possible many update failures.

2. Redis atomic operation

Execute stock deduction in a Lua script to guarantee atomicity.

String lua = "if redis.call('get', KEYS[1]) >= ARGV[1] then " +
             "return redis.call('decrby', KEYS[1], ARGV[1]) " +
             "else return -1 end";

public boolean preDeduct(String itemId, int count) {
    RedisScript
script = new DefaultRedisScript<>(lua, Long.class);
    Long result = redisTemplate.execute(script, Collections.singletonList(itemId), count);
    return result != null && result >= 0;
}

Performance: single‑node QPS 500 (DB) vs 80 000 (Redis).

3. Distributed lock (Redisson)

Lock at the product level to serialize critical sections.

RLock lock = redisson.getLock("stock_lock:" + productId);
try {
    if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
        // execute stock operation
    }
} finally {
    lock.unlock();
}

4. Message‑queue peak‑shaving (RocketMQ)

Use transactional messages to decouple stock deduction from the request path.

TransactionMQProducer producer = new TransactionMQProducer("stock_group");
producer.setExecutor(new TransactionListener() {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg) {
        // deduct DB stock
        return LocalTransactionState.COMMIT_MESSAGE;
    }
});

5. Pre‑deduct stock

Rate‑limit requests and write a pre‑deduction record before the final DB update.

RateLimiter limiter = RateLimiter.create(1000); // 1000 tokens per second

public boolean preDeduct(Long itemId) {
    if (!limiter.tryAcquire()) return false;
    preStockDao.insert(itemId, userId);
    return true;
}

Common pitfalls

Cache‑DB inconsistency

Deleting the cache before updating the DB creates a race window.

redisTemplate.delete("stock:" + productId);
productDao.updateStock(productId, newStock); // concurrency window

Missing stock rollback

Canceling an order must roll back stock within the same transaction.

@Transactional
public void cancelOrder(Order order) {
    stockDao.restock(order.getItemId(), order.getCount());
    orderDao.delete(order.getId());
}

Lock granularity too coarse

Global locks cause unnecessary request rejection.

RLock globalLock = redisson.getLock("global_stock_lock");

Conclusion

In practice, a combination of Redis as the first line, distributed lock for critical paths, and pre‑deduction with a message queue yields the most reliable solution. Real‑world tests during a major sale showed 120 000 QPS with zero overselling.

RedisMessage QueueDistributed Lockinventory managementoptimistic lockoverselling
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

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.