Distributed Snowflake ID Generation in Java: Hutool and MyBatis-Plus Demo

This article walks through the Snowflake algorithm for distributed ID generation, detailing its timestamp, workerId and sequence fields, handling clock rollback and sequence overflow, and provides a complete Java implementation with integration examples for Hutool and MyBatis‑Plus, including configuration and test code.

Yumin Fish Harvest
Yumin Fish Harvest
Yumin Fish Harvest
Distributed Snowflake ID Generation in Java: Hutool and MyBatis-Plus Demo

The Snowflake algorithm solves the problem of generating unique identifiers in a distributed system by combining three components: a timestamp, a node identifier (workerId), and a per‑millisecond sequence number. The timestamp occupies 41 bits, giving a usable range of about 69.7 years; the node identifier uses 10 bits, allowing up to 1,024 distinct nodes; the sequence uses 12 bits, providing 4,096 IDs per node per millisecond.

In Java the algorithm is implemented by storing two mutable state fields: lastTimestamp (the timestamp of the last generated ID) and sequence (the current sequence value). The nextId() method reads the current time, compares it with lastTimestamp, and follows three branches:

Current time > lastTimestamp : a new millisecond has begun, so the sequence resets to 0.

Current time == lastTimestamp : the sequence is incremented; if it overflows to 0 the generator waits for the next millisecond.

Current time < lastTimestamp : a clock rollback is detected. If the rollback is within a configurable maxBackwardMs the thread waits until the system clock catches up; otherwise an exception is thrown.

All updates to lastTimestamp and sequence are protected by a synchronized method, guaranteeing that a single Java instance cannot produce duplicate IDs even under concurrent calls. The algorithm also checks that the timestamp delta stays within the 41‑bit limit and that the configured epoch, workerId and maxBackwardMs are valid.

The article provides a full Java source file SnowflakeIdGenerator with constants for bit widths, masks, and shift amounts, constructors for production and testing (allowing a custom LongSupplier for the clock), and helper methods for extracting the timestamp, workerId and sequence from a generated ID.

Integration with the Hutool library is shown: Hutool’s Snowflake splits the 10‑bit node field into a 5‑bit datacenterId and a 5‑bit workerId. The article explains how to map a combined workerId (0‑1023) to these two values and how to configure them via Spring Boot properties ( SNOWFLAKE_WORKER_ID and SNOWFLAKE_DATACENTER_ID). A Spring @ConfigurationProperties class and a bean factory method create the Hutool Snowflake instance.

For MyBatis‑Plus, the @TableId(type = IdType.ASSIGN_ID) annotation tells the framework to assign the primary key before an INSERT. The default DefaultIdentifierGenerator internally uses a Sequence implementation that follows the same Snowflake layout (41‑bit time, 5‑bit datacenterId, 5‑bit workerId, 12‑bit sequence). The article shows how to supply explicit workerId and datacenterId values via custom configuration and a bean that returns a IdentifierGenerator wrapping the built‑in DefaultIdentifierGenerator. It also demonstrates a custom adapter that delegates to the previously defined SnowflakeIdGenerator so that both business‑code calls and MyBatis‑Plus inserts share the same generator instance.

Configuration examples include Maven dependencies for Hutool (v5.8.46) and MyBatis‑Plus Spring Boot 3 starter (v3.5.16), YAML/YML property definitions, and environment‑variable overrides. The article stresses that node IDs must be unique across all running instances; it discusses several allocation strategies such as fixed configuration, StatefulSet ordinals, and external lease services (Redis/ZooKeeper) to avoid collisions.

Testing code is provided for both the raw generator and the MyBatis‑Plus integration. Unit tests verify that consecutive calls produce different IDs, that the generated IDs contain the configured worker and datacenter IDs, and that MyBatis‑Plus correctly fills the id field before insertion. The article also includes integration tests that create a table, insert a row, and assert that the primary key is non‑null and persisted.

Production considerations highlighted are:

Never change the epoch or bit layout after deployment.

Store the generated ID as a BIGINT and also keep a separate timestamp column if ordering is needed.

Use string representation ( nextIdStr()) when sending IDs to JavaScript to avoid precision loss.

Maintain a unique database index on the ID column as a final safety net.

Overall, the article demonstrates a complete, thread‑safe Snowflake implementation in Java, shows how to integrate it with popular libraries (Hutool and MyBatis‑Plus), and provides practical guidance on configuration, testing, and deployment to ensure globally unique identifiers in a distributed environment.

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.

distributed systemsjavamybatis-plushutoolsnowflakeid generation
Yumin Fish Harvest
Written by

Yumin Fish Harvest

A deep‑sea salvage fisherman sharing architecture insights, practical tips, and lessons learned.

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.