Multiply a timestamp by 100,000, then add a cryptographically secure random integer from 0 through 99,999. For example, using milliseconds since the Unix epoch gives the form timestamp_ms × 100000 + suffix. This produces an integer, but it does not guarantee uniqueness: store it behind a database uniqueness constraint and retry if an insert conflicts.
Generate the integer
In Python, time.time_ns() // 1_000_000 gives the current Unix timestamp in milliseconds. Use secrets.randbelow(100_000) to choose the suffix, including values from 0 to 99,999:
As an Amazon Associate I earn from qualifying purchases.
import secrets
import time
timestamp_ms = time.time_ns() // 1_000_000
suffix = secrets.randbelow(100_000)
identifier = timestamp_ms * 100_000 + suffix
The suffix is conceptually five digits, so 42 is formatted as 00042. Integer arithmetic does not preserve those leading zeroes; the numeric result is still valid. If you need to display the suffix separately, format it with five digits.
Understand the collision risk
Every ID created during the same millisecond shares the same timestamp component. Those IDs differ only by one of 100,000 possible suffixes. Two processes can choose the same suffix, and a process restart does not preserve its earlier random choices. A cryptographically secure random generator makes values difficult to predict and collisions less likely, but it cannot guarantee that a value has never been used.
#1 Best Overall
For a custom ID that must be unique in your application, make the database enforce that requirement. Put a unique constraint or index on the ID column; attempt the insert; and, if it fails specifically because the ID already exists, generate a new candidate and retry. Use a bounded retry policy and handle exhaustion as an error rather than assuming a retry must succeed. RFC 9562 recommends a cryptographically secure pseudorandom number generator for low collision likelihood and calls for consistent handling of clock changes: RFC 9562.
Choose a design for your requirements
| Approach | Numeric? | Ordering | Coordination and trade-offs |
|---|---|---|---|
| Timestamp plus random five-digit suffix | Yes | Roughly time-based; suffix order within a millisecond is random | No coordination to generate candidates, but uniqueness needs a database check and retry. Collisions remain possible. |
| UUIDv4 | No; conventionally represented as a UUID | No time ordering | A straightforward general-purpose unique-ID option. Python’s documentation says that if all you want is a unique ID, you should probably call uuid1() or uuid4(): Python uuid documentation. |
| UUIDv7 | No; conventionally represented as a UUID | Time-oriented | Includes a 48-bit Unix-epoch timestamp in milliseconds. RFC 9562 describes monotonic methods for generating multiple identifiers in one timestamp tick: RFC 9562. |
| ULID | No; commonly represented as a 26-character string | Time-oriented | The Python ULID documentation describes a thread-safe generator with a strict monotonic default that increments randomness when another ULID is generated in the same millisecond: Python ULID documentation. |
| Snowflake-style numeric ID | Yes | Time-oriented | Encodes timestamp and generator information to support generation across workers. The SKA Observatory documents a 63-bit design with a millisecond timestamp, 10-bit generator ID, and 11-bit random suffix: SKA Observatory Snowflake documentation. |
When multiple servers generate IDs
If many workers generate IDs independently, a timestamp and random suffix can collide across workers just as they can within one process. A larger random space reduces risk but does not turn random selection into a uniqueness guarantee. A database constraint with retries remains necessary if you keep this format and all workers can reach the same persistence layer.
Rank #2
If the ID must be numeric and workers need to generate IDs without checking a shared database for every candidate, use a design that allocates distinct worker identifiers and includes sequence or random bits, such as a Snowflake-style scheme. Its correctness depends on assigning worker IDs without duplicates and handling clock rollback and sequence exhaustion. RFC 9562 notes that global uniqueness cannot be guaranteed without a shared knowledge scheme; its timestamp guidance also makes clock behavior part of the design, not an incidental detail: RFC 9562.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsDo not use the ID as a secret
A timestamp-based identifier exposes timing information, and a five-digit suffix has only 100,000 possibilities. Even when the suffix comes from a CSPRNG, this format is not an authentication token or a substitute for an access-control check. Use a dedicated, sufficiently random secret for credentials and tokens.
Quick Recap
Rank #4
Practical decision
- Keep timestamp-plus-suffix when the exact numeric format is required and you can enforce uniqueness in storage.
- Use UUIDv4 when you want a simple general-purpose identifier and do not need time ordering.
- Consider UUIDv7 or ULID when time-oriented sorting is useful and a non-integer representation is acceptable.
- Choose a Snowflake-style scheme when numeric IDs must be generated across coordinated workers.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




