CosId стремится предоставить универсальный, гибкий и высокопроизводительный распределенный генератор уникальных идентификаторов.
CosIdGenerator
: автономный (производительность TPS: 15 570 085 операций в секунду), в три раза превышает производительность UUID.randomUUID()
, глобально возрастающий с течением времени.SnowflakeId
: автономный (производительность TPS: 4 096 000 операций в секунду) JMH Benchmark, решает две основные проблемы SnowflakeId
: проблему выделения номера машины и проблему обратной перемотки часов и предлагает более удобный и гибкий опыт.SegmentId
: каждый раз получает сегмент (шаг
) идентификатора для снижения частоты запросов сети к распределителю IdSegment
и повышения производительности.
IdSegmentDistributor
:
RedisIdSegmentDistributor
: распределитель IdSegment
на основе Redis.JdbcIdSegmentDistributor
: распределитель IdSegment
на основе JDBC, поддерживающий различные реляционные базы данных.ZookeeperIdSegmentDistributor
: распределитель IdSegment
на основе Zookeeper.MongoIdSegmentDistributor
: распределитель IdSegment
на основе MongoDB.SegmentChainId
(рекомендовано): SegmentChainId
(без блокировки) является улучшенной версией SegmentId
. Схема дизайна представлена ниже. PrefetchWorker
поддерживает безопасное расстояние, так что SegmentChainId
достигает приблизительно производительности AtomicLong: 127 439 148+ операций в секунду JMH Benchmark.
PrefetchWorker
поддерживает безопасное расстояние (safeDistance
) и поддерживает динамическое расширение и сокращение безопасного расстояния в зависимости от состояния голода.
SnowflakeId
— это алгоритм распределенного генератора уникальных идентификаторов, использующий битовое делениеLong
(64-битного) для генерации идентификатора. Общая схема распределения битов:timestamp
(41-бит) +machineId
(10-бит) +sequence
(12-бит) = 63-бит.
timestamp
= (1L << 41) / (1000 / 3600 / 24 / 365) ≈ 69 лет хранения абсолютного времени, то есть доступное время равно EPOCH
+ 69 лет. Обычно требуется настроить EPOCH
как время начала разработки продукта. Кроме того, можно увеличить количество выделенных битов за счет сжатия других областей, тем самым продлевая доступное время.machineId
= (1L << 10) = 1024 Это значит, что можно развернуть до 1024 экземпляров одного бизнеса (в концепции Kubernetes нет главного и вторичного экземпляра, поэтому используется определение Kubernetes напрямую здесь). Обычно такого количества экземпляров не требуется, поэтому его следует переопределить в соответствии со шкалой развертывания.sequence
= (1L << 12) * 1000 = 4096000 Это значит, что одна машина может генерировать около 409 миллионов идентификаторов в секунду, а глобальная кластеризация одного сервиса может генерировать 4096000 * 1024 = 4194304000 = 4,19 миллиардов (TPS)
.Из дизайна SnowflakeId
видно:
timestamp
, поэтому SnowflakeId
локально монотонно возрастает и глобально возрастает благодаря синхронизации часов.SnowflakeId
не имеет сильной зависимости от третьих сторон и имеет очень высокую производительность.machineId
должен быть установлен вручную. При установке machineId
вручную во время развертывания это будет очень неэффективно.Основная задача этого модуля — решение двух основных проблем SnowflakeId
: проблемы выделения номера машины и проблемы обратной перемотки часов, а также предоставление более удобного и гибкого опыта.
В настоящее время CosId предоставляет следующие три распределителя
MachineId
.
cosid:
snowflake:
machine:
distributor:
type: manual
manual:
machine-id: 0
Вручную распределяет
MachineId
.
cosid:
snowflake:
machine:
distributor:
type: stateful_set
Использует стабильный идентификационный идентификатор
StatefulSet
Kubernetes в качестве номера машины.
cosid:
snowflake:
machine:
distributor:
type: redis
Использует Redis в качестве хранилища для распределения номера машины.
cosid:
snowflake:
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
```По умолчанию `DefaultClockBackwardsSynchronizer` использует стратегию активного ожидания для синхронизации обратной перемотки часов. `spinThreshold` (значение по умолчанию 10 миллисекунд) используется для установки порогового значения ожидания в режиме спиннинга. Когда значение больше `spinThreshold`, используется метод `Thread.sleep()` для ожидания синхронизации часов. Если значение превышает `brokenThreshold` (значение по умолчанию 2 секунды), выбрасывается исключение `ClockTooManyBackwardsException`.
### MachineStateStorage
```java
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 lastTimestamp) {
return new MachineState(machineId, lastTimestamp);
}
}
cosid:
snowflake:
machine:
state-storage:
local:
state-location: ./cosid-machine-state/
По умолчанию LocalMachineStateStorage
использует локальное файловое хранилище для хранения номера машины и последнего временного штампа, который используется как кэш MachineState
.
cosid:
snowflake:
share:
clock-sync: true
По умолчанию SnowflakeId
выбрасывает исключение ClockBackwardsException
при обратной перемотке часов, тогда как использование ClockSyncSnowflakeId
позволяет использовать ClockBackwardsSynchronizer
для активного ожидания синхронизации часов для повторной генерации идентификатора, предоставляя более удобный пользовательский опыт.
SnowflakeId snowflakeId = SafeJavaScriptSnowflakeId.ofMillisecond(1);
Число Number.MAX_SAFE_INTEGER
JavaScript имеет только 53 бита. Если 63-битный SnowflakeId
передается впереди без преобразования, он может переполниться. Обычно мы можем преобразовать SnowflakeId
в тип String
или настраивать распределение битов SnowflakeId
для уменьшения количества битов SnowflakeId
, чтобы обеспечить корректное представление идентификатора в фронтенде.
SnowflakeId
в более читаемый SnowflakeIdState
)cosid:
snowflake:
share:
friendly: true
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;
}
public interface SnowflakeFriendlyId extends SnowflakeId {
SnowflakeIdState friendlyId(long id);
SnowflakeIdState ofFriendlyId(String friendlyId);
default SnowflakeIdState friendlyId() {
long id = generate();
return friendlyId(id);
}
}
SnowflakeFriendlyId snowflakeFriendlyId = new DefaultSnowflakeFriendlyId(snowflakeId);
SnowflakeIdState idState = snowflakeFriendlyId.friendlyId();
idState.getFriendlyId(); //20210623131730192-1-0
cosid:
segment:
enabled: true
distributor:
type: redis
Инициализация таблицы
cosid
CREATE TABLE IF NOT EXISTS cosid (
name VARCHAR(100) NOT NULL COMMENT '{namespace}.{name}',
last_max_id BIGINT NOT NULL DEFAULT 0,
last_fetch_time BIGINT NOT NULL,
CONSTRAINT cosid_pk PRIMARY KEY (name)
) ENGINE = InnoDB;
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_db
username: root
password: root
cosid:
segment:
enabled: true
distributor:
type: jdbc
jdbc:
enable-auto-init-cosid-table: false
enable-auto-init-id-segment: true
При включении enable-auto-init-id-segment: true
, приложение попытается создать запись idSegment
при запуске, чтобы избежать необходимости её создания вручную. Аналогично выполнению следующего скрипта инициализации SQL, нет необходимости беспокоиться о неправильной операции, так как name
является первичным ключом.
INSERT INTO cosid (name, last_max_id, last_fetch_time) VALUES ('namespace.name', 0, UNIX_TIMESTAMP());
cosid:
segment:
enabled: true
mode: chain
chain:
safe-distance: 5
prefetch-worker:
core-pool-size: 2
prefetch-period: 1s
distributor:
type: redis
share:
offset: 0
step: 100
provider:
bizC:
offset: 10000
step: 100
bizD:
offset: 10000
step: 100
cosid:
snowflake:
provider:
bizA:
#timestamp-bit:
sequence-bit: 12
bizB:
#timestamp-bit:
sequence-bit: 12
IdGenerator idGenerator = idGeneratorProvider.get("bizA");
На практике мы обычно не используем одинаковый IdGenerator
для всех бизнес-сервисов, но каждый бизнес использует свой уникальный IdGenerator
. В этом случае существует IdGeneratorProvider
, который решает эту проблему, являясь контейнером для IdGenerator
. Вы можете получить соответствующий IdGenerator
по имени бизнеса.
Kotlin DSL
implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}")
@Target(ElementType.FIELD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CosId {
String value() default IdGeneratorProvider.SHARE;
boolean friendlyId() default false;
}
public class LongIdEntity {
@CosId(value = "safeJs")
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public class FriendlyIdEntity {
@CosId(friendlyId = true)
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
@Mapper
public interface OrderRepository {
@Insert("insert into t_table (id) values (#{id});")
void insert(LongIdEntity order);
@Insert({
"<script>",
"insert into t_friendly_table (id)",
"VALUES",
"<foreach item='item' collection='list' open='' separator=',' close=''>",
"(#{item.id})",
"</foreach>",
"</script>"
})
void insertList(List<FriendlyIdEntity> list);
}
LongIdEntity entity = new LongIdEntity();
entityRepository.insert(entity);
/**
* {
* "id": 208796080181248
* }
*/
return entity;
spring:
shardingsphere:
rules:
sharding:
key-generators:
cosid:
type: COSID
props:
id-name: __share__
Простота использования: поддерживает несколько типов данных (Long
/LocalDateTime
/DATE
/String
/SnowflakeId
). Официальная реализация сначала преобразует данные в строку, а затем в LocalDateTime
. Успешность преобразования зависит от символов формата времени.
Производительность: по сравнению с org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm
, производительность выше в 1200–4000 раз.| PreciseShardingValue | RangeShardingValue |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |
|
CosIdIntervalShardingAlgorithm
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_INTERVAL
props:
logic-name-prefix: logic-name-prefix
id-name: cosid-name
datetime-lower: 2021-12-08 22:00:00
datetime-upper: 2022-12-01 00:00:00
sharding-suffix-pattern: yyyyMM
datetime-interval-unit: MONTHS
datetime-interval-amount: 1
org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm
, производительность выше в 1200–4000 раз. Имеет более высокую стабильность и значительное снижение производительности.PreciseShardingValue | RangeShardingValue |
---|---|
![]() |
![]() |
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_MOD
props:
mod: 4
logic-name-prefix: t_table_
В проекте представлены примеры использования различных сценариев (например,
jdbc
/proxy
/redis-cosid
/redis
/shardingsphere
/zookeeper
).
Kotlin DSL
val cosidVersion = "1.14.5";
implementation("me.ahoo.cosid:cosid-spring-boot-starter:${cosidVersion}")
<?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>1.14.5</cosid.version>
</properties>
<dependencies>
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>cosid-spring-boot-starter</artifactId>
<version>${cosid.version}</version>
</dependency>
</dependencies>
</project>
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/cosid_db_0
username: root
password: root
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/cosid_db_1
username: root
password: root
props:
sql-show: true
rules:
sharding:
binding-tables:
- t_order,t_order_item
tables:
cosid:
actual-data-nodes: ds0.cosid
t_table:
actual-data-nodes: ds0.t_table_$->{0..1}
table-strategy:
standard:
sharding-column: id
sharding-algorithm-name: table-inline
t_date_log:
actual-data-nodes: ds0.t_date_log_202112
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: data-log-interval
sharding-algorithms:
table-inline:
type: COSID_MOD
props:
mod: 2
logic-name-prefix: t_table_
data-log-interval:
type: COSID_INTERVAL
props:
logic-name-prefix: t_date_log_
datetime-lower: 2021-12-08 22:00:00
datetime-upper: 2022-12-01 00:00:00
sharding-suffix-pattern: yyyyMM
datetime-interval-unit: MONTHS
datetime-interval-amount: 1
key-generators:
snowflake:
type: COSID
props:
id-name: snowflake
cosid:
namespace: ${spring.application.name}
machine-device:
enabled: true
# stability: true
# bit-machine-device: 10
# instance-id: ${HOSTNAME}
distributor:
type: redis
# manual:
# machine-id: 0
snowflake:
enabled: true
# epoch: 1577203200000
back-time:
spin-rate: 10
break-rate: 2000
availability:
clock-sync: true
friendliness: true
provider:
order_item:
# test-time-bit:
sequence-bit: 12
snowflake:
sequence-bits: 12
safeJs:
machine-bit: 3
sequence-bits: 9
segment:
enabled: true
mode: chain
chain:
safety-distance: 5
preprocessor-worker:
main-pool-size: 2
preprocessing-period: 1s
distributor:
type: redis
availability:
offset: 0
step: 100
provider:
order:
offset: 10000
step: 100
long-id:
offset: 10000
step: 100
gradle cosid-core:jmh
# или
java -jar cosid-core/build/libs/cosid-core-1.14.5-jmh.jar -bm thrpt -wi 1 -rf json -f 1
Benchmark Mode Cnt Score Error Units
SnowflakeIdBenchmark.millisecondSnowflakeId_friendlyId thrpt 4020311.665 ops/s
SnowflakeIdBenchmark.millisecondSnowflakeId_generate thrpt 4095403.859 ops/s
SnowflakeIdBenchmark.safeJsMillisecondSnowflakeId_generate thrpt 511654.048 ops/s
SnowflakeIdBenchmark.safeJsSecondSnowflakeId_generate thrpt 539818.563 ops/s
SnowflakeIdBenchmark.secondSnowflakeId_generate thrpt 4206843.941 ops/s
CosId (
SegmentChainId
) работает в пять раз быстрее, чем Leaf(segment
).
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )