Generating Trillions of Unique Order IDs Without Collisions

The article analyzes why simple auto‑increment or timestamp‑based IDs fail at massive scales, compares common distributed ID schemes, and presents an improved Snowflake‑plus‑segment hybrid solution with clock‑rollback protection, automatic machine/room allocation, and production‑grade safeguards for trillion‑level order processing.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Generating Trillions of Unique Order IDs Without Collisions

Core evaluation criteria for a distributed ID

Global uniqueness – no duplicate IDs across machines, rooms, shards, or services.

Monotonic order – IDs increase over time to support range queries and sharding.

High performance – generate >100 k IDs per second without DB or Redis bottlenecks.

Security – IDs must not expose total order volume.

High availability – continue generating IDs during clock rollback, DB/Redis failures.

Capacity – support trillion‑level orders without bit overflow.

Distributed compatibility – seamless expansion across data‑centers without manual reconfiguration.

Survey of existing solutions

UUID/GUID

Implementation: 36‑character string composed of MAC address, random number and timestamp.

Advantages: No external dependencies.

Critical flaws: Unordered, large storage footprint, unsuitable as sharding key, terrible query performance at trillion scale.

Database auto‑increment primary key

Implementation: SINGLE_TABLE AUTO_INCREMENT.

Advantages: Simple, naturally ordered and unique.

Critical flaws: Single‑node throughput limit, duplicate IDs after sharding, costly migration, impossible for trillion‑scale.

Database segment (range allocation)

Implementation: Service fetches a contiguous block of IDs from the DB and caches it in memory; when exhausted, it fetches the next block.

Advantages: Reduces DB hits, ordered, globally unique, fault‑tolerant persistence.

Drawbacks: Exhaustion blocks requests, single‑point DB failure, manual step‑size adjustment on scaling.

Redis INCR

Implementation: Atomic INCR combined with timestamp and machine tag.

Advantages: Extremely fast, no duplicates under normal operation.

Drawbacks: Redis crash or persistence loss leads to duplicates; memory consumption grows with massive ID volume; multi‑shard deployment needs extra partition tags; cross‑region sync is complex.

Native Snowflake (Twitter)

Structure: 1‑bit sign + 41‑bit timestamp + 10‑bit machine ID + 12‑bit sequence (64‑bit long).

Advantages: Pure‑memory calculation, no third‑party services, ordered, 4 k IDs/s per node.

Fatal issues: Clock rollback causes duplicates; only 1024 machines supported; 12‑bit sequence caps at 4096 IDs per millisecond, which overflows under flash‑sale traffic.

Improved hybrid: segment + Snowflake

Normal traffic: use improved Snowflake in‑memory generation – no I/O, handles millions of QPS.

Clock rollback or sequence overflow: switch to DB‑pre‑allocated segment to guarantee uniqueness.

Machine IDs are assigned centrally in the DB, allowing automatic expansion beyond the native 1024‑node limit.

3‑bit room code separates up to eight data‑centers, enabling seamless multi‑region deployment.

Revised Snowflake bit layout for trillion‑scale

New 64‑bit composition: sign(1) | timestamp(42) | room(3) | worker(13) | sequence(5) Sign bit fixed to 0 ensures positive numbers.

42‑bit timestamp provides ~140 years of range.

3‑bit room code supports up to 8 data‑centers.

13‑bit worker ID allows 8192 instances.

5‑bit sequence yields 32 IDs per millisecond; combined with an in‑memory buffer it satisfies peak load.

Code illustration

Worker‑node table (MySQL)

CREATE TABLE id_worker_node (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '机器唯一ID',
    room_code TINYINT NOT NULL COMMENT '机房编码 0-7',
    host_name VARCHAR(64) NOT NULL COMMENT '服务主机名',
    ip VARCHAR(32) NOT NULL COMMENT '服务IP',
    create_time DATETIME DEFAULT NOW(),
    update_time DATETIME DEFAULT NOW(),
    UNIQUE uk_host_ip(host_name,ip)
) COMMENT '分布式ID机器节点分配表';

On startup the service queries the table by IP/hostname; if no record exists it inserts one, automatically obtaining a unique workerId.

Improved Snowflake generator (Java)

import java.sql.Timestamp;
import java.util.concurrent.atomic.AtomicLong;

public class ImprovedSnowflakeIdGenerator {
    // Bit allocation
    private static final long TIMESTAMP_BIT = 42L;
    private static final long ROOM_BIT = 3L;
    private static final long WORKER_BIT = 13L;
    private static final long SEQ_BIT = 5L;

    private static final long MAX_ROOM_ID = (1L << ROOM_BIT) - 1;
    private static final long MAX_WORKER_ID = (1L << WORKER_BIT) - 1;
    private static final long MAX_SEQ = (1L << SEQ_BIT) - 1;

    private static final long WORKER_SHIFT = SEQ_BIT;
    private static final long ROOM_SHIFT = WORKER_SHIFT + WORKER_BIT;
    private static final long TIMESTAMP_SHIFT = ROOM_SHIFT + ROOM_BIT;

    // Base time: 2025‑01‑01
    private static final long BASE_TIME = new Timestamp(1735689600000L).getTime();

    private final long roomId;
    private final long workerId;
    private long lastTimestamp;
    private final AtomicLong sequence = new AtomicLong(0);

    public ImprovedSnowflakeIdGenerator(long roomId, long workerId) {
        if (roomId < 0 || roomId > MAX_ROOM_ID) {
            throw new IllegalArgumentException("机房编码超出范围");
        }
        if (workerId < 0 || workerId > MAX_WORKER_ID) {
            throw new IllegalArgumentException("机器ID超出范围");
        }
        this.roomId = roomId;
        this.workerId = workerId;
        this.lastTimestamp = System.currentTimeMillis();
    }

    public synchronized long nextId() {
        long currentTime = System.currentTimeMillis();
        // Clock‑rollback detection
        if (currentTime < lastTimestamp) {
            throw new ClockBackwardException("系统时钟回拨,时间差:" + (lastTimestamp - currentTime));
        }
        if (currentTime == lastTimestamp) {
            long seq = sequence.incrementAndGet() & MAX_SEQ;
            if (seq == 0) {
                currentTime = waitNextMilli(lastTimestamp);
            }
        } else {
            sequence.set(0);
        }
        lastTimestamp = currentTime;

        long timePart = (currentTime - BASE_TIME) << TIMESTAMP_SHIFT;
        long roomPart = roomId << ROOM_SHIFT;
        long workerPart = workerId << WORKER_SHIFT;
        long seqPart = sequence.get();

        return timePart | roomPart | workerPart | seqPart;
    }

    private long waitNextMilli(long lastTime) {
        long now = System.currentTimeMillis();
        while (now <= lastTime) {
            now = System.currentTimeMillis();
        }
        return now;
    }
}

class ClockBackwardException extends RuntimeException {
    public ClockBackwardException(String msg) { super(msg); }
}

Segment‑based fallback generator

@Component
public class SegmentIdGenerator {
    @Autowired
    private IdSegmentMapper segmentMapper;

    private long currentStart;
    private long currentEnd;
    private AtomicLong currentId;

    @PostConstruct
    public void initSegment() {
        loadNewSegment();
    }

    private synchronized void loadNewSegment() {
        // Atomically increase step size (e.g., 10 000)
        int row = segmentMapper.updateSegmentStep(10000);
        SegmentPO po = segmentMapper.selectOne();
        this.currentStart = po.getStartId();
        this.currentEnd = po.getEndId();
        this.currentId = new AtomicLong(currentStart);
    }

    public long nextSegmentId() {
        long id = currentId.incrementAndGet();
        if (id > currentEnd) {
            loadNewSegment();
            return nextSegmentId();
        }
        return id;
    }
}

Unified ID service (automatic switch)

@Service
public class DistributeIdService {
    @Autowired
    private SegmentIdGenerator segmentIdGenerator;
    private ImprovedSnowflakeIdGenerator snowflakeGenerator;

    @PostConstruct
    public void init() {
        long roomId = 0; // fetched from config or DB
        long workerId = getLocalWorkerId(); // IP‑based lookup
        snowflakeGenerator = new ImprovedSnowflakeIdGenerator(roomId, workerId);
    }

    public long getNextId() {
        try {
            return snowflakeGenerator.nextId(); // fast path
        } catch (ClockBackwardException e) {
            return segmentIdGenerator.nextSegmentId(); // fallback
        }
    }

    public String getOrderNo() {
        long id = getNextId();
        return "ORD" + id; // optional business prefix
    }

    private long getLocalWorkerId() {
        // Query id_worker_node by IP/hostname, auto‑insert if missing
        return 1;
    }
}

Controller usage example

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private DistributeIdService idService;

    @PostMapping("/create")
    public Result createOrder() {
        String orderNo = idService.getOrderNo(); // globally unique
        // ... create order logic ...
        return Result.success(orderNo);
    }
}

Production‑grade safeguards for trillion‑scale

Clock‑rollback protection

Record service start timestamp; each ID generation compares current time and triggers immediate fallback on backward movement.

Configure OS NTP with minimal adjustment frequency to avoid large jumps.

Persist segment ranges in DB; even if the clock jumps back, pre‑allocated ranges guarantee uniqueness.

Monitoring captures ClockBackwardException and raises alerts for operations teams.

Automatic machine‑ID allocation

On startup the service reads its IP/hostname, queries the id_worker_node table, inserts a row if absent, and receives a unique workerId. Cluster expansion requires no configuration changes.

Segment pre‑loading buffer

An asynchronous thread monitors remaining capacity; when the current segment falls below 20 % it pre‑fetches the next block, ensuring no DB‑induced latency during flash‑sale spikes.

Multi‑data‑center isolation

Room codes are fixed per region (e.g., East China 0, North China 1, South China 2). Different rooms generate disjoint ID spaces, preventing cross‑region collisions.

Rate limiting and monitoring

Global QPS of ID generation is monitored; exceeding thresholds triggers throttling.

Count of Snowflake‑to‑segment fallbacks is recorded to gauge clock‑rollback frequency.

Segment table is periodically inspected; step size is dynamically increased under high load to reduce DB round‑trips.

Sharding‑friendly ID

The monotonically increasing 64‑bit ID can serve directly as a sharding key; modulo or time‑range partitioning yields efficient pagination and range queries, far superior to unordered UUIDs.

Common pitfalls and their remedies

Clock rollback leads to duplicate primary keys

Root cause: NTP sync moves the clock backward, causing identical millisecond timestamps.

Solution: Detect rollback, switch to segment fallback, and raise alerts.

Cluster expansion beyond 1024 nodes

Root cause: Native Snowflake limits machine ID to 10 bits.

Solution: Expand to 13 bits, supporting up to 8192 instances.

Flash‑sale peak exceeding 4096 IDs per millisecond

Root cause: 12‑bit sequence caps at 4096.

Solution: Use in‑memory segment buffer; when sequence exhausts, automatically fall back to DB segment without blocking.

Redis crash causing ID duplication

Root cause: No persistent segment record.

Solution: Discard pure Redis approach; rely on DB‑persisted segment as fallback.

Cross‑region ID collisions

Root cause: Identical worker IDs across data‑centers.

Solution: Encode a 3‑bit room identifier, guaranteeing isolation.

Conclusion

Supporting trillion‑level orders cannot rely on a single ID scheme; each has fatal drawbacks. The proposed hybrid architecture uses an improved Snowflake generator for high‑performance primary path and a database‑persisted segment generator as a safety net, together with automatic machine/room allocation, clock‑rollback detection, and comprehensive monitoring. This design satisfies global uniqueness, ordering, performance, security, scalability, and high availability, making it suitable for e‑commerce, logistics, payment and any massive‑order business.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMicroserviceshigh concurrencyMySQLSnowflakedistributed IDsegment allocation
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

0 followers
Reader feedback

How this landed with the community

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.