Releases: Ahoo-Wang/CosId
1.2.1
1.1.8
- add IdSegmentDistributor.Mock for test
- optimize unit-test
- optimize jmh-test
- add support to enable SegmentChainId through configuration (spring-boot-starter-cosid)
- add RedisIdBenchmark VS RedisChainIdBenchmark TPS to README
- add LifecycleSegmentChainId for shutdown prefetchWorker
- Update performance report
1.1.4
- abstract
IdSegment
interface - enhance
DefaultSegmentId
to prevent back-end storageIdSegment
loss/rollback - enhance
IdSegmentDistributor
to support batch acquisition ofIdSegment
. - optimize the performance of
SegmentChainId
(lock-free), the ultimate performance after optimization is close toAtomicLong
1.1.2
1.1.0
1.0.4
1.0.3
1.0.0
0.9.8
- add support customize ClockBackwardsSynchronizer(spring-boot-starter-cosid)
- optimize SnowflakeIdProperties for Configuration experience(spring-boot-starter-cosid)
- rename LocalMachineState to MachineStateStorage
- add support customize IdDefinition to enable ClockSyncSnowflakeId
- optimize customize epoch (spring-boot-starter-cosid)
0.9.2
CosId Universal, flexible, high-performance distributed ID generator
Introduction
CosId provide a universal, flexible and high-performance distributed ID generator. Two major types of ID generators are currently provided:SnowflakeId (Stand-alone TPS performance:4,090,000 JMH Benchmark)、RedisIdGenerator (Stand-alone TPS performance(Step 1000):36,874,696 JMH Benchmark)。
SnowflakeId
SnowflakeId is a distributed ID algorithm that uses
Long
(64 bits) bit partition to generate ID.
The general bit allocation scheme is :timestamp
(41 bits) +machineId
(10 bits) +sequence
(12 bits) = 63 bits 。
- 41 bits
timestamp
= (1L<<41)/(1000/3600/365) approximately 69 years of timestamp can be stored, that is, the usable absolute time isEPOCH
+ 69 years. Generally, we need to customizeEPOCH
as the product development time. In addition, we can increase the number of allocated bits by compressing other areas, The number of timestamp bits to extend the available time. - 10 bits
machineId
= (1L<<10) = 1024 That is, 1024 copies of the same business can be deployed (there is no master-slave copy in the Kubernetes concept, and the definition of Kubernetes is directly used here) instances. Generally, there is no need to use so many, so it will be redefined according to the scale of deployment. - 12 bits
sequence
= (1L<<12) * 1000 = 4096000 That is, a single machine can generate about 409W ID per second, and a global same-service cluster can generate 40960001024=419430W=4.19 billion (TPS).
It can be seen from the design of SnowflakeId:
- 👍 The first 41 bits are a
timestamp
,So SnowflakeId is local monotonically increasing, and affected by global clock synchronization SnowflakeId is global trend increasing. - 👍
SnowflakeId
does not have a strong dependency on any third-party middleware, and its performance is also very high. - 👍 The bit allocation scheme can be flexibly configured according to the needs of the business system to achieve the optimal use effect.
- 👎 Strong reliance on the local clock, potential clock callback problems will cause ID duplication.
- 👎 The
machineId
needs to be set manually. If themachineId
is manually assigned during actual deployment, it will be very inefficient.
It mainly solves two major problems of SnowflakeId
: machine number allocation problem and clock callback problem. And provide a more friendly and flexible experience.
MachineIdDistributor
Currently CosId provides the following three
MachineId
distributors.
ManualMachineIdDistributor
cosid:
snowflake:
manual:
enabled: true
machine-id: 1
Manually distribute
MachineId
StatefulSetMachineIdDistributor
cosid:
snowflake:
stateful-set:
enabled: true
Use the stable identification ID provided by the
StatefulSet
ofKubernetes
as the machine number.
RedisMachineIdDistributor
cosid:
snowflake:
redis:
enabled: true
Use Redis as the distribution store for the machine number.
ClockBackwardsSynchronizer
The default DefaultClockBackwardsSynchronizer
clock callback synchronizer uses active wait synchronization strategy, spinThreshold
(default value 20 milliseconds) is used to set the spin wait threshold, when it is greater than spinThreshold
, use thread sleep to wait for clock synchronization, if it exceeds BrokenThreshold
(default value 2 seconds) will directly throw a ClockTooManyBackwardsException
exception.
LocalMachineState
public class MachineState {
public static final MachineState NOT_FOUND = of(-1, -1);
private final int machineId;
private final long lastTimeStamp;
public MachineState(int machineId, long lastTimeStamp) {
this.machineId = machineId;
this.lastTimeStamp = lastTimeStamp;
}
public int getMachineId() {
return machineId;
}
public long getLastTimeStamp() {
return lastTimeStamp;
}
public static MachineState of(int machineId, long lastStamp) {
return new MachineState(machineId, lastStamp);
}
}
The default FileLocalMachineState
local machine state storage uses a local file to store the machine number and the most recent timestamp, which is used as a MachineState
cache.
ClockSyncSnowflakeId
The default SnowflakeId
will directly throw a ClockBackwardsException
when a clock callback occurs, while using the ClockSyncSnowflakeId
will use the ClockBackwardsSynchronizer
to actively wait for clock synchronization to regenerate the ID, providing a more user-friendly experience.
SafeJavaScriptSnowflakeId
SnowflakeId snowflakeId = SafeJavaScriptSnowflakeId.ofMillisecond(1);
The Number.MAX_SAFE_INTEGER
of JavaScript
has only 53 bits. If the 63-bit SnowflakeId
is directly returned to the front end, the value will overflow. Usually we can convert SnowflakeId
to String type or customize SnowflakeId
Bit allocation is used to shorten the number of bits of SnowflakeId
so that ID
does not overflow when it is provided to the front end.
SnowflakeIdStateParser (Can parse SnowflakeId
into a more readable SnowflakeIdState
)
public class SnowflakeIdState {
private final long id;
private final int machineId;
private final long sequence;
private final LocalDateTime timestamp;
/**
* {@link #timestamp}-{@link #machineId}-{@link #sequence}
*/
private final String friendlyId;
}
SnowflakeIdState idState=snowflakeIdStateParser.parse(id);
idState.getFriendlyId(); //20210623131730192-1-0
RedisIdGenerator
When the step size of RedisIdGenerator
is set to 1 (one Redis network IO request is required for each generation of ID
) TPS performance is about 21W+/s (JMH benchmark), if we are correct in some scenarios ID generated TPS performance has higher requirements, so you can choose to increase the step size of each ID
distribution to reduce the frequency of network IO requests and improve the performance of IdGenerator
(for example, increase the step size to 1000, and the performance can be increased to 3545W+/s JMH benchmark).
IdGeneratorProvider
cosid:
snowflake:
provider:
bizA:
# epoch:
# timestamp-bit:
# machine-bit:
sequence-bit: 12
bizB:
# epoch:
# timestamp-bit:
# machine-bit:
sequence-bit: 12
IdGenerator idGenerator = idGeneratorProvider.get("bizA");
In actual use, we generally do not use the same IdGenerator
for all business services, but different businesses use different IdGenerator
, then IdGeneratorProvider
exists to solve this problem, and it is the container of IdGenerator
, You can get the corresponding IdGenerator
by the business name.
Examples
Installation
Gradle
Kotlin DSL
val cosidVersion = "0.9.2";
implementation("me.ahoo.cosid:spring-boot-starter-cosid:${cosidVersion}")
Maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>demo</artifactId>
<properties>
<cosid.version>0.9.2</cosid.version>
</properties>
<dependencies>
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>spring-boot-starter-cosid</artifactId>
<version>${cosid.version}</version>
</dependency>
</dependencies>
</project>
application.yaml
cosid:
namespace: ${spring.application.name}
snowflake:
# instance-id:
# stable: true
# machine-bit: 10
# instance-id: ${HOSTNAME}
# stateful-set:
# enabled: true
# manual:
# enabled: true
# machine-id: 1
redis:
enabled: true
provider:
order:
# epoch:
# timestamp-bit:
sequence-bit: 12
user:
# epoch:
# timestamp-bit:
sequence-bit: 12
enabled: true
# redis:
# enabled: true
# provider:
# order:
# step: 100
JMH-Benchmark
SnowflakeId
Benchmark Mode Cnt Score Error Units
SnowflakeIdBenchmark.millisecondSnowflakeId_generate thrpt 4093924.313 ops/s
SnowflakeIdBenchmark.safeJsMillisecondSnowflakeId_generate thrpt 511542.292 ops/s
SnowflakeIdBenchmark.safeJsSecondSnowflakeId_generate thrpt 511939.629 ops/s
SnowflakeIdBenchmark.secondSnowflakeId_generate thrpt 4204761.870 ops/s
RedisIdGenerator
Benchmark Mode Cnt Score Error Units
RedisId...