CosIdGenerator
: Stand-alone производительность TPS: 15 570 085 операций/секунду, в три раза выше, чем у UUID.randomUUID()
, глобальный тренд увеличения на основе времени.SnowflakeId
: Stand-alone производительность TPS: OnClickListener 4 096 000 операций/секунду JMH Benchmark, решает две основные проблемы SnowflakeId
: проблему распределения номеров машин и проблему откатывания времени, и предоставляет более дружественный и гибкий опыт.SegmentId
: Получает ID сегмента (Step
) каждый раз, чтобы уменьшить частоту запросов к сети IO дистрибьютора IdSegment
и улучшить производительность.
IdSegmentDistributor
:
RedisIdSegmentDistributor
: Дистрибьютор IdSegment
на основе Redis.JdbcIdSegmentDistributor
: Дистрибьютор IdSegment
на основе JDBC, поддерживающий различные реляционные базы данных.ZookeeperIdSegmentDistributor
: Дистрибьютор IdSegment
на основе Zookeeper.MongoIdSegmentDistributor
: Дистрибьютор IdSegment
на основе MongoDB.SegmentChainId
(рекомендуется): SegmentChainId
(без блокировки) является улучшением SegmentId
, схема дизайна представлена ниже. PrefetchWorker
поддерживает безопасное расстояние, чтобы SegmentChainId
достигал приблизительно производительности TPS: 127 439 148+ операций/секунду JMH Benchmark.
PrefetchWorker
поддерживает безопасное расстояние (safeDistance
) и поддерживает динамическое расширение и сокращение безопасного расстояния в зависимости от статуса голода.## SnowflakeIdИсправлено:
SnowflakeId — это распределённый алгоритм генерации идентификаторов, использующий
Long
(64-бит) разбиение на части для генерации ID. Общая схема распределения битов: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 напрямую). Обычно требуется так много копий, поэтому machineId
будет переопределен в зависимости от масштаба развертывания.sequence
= (1L<<12) * 1000 = 4096000, то есть одна машина может генерировать около 409 миллионов ID в секунду, а глобальная кластерная система одного и того же сервиса может генерировать 4096000*1024=4194304000=4,19 миллиарда (TPS)
.Из дизайна SnowflakeId можно сделать вывод:- :thumbs_up: Первые 41 бит — это timestamp
, поэтому SnowflakeId локально монотонно увеличивается, и, подверженный глобальной синхронизации часов, SnowflakeId глобально увеличивается.
machineId
необходимо задавать вручную. Если machineId
задаётся вручную при развертывании, это будет очень неэффективно.---Основная задача — решение двух основных проблем SnowflakeId: проблемы распределения номеров машин и проблемы перемещения часов назад, а также предоставление более дружественного и гибкого опыта.### MachineIdDistributor
В настоящее время CosId предоставляет три дистрибьютора
MachineId
.
cosid:
snowflake:
machine:
distributor:
type: manual
manual:
machine-id: 0
Ручное распределение
MachineId
cosid:
snowflake:
machine:
distributor:
type: stateful_set
Использует стабильный идентификатор ID, предоставленный
StatefulSet
Kubernetes, как номер машины.
cosid:
snowflake:
machine:
distributor:
type: redis
Использует Redis в качестве хранилища для распределения номера машины.
cosid:
snowflake:
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
```По умолчанию используется синхронизатор `DefaultClockBackwardsSynchronizer`, который использует стратегию активного ожидания. Параметр `spinThreshold` (значение по умолчанию 10 миллисекунд) используется для установки порога ожидания вращения. Когда значение превышает `spinThreshold`, используется ожидание потока для ожидания синхронизации часов. Если значение превышает `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 lastStamp) {
return new MachineState(machineId, lastStamp);
}
}
cosid:
snowflake:
machine:
state-storage:
local:
state-location: ./cosid-machine-state/
По умолчанию LocalMachineStateStorage
локальное хранилище состояния машины использует локальный файл для хранения номера машины и самого последнего таймстампа, который используется как кэш состояния машины.
cosid:
snowflake:
share:
clock-sync: true
По умолчанию SnowflakeId
будет выбрасывать исключение ClockBackwardsException
при обратном движении часов, в то время как использование ClockSyncSnowflakeId
будет использовать ClockBackwardsSynchronizer
для активного ожидания синхронизации часов для перегенерации идентификатора, что обеспечивает более удобный для пользователя опыт.
SnowflakeId snowflakeId = SafeJavaScriptSnowflakeId.ofMillisecond(1);
Number.MAX_SAFE_INTEGER
в JavaScript
имеет только 53 бита. Если 63-битный SnowflakeId
будет напрямую возвращен на фронтенд, значение переполнится. Обычно мы можем преобразовать SnowflakeId
в строковый тип или настраивать распределение битов SnowflakeId
, чтобы уменьшить количество битов SnowflakeId
, чтобы идентификатор не переполнялся при передаче на фронтенд.### SnowflakeFriendlyId (Может преобразовать 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
при запуске, чтобы избежать ручного создания. Аналогично выполнению следующего скрипта инициализации, не нужно беспокоиться о неправильных действиях, так как name
является первичным ключом.```mysql
insert into cosid
(name, last_max_id, last_fetch_time)
values
('namespace.name', 0, unix_timestamp());
### SegmentChainId

```yaml
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);
}
``````java
LongIdEntity entity = new LongIdEntity();
entityRepository.insert(entity);
/**
* {
* "id": 208796080181248
* }
*/
return entity;
spring:
shardingsphere:
rules:
sharding:
key-generators:
cosid:
type: COSID
props:
id-name: __share__
PreciseShardingValue | RangeShardingValue |
---|---|
![]() |
![]() |
#### CosIdModShardingAlgorithm
<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/CosIdModShardingAlgorithm.png" alt="CosId Mod Sharding Algorithm"/>
</p>
- Производительность: В сравнении с `org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm`, производительность в 1200~4000 раз выше. Имеет более высокую стабильность и отсутствие серьезного ухудшения производительности.
| **PreciseShardingValue** | **RangeShardingValue** |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|  |  |```yaml
spring:
shardingsphere:
rules:
шардирование:
шард-алгоритмы:
alg-name:
type: COSID_MOD
props:
mod: 4
логическое-имя-префикс: t_table_
```## Примеры
> В проекте представлены примеры использования различных сценариев (например, `jdbc`, `proxy`, `redis-cosid`, `redis`, `shardingsphere`, `zookeeper` и т.д.). В процессе работы можно использовать эти примеры для быстрого подключения с помощью соответствующей конфигурации.
[Посмотреть примеры](https://github.com/Ahoo-Wang/CosId/tree/main/examples)
## Установка
### Gradle
> Kotlin DSL
```kotlin
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>
В проекте представлены примеры использования различных сценариев (например,
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>
```### application.yaml
```yaml
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbcUrl: 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
jdbcUrl: 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}
машинное:
включен: true
# стабильный: true
# машинный-бит: 10
# инстанс-id: ${HOSTNAME}
дистрибьютор:
тип: redis
# ручной:
# машинный-id: 0
снوفлэйк:
включен: true
# эпоха: 1577203200000
часовой_обратный:
spin-порог: 10
``` поломанный-порог: 2000
разделяемый:
часовой_синхрон: true
дружественный: true
поставщик:
order_item:
# timestamp-бит:
последовательный_бит: 12
снофлэйк:
последовательный_бит: 12
safeJs:
машинный_бит: 3
последовательный_бит: 9
сегмент:
включен: true
режим: цепь
цепь:
безопасное_расстояние: 5
prefetch_worker:
core_pool_размер: 2
prefetch_период: 1с
дистрибьютор:
тип: redis
разделяемый:
смещение: 0
шаг: 100
поставщик:
order:
смещение: 10000
шаг: 100
longId:
смещение: 10000
шаг: 100
```## JMH-Benchmark
- Разработка ноутбука: MacBook Pro (M1)
- Все тесты производительности проводятся на ноутбуке для разработки.
- Установка Redis на ноутбуке для разработки.
### SnowflakeId
```shell
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
В статистике перцентиль (или центиль) — это оценка, ниже которой находится заданный процент оценок в её частотном распределении (исключающее определение) или оценка, ниже которой (исключающее определение) или включая которую (включающее определение) находится заданный процент оценок. Например, 50-й перцентиль (медиана) — это оценка, ниже которой (исключающее определение) или включая которую (включающее определение) находится 50% оценок в распределении.
CosId (
SegmentChainId
) в 5 раз быстрее, чем Leaf(segment
).
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )