Which GUID Strategy Is Best for High‑Performance Java Services?
Generating globally unique identifiers (GUIDs) is essential for performance testing and business scenarios such as user behavior tracking, and this article compares five approaches—UUID, centralized services, Snowflake, ThreadLocal, and Atomic classes—detailing their principles, advantages, drawbacks, and suitable use cases for high‑concurrency systems.
Generating globally unique identifiers (GUIDs) is a common requirement in performance testing and business scenarios. In the Supermarket 8 tracking system, unique IDs are used for database indexing, request tracing, and log analysis, e.g., for user actions like browsing or payment.
11.1 Unique Identifier Solutions
Below are five common GUID generation schemes and their characteristics.
UUID (java.util.UUID)
Principle : Generates a 128‑bit random identifier formatted as a 36‑character string, e.g., 3b9f3ef9-3c5b-449e-be3a-204c58252e1d.
Advantages
Global uniqueness : Extremely low collision probability.
High performance : Fast generation without network calls.
Stateless : No central coordination required.
Disadvantages
Length : 36 characters consume more storage.
Readability : No business meaning.
Non‑sequential : Unsuitable where ordered IDs are needed.
Use case : Suitable for logging and request tracing in the tracking system, but not ideal as a database primary key.
Example :
UUID uuid = UUID.randomUUID();
System.out.println(uuid); // 3b9f3ef9-3c5b-449e-be3a-204c58252e1dCentralized Service
Principle : Use a central component such as Redis, MySQL, or ZooKeeper to generate incremental IDs, e.g., Redis INCR command.
Advantages
Uniqueness : Central management guarantees global uniqueness.
Traceability : Can integrate with distributed logging for request chain analysis.
Disadvantages
Performance bottleneck : Network latency and load on the central service.
Single‑point risk : Service outage blocks ID generation.
Use case : Combine data‑center ID and local sequence with Redis for distributed unique IDs, but network delay must be optimized.
Snowflake Algorithm
Principle : Generates a 64‑bit ID composed of timestamp, machine (or data‑center + worker) ID, and sequence, ensuring uniqueness and monotonicity.
Advantages
High performance : Local generation without network overhead.
Ordered : Timestamp and sequence provide roughly increasing IDs.
Scalable : Supports distributed deployment via machine IDs.
Disadvantages
Complex implementation : Requires custom code and handling of clock rollback.
Clock dependency : Time synchronization issues may cause collisions.
Use case : Ideal for order‑system primary keys in a high‑concurrency, distributed environment.
Implementation :
public class SnowflakeWorker {
private static final long START_TIMESTAMP = 1712972492276L;
private long dataId, workerId, sequence = 0L;
// fields and constants omitted for brevity
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
timestamp = nextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
long id = ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
| (dataId << DATA_CENTER_ID_SHIFT)
| (workerId << WORKER_ID_SHIFT) | sequence;
return id & Long.MAX_VALUE;
}
// nextMillis method omitted
}Example :
SnowflakeWorker snowflakeWorker = new SnowflakeWorker(1, 1);
for (int i = 0; i < 5; i++) {
System.out.println(snowflakeWorker.nextId());
}ThreadLocal
Principle : Each thread holds an independent counter via ThreadLocal, combining thread name and sequence, e.g., ThreadName-Sequence.
Advantages
High performance : Local counter, no lock overhead.
Simplicity : Easy to use with ThreadLocal.
Debuggable : ID contains thread information.
Disadvantages
Non‑global ordering : IDs are not sequential across threads.
Limited range : Integer counter may overflow (replaceable by Long).
Use case : Suitable for single‑machine load‑testing where IDs are needed for log tracing but not for distributed uniqueness.
Example :
ThreadLocal<Long> exclusive = ThreadLocal.withInitial(() -> 0L);
for (int i = 0; i < 4; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 10; j++) {
Long id = exclusive.get();
exclusive.set(id + 1);
System.out.println(Thread.currentThread().getName() + "-" + (100000000 + id));
}
});
thread.setName("Thread-" + i);
thread.start();
}AtomicInteger
Principle : Use java.util.concurrent.atomic.AtomicInteger with getAndIncrement to produce thread‑safe incremental IDs.
Advantages
High performance : CAS‑based, lock‑free.
Simplicity : No explicit thread management.
Monotonic : Globally increasing IDs, good for single‑node scenarios.
Disadvantages
Not distributed : Cannot be shared across multiple nodes.
Overflow risk : May need AtomicLong for larger ranges.
Use case : Ideal for high‑concurrency, single‑machine tracking ID generation.
Example :
AtomicInteger count = new AtomicInteger();
for (int i = 0; i < 4; i++) {
new Thread(() -> {
for (int j = 0; j < 10; j++) {
System.out.println(count.getAndIncrement() + 10000000);
}
}).start();
}11.4.2 Performance Analysis and Selection
In the tracking system, GUID generation must balance performance, uniqueness, and scenario suitability:
Single‑node : AtomicInteger offers the highest throughput for local log processing.
Distributed : Snowflake provides both performance and ordered IDs, making it appropriate for order‑system primary keys.
Log tracing : UUID or ThreadLocal generate readable IDs for debugging, suitable when performance is not critical.
Avoid centralized services : Due to network latency, Redis‑based IDs are not recommended unless node‑ID optimization is applied.
11.4.3 Optimization Recommendations
Single‑machine optimization : Use AtomicInteger or ThreadLocal together with object pools (e.g., FunObjPool) to reuse UserBehavior instances and reduce memory allocation.
Distributed optimization : Deploy Snowflake with configured data‑center and worker IDs, and regularly verify clock synchronization.
Monitoring and regression : After code changes, benchmark ID generation to ensure no new bottlenecks appear.
Length control : Adjust the format of IDs generated by ThreadLocal or AtomicInteger to meet storage constraints.
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.
