Backend Development 9 min read

Design and Implementation of a Distributed Unique ID Generator for Ctrip User Accounts

This article describes Ctrip's design of a distributed, globally unique user ID generator for MySQL migration, covering requirements, evaluation of common approaches such as auto‑increment, UUID, Snowflake and Redis, and detailing the final Flicker‑based solution with segment caching using AtomicLong to achieve high concurrency and reliability.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Design and Implementation of a Distributed Unique ID Generator for Ctrip User Accounts

In a distributed architecture, generating a globally unique sequence number is a frequent challenge, especially when sharding databases. Ctrip needed a new user ID generation scheme during its MySQL migration to support the massive volume of new registrations.

The required features include global uniqueness, high concurrency support, the ability to encode certain attributes, high reliability with fault tolerance, and strong performance.

The article reviews common solutions: database auto‑increment (simple but puts pressure on a single table), UUID (low DB load but long and unordered), Snowflake (Twitter’s 64‑bit ID with time, machine, and sequence components), and Redis INCR (fast, scalable, but requires an additional component). Each approach’s advantages and disadvantages are discussed.

Ctrip’s final design builds on the Flicker solution, using a single MySQL table with an auto‑increment column and a stub column storing the server IP. A new ID is obtained by executing REPLACE INTO SEQUENCE_GENERATOR_TABLE (stub) VALUES ("192.168.1.1"); followed by SELECT id FROM SEQUENCE_GENERATOR_TABLE WHERE stub = "192.168.1.1"; . This guarantees a unique ID per server.

To support multiple servers, each server updates only its own row, preventing conflicts. The table may contain rows such as:

id stub 5 192.168.1.1 2 192.168.1.2 3 192.168.1.3 4 192.168.1.4

To reduce database load, the system allocates ID blocks (segments) and caches them in memory. When a block is fetched, the base value is multiplied (e.g., by 1000) and stored in an AtomicLong :

atomic.set(n * 1000);

The maximum ID of the segment is recorded:

currentMaxId = (n + 1) * 1000;

Each request obtains the next ID with:

Long uid = atomic.incrementAndGet();

If the current ID reaches currentMaxId , the thread blocks while the earliest thread fetches a new segment from the database and updates the cached values. This logic fits in fewer than 20 lines of code and solves the distributed ID generation problem.

The design acknowledges that after a server restart, some IDs in the in‑memory cache may be lost, so the segment size should be tuned to balance performance gains against ID waste. In practice, the performance improvement outweighs the minor waste.

After more than five months in production, the solution has proven stable, with the SOA service averaging 0.59 ms response time and client calls averaging 2.52 ms.

Backenddistributed systemsJavaRedishigh concurrencyMySQLUnique ID
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

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.