#2 6

Merged
yingying merged 2 commits from 6 into hg1 1 year ago
  1. +15
    -0
      .gitignore
  2. +201
    -0
      LICENSE
  3. +720
    -0
      README.md
  4. +363
    -0
      README.zh-CN.md
  5. +245
    -0
      build.gradle.kts
  6. +18
    -0
      codecov.yml
  7. BIN
      cosid-proxy/src/main/java/me/ahoo/cosid/proxy/doc-files/CosId-Proxy.png
  8. +78
    -0
      examples/cosid-example-jdbc/build.gradle.kts
  9. +105
    -0
      examples/cosid-example-jdbc/init-script/init-example-schemal.sql
  10. +29
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/AppServer.java
  11. +26
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/config/AppConfig.java
  12. +56
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/EntityController.java
  13. +88
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/IdController.java
  14. +73
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/OrderController.java
  15. +34
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/UserController.java
  16. +27
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/dto/AsStringDto.java
  17. +38
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/FriendlyIdEntity.java
  18. +38
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/LongIdEntity.java
  19. +39
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/Order.java
  20. +44
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/OrderItem.java
  21. +18
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/User.java
  22. +35
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/repository/EntityRepository.java
  23. +41
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/repository/OrderRepository.java
  24. +23
    -0
      examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/repository/UserRepository.java
  25. +103
    -0
      examples/cosid-example-jdbc/src/main/resources/application.yaml
  26. +47
    -0
      examples/cosid-example-jdbc/src/main/resources/mappers/OrderMapper.xml
  27. +34
    -0
      examples/cosid-example-jdbc/src/main/resources/mappers/UserMapper.xml
  28. +32
    -0
      examples/cosid-example-jdbc/src/test/java/me/ahoo/cosid/example/jdbc/AppServerTest.java
  29. +65
    -0
      examples/cosid-example-proxy/build.gradle.kts
  30. +29
    -0
      examples/cosid-example-proxy/src/main/java/me/ahoo/cosid/example/proxy/AppServer.java
  31. +50
    -0
      examples/cosid-example-proxy/src/main/java/me/ahoo/cosid/example/proxy/controller/IdController.java
  32. +25
    -0
      examples/cosid-example-proxy/src/main/resources/application.yaml
  33. +33
    -0
      examples/cosid-example-proxy/src/test/java/me/ahoo/cosid/example/proxy/AppServerTest.java
  34. +65
    -0
      examples/cosid-example-redis-cosid/build.gradle.kts
  35. +29
    -0
      examples/cosid-example-redis-cosid/src/main/java/me/ahoo/cosid/example/cosid/AppServer.java
  36. +50
    -0
      examples/cosid-example-redis-cosid/src/main/java/me/ahoo/cosid/example/cosid/controller/IdController.java
  37. +22
    -0
      examples/cosid-example-redis-cosid/src/main/resources/application.yaml
  38. +32
    -0
      examples/cosid-example-redis-cosid/src/test/java/me/ahoo/cosid/example/cosid/AppServerTest.java
  39. +67
    -0
      examples/cosid-example-redis/build.gradle.kts
  40. +29
    -0
      examples/cosid-example-redis/src/main/java/me/ahoo/cosid/example/redis/AppServer.java
  41. +50
    -0
      examples/cosid-example-redis/src/main/java/me/ahoo/cosid/example/redis/controller/IdController.java
  42. +26
    -0
      examples/cosid-example-redis/src/main/resources/application.yaml
  43. +32
    -0
      examples/cosid-example-redis/src/test/java/me/ahoo/cosid/example/redis/AppServerTest.java
  44. +78
    -0
      examples/cosid-example-shardingsphere/build.gradle.kts
  45. +56
    -0
      examples/cosid-example-shardingsphere/init-script/init-example-schemal.sql
  46. +29
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/AppServer.java
  47. +26
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/config/AppConfig.java
  48. +77
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/controller/IntervalShardingAlgorithmController.java
  49. +46
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/DateLogEntity.java
  50. +42
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/LocalDateTimeLogEntity.java
  51. +34
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/SnowflakeLogEntity.java
  52. +41
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/TimestampLogEntity.java
  53. +38
    -0
      examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/repository/IntervalShardingAlgorithmRepository.java
  54. +149
    -0
      examples/cosid-example-shardingsphere/src/main/resources/application.yaml
  55. +31
    -0
      examples/cosid-example-shardingsphere/src/main/resources/mappers/IntervalShardingAlgorithmRepository.xml
  56. +67
    -0
      examples/cosid-example-zookeeper/build.gradle.kts
  57. +29
    -0
      examples/cosid-example-zookeeper/src/main/java/me/ahoo/cosid/example/zookeeper/AppServer.java
  58. +50
    -0
      examples/cosid-example-zookeeper/src/main/java/me/ahoo/cosid/example/zookeeper/controller/IdController.java
  59. +26
    -0
      examples/cosid-example-zookeeper/src/main/resources/application.yaml
  60. +32
    -0
      examples/cosid-example-zookeeper/src/test/java/me/ahoo/cosid/example/zookeeper/AppServerTest.java
  61. +103
    -0
      examples/cosid-example/build.gradle.kts
  62. +53
    -0
      examples/cosid-example/src/dist/config/application.yaml
  63. +12
    -0
      examples/cosid-example/src/dist/config/bootstrap.yaml
  64. +0
    -0
      examples/cosid-example/src/dist/logs/.gitignore
  65. +77
    -0
      examples/cosid-example/src/init-script/init-example-schemal.sql
  66. +29
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/AppServer.java
  67. +26
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/config/AppConfig.java
  68. +55
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/EntityController.java
  69. +96
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/IdController.java
  70. +72
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/IntervalShardingAlgorithmController.java
  71. +69
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/OrderController.java
  72. +76
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/dto/AsStringDto.java
  73. +38
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/FriendlyIdEntity.java
  74. +38
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/LongIdEntity.java
  75. +41
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/Order.java
  76. +42
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/OrderItem.java
  77. +46
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/DateLogEntity.java
  78. +40
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/LocalDateTimeLogEntity.java
  79. +32
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/SnowflakeLogEntity.java
  80. +41
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/TimestampLogEntity.java
  81. +33
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/repository/EntityRepository.java
  82. +36
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/repository/IntervalShardingAlgorithmRepository.java
  83. +41
    -0
      examples/cosid-example/src/main/java/me/ahoo/cosid/example/repository/OrderRepository.java
  84. +220
    -0
      examples/cosid-example/src/main/resources/application.yaml
  85. +12
    -0
      examples/cosid-example/src/main/resources/bootstrap.yaml
  86. +32
    -0
      examples/cosid-example/src/main/resources/mappers/IntervalShardingAlgorithmRepository.xml
  87. +47
    -0
      examples/cosid-example/src/main/resources/mappers/OrderMapper.xml
  88. +23
    -0
      gradle.properties
  89. BIN
      gradle/wrapper/gradle-wrapper.jar
  90. +5
    -0
      gradle/wrapper/gradle-wrapper.properties
  91. +244
    -0
      gradlew
  92. +92
    -0
      gradlew.bat
  93. +12
    -0
      lombok.config
  94. +5
    -0
      renovate.json
  95. +63
    -0
      settings.gradle.kts
  96. +199
    -0
      wiki/Distributed-ID.zh-CN.md
  97. +107
    -0
      wiki/SegmentChainId.zh-CN.md
  98. +696
    -0
      wiki/getting-started.md
  99. BIN
      wiki/img/Throughput-Of-IntervalShardingAlgorithm-PreciseShardingValue.png
  100. BIN
      wiki/img/Throughput-Of-IntervalShardingAlgorithm-RangeShardingValue.png

+ 15
- 0
.gitignore View File

@@ -0,0 +1,15 @@
.gradle
/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
/cosid-benchmark/.idea/**

+ 201
- 0
LICENSE View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.

"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:

(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and

(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and

(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and

(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.

You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [2021] [ahoo wang <ahoowang@qq.com>]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 720
- 0
README.md View File

@@ -0,0 +1,720 @@
<p align="center" style="text-align:center">
<img width="300" src="./document/docs/.vuepress/public/logo.png"/>
</p>

# [CosId](https://cosid.ahoo.me/) Universal, flexible, high-performance distributed ID generator

[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![GitHub release](https://img.shields.io/github/release/Ahoo-Wang/CosId.svg)](https://github.com/Ahoo-Wang/CosId/releases)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/me.ahoo.cosid/cosid-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/me.ahoo.cosid/cosid-core)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/dfd1d6237a1644409548ebfbca300dc1)](https://app.codacy.com/gh/Ahoo-Wang/CosId?utm_source=github.com&utm_medium=referral&utm_content=Ahoo-Wang/CosId&utm_campaign=Badge_Grade_Settings)
[![codecov](https://codecov.io/gh/Ahoo-Wang/CosId/branch/main/graph/badge.svg?token=L0N51NB7ET)](https://codecov.io/gh/Ahoo-Wang/CosId)
![Integration Test Status](https://github.com/Ahoo-Wang/CosId/actions/workflows/integration-test.yml/badge.svg)

> [中文文档](https://cosid.ahoo.me/)

## Introduction

*[CosId](https://github.com/Ahoo-Wang/CosId)* aims to provide a universal, flexible and high-performance distributed ID
generator.

- `CosIdGenerator` : Stand-alone *TPS performance:15,570,085 ops/s* , three times that of `UUID.randomUUID()`.
- `SnowflakeId` : Stand-alone *TPS performance:4,096,000 ops/s* [JMH Benchmark](#jmh-benchmark) , It mainly solves two major
problems of `SnowflakeId`: machine number allocation problem and clock backwards problem and provide a more friendly
and flexible experience.
- `SegmentId`: Get a segment (`Step`) ID every time to reduce the network IO request frequency of the `IdSegment`
distributor and improve performance.
- `IdSegmentDistributor`:
- `RedisIdSegmentDistributor`: `IdSegment` distributor based on *Redis*.
- `JdbcIdSegmentDistributor`: The *Jdbc-based* `IdSegment` distributor supports various relational databases.
- `ZookeeperIdSegmentDistributor`: `IdSegment` distributor based on *Zookeeper*.
- `SegmentChainId`(**recommend**):`SegmentChainId` (*lock-free*) is an enhancement of `SegmentId`, the design
diagram is as follows. `PrefetchWorker` maintains a `safe distance`, so that `SegmentChainId` achieves
approximately `AtomicLong` *TPS performance: 127,439,148+ ops/s* [JMH Benchmark](#jmh-benchmark) .
- `PrefetchWorker` maintains a safe distance (`safeDistance`), and supports dynamic `safeDistance` expansion and
contraction based on hunger status.

## SnowflakeId

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/Snowflake-identifier.png" alt="Snowflake"/>
</p>

> *SnowflakeId* is a distributed ID algorithm that uses `Long` (64-bit) bit partition to generate ID.
> The general bit allocation scheme is : `timestamp` (41-bit) + `machineId` (10-bit) + `sequence` (12-bit) = 63-bit。

- 41-bit `timestamp` = (1L<<41)/(1000/3600/365) approximately 69 years of timestamp can be stored, that is, the usable
absolute time is `EPOCH` + 69 years. Generally, we need to customize `EPOCH` 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-bit `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-bit `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 `4096000*1024=4194304000=4.19 billion (TPS)`.

It can be seen from the design of SnowflakeId:

- :thumbsup: The first 41-bit are a `timestamp`,So *SnowflakeId* is local monotonically increasing, and affected by
global clock synchronization *SnowflakeId* is global trend increasing.
- :thumbsup: `SnowflakeId` does not have a strong dependency on any third-party middleware, and its performance is also
very high.
- :thumbsup: The bit allocation scheme can be flexibly configured according to the needs of the business system to
achieve the optimal use effect.
- :thumbsdown: Strong reliance on the local clock, potential clock moved backwards problems will cause ID duplication.
- :thumbsdown: The `machineId` needs to be set manually. If the `machineId` is manually assigned during actual
deployment, it will be very inefficient.

---

*[CosId-SnowflakeId](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-core/src/main/java/me/ahoo/cosid/snowflake)*

It mainly solves two major problems of `SnowflakeId`: machine number allocation problem and clock backwards problem and
provide a more friendly and flexible experience.

### MachineIdDistributor

> Currently [CosId](https://github.com/Ahoo-Wang/CosId) provides the following three `MachineId` distributors.

#### ManualMachineIdDistributor

```yaml
cosid:
snowflake:
machine:
distributor:
type: manual
manual:
machine-id: 0
```

> Manually distribute `MachineId`

#### StatefulSetMachineIdDistributor

```yaml
cosid:
snowflake:
machine:
distributor:
type: stateful_set
```

> Use the stable identification ID provided by the `StatefulSet` of `Kubernetes` as the machine number.

#### RedisMachineIdDistributor

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/RedisMachineIdDistributor.png" alt="Redis Machine Id Distributor"/>
</p>
<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/Machine-Id-Safe-Guard.png" alt="Machine Id Safe Guard"/>
</p>

```yaml
cosid:
snowflake:
machine:
distributor:
type: redis
```

> Use *Redis* as the distribution store for the machine number.

### ClockBackwardsSynchronizer

```yaml
cosid:
snowflake:
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
```

The default `DefaultClockBackwardsSynchronizer` clock moved backwards synchronizer uses active wait synchronization
strategy, `spinThreshold` (default value 10 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.

### 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);
}
}
```

```yaml
cosid:
snowflake:
machine:
state-storage:
local:
state-location: ./cosid-machine-state/
```

The default `LocalMachineStateStorage` 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

```yaml
cosid:
snowflake:
share:
clock-sync: true
```

The default `SnowflakeId` will directly throw a `ClockBackwardsException` when a clock moved backwards 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

```java
SnowflakeId snowflakeId=SafeJavaScriptSnowflakeId.ofMillisecond(1);
```

The `Number.MAX_SAFE_INTEGER` of `JavaScript` has only 53-bit. 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.

### SnowflakeFriendlyId (Can parse `SnowflakeId` into a more readable `SnowflakeIdState`)

```yaml
cosid:
snowflake:
share:
friendly: true
```

```java
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;
}
```

```java
public interface SnowflakeFriendlyId extends SnowflakeId {
SnowflakeIdState friendlyId(long id);
SnowflakeIdState ofFriendlyId(String friendlyId);
default SnowflakeIdState friendlyId() {
long id = generate();
return friendlyId(id);
}
}
```

```java
SnowflakeFriendlyId snowflakeFriendlyId=new DefaultSnowflakeFriendlyId(snowflakeId);
SnowflakeIdState idState=snowflakeFriendlyId.friendlyId();
idState.getFriendlyId(); //20210623131730192-1-0
```

## SegmentId

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/SegmentId.png" alt="Segment Id"/>
</p>

### RedisIdSegmentDistributor

```yaml
cosid:
segment:
enabled: true
distributor:
type: redis
```

### JdbcIdSegmentDistributor

> Initialize the `cosid` table

```mysql
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;
```

```yaml
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
```

After enabling `enable-auto-init-id-segment:true`, the application will try to create the `idSegment` record when it
starts to avoid manual creation. Similar to the execution of the following initialization sql script, there is no need
to worry about misoperation, because `name` is the primary key.

```mysql
insert into cosid
(name, last_max_id, last_fetch_time)
value
('namespace.name', 0, unix_timestamp());
```

### SegmentChainId

![SegmentChainId](./docs/SegmentChainId.png)

```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
```

## IdGeneratorProvider

```yaml
cosid:
snowflake:
provider:
bizA:
# timestamp-bit:
sequence-bit: 12
bizB:
# timestamp-bit:
sequence-bit: 12
```

```java
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.

### CosIdPlugin (MyBatis Plugin)

> Kotlin DSL

``` kotlin
implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}")
```

```java

@Target({ElementType.FIELD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CosId {
String value() default IdGeneratorProvider.SHARE;
boolean friendlyId() default false;
}
```

```java
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;
}
}
```

```java

@Mapper
public interface OrderRepository {
@Insert("insert into t_table (id) value (#{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;
```

### ShardingSphere Plugin

> Kotlin DSL

``` kotlin
implementation("me.ahoo.cosid:cosid-shardingsphere:${cosidVersion}")
```

#### CosIdKeyGenerateAlgorithm (Distributed-Id)

```yaml
spring:
shardingsphere:
rules:
sharding:
key-generators:
cosid:
type: COSID
props:
id-name: __share__
```

#### Interval-based time range sharding algorithm

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/CosIdIntervalShardingAlgorithm.png" alt="CosIdIntervalShardingAlgorithm"/>
</p>

- Ease of use: supports multiple data types (`Long`/`LocalDateTime`/`DATE`/ `String` / `SnowflakeId`),The official
implementation is to first convert to a string and then convert to `LocalDateTime`, the conversion success rate is
affected by the time formatting characters.
- Performance: Compared to `org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm`
,The performance is *1200~4000* times higher.

| **PreciseShardingValue** | **RangeShardingValue** |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Throughput Of IntervalShardingAlgorithm - PreciseShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-IntervalShardingAlgorithm-PreciseShardingValue.png) | ![Throughput Of IntervalShardingAlgorithm - RangeShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-IntervalShardingAlgorithm-RangeShardingValue.png) |

- CosIdIntervalShardingAlgorithm
- type: COSID_INTERVAL

```yaml
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
```

#### CosIdModShardingAlgorithm

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/CosIdModShardingAlgorithm.png" alt="CosId Mod Sharding Algorithm"/>
</p>

- Performance: Compared to `org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm`
,The performance is *1200~4000* times higher.And it has higher stability and no serious performance degradation.

| **PreciseShardingValue** | **RangeShardingValue** |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Throughput Of ModShardingAlgorithm - PreciseShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-ModShardingAlgorithm-PreciseShardingValue.png) | ![Throughput Of ModShardingAlgorithm - RangeShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-ModShardingAlgorithm-RangeShardingValue.png) |

```yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_MOD
props:
mod: 4
logic-name-prefix: t_table_
```

## Examples
> 项目中根据使用的场景(`jdbc`/`proxy`/`redis-cosid`/`redis`/`shardingsphere`/`zookeeper`等)提供了对应的例子,实践过程中可以参照配置快速接入。

[点击查看Examples](https://github.com/Ahoo-Wang/CosId/tree/main/examples)

## Installation

### Gradle

> Kotlin DSL

``` kotlin
val cosidVersion = "1.14.5";
implementation("me.ahoo.cosid:cosid-spring-boot-starter:${cosidVersion}")
```

### Maven

```xml
<?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}
machine:
enabled: true
# stable: true
# machine-bit: 10
# instance-id: ${HOSTNAME}
distributor:
type: redis
# manual:
# machine-id: 0
snowflake:
enabled: true
# epoch: 1577203200000
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
share:
clock-sync: true
friendly: true
provider:
order_item:
# timestamp-bit:
sequence-bit: 12
snowflake:
sequence-bit: 12
safeJs:
machine-bit: 3
sequence-bit: 9
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:
order:
offset: 10000
step: 100
longId:
offset: 10000
step: 100
```

## JMH-Benchmark

- The development notebook : MacBook Pro (M1)
- All benchmark tests are carried out on the development notebook.
- Deploying Redis on the development notebook.

### SnowflakeId

``` shell
gradle cosid-core:jmh
# or
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
```

### Throughput (ops/s) of SegmentChainId

<p align="center" >
<img src="./document/docs/.vuepress/public/assets/perf/Throughput-Of-SegmentChainId.png" alt="Throughput-Of-SegmentChainId"/>
</p>

### Percentile-Sample (*P9999=0.208 us/op*) of SegmentChainId

> In statistics, a [percentile](https://en.wikipedia.org/wiki/Percentile) (or a centile) is a score below which a given percentage of scores in its frequency distribution falls (exclusive definition) or a score at or below which a given percentage falls (inclusive definition). For example, the 50th percentile (the median) is the score below which (exclusive) or at or below which (inclusive) 50% of the scores in the distribution may be found.

<p align="center" >
<img src="./document/docs/.vuepress/public/assets/perf/Percentile-Sample-Of-SegmentChainId.png" alt="Percentile-Sample-Of-SegmentChainId"/>
</p>

## Community Partners and Sponsors

<a href="https://www.jetbrains.com/?from=CosId" target="_blank">
<img src="./docs/jetbrains-logo.png" title="JetBrains" width=130 />
</a>

+ 363
- 0
README.zh-CN.md View File

@@ -0,0 +1,363 @@
<p align="center" style="text-align:center">
<img width="300" src="./document/docs/.vuepress/public/logo.png" alt="CosId Logo"/>
</p>

# [CosId](https://cosid.ahoo.me/) 通用、灵活、高性能分布式 ID 生成器

[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![GitHub release](https://img.shields.io/github/release/Ahoo-Wang/CosId.svg)](https://github.com/Ahoo-Wang/CosId/releases)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/me.ahoo.cosid/cosid-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/me.ahoo.cosid/cosid-core)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/dfd1d6237a1644409548ebfbca300dc1)](https://app.codacy.com/gh/Ahoo-Wang/CosId?utm_source=github.com&utm_medium=referral&utm_content=Ahoo-Wang/CosId&utm_campaign=Badge_Grade_Settings)
[![codecov](https://codecov.io/gh/Ahoo-Wang/CosId/branch/main/graph/badge.svg?token=L0N51NB7ET)](https://codecov.io/gh/Ahoo-Wang/CosId)
![Integration Test Status](https://github.com/Ahoo-Wang/CosId/actions/workflows/integration-test.yml/badge.svg)

> [English Document](https://github.com/Ahoo-Wang/CosId/blob/main/README.md)

## 简介

*[CosId](https://github.com/Ahoo-Wang/CosId)* 旨在提供通用、灵活、高性能的分布式 ID 生成器。

- `CosIdGenerator` : *单机 TPS 性能:1557W/s*,三倍于 `UUID.randomUUID()`。
- `SnowflakeId` : *单机 TPS 性能:409W/s* [JMH 基准测试](https://cosid.ahoo.me/guide/perf-test.html) , 主要解决 *时钟回拨问题* 、*机器号分配问题*、*取模分片不均匀问题* 并且提供更加友好、灵活的使用体验。
- `SegmentId`: 每次获取一段 (`Step`) ID,来降低号段分发器的网络IO请求频次提升性能。
- `IdSegmentDistributor`: 号段分发器(号段存储器)
- `RedisIdSegmentDistributor`: 基于 *Redis* 的号段分发器。
- `JdbcIdSegmentDistributor`: 基于 *Jdbc* 的号段分发器,支持各种关系型数据库。
- `ZookeeperIdSegmentDistributor`: 基于 *Zookeeper* 的号段分发器。
- `SegmentChainId`(**推荐**):`SegmentChainId` (*lock-free*) 是对 `SegmentId` 的增强。性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](https://cosid.ahoo.me/guide/perf-test.html) 。
- `PrefetchWorker` 维护安全距离(`safeDistance`), 并且支持基于饥饿状态的动态`safeDistance`扩容/收缩。

## [快速开始](https://cosid.ahoo.me/guide/getting-started.html)

## 背景(为什么需要*分布式ID*)

在软件系统演进过程中,随着业务规模的增长 (TPS/存储容量),我们需要通过集群化部署来分摊计算、存储压力。
应用服务的无状态设计使其具备了伸缩性。在使用 **Kubernetes** 部署时我们只需要一行命令即可完成服务伸缩
(`kubectl scale --replicas=5 deployment/order-service`)。

但对于有状态的数据库就不那么容易了,此时数据库变成系统的性能瓶颈是显而易见的。

### 分库分表

> 从微服务的角度来理解垂直拆分其实就是微服务拆分。以限界上下文来定义服务边界将大服务/单体应用拆分成多个自治的粒度更小的服务,因为自治性规范要求,数据库也需要进行业务拆分。
> 但垂直拆分后的单个微服务依然会面临 TPS/存储容量 的挑战,所以这里我们重点讨论水平拆分的方式。

<p align="center" >
<img src="document/docs/.vuepress/public/assets/shardingsphere/sharding-db.png" alt="分库分表"/>
</p>

数据库分库分表方案是逻辑统一,物理分区自治的方案。其核心设计在于中间层映射方案的设计 (上图 **Mapping**),即分片算法的设计。
几乎所有编程语言都内置实现了散列表(java:`HashMap`/csharp:`Dictionary`/python:`dict`/go:`map` ...)。分片算法跟散列表高度相似(`hashCode`),都得通过 `key`/`shardingValue` 映射到对应的槽位(`slot`)。

那么 `shardingValue` 从哪里来呢?**CosId**!!!

> 当然还有很多分布式场景需要*分布式ID*,这里不再一一列举。

## 分布式ID方案的核心指标

- **全局(相同业务)唯一性**:唯一性保证是**ID**的必要条件,假设ID不唯一就会产生主键冲突,这点很容易可以理解。
- 通常所说的全局唯一性并不是指所有业务服务都要唯一,而是相同业务服务不同部署副本唯一。
比如 Order 服务的多个部署副本在生成`t_order`这张表的`Id`时是要求全局唯一的。至于`t_order_item`生成的`ID`与`t_order`是否唯一,并不影响唯一性约束,也不会产生什么副作用。
不同业务模块间也是同理。即唯一性主要解决的是ID冲突问题。
- **有序性**:有序性保证是面向查询的数据结构算法(除了Hash算法)所必须的,是**二分查找法**(分而治之)的前提。
- MySq-InnoDB B+树是使用最为广泛的,假设 Id 是无序的,B+ 树 为了维护 ID 的有序性,就会频繁的在索引的中间位置插入而挪动后面节点的位置,甚至导致频繁的页分裂,这对于性能的影响是极大的。那么如果我们能够保证ID的有序性这种情况就完全不同了,只需要进行追加写操作。所以 ID 的有序性是非常重要的,也是ID设计不可避免的特性。
- **吞吐量/性能(ops/time)**:即单位时间(每秒)能产生的ID数量。生成ID是非常高频的操作,也是最为基本的。假设ID生成的性能缓慢,那么不管怎么进行系统优化也无法获得更好的性能。
- 一般我们会首先生成ID,然后再执行写入操作,假设ID生成缓慢,那么整体性能上限就会受到限制,这一点应该不难理解。
- **稳定性(time/op)**:稳定性指标一般可以采用**每个操作的时间进行百分位采样**来分析,比如 *[CosId](https://github.com/Ahoo-Wang/CosId)* 百分位采样 **P9999=0.208 us/op**,即 **0% ~ 99.99%** 的单位操作时间小于等于 **0.208 us/op**。
- [百分位数 WIKI](https://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E4%BD%8D%E6%95%B0) :统计学术语,若将一组数据从小到大排序,并计算相应的累计百分点,则某百分点所对应数据的值,就称为这百分点的百分位数,以Pk表示第k百分位数。百分位数是用来比较个体在群体中的相对地位量数。
- 为什么不用平均*每个操作的时间*:马老师的身价跟你的身价能平均么?平均后的值有意义不?
- 可以使用最小*每个操作的时间*、最大*每个操作的时间*作为参考吗?因为最小、最大值只说明了零界点的情况,虽说可以作为稳定性的参考,但依然不够全面。而且*百分位数*已经覆盖了这俩个指标。
- **自治性(依赖)**:主要是指对外部环境有无依赖,比如**号段模式**会强依赖第三方存储中间件来获取`NexMaxId`。自治性还会对可用性造成影响。
- **可用性**:分布式ID的可用性主要会受到自治性影响,比如**SnowflakeId**会受到时钟回拨影响,导致处于短暂时间的不可用状态。而**号段模式**会受到第三方发号器(`NexMaxId`)的可用性影响。
- [可用性 WIKI](https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%94%A8%E6%80%A7) :在一个给定的时间间隔内,对于一个功能个体来讲,总的可用时间所占的比例。
- MTBF:平均故障间隔
- MDT:平均修复/恢复时间
- Availability=MTBF/(MTBF+MDT)
- 假设MTBF为1年,MDT为1小时,即`Availability=(365*24)/(365*24+1)=0.999885857778792≈99.99%`,也就是我们通常所说对可用性4个9。
- **适应性**:是指在面对外部环境变化的自适应能力,这里我们主要说的是面对流量突发时动态伸缩分布式ID的性能,
- **SegmentChainId**可以基于**饥饿状态**进行**安全距离**的动态伸缩。
- **SnowflakeId**常规位分配方案性能恒定409.6W,虽然可以通过调整位分配方案来获得不同的TPS性能,但是位分配方法的变更是破坏性的,一般根据业务场景确定位分配方案后不再变更。
- **存储空间**:还是用MySq-InnoDB B+树来举例,普通索引(二级索引)会存储主键值,主键越大占用的内存缓存、磁盘空间也会越大。Page页存储的数据越少,磁盘IO访问的次数会增加。总之在满足业务需求的情况下,尽可能小的存储空间占用在绝大多数场景下都是好的设计原则。

### 不同分布式ID方案核心指标对比

| 分布式ID | 全局唯一性 | 有序性 | 吞吐量 | 稳定性(1s=1000,000us) | 自治性 | 可用性 | 适应性 | 存储空间 |
|:--------------:|:-----:|:---------------------------:|-----------------:|:--------------------|:----------:|:----------------------------------------:|:---:|:-------:|
| UUID/GUID | 是 | 完全无序 | 3078638(ops/s) | P9999=0.325(us/op) | 完全自治 | 100% | 否 | 128-bit |
| SnowflakeId | 是 | 本地单调递增,全局趋势递增(受全局时钟影响) | 4096000(ops/s) | P9999=0.244(us/op) | 依赖时钟 | 时钟回拨会导致短暂不可用 | 否 | 64-bit |
| SegmentId | 是 | 本地单调递增,全局趋势递增(受Step影响) | 29506073(ops/s) | P9999=46.624(us/op) | 依赖第三方号段分发器 | 受号段分发器可用性影响 | 否 | 64-bit |
| SegmentChainId | 是 | 本地单调递增,全局趋势递增(受Step、安全距离影响) | 127439148(ops/s) | P9999=0.208(us/op) | 依赖第三方号段分发器 | 受号段分发器可用性影响,但因安全距离存在,预留ID段,所以高于SegmentId | 是 | 64-bit |

### 有序性(要想分而治之·二分查找法,必须要维护我)

刚刚我们已经讨论了ID有序性的重要性,所以我们设计ID算法时应该尽可能地让ID是单调递增的,比如像表的自增主键那样。但是很遗憾,因全局时钟、性能等分布式系统问题,我们通常只能选择局部单调递增、全局趋势递增的组合(就像我们在分布式系统中不得不的选择最终一致性那样)以获得多方面的权衡。下面我们来看一下什么是单调递增与趋势递增。

#### 有序性之单调递增

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/monotonically-increasing.png" alt="单调递增"/>
</p>

单调递增:T表示全局绝对时点,假设有T<sub>n+1</sub>>T<sub>n</sub>(绝对时间总是往前进的,这里不考虑相对论、时间机器等),那么必然有F(T<sub>n+1</sub>)>F(T<sub>n</sub>),数据库自增主键就属于这一类。
另外需要特别说明的是单调递增跟连续性递增是不同的概念。 连续性递增:`F(n+1)=(F(n)+step)`即下一次获取的ID一定等于当前`ID+Step`,当`Step=1`时类似于这样一个序列:`1->2->3->4->5`。

> 扩展小知识:数据库的自增主键也不是连续性递增的,相信你一定遇到过这种情况,请思考一下数据库为什么这样设计?

#### 有序性之趋势递增

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/trend-increasing.png" alt="趋势递增"/>
</p>

趋势递增:T<sub>n</sub>>T<sub>n-s</sub>,那么大概率有F(T<sub>n</sub>)>F(T<sub>n-s</sub>)。虽然在一段时间间隔内有乱序,但是整体趋势是递增。从上图上看,是有上升趋势的(趋势线)。
- 在**SnowflakeId**中<sub>n-s</sub>受到全局时钟同步影响。
- 在号段模式(**SegmentId**)中<sub>n-s</sub>受到号段可用区间(`Step`)影响。

## 分布式ID分配方案

### UUID/GUID

- :thumbsup:不依赖任何第三方中间件
- :thumbsup:性能高
- :thumbsdown:完全无序
- :thumbsdown:空间占用大,需要占用128位存储空间。

UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据库的主键索引效率低下(为了维护索引树,频繁的索引中间位置插入数据,而不是追加写)。这也是UUID不适用于数据库主键的最为重要的原因。

### SnowflakeId

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/Snowflake-identifier.png" alt="Snowflake 雪花算法"/>
</p>

> *SnowflakeId*使用`Long`(64-bit)位分区来生成ID的一种分布式ID算法。
> 通用的位分配方案为:`timestamp`(41-bit)+`machineId`(10-bit)+`sequence`(12-bit)=63-bit。

- 41-bit`timestamp`=(1L<<41)/(1000/3600/365),约可以存储69年的时间戳,即可以使用的绝对时间为`EPOCH`+69年,一般我们需要自定义`EPOCH`为产品开发时间,另外还可以通过压缩其他区域的分配位数,来增加时间戳位数来延长可用时间。
- 10-bit`machineId`=(1L<<10)=1024,即相同业务可以部署1024个副本(在Kubernetes概念里没有主从副本之分,这里直接沿用Kubernetes的定义)。一般情况下没有必要使用这么多位,所以会根据部署规模需要重新定义。
- 12-bit`sequence`=(1L<<12)*1000=4096000,即单机每秒可生成约409W的ID,全局同业务集群可产生`4096000*1024=419430W=41.9亿(TPS)`。

从 *SnowflakeId* 设计上可以看出:

- :thumbsup:`timestamp`在高位,单实例*SnowflakeId*是会保证时钟总是向前的(校验本机时钟回拨),所以是本机单调递增的。受全局时钟同步/时钟回拨影响*SnowflakeId*是全局趋势递增的。
- :thumbsup:*SnowflakeId*不对任何第三方中间件有强依赖关系,并且性能也非常高。
- :thumbsup:位分配方案可以按照业务系统需要灵活配置,来达到最优使用效果。
- :thumbsdown:强依赖本机时钟,潜在的时钟回拨问题会导致ID重复、处于短暂的不可用状态。
- :thumbsdown:`machineId`需要手动设置,实际部署时如果采用手动分配`machineId`,会非常低效。

#### SnowflakeId之机器号分配问题

在**SnowflakeId**中根据业务设计的位分配方案确定了基本上就不再有变更了,也很少需要维护。但是`machineId`总是需要配置的,而且集群中是不能重复的,否则分区原则就会被破坏而导致ID唯一性原则破坏,当集群规模较大时`machineId`的维护工作是非常繁琐,低效的。
> 有一点需要特别说明的,**SnowflakeId**的**MachineId**是逻辑上的概念,而不是物理概念。
> 想象一下假设**MachineId**是物理上的,那么意味着一台机器拥有只能拥有一个**MachineId**,那会产生什么问题呢?

> 目前 *[CosId](https://github.com/Ahoo-Wang/CosId)* 提供了以下五种 `MachineId` 分配器。

- ManualMachineIdDistributor: 手动配置`machineId`,一般只有在集群规模非常小的时候才有可能使用,不推荐。
- StatefulSetMachineIdDistributor: 使用`Kubernetes`的`StatefulSet`提供的稳定的标识ID(HOSTNAME=service-01)作为机器号。
- RedisMachineIdDistributor: 使用**Redis**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。
- JdbcMachineIdDistributor: 使用**关系型数据库**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。
- ZookeeperMachineIdDistributor: 使用**ZooKeeper**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/RedisMachineIdDistributor.png" alt="Redis MachineId Distributor"/>
</p>

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/Machine-Id-Safe-Guard.png" alt="Machine Id Safe Guard"/>
</p>

#### SnowflakeId之时钟回拨问题

时钟回拨的致命问题是会导致ID重复、冲突(这一点不难理解),ID重复显然是不能被容忍的。
在**SnowflakeId**算法中,按照**MachineId**分区ID,我们不难理解的是不同**MachineId**是不可能产生相同ID的。所以我们解决的时钟回拨问题是指当前**MachineId**的时钟回拨问题,而不是所有集群节点的时钟回拨问题。

**MachineId**时钟回拨问题大体可以分为俩种情况:

- 运行时时钟回拨:即在运行时获取的当前时间戳比上一次获取的时间戳小。这个场景的时钟回拨是很容易处理的,一般**SnowflakeId**代码实现时都会存储`lastTimestamp`用于运行时时钟回拨的检查,并抛出时钟回拨异常。
- 时钟回拨时直接抛出异常是不太好地实践,因为下游使用方几乎没有其他处理方案(噢,我还能怎么办呢,等吧),时钟同步是唯一的选择,当只有一种选择时就不要再让用户选择了。
- `ClockSyncSnowflakeId`是`SnowflakeId`的包装器,当发生时钟回拨时会使用`ClockBackwardsSynchronizer`主动等待时钟同步来重新生成ID,提供更加友好的使用体验。
- 启动时时钟回拨:即在启动服务实例时获取的当前时钟比上次关闭服务时小。此时的`lastTimestamp`是无法存储在进程内存中的。当获取的外部存储的**机器状态**大于当前时钟时钟时,会使用`ClockBackwardsSynchronizer`主动同步时钟。
- LocalMachineStateStorage:使用本地文件存储`MachineState`(机器号、最近一次时间戳)。因为使用的是本地文件所以只有当实例的部署环境是稳定的,`LocalMachineStateStorage`才适用。
- RedisMachineIdDistributor:将`MachineState`存储在**Redis**分布式缓存中,这样可以保证总是可以获取到上次服务实例停机时**机器状态**。

#### SnowflakeId之JavaScript数值溢出问题

`JavaScript`的`Number.MAX_SAFE_INTEGER`只有53-bit,如果直接将63位的`SnowflakeId`返回给前端,那么会产生值溢出的情况(所以这里我们应该知道后端传给前端的`long`值溢出问题,**迟早**会出现,只不过SnowflakeId出现得更快而已)。
很显然溢出是不能被接受的,一般可以使用以下俩种处理方案:
- 将生成的63-bit`SnowflakeId`转换为`String`类型。
- 直接将`long`转换成`String`。
- 使用`SnowflakeFriendlyId`将`SnowflakeId`转换成比较友好的字符串表示:`{timestamp}-{machineId}-{sequence} -> 20210623131730192-1-0`
- 自定义`SnowflakeId`位分配来缩短`SnowflakeId`的位数(53-bit)使 `ID` 提供给前端时不溢出
- 使用`SafeJavaScriptSnowflakeId`(`JavaScript` 安全的 `SnowflakeId`)

## 号段模式(SegmentId)

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/SegmentId.png" alt="Segment Id"/>
</p>

从上面的设计图中,不难看出**号段模式**基本设计思路是通过每次获取一定长度(Step)的可用ID(Id段/号段),来降低网络IO请求次数,提升性能。

- :thumbsdown:强依赖第三方号段分发器,可用性受到第三方分发器影响。
- :thumbsdown:每次号段用完时获取`NextMaxId`需要进行网络IO请求,此时的性能会比较低。
- 单实例ID单调递增,全局趋势递增。
- 从设计图中不难看出**Instance 1**每次获取的`NextMaxId`,一定比上一次大,意味着下一次的号段一定比上一次大,所以从单实例上来看是单调递增的。
- 多实例各自持有的不同的号段,意味着同一时刻不同实例生成的ID是乱序的,但是整体趋势的递增的,所以全局趋势递增。
- ID乱序程度受到Step长度以及集群规模影响(从趋势递增图中不难看出)。
- 假设集群中只有一个实例时**号段模式**就是单调递增的。
- `Step`越小,乱序程度越小。当`Step=1`时,将无限接近单调递增。需要注意的是这里是无限接近而非等于单调递增,具体原因你可以思考一下这样一个场景:
- 号段分发器T<sub>1</sub>时刻给**Instance 1**分发了`ID=1`,T<sub>2</sub>时刻给**Instance 2**分发了`ID=2`。因为机器性能、网络等原因,`Instance 2`网络IO写请求先于`Instance 1`到达。那么这个时候对于数据库来说,ID依然是乱序的。

## 号段链模式(SegmentChainId)

[分布式ID(CosId)之号段链模式性能(1.2亿/s)解析](https://cosid.ahoo.me/guide/segment-chain.html)

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/SegmentChainId.png" alt="Segment Chain Id"/>
</p>

**SegmentChainId**是**SegmentId**增强版,相比于**SegmentId**有以下优势:

- 稳定性:**SegmentId**的稳定性问题(P9999=46.624(us/op))主要是因为号段用完之后同步进行`NextMaxId`的获取导致的(会产生网络IO)。
- **SegmentChainId** (P9999=0.208(us/op))引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取,性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark) 。
- 适应性:从**SegmentId**介绍中我们知道了影响**ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。
- `Step`应该近可能小才能使得**ID单调递增**的可能性增大。
- `Step`太小会影响吞吐量,那么我们如何合理设置`Step`呢?答案是我们无法准确预估所有时点的吞吐量需求,那么最好的办法是吞吐量需求高时,Step自动增大,吞吐量低时Step自动收缩。
- **SegmentChainId**引入了**饥饿状态**的概念,**PrefetchWorker**会根据**饥饿状态**检测当前**安全距离**是否需要膨胀或者收缩,以便获得吞吐量与有序性之间的权衡,这便是**SegmentChainId**的自适应性。

## 集成

### CosIdPlugin(MyBatis 插件)

> Kotlin DSL

``` kotlin
implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}")
```

```java
public class Order {

@CosId(value = "order")
private Long orderId;
private Long userId;

public Long getOrderId() {
return orderId;
}

public void setOrderId(Long orderId) {
this.orderId = orderId;
}

public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}
}
```

### ShardingSphere 插件

> `CosIdKeyGenerateAlgorithm`、`CosIdModShardingAlgorithm`、`CosIdIntervalShardingAlgorithm` 已合并至 [ShardingSphere](https://github.com/apache/shardingsphere/pull/14132) 官方,未来 *[cosid-shardingsphere](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-shardingsphere)* 模块的维护可能会以官方为主。

> Kotlin DSL

``` kotlin
implementation("me.ahoo.cosid:cosid-shardingsphere:${cosidVersion}")
```

#### CosIdKeyGenerateAlgorithm (分布式主键)

```yaml
spring:
shardingsphere:
rules:
sharding:
key-generators:
cosid:
type: COSID
props:
id-name: __share__
```

#### 基于间隔的时间范围分片算法

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/CosIdIntervalShardingAlgorithm.png" alt="CosId Interval Sharding Algorithm"/>
</p>

- 易用性: 支持多种数据类型 (`Long`/`LocalDateTime`/`DATE`/ `String` / `SnowflakeId`),而官方实现是先转换成字符串再转换成`LocalDateTime`,转换成功率受时间格式化字符影响。
- 性能 : 相比于 `org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm` 性能高出 *1200~4000* 倍。

| **PreciseShardingValue** | **RangeShardingValue** |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Throughput Of IntervalShardingAlgorithm - PreciseShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-IntervalShardingAlgorithm-PreciseShardingValue.png) | ![Throughput Of IntervalShardingAlgorithm - RangeShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-IntervalShardingAlgorithm-RangeShardingValue.png) |

- CosIdIntervalShardingAlgorithm
- type: COSID_INTERVAL
- SnowflakeIntervalShardingAlgorithm
- type: COSID_INTERVAL_SNOWFLAKE

```yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_INTERVAL_{type_suffix}
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
```

#### 取模分片算法

<p align="center">
<img src="./document/docs/.vuepress/public/assets/design/CosIdModShardingAlgorithm.png" alt="CosIdModShardingAlgorithm"/>
</p>

- 性能 : 相比于 `org.apache.shardingsphere.sharding.algorithm.sharding.mod.ModShardingAlgorithm` 性能高出 *1200~4000* 倍。并且稳定性更高,不会出现严重的性能退化。

| **PreciseShardingValue** | **RangeShardingValue** |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Throughput Of ModShardingAlgorithm - PreciseShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-ModShardingAlgorithm-PreciseShardingValue.png) | ![Throughput Of ModShardingAlgorithm - RangeShardingValue](./document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-ModShardingAlgorithm-RangeShardingValue.png) |

```yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_MOD
props:
mod: 4
logic-name-prefix: t_table_
```

## 性能测试报告

### SegmentChainId-吞吐量 (ops/s)

<p align="center" >
<img src="./document/docs/.vuepress/public/assets/perf/Throughput-Of-SegmentChainId.png" alt="Throughput-Of-SegmentChainId"/>
</p>

### SegmentChainId-每次操作耗时的百分位数(us/op)

> [百分位数](https://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E4%BD%8D%E6%95%B0) ,统计学术语,若将一组数据从小到大排序,并计算相应的累计百分点,则某百分点所对应数据的值,就称为这百分点的百分位数,以Pk表示第k百分位数。百分位数是用来比较个体在群体中的相对地位量数。

<p align="center" >
<img src="./document/docs/.vuepress/public/assets/perf/Percentile-Sample-Of-SegmentChainId.png" alt="Percentile-Sample-Of-SegmentChainId"/>
</p>

+ 245
- 0
build.gradle.kts View File

@@ -0,0 +1,245 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id("io.github.gradle-nexus.publish-plugin")
id("me.champeau.jmh")
`java-library`
jacoco
}

val bomProjects = setOf(
project(":cosid-bom"),
project(":cosid-dependencies")
)

val coreProjects = setOf(
project(":cosid-core")
)
val serverProjects = setOf(
project(":cosid-example"),
project(":cosid-example-jdbc"),
project(":cosid-example-proxy"),
project(":cosid-example-redis"),
project(":cosid-example-redis-cosid"),
project(":cosid-example-zookeeper"),
project(":cosid-example-shardingsphere"),
project(":cosid-proxy-server")
)

val testProject = project(":cosid-test")
val publishProjects = subprojects - serverProjects
val libraryProjects = publishProjects - bomProjects

ext {
set("libraryProjects", libraryProjects)
}

allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}

configure(bomProjects) {
apply<JavaPlatformPlugin>()
configure<JavaPlatformExtension> {
allowDependencies()
}
}

configure(libraryProjects) {
apply<CheckstylePlugin>()
configure<CheckstyleExtension> {
toolVersion = "9.2.1"
}
apply<com.github.spotbugs.snom.SpotBugsPlugin>()
configure<com.github.spotbugs.snom.SpotBugsExtension> {
excludeFilter.set(file("${rootDir}/config/spotbugs/exclude.xml"))
}
apply<JacocoPlugin>()
apply<JavaLibraryPlugin>()
configure<JavaPluginExtension> {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
withJavadocJar()
withSourcesJar()
}
apply<me.champeau.jmh.JMHPlugin>()
configure<me.champeau.jmh.JmhParameters> {
val delimiter = ',';
val jmhIncludesKey = "jmhIncludes"
val jmhExcludesKey = "jmhExcludes"
val jmhThreadsKey = "jmhThreads"
val jmhModeKey = "jmhMode"

if (project.hasProperty(jmhIncludesKey)) {
val jmhIncludes = project.properties[jmhIncludesKey].toString().split(delimiter)
includes.set(jmhIncludes)
}
if (project.hasProperty(jmhExcludesKey)) {
val jmhExcludes = project.properties[jmhExcludesKey].toString().split(delimiter)
excludes.set(jmhExcludes)
}

warmupIterations.set(1)
iterations.set(1)
resultFormat.set("json")

var jmhMode = listOf(
"thrpt"
)
if (project.hasProperty(jmhModeKey)) {
jmhMode = project.properties[jmhModeKey].toString().split(delimiter)
}
benchmarkMode.set(jmhMode)
var jmhThreads = 1
if (project.hasProperty(jmhThreadsKey)) {
jmhThreads = Integer.valueOf(project.properties[jmhThreadsKey].toString())
}
threads.set(jmhThreads)
fork.set(1)
jvmArgs.set(listOf("-Dlogback.configurationFile=${rootProject.rootDir}/config/logback-jmh.xml"))
}

tasks.withType<Test> {
useJUnitPlatform()
// fix logging missing code for JacocoPlugin
jvmArgs = listOf("-Dlogback.configurationFile=${rootProject.rootDir}/config/logback.xml")
}

dependencies {
api(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
testAnnotationProcessor(platform(project(":cosid-dependencies")))
jmh(platform(project(":cosid-dependencies")))
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
testCompileOnly("org.projectlombok:lombok")
testAnnotationProcessor("org.projectlombok:lombok")
implementation("com.google.guava:guava")
implementation("org.slf4j:slf4j-api")
testImplementation("ch.qos.logback:logback-classic")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testImplementation("org.junit-pioneer:junit-pioneer")
testImplementation("org.hamcrest:hamcrest")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
jmh("org.openjdk.jmh:jmh-core")
jmh("org.openjdk.jmh:jmh-generator-annprocess")
}
}

configure(publishProjects) {
val isBom = bomProjects.contains(this)
apply<MavenPublishPlugin>()
apply<SigningPlugin>()
configure<PublishingExtension> {
repositories {
maven {
name = "projectBuildRepo"
url = uri(layout.buildDirectory.dir("repos"))
}
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/Ahoo-Wang/CosId")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
publications {
val publishName = if (isBom) "mavenBom" else "mavenLibrary"
val publishComponentName = if (isBom) "javaPlatform" else "java"
create<MavenPublication>(publishName) {
from(components[publishComponentName])
pom {
name.set(rootProject.name)
description.set(getPropertyOf("description"))
url.set(getPropertyOf("website"))
issueManagement {
system.set("GitHub")
url.set(getPropertyOf("issues"))
}
scm {
url.set(getPropertyOf("website"))
connection.set(getPropertyOf("vcs"))
}
licenses {
license {
name.set(getPropertyOf("license_name"))
url.set(getPropertyOf("license_url"))
distribution.set("repo")
}
}
developers {
developer {
id.set("ahoo-wang")
name.set("ahoo wang")
organization {
url.set(getPropertyOf("website"))
}
}
}
}
}
}
}

configure<SigningExtension> {
val isInCI = null != System.getenv("CI");
if (isInCI) {
val signingKeyId = System.getenv("SIGNING_KEYID")
val signingKey = System.getenv("SIGNING_SECRETKEY")
val signingPassword = System.getenv("SIGNING_PASSWORD")
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
}

if (isBom) {
sign(extensions.getByType(PublishingExtension::class).publications.get("mavenBom"))
} else {
sign(extensions.getByType(PublishingExtension::class).publications.get("mavenLibrary"))
}
}
}

nexusPublishing {
repositories {
sonatype {
username.set(System.getenv("MAVEN_USERNAME"))
password.set(System.getenv("MAVEN_PASSWORD"))
}
}
}

fun getPropertyOf(name: String) = project.properties[name]?.toString()

tasks.register<JacocoReport>("codeCoverageReport") {
executionData(fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec"))
libraryProjects.forEach {
dependsOn(it.tasks.test)
if (testProject != it) {
sourceSets(it.sourceSets.main.get())
}
}
reports {
xml.required.set(true)
html.outputLocation.set(file("${buildDir}/reports/jacoco/report.xml"))
csv.required.set(false)
html.required.set(true)
html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/"))
}
}

+ 18
- 0
codecov.yml View File

@@ -0,0 +1,18 @@
coverage:
range: 60..100
round: down
precision: 2
status:
patch:
default:
target: 60%
threshold: 1%
project:
default:
target: 80%
threshold: 1%
ignore:
- "cosid-test/.*"
- "examples/.*"
- "cosid-benchmark/.*"
- "cosid-proxy-server/.*"

BIN
cosid-proxy/src/main/java/me/ahoo/cosid/proxy/doc-files/CosId-Proxy.png View File

Before After
Width: 481  |  Height: 391  |  Size: 52 KiB Width: 481  |  Height: 391  |  Size: 68 KiB

+ 78
- 0
examples/cosid-example-jdbc/build.gradle.kts View File

@@ -0,0 +1,78 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.jdbc.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-jackson"))

//region cosid-mybatis
implementation(project(":cosid-mybatis"))
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter")
//endregion

//region cosid-jdbc
implementation(project(":cosid-jdbc"))
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("com.mysql:mysql-connector-j")
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-shardingsphere"))
implementation("org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter")
//endregion

implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:2.6.9")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 105
- 0
examples/cosid-example-jdbc/init-script/init-example-schemal.sql View File

@@ -0,0 +1,105 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
create database if not exists cosid_db;
use cosid_db;

create table if not exists cosid
(
name varchar(100) not null comment '{namespace}.{name}',
last_max_id bigint unsigned not null default 0,
last_fetch_time bigint unsigned not null default 0,
constraint cosid_pk
primary key (name)
) engine = InnoDB;

insert into cosid
(name, last_max_id, last_fetch_time)
value
('namespace.name', 0, unix_timestamp());

create table if not exists cosid_machine
(
name varchar(100) not null comment '{namespace}.{machine_id}',
namespace varchar(100) not null,
machine_id integer unsigned not null default 0,
last_timestamp bigint unsigned not null default 0,
instance_id varchar(100) not null default '',
distribute_time bigint unsigned not null default 0,
revert_time bigint unsigned not null default 0,
constraint cosid_machine_pk
primary key (name)
) engine = InnoDB;

create index idx_namespace on cosid_machine (namespace);
create index idx_instance_id on cosid_machine (instance_id);

create table t_friendly_table
(
id varchar(25) not null primary key
);

create table t_table
(
id bigint not null primary key
);


create table t_order
(
order_id bigint not null primary key,
user_id bigint not null
);

create table t_order_item
(
order_item_id bigint not null primary key,
order_id bigint not null
);


create table t_user_0
(
id bigint not null
primary key,
age int null,
name varchar(50) null,
create_time datetime null
);

create table t_user_1
(
id bigint not null
primary key,
age int null,
name varchar(50) null,
create_time datetime null
);

create table t_user_2
(
id bigint not null
primary key,
age int null,
name varchar(50) null,
create_time datetime null
);

create table t_user_3
(
id bigint not null
primary key,
age int null,
name varchar(50) null,
create_time datetime null
);


+ 29
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer .
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 26
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/config/AppConfig.java View File

@@ -0,0 +1,26 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.config;

import org.springframework.context.annotation.Configuration;

/**
* AppConfig.
*
* @author Rocher Kong
*/
@Configuration
public class AppConfig {

}

+ 56
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/EntityController.java View File

@@ -0,0 +1,56 @@
package me.ahoo.cosid.example.jdbc.controller;

import me.ahoo.cosid.example.jdbc.dto.AsStringDto;
import me.ahoo.cosid.example.jdbc.entity.FriendlyIdEntity;
import me.ahoo.cosid.example.jdbc.entity.LongIdEntity;
import me.ahoo.cosid.example.jdbc.repository.EntityRepository;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* Entity Controller.
*
* @author Rocher Kong
*/
@RestController
@RequestMapping("test")
public class EntityController {

private final EntityRepository entityRepository;

public EntityController(EntityRepository entityRepository) {
this.entityRepository = entityRepository;
}

@PostMapping("/long")
public LongIdEntity longId() {
LongIdEntity entity = new LongIdEntity();
entityRepository.insert(entity);
/**
* {
* "id": 208796080181248
* }
*/
return entity;
}

@PostMapping("/friendly")
public FriendlyIdEntity friendly() {
FriendlyIdEntity entity = new FriendlyIdEntity();
entityRepository.insertFriendly(entity);
return entity;
}

@PostMapping("/asStringDto")
public AsStringDto getJacksonDto() {
AsStringDto dto = new AsStringDto();
dto.setId(123456L);
dto.setFriendlyId(123456L);
dto.setRadixPadStartId(123456L);
dto.setRadixPadStartCharSize10Id(123456L);
dto.setRadixId(123456L);
return dto;
}
}

+ 88
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/IdController.java View File

@@ -0,0 +1,88 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.controller;

import me.ahoo.cosid.IdGenerator;
import me.ahoo.cosid.provider.DefaultIdGeneratorProvider;
import me.ahoo.cosid.provider.IdGeneratorProvider;
import me.ahoo.cosid.snowflake.SafeJavaScriptSnowflakeId;
import me.ahoo.cosid.snowflake.SnowflakeFriendlyId;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

/**
* IdController.
*
* @author ahoo wang
*/
@RestController
@RequestMapping("ids")
public class IdController {
private final IdGeneratorProvider provider;

public IdController() {
this.provider = DefaultIdGeneratorProvider.INSTANCE;
}

private IdGenerator getIdGenerator(String name) {
Preconditions.checkNotNull(name, "name can not be null");
Optional<IdGenerator> optionalIdGenerator = provider.get(name);
if (!optionalIdGenerator.isPresent()) {
throw new IllegalArgumentException(Strings.lenientFormat("idGenerator:[%s] not fond.", name));
}
return optionalIdGenerator.get();
}

@GetMapping
public long generate() {
return provider
.getShare()
.generate();
}

@GetMapping("/as-string")
public String generateAsString() {
return provider
.getShare()
.generateAsString();
}


@GetMapping("{name}")
public Object generateByName(@PathVariable String name) {
IdGenerator idGenerator = getIdGenerator(name);
long id = idGenerator.generate();
if (SafeJavaScriptSnowflakeId.isSafeJavaScript(id)) {
return id;
}
return String.valueOf(id);
}

@GetMapping("{name}/friendlyId")
public String friendlyId(@PathVariable String name) {
IdGenerator idGenerator = getIdGenerator(name);
if (idGenerator instanceof SnowflakeFriendlyId) {
return idGenerator.generateAsString();
}
throw new IllegalArgumentException(Strings.lenientFormat("idGenerator:[%s] is not SnowflakeFriendlyId.", name));
}

}

+ 73
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/OrderController.java View File

@@ -0,0 +1,73 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.controller;


import me.ahoo.cosid.example.jdbc.entity.Order;
import me.ahoo.cosid.example.jdbc.entity.OrderItem;
import me.ahoo.cosid.example.jdbc.repository.OrderRepository;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.base.Splitter;

import java.util.List;
import java.util.stream.Collectors;


/**
* OrderController.
*
* @author Rocher Kong
*/
@RestController
@RequestMapping("order")
public class OrderController {
private final OrderRepository orderRepository;

public OrderController(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}

@Transactional
@PostMapping("/{userId}")
public long create(@PathVariable long userId) {
Order order = new Order();
order.setUserId(userId);
orderRepository.insert(order);
OrderItem orderItem1 = new OrderItem();
orderItem1.setOrderId(order.getOrderId());
OrderItem orderItem2 = new OrderItem();
orderItem2.setOrderId(order.getOrderId());
orderRepository.insertItem(orderItem1);
orderRepository.insertItem(orderItem2);
return order.getOrderId();
}

@GetMapping("/{id}")
public Order get(@PathVariable long id) {
return orderRepository.getById(id);
}

@GetMapping("/list/{ids}")
public List<Order> get(@PathVariable String ids) {
List<Long> idList = Splitter.on(",").omitEmptyStrings().trimResults().splitToStream(ids).map(Long::parseLong).collect(Collectors.toList());
return orderRepository.getByIds(idList);
}

}

+ 34
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/controller/UserController.java View File

@@ -0,0 +1,34 @@
package me.ahoo.cosid.example.jdbc.controller;

import me.ahoo.cosid.example.jdbc.entity.User;
import me.ahoo.cosid.example.jdbc.repository.UserRepository;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

/**
* User Controller.
*
* @author : Rocher Kong
*/
@RestController
@RequestMapping("user")
public class UserController {
private final UserRepository userRepository;

public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}

@GetMapping("save")
public void save() {
User user = new User();
user.setAge(33);
user.setName("Rocher");
user.setCreateTime(LocalDateTime.now());
userRepository.insert(user);
}
}

+ 27
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/dto/AsStringDto.java View File

@@ -0,0 +1,27 @@
package me.ahoo.cosid.example.jdbc.dto;

import me.ahoo.cosid.jackson.AsString;

import lombok.Data;

/** Dto for jackson.
*
* @author : Rocher Kong
*/
@Data
public class AsStringDto {
@AsString
private Long id;

@AsString(AsString.Type.RADIX)
private Long radixId;

@AsString(value = AsString.Type.RADIX, radixPadStart = true)
private Long radixPadStartId;

@AsString(value = AsString.Type.RADIX, radixPadStart = true, radixCharSize = 10)
private Long radixPadStartCharSize10Id;

@AsString(AsString.Type.FRIENDLY_ID)
private long friendlyId;
}

+ 38
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/FriendlyIdEntity.java View File

@@ -0,0 +1,38 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* create table t_friendly_table
* (
* id varchar(25) not null primary key
* );.
*
* @author Rocher Kong
*/
public class FriendlyIdEntity {

@CosId
private String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

+ 38
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/LongIdEntity.java View File

@@ -0,0 +1,38 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* create table t_table
* (
* id bigint not null primary key
* );.
*
* @author Rocher Kong
*/
public class LongIdEntity {

@CosId(value = "longId")
private Long id;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}

+ 39
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/Order.java View File

@@ -0,0 +1,39 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.entity;


import me.ahoo.cosid.annotation.CosId;

public class Order {
@CosId
private Long orderId;
private Long userId;

public Long getOrderId() {
return orderId;
}

public void setOrderId(Long orderId) {
this.orderId = orderId;
}

public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}
}

+ 44
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/OrderItem.java View File

@@ -0,0 +1,44 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* OrderItem.
*
* @author Rocher Kong
*/
public class OrderItem {
@CosId(value = "order_item")
private Long orderItemId;
private Long orderId;

public Long getOrderItemId() {
return orderItemId;
}

public void setOrderItemId(Long orderItemId) {
this.orderItemId = orderItemId;
}

public Long getOrderId() {
return orderId;
}

public void setOrderId(Long orderId) {
this.orderId = orderId;
}

}

+ 18
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/entity/User.java View File

@@ -0,0 +1,18 @@
package me.ahoo.cosid.example.jdbc.entity;

import lombok.Data;

import java.time.LocalDateTime;

/**
* 用户表.
*
* @author : Rocher Kong
*/
@Data
public class User {
private Long id;
private Integer age;
private String name;
private LocalDateTime createTime;
}

+ 35
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/repository/EntityRepository.java View File

@@ -0,0 +1,35 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.repository;

import me.ahoo.cosid.example.jdbc.entity.FriendlyIdEntity;
import me.ahoo.cosid.example.jdbc.entity.LongIdEntity;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

/**
* EntityRepository.
*
* @author Rocher Kong
*/
@Mapper
public interface EntityRepository {

@Insert("insert into t_table (id) value (#{id});")
void insert(LongIdEntity entity);

@Insert("insert into t_friendly_table (id) value (#{id});")
void insertFriendly(FriendlyIdEntity entity);
}

+ 41
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/repository/OrderRepository.java View File

@@ -0,0 +1,41 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc.repository;

import me.ahoo.cosid.example.jdbc.entity.Order;
import me.ahoo.cosid.example.jdbc.entity.OrderItem;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
* OrderRepository.
*
* @author Rocher Kong
*/
@Mapper
public interface OrderRepository {

void insert(Order order);

void insertItem(OrderItem orderItem);

Order getById(@Param("orderId") long orderId);

List<Order> query();

List<Order> getByIds(@Param("ids") List<Long> ids);
}

+ 23
- 0
examples/cosid-example-jdbc/src/main/java/me/ahoo/cosid/example/jdbc/repository/UserRepository.java View File

@@ -0,0 +1,23 @@
package me.ahoo.cosid.example.jdbc.repository;

import me.ahoo.cosid.example.jdbc.entity.User;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/** UserRepository.
*
* @author : Rocher Kong
*/
@Mapper
public interface UserRepository {
void insert(User order);

User getById(@Param("userId") long userId);

List<User> query();

List<User> getByIds(@Param("ids") List<Long> ids);
}

+ 103
- 0
examples/cosid-example-jdbc/src/main/resources/application.yaml View File

@@ -0,0 +1,103 @@
server:
port: 8600
spring:
application:
name: ${service.name:cosid-example-jdbc}
shardingsphere:
datasource:
datasource-0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/cosid_db
username: root
password: root
names: datasource-0
rules:
sharding:
key-generators:
cosid:
type: COSID
props:
id-name: __share__
tables:
t_user:
actual-data-nodes: datasource-0.t_user_$->{0..3}
#分库策略
database-strategy:
standard: # 用于单分片键的标准分片场景
sharding-column: id
sharding-algorithm-name: db-inline
#分表策略
table-strategy:
#标准策略
standard:
sharding-column: id
sharding-algorithm-name: user-inline
key-generate-strategy:
column: id
key-generator-name: cosid
sharding-algorithms:
db-inline:
type: MOD
props:
sharding-count: 1 # 表示有1个分片库数量
user-inline:
type: COSID_MOD
props:
mod: 4
logic-name-prefix: t_user_
cosid:
enabled: true
namespace: ${spring.application.name}
machine:
enabled: true
distributor:
type: jdbc
guarder:
enabled: true
snowflake:
enabled: true
provider:
short_id:
converter:
prefix: cosid_
type: radix
radix:
char-size: 11
pad-start: false
order_item:
sequence-bit: 12
snowflake:
sequence-bit: 12
safeJs:
sequence-bit: 9
segment:
enabled: true
mode: chain
distributor:
type: jdbc
share:
offset: 0
step: 100
converter:
prefix: cosid_
type: radix
radix:
char-size: 6
pad-start: false
provider:
order:
offset: 10000
step: 100
longId:
offset: 10000
step: 100

mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:/mappers/*.xml

logging:
level:
me.ahoo.cosid: debug

+ 47
- 0
examples/cosid-example-jdbc/src/main/resources/mappers/OrderMapper.xml View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.ahoo.cosid.example.jdbc.repository.OrderRepository">

<!--me.ahoo.cosid.shardingsphere.sharding.key.CosIdKeyGenerateAlgorithm-->
<insert id="insert" useGeneratedKeys="true" keyProperty="orderId">
insert into t_order (user_id,order_id) value (#{userId},#{orderId});
</insert>
<!--me.ahoo.cosid.annotation.CosId-->
<insert id="insertItem">
insert into t_order_item (order_item_id, order_id) value (#{orderItemId},#{orderId});
</insert>

<select id="getById" resultType="me.ahoo.cosid.example.jdbc.entity.Order">
select *
from t_order
where order_id = #{orderId}
</select>

<select id="query" resultType="me.ahoo.cosid.example.jdbc.entity.Order">
select *
from t_order
where order_id > 92301
</select>
<select id="getByIds" resultType="me.ahoo.cosid.example.jdbc.entity.Order">
select *
from t_order
where order_id in
<foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</select>

</mapper>

+ 34
- 0
examples/cosid-example-jdbc/src/main/resources/mappers/UserMapper.xml View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.ahoo.cosid.example.jdbc.repository.UserRepository">
<insert id="insert">
insert into t_user(age,name,create_time) value(#{age},#{name},#{createTime});
</insert>
<select id="getById" resultType="me.ahoo.cosid.example.jdbc.entity.User">
select * from t_user where id=#{userId};
</select>
<select id="query" resultType="me.ahoo.cosid.example.jdbc.entity.User">
select * from t_user where create_time>now();
</select>
<select id="getByIds" resultType="me.ahoo.cosid.example.jdbc.entity.User">
select *
from t_user
where id in
<foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
</mapper>

+ 32
- 0
examples/cosid-example-jdbc/src/test/java/me/ahoo/cosid/example/jdbc/AppServerTest.java View File

@@ -0,0 +1,32 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.jdbc;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.springframework.boot.test.context.SpringBootTest;

/**
* AppServerTest .
*
* @author ahoo wang
*/
@DisabledIfEnvironmentVariable(named = "CODECOV", matches = "true")
@SpringBootTest
class AppServerTest {
@Test
void contextLoads() {
}
}

+ 65
- 0
examples/cosid-example-proxy/build.gradle.kts View File

@@ -0,0 +1,65 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.proxy.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-jackson"))
implementation(project(":cosid-proxy"))
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 29
- 0
examples/cosid-example-proxy/src/main/java/me/ahoo/cosid/example/proxy/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.proxy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer .
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 50
- 0
examples/cosid-example-proxy/src/main/java/me/ahoo/cosid/example/proxy/controller/IdController.java View File

@@ -0,0 +1,50 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.proxy.controller;

import me.ahoo.cosid.provider.IdGeneratorProvider;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* IdController.
*
* @author ahoo wang
*/
@RestController
@RequestMapping("ids")
public class IdController {
private final IdGeneratorProvider provider;
public IdController(IdGeneratorProvider provider) {
this.provider = provider;
}
@GetMapping
public long generate() {
return provider
.getShare()
.generate();
}
@GetMapping("/as-string")
public String generateAsString() {
return provider
.getShare()
.generateAsString();
}
}

+ 25
- 0
examples/cosid-example-proxy/src/main/resources/application.yaml View File

@@ -0,0 +1,25 @@
server:
port: 8600
spring:
application:
name: ${service.name:cosid-example-proxy}
cosid:
namespace: ${spring.application.name}
machine:
enabled: true
distributor:
type: proxy
guarder:
enabled: true
snowflake:
enabled: true

# segment:
# enabled: true
# mode: chain
# distributor:
# type: proxy

logging:
level:
me.ahoo.cosid: debug

+ 33
- 0
examples/cosid-example-proxy/src/test/java/me/ahoo/cosid/example/proxy/AppServerTest.java View File

@@ -0,0 +1,33 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.proxy;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.springframework.boot.test.context.SpringBootTest;

/**
* AppServerTest .
*
* @author ahoo wang
*/
@DisabledIfEnvironmentVariable(named = "CODECOV", matches = "true")
@DisabledIfEnvironmentVariable(named = "CI", matches = "true")
@SpringBootTest
class AppServerTest {
@Test
void contextLoads() {
}
}

+ 65
- 0
examples/cosid-example-redis-cosid/build.gradle.kts View File

@@ -0,0 +1,65 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.redis.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-spring-redis"))
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 29
- 0
examples/cosid-example-redis-cosid/src/main/java/me/ahoo/cosid/example/cosid/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.cosid;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer .
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 50
- 0
examples/cosid-example-redis-cosid/src/main/java/me/ahoo/cosid/example/cosid/controller/IdController.java View File

@@ -0,0 +1,50 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.cosid.controller;

import me.ahoo.cosid.cosid.CosIdGenerator;
import me.ahoo.cosid.cosid.CosIdState;
import me.ahoo.cosid.provider.IdGeneratorProvider;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* IdController.
*
* @author ahoo wang
*/
@RestController
@RequestMapping("ids")
public class IdController {
private final IdGeneratorProvider provider;
private final CosIdGenerator cosIdGenerator;
public IdController(IdGeneratorProvider provider, CosIdGenerator cosIdGenerator) {
this.provider = provider;
this.cosIdGenerator = cosIdGenerator;
}
@GetMapping
public String generateAsString() {
return cosIdGenerator.generateAsString();
}
@GetMapping("/as-state")
public CosIdState asState(String id) {
return cosIdGenerator.getStateParser().asState(id);
}
}

+ 22
- 0
examples/cosid-example-redis-cosid/src/main/resources/application.yaml View File

@@ -0,0 +1,22 @@
server:
port: 8610
spring:
application:
name: ${service.name:cosid-example-redis}
redis:
host: localhost
cosid:
namespace: ${spring.application.name}
machine:
enabled: true
machine-bit: 16
distributor:
type: redis
guarder:
enabled: true
generator:
enabled: true

logging:
level:
me.ahoo.cosid: debug

+ 32
- 0
examples/cosid-example-redis-cosid/src/test/java/me/ahoo/cosid/example/cosid/AppServerTest.java View File

@@ -0,0 +1,32 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.cosid;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.springframework.boot.test.context.SpringBootTest;

/**
* AppServerTest .
*
* @author ahoo wang
*/
@DisabledIfEnvironmentVariable(named = "CODECOV", matches = "true")
@SpringBootTest
class AppServerTest {
@Test
void contextLoads() {
}
}

+ 67
- 0
examples/cosid-example-redis/build.gradle.kts View File

@@ -0,0 +1,67 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.redis.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-jackson"))

implementation(project(":cosid-spring-redis"))
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 29
- 0
examples/cosid-example-redis/src/main/java/me/ahoo/cosid/example/redis/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer .
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 50
- 0
examples/cosid-example-redis/src/main/java/me/ahoo/cosid/example/redis/controller/IdController.java View File

@@ -0,0 +1,50 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.redis.controller;

import me.ahoo.cosid.provider.IdGeneratorProvider;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* IdController.
*
* @author ahoo wang
*/
@RestController
@RequestMapping("ids")
public class IdController {
private final IdGeneratorProvider provider;
public IdController(IdGeneratorProvider provider) {
this.provider = provider;
}
@GetMapping
public long generate() {
return provider
.getShare()
.generate();
}
@GetMapping("/as-string")
public String generateAsString() {
return provider
.getShare()
.generateAsString();
}
}

+ 26
- 0
examples/cosid-example-redis/src/main/resources/application.yaml View File

@@ -0,0 +1,26 @@
server:
port: 8601
spring:
application:
name: ${service.name:cosid-example-redis}
redis:
host: localhost
cosid:
namespace: ${spring.application.name}
machine:
enabled: true
distributor:
type: redis
guarder:
enabled: true
snowflake:
enabled: true
# segment:
# enabled: true
# mode: chain
# distributor:
# type: redis

logging:
level:
me.ahoo.cosid: debug

+ 32
- 0
examples/cosid-example-redis/src/test/java/me/ahoo/cosid/example/redis/AppServerTest.java View File

@@ -0,0 +1,32 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.redis;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.springframework.boot.test.context.SpringBootTest;

/**
* AppServerTest .
*
* @author ahoo wang
*/
@DisabledIfEnvironmentVariable(named = "CODECOV", matches = "true")
@SpringBootTest
class AppServerTest {
@Test
void contextLoads() {
}
}

+ 78
- 0
examples/cosid-example-shardingsphere/build.gradle.kts View File

@@ -0,0 +1,78 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.shardingsphere.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-jackson"))

//region cosid-mybatis
implementation(project(":cosid-mybatis"))
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter")
//endregion

//region cosid-jdbc
implementation(project(":cosid-jdbc"))
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("com.mysql:mysql-connector-j")
implementation(project(":cosid-shardingsphere"))
implementation("org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter")
//endregion

implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 56
- 0
examples/cosid-example-shardingsphere/init-script/init-example-schemal.sql View File

@@ -0,0 +1,56 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- ds0
create table cosid
(
name varchar(100) not null comment '{namespace}.{name}'
primary key,
last_max_id bigint default 0 not null,
last_fetch_time bigint not null
);

-- ds0 & ds1
create table t_date_log_202205
(
id bigint not null,
create_time datetime not null,
constraint t_date_log_202205_pk
primary key (id)
);


create table t_date_time_log_202205
(
id bigint not null,
create_time datetime not null,
constraint t_date_time_log_202205_pk
primary key (id)
);


create table t_timestamp_log_202205
(
id bigint not null,
create_time bigint not null,
constraint t_timestamp_log_202205_pk
primary key (id)
);



create table t_snowflake_log_202205
(
id bigint not null,
constraint t_snowflake_log_202205_pk
primary key (id)
);

+ 29
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer .
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 26
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/config/AppConfig.java View File

@@ -0,0 +1,26 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.config;

import org.springframework.context.annotation.Configuration;

/**
* AppConfig.
*
* @author Rocher Kong
*/
@Configuration
public class AppConfig {

}

+ 77
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/controller/IntervalShardingAlgorithmController.java View File

@@ -0,0 +1,77 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.controller;



import me.ahoo.cosid.example.shardingsphere.entity.DateLogEntity;
import me.ahoo.cosid.example.shardingsphere.entity.LocalDateTimeLogEntity;
import me.ahoo.cosid.example.shardingsphere.entity.SnowflakeLogEntity;
import me.ahoo.cosid.example.shardingsphere.entity.TimestampLogEntity;
import me.ahoo.cosid.example.shardingsphere.repository.IntervalShardingAlgorithmRepository;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

/**
* IntervalShardingAlgorithmController.
*
* @author ahoo wang
*/
@RestController
@RequestMapping("interval")
public class IntervalShardingAlgorithmController {

private final IntervalShardingAlgorithmRepository algorithmRepository;

public IntervalShardingAlgorithmController(IntervalShardingAlgorithmRepository algorithmRepository) {
this.algorithmRepository = algorithmRepository;
}

@PostMapping("/insertDate")
public DateLogEntity insertDate() {
DateLogEntity log = new DateLogEntity();
log.setCreateTime(new Date());
algorithmRepository.insertDate(log);
return log;
}

@PostMapping("/insertTimestamp")
public TimestampLogEntity insertTimestamp() {
TimestampLogEntity log = new TimestampLogEntity();
log.setCreateTime(System.currentTimeMillis());
algorithmRepository.insertTimestamp(log);
return log;
}

@PostMapping("/insertDateTime")
public LocalDateTimeLogEntity insertDateTime() {
LocalDateTimeLogEntity log = new LocalDateTimeLogEntity();
log.setCreateTime(LocalDateTime.now(ZoneId.systemDefault()));
algorithmRepository.insertDateTime(log);
return log;
}

@PostMapping("/insertSnowflake")
public SnowflakeLogEntity insertSnowflake() {
SnowflakeLogEntity log = new SnowflakeLogEntity();
algorithmRepository.insertSnowflake(log);
return log;
}
}

+ 46
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/DateLogEntity.java View File

@@ -0,0 +1,46 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.entity;

import java.util.Date;

/**
* create table t_friendly_table
* (
* id varchar(25) not null primary key
* );.
*
* @author Rocher Kong
*/
public class DateLogEntity {

private long id;
private Date createTime;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

+ 42
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/LocalDateTimeLogEntity.java View File

@@ -0,0 +1,42 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.entity;

import java.time.LocalDateTime;

/**
* LocalDateTimeLogEntity.
*
* @author Rocher Kong
*/
public class LocalDateTimeLogEntity {
private long id;
private LocalDateTime createTime;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public LocalDateTime getCreateTime() {
return createTime;
}

public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
}

+ 34
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/SnowflakeLogEntity.java View File

@@ -0,0 +1,34 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* SnowflakeLogEntity.
*
* @author Rocher Kong
*/
public class SnowflakeLogEntity {
@CosId("snowflake")
private long id;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}
}

+ 41
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/entity/TimestampLogEntity.java View File

@@ -0,0 +1,41 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.entity;

/**
* TimestampLogEntity.
*
* @author Rocher Kong
*/
public class TimestampLogEntity {

private long id;
private long createTime;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public long getCreateTime() {
return createTime;
}

public void setCreateTime(long createTime) {
this.createTime = createTime;
}
}

+ 38
- 0
examples/cosid-example-shardingsphere/src/main/java/me/ahoo/cosid/example/shardingsphere/repository/IntervalShardingAlgorithmRepository.java View File

@@ -0,0 +1,38 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.shardingsphere.repository;

import me.ahoo.cosid.example.shardingsphere.entity.DateLogEntity;
import me.ahoo.cosid.example.shardingsphere.entity.LocalDateTimeLogEntity;
import me.ahoo.cosid.example.shardingsphere.entity.SnowflakeLogEntity;
import me.ahoo.cosid.example.shardingsphere.entity.TimestampLogEntity;

import org.apache.ibatis.annotations.Mapper;

/**
* IntervalShardingAlgorithmRepository.
*
* @author Rocher Kong
*/
@Mapper
public interface IntervalShardingAlgorithmRepository {

void insertDate(DateLogEntity log);

void insertTimestamp(TimestampLogEntity log);

void insertDateTime(LocalDateTimeLogEntity log);

void insertSnowflake(SnowflakeLogEntity log);
}

+ 149
- 0
examples/cosid-example-shardingsphere/src/main/resources/application.yaml View File

@@ -0,0 +1,149 @@
server:
port: 8600
spring:
application:
name: ${service.name:cosid-example-jdbc}
redis:
url: redis://localhost:6379
shardingsphere:
mode:
type: standalone
# repository:
# type: file
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:
tables:
cosid:
actual-data-nodes: ds0.cosid
t_date_log:
actual-data-nodes: ds0.t_date_log_202205
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: data-log-interval
t_date_time_log:
actual-data-nodes: ds0.t_date_time_log_202205
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: data-time-log-interval
t_timestamp_log:
actual-data-nodes: ds0.t_timestamp_log_202205
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: timestamp-log-interval
t_snowflake_log:
actual-data-nodes: ds0.t_snowflake_log_202205
table-strategy:
standard:
sharding-column: id
sharding-algorithm-name: snowflake-log-interval
sharding-algorithms:
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
data-time-log-interval:
type: COSID_INTERVAL
props:
logic-name-prefix: t_date_time_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
timestamp-log-interval:
type: COSID_INTERVAL
props:
logic-name-prefix: t_timestamp_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
snowflake-log-interval:
type: COSID_INTERVAL_SNOWFLAKE
props:
logic-name-prefix: t_snowflake_log_
id-name: snowflake
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:
enabled: true
distributor:
type: jdbc
guarder:
enabled: true
snowflake:
enabled: true
provider:
short_id:
converter:
prefix: cosid_
type: radix
radix:
char-size: 11
pad-start: false
snowflake:
sequence-bit: 12
safeJs:
sequence-bit: 9
segment:
enabled: true
mode: chain
distributor:
type: jdbc
provider:
longId:
offset: 10000
step: 100
mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:/mappers/*.xml

logging:
level:
me.ahoo.cosid: debug

+ 31
- 0
examples/cosid-example-shardingsphere/src/main/resources/mappers/IntervalShardingAlgorithmRepository.xml View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.ahoo.cosid.example.shardingsphere.repository.IntervalShardingAlgorithmRepository">
<insert id="insertDate" useGeneratedKeys="true" keyProperty="id">
insert into t_date_log (create_time) value (#{createTime});
</insert>

<insert id="insertDateTime" useGeneratedKeys="true" keyProperty="id">
insert into t_date_time_log (create_time) value (#{createTime});
</insert>

<insert id="insertTimestamp" useGeneratedKeys="true" keyProperty="id">
insert into t_timestamp_log (create_time) value (#{createTime});
</insert>
<insert id="insertSnowflake">
insert into t_snowflake_log (id) value (#{id});
</insert>
</mapper>

+ 67
- 0
examples/cosid-example-zookeeper/build.gradle.kts View File

@@ -0,0 +1,67 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.zookeeper.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-jackson"))
//region cosid-zookeeper
implementation(project(":cosid-zookeeper"))
//endregion
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 29
- 0
examples/cosid-example-zookeeper/src/main/java/me/ahoo/cosid/example/zookeeper/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.zookeeper;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer .
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 50
- 0
examples/cosid-example-zookeeper/src/main/java/me/ahoo/cosid/example/zookeeper/controller/IdController.java View File

@@ -0,0 +1,50 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.zookeeper.controller;

import me.ahoo.cosid.provider.IdGeneratorProvider;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* IdController.
*
* @author ahoo wang
*/
@RestController
@RequestMapping("ids")
public class IdController {
private final IdGeneratorProvider provider;
public IdController(IdGeneratorProvider provider) {
this.provider = provider;
}
@GetMapping
public long generate() {
return provider
.getShare()
.generate();
}
@GetMapping("/as-string")
public String generateAsString() {
return provider
.getShare()
.generateAsString();
}
}

+ 26
- 0
examples/cosid-example-zookeeper/src/main/resources/application.yaml View File

@@ -0,0 +1,26 @@
server:
port: 8602
spring:
application:
name: ${service.name:cosid-example-zookeeper}
cosid:
zookeeper:
connect-string: localhost:2181
namespace: ${spring.application.name}
machine:
enabled: true
distributor:
type: zookeeper
guarder:
enabled: true
snowflake:
enabled: true
# segment:
# enabled: true
# mode: chain
# distributor:
# type: zookeeper

logging:
level:
me.ahoo.cosid: debug

+ 32
- 0
examples/cosid-example-zookeeper/src/test/java/me/ahoo/cosid/example/zookeeper/AppServerTest.java View File

@@ -0,0 +1,32 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.zookeeper;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.springframework.boot.test.context.SpringBootTest;

/**
* AppServerTest .
*
* @author ahoo wang
*/
@DisabledIfEnvironmentVariable(named = "CODECOV", matches = "true")
@SpringBootTest
class AppServerTest {
@Test
void contextLoads() {
}
}

+ 103
- 0
examples/cosid-example/build.gradle.kts View File

@@ -0,0 +1,103 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
application
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

application {
mainClass.set("me.ahoo.cosid.example.AppServer")
applicationDefaultJvmArgs = listOf(
"-Xms512M",
"-Xmx512M",
"-XX:MaxMetaspaceSize=128M",
"-XX:MaxDirectMemorySize=256M",
"-Xss1m",
"-server",
"-XX:+UseG1GC",
"-Xlog:gc*:file=logs/${applicationName}-gc.log:time,tags:filecount=10,filesize=32M",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=data",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Dcom.sun.management.jmxremote.port=5555",
"-Dspring.cloud.bootstrap.enabled=true",
"-Dspring.cloud.bootstrap.location=config/bootstrap.yaml",
"-Dspring.config.location=file:./config/"
)
}

dependencies {
implementation(platform(project(":cosid-dependencies")))
annotationProcessor(platform(project(":cosid-dependencies")))
implementation(project(":cosid-spring-boot-starter"))
implementation(project(":cosid-jackson"))
implementation(project(":cosid-proxy"))
//region cosid-redis
// implementation(project(":cosid-redis"))
// implementation("me.ahoo.cosky:cosky-spring-cloud-core")
// or
// implementation(project(":cosid-spring-boot-starter")){
// capabilities {
// requireCapability("me.ahoo.cosid:redis-support")
// }
// }
//endregion
//region cosid-spring-redis
implementation(project(":cosid-spring-redis"))
implementation("org.springframework.boot:spring-boot-starter-data-redis")
// or
// implementation(project(":cosid-spring-boot-starter")){
// capabilities {
// requireCapability("me.ahoo.cosid:spring-redis-support")
// }
// }

//endregion
//region cosid-jdbc
implementation(project(":cosid-jdbc"))
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("com.mysql:mysql-connector-j")
implementation(project(":cosid-shardingsphere"))
implementation("org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter")
//endregion

//region cosid-zookeeper
// implementation(project(":cosid-zookeeper"))
//endregion

//region cosid-mybatis
implementation(project(":cosid-mybatis"))
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter")
//endregion
implementation("org.springframework.boot:spring-boot-starter-actuator")

implementation("com.google.guava:guava")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}

+ 53
- 0
examples/cosid-example/src/dist/config/application.yaml View File

@@ -0,0 +1,53 @@
cosid:
namespace: ${spring.application.name}
snowflake:
enabled: true
# epoch: 1577203200000
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
machine:
# stable: true
# machine-bit: 10
# instance-id: ${HOSTNAME}
distributor:
type: redis
# manual:
# machine-id: 0
state-storage:
local:
state-location: ./cosid-machine-state/
share:
clock-sync: true
friendly: true

provider:
bizA:
# timestamp-bit:
sequence-bit: 12
safeJs:
machine-bit: 3
sequence-bit: 9
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
logging:
level:
me.ahoo.cosid: info

+ 12
- 0
examples/cosid-example/src/dist/config/bootstrap.yaml View File

@@ -0,0 +1,12 @@
spring:
application:
name: ${service.name:cosid-example}
cloud:
cosky:
redis:
mode: ${cosky.redis.mode:standalone}
url: ${cosky.redis.uri:redis://localhost:6379}

logging:
file:
name: logs/${spring.application.name}.log

+ 0
- 0
examples/cosid-example/src/dist/logs/.gitignore View File


+ 77
- 0
examples/cosid-example/src/init-script/init-example-schemal.sql View File

@@ -0,0 +1,77 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-- ds0
create table t_friendly_table
(
id varchar(25) not null primary key
);

create table t_table_0
(
id bigint not null primary key
);
create table t_table_1
(
id bigint not null primary key
);

-- ds0 & ds1
create table t_order
(
order_id bigint not null primary key,
user_id bigint not null
);

create table t_order_item
(
order_item_id bigint not null primary key,
order_id bigint not null
);


-- interval

create table t_date_log_202201
(
id bigint not null,
create_time datetime not null,
constraint t_date_log_202201_pk
primary key (id)
);


create table t_date_time_log_202201
(
id bigint not null,
create_time datetime not null,
constraint t_date_time_log_202201_pk
primary key (id)
);


create table t_timestamp_log_202201
(
id bigint not null,
create_time bigint not null,
constraint t_timestamp_log_202201_pk
primary key (id)
);



create table t_snowflake_log_202201
(
id bigint not null,
constraint t_snowflake_log_202201_pk
primary key (id)
);

+ 29
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/AppServer.java View File

@@ -0,0 +1,29 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* AppServer.
*
* @author ahoo wang
*/
@SpringBootApplication
public class AppServer {
public static void main(String[] args) {
SpringApplication.run(AppServer.class, args);
}
}

+ 26
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/config/AppConfig.java View File

@@ -0,0 +1,26 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.config;

import org.springframework.context.annotation.Configuration;

/**
* AppConfig.
*
* @author ahoo wang
*/
@Configuration
public class AppConfig {

}

+ 55
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/EntityController.java View File

@@ -0,0 +1,55 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.controller;

import me.ahoo.cosid.example.entity.FriendlyIdEntity;
import me.ahoo.cosid.example.entity.LongIdEntity;
import me.ahoo.cosid.example.repository.EntityRepository;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author ahoo wang
*/
@RestController
@RequestMapping("test")
public class EntityController {

private final EntityRepository entityRepository;

public EntityController(EntityRepository entityRepository) {
this.entityRepository = entityRepository;
}

@PostMapping("/long")
public LongIdEntity longId() {
LongIdEntity entity = new LongIdEntity();
entityRepository.insert(entity);
/**
* {
* "id": 208796080181248
* }
*/
return entity;
}

@PostMapping("/friendly")
public FriendlyIdEntity friendly() {
FriendlyIdEntity entity = new FriendlyIdEntity();
entityRepository.insertFriendly(entity);
return entity;
}
}

+ 96
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/IdController.java View File

@@ -0,0 +1,96 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.controller;

import me.ahoo.cosid.IdGenerator;
import me.ahoo.cosid.example.dto.AsStringDto;
import me.ahoo.cosid.provider.IdGeneratorProvider;
import me.ahoo.cosid.snowflake.SafeJavaScriptSnowflakeId;
import me.ahoo.cosid.snowflake.SnowflakeFriendlyId;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

/**
* @author ahoo wang
*/
@RestController
@RequestMapping("id")
public class IdController {
private final IdGeneratorProvider idGeneratorProvider;

public IdController(IdGeneratorProvider idGeneratorProvider) {
this.idGeneratorProvider = idGeneratorProvider;
}

private IdGenerator getIdGenerator(String name) {
Preconditions.checkNotNull(name, "name can not be null");
Optional<IdGenerator> optionalIdGenerator = idGeneratorProvider.get(name);
if (!optionalIdGenerator.isPresent()) {
throw new IllegalArgumentException(Strings.lenientFormat("idGenerator:[%s] not fond.", name));
}
return optionalIdGenerator.get();
}

@GetMapping("{name}")
public Object generate(@PathVariable String name) {
IdGenerator idGenerator = getIdGenerator(name);
long id = idGenerator.generate();
if (SafeJavaScriptSnowflakeId.isSafeJavaScript(id)) {
return id;
}
return String.valueOf(id);
}

@GetMapping("{name}/friendlyId")
public String friendlyId(@PathVariable String name) {
IdGenerator idGenerator = getIdGenerator(name);
if (idGenerator instanceof SnowflakeFriendlyId) {
return idGenerator.generateAsString();
}
throw new IllegalArgumentException(Strings.lenientFormat("idGenerator:[%s] is not SnowflakeFriendlyId.", name));
}

@GetMapping("shortId")
public String shortId() {
IdGenerator idGenerator = getIdGenerator("short_id");
return idGenerator.generateAsString();
}

@GetMapping("asString")
public AsStringDto asString() {
IdGenerator idGenerator = getIdGenerator("short_id");
long id = idGenerator.generate();
AsStringDto dto = new AsStringDto();
dto.setId(id);
dto.setRadixId(id);
dto.setRadixPadStartId(id);
dto.setRadixPadStartCharSize10Id(id);
dto.setFriendlyId(id);
return dto;
}

@PostMapping("asStringDes")
public AsStringDto asStringDes(@RequestBody AsStringDto dto) {
return dto;
}
}

+ 72
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/IntervalShardingAlgorithmController.java View File

@@ -0,0 +1,72 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.controller;

import me.ahoo.cosid.example.entity.interval.DateLogEntity;
import me.ahoo.cosid.example.entity.interval.LocalDateTimeLogEntity;
import me.ahoo.cosid.example.entity.interval.SnowflakeLogEntity;
import me.ahoo.cosid.example.entity.interval.TimestampLogEntity;
import me.ahoo.cosid.example.repository.IntervalShardingAlgorithmRepository;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Date;

/**
* @author ahoo wang
*/
@RestController
@RequestMapping("interval")
public class IntervalShardingAlgorithmController {

private final IntervalShardingAlgorithmRepository algorithmRepository;

public IntervalShardingAlgorithmController(IntervalShardingAlgorithmRepository algorithmRepository) {
this.algorithmRepository = algorithmRepository;
}

@PostMapping("/insertDate")
public DateLogEntity insertDate() {
DateLogEntity log = new DateLogEntity();
log.setCreateTime(new Date());
algorithmRepository.insertDate(log);
return log;
}

@PostMapping("/insertTimestamp")
public TimestampLogEntity insertTimestamp() {
TimestampLogEntity log = new TimestampLogEntity();
log.setCreateTime(System.currentTimeMillis());
algorithmRepository.insertTimestamp(log);
return log;
}

@PostMapping("/insertDateTime")
public LocalDateTimeLogEntity insertDateTime() {
LocalDateTimeLogEntity log = new LocalDateTimeLogEntity();
log.setCreateTime(LocalDateTime.now());
algorithmRepository.insertDateTime(log);
return log;
}

@PostMapping("/insertSnowflake")
public SnowflakeLogEntity insertSnowflake() {
SnowflakeLogEntity log = new SnowflakeLogEntity();
algorithmRepository.insertSnowflake(log);
return log;
}
}

+ 69
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/controller/OrderController.java View File

@@ -0,0 +1,69 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.controller;

import me.ahoo.cosid.example.entity.Order;
import me.ahoo.cosid.example.entity.OrderItem;
import me.ahoo.cosid.example.repository.OrderRepository;

import com.google.common.base.Splitter;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;

/**
* @author ahoo wang
*/
@RestController
@RequestMapping("order")
public class OrderController {
private final OrderRepository orderRepository;

public OrderController(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}

@Transactional
@PostMapping("/{userId}")
public long create(@PathVariable long userId) {
Order order = new Order();
order.setUserId(userId);
orderRepository.insert(order);
OrderItem orderItem1 = new OrderItem();
orderItem1.setOrderId(order.getOrderId());
OrderItem orderItem2 = new OrderItem();
orderItem2.setOrderId(order.getOrderId());
orderRepository.insertItem(orderItem1);
orderRepository.insertItem(orderItem2);
return order.getOrderId();
}

@GetMapping("/{id}")
public Order get(@PathVariable long id) {
return orderRepository.getById(id);
}

@GetMapping("/list/{ids}")
public List<Order> get(@PathVariable String ids) {
List<Long> idList = Splitter.on(",").omitEmptyStrings().trimResults().splitToStream(ids).map(Long::parseLong).collect(Collectors.toList());
return orderRepository.getByIds(idList);
}

}

+ 76
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/dto/AsStringDto.java View File

@@ -0,0 +1,76 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.dto;

import me.ahoo.cosid.jackson.AsString;

/**
* @author ahoo wang
*/
public class AsStringDto {

@AsString
private Long id;
@AsString(AsString.Type.RADIX)
private Long radixId;

@AsString(value = AsString.Type.RADIX, radixPadStart = true)
private Long radixPadStartId;

@AsString(value = AsString.Type.RADIX, radixPadStart = true, radixCharSize = 10)
private Long radixPadStartCharSize10Id;

@AsString(AsString.Type.FRIENDLY_ID)
private long friendlyId;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Long getRadixId() {
return radixId;
}

public void setRadixId(Long radixId) {
this.radixId = radixId;
}

public Long getRadixPadStartId() {
return radixPadStartId;
}

public void setRadixPadStartId(Long radixPadStartId) {
this.radixPadStartId = radixPadStartId;
}

public Long getRadixPadStartCharSize10Id() {
return radixPadStartCharSize10Id;
}

public void setRadixPadStartCharSize10Id(Long radixPadStartCharSize10Id) {
this.radixPadStartCharSize10Id = radixPadStartCharSize10Id;
}

public long getFriendlyId() {
return friendlyId;
}

public void setFriendlyId(long friendlyId) {
this.friendlyId = friendlyId;
}
}

+ 38
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/FriendlyIdEntity.java View File

@@ -0,0 +1,38 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* create table t_friendly_table
* (
* id varchar(25) not null primary key
* );
*
* @author ahoo wang
*/
public class FriendlyIdEntity {

@CosId
private String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

+ 38
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/LongIdEntity.java View File

@@ -0,0 +1,38 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* create table t_table
* (
* id bigint not null primary key
* );
*
* @author ahoo wang
*/
public class LongIdEntity {

@CosId(value = "longId")
private Long id;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}

+ 41
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/Order.java View File

@@ -0,0 +1,41 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* @author ahoo wang
*/
public class Order {

private Long orderId;
private Long userId;

public Long getOrderId() {
return orderId;
}

public void setOrderId(Long orderId) {
this.orderId = orderId;
}

public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}
}

+ 42
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/OrderItem.java View File

@@ -0,0 +1,42 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity;

import me.ahoo.cosid.annotation.CosId;

/**
* @author ahoo wang
*/
public class OrderItem {
@CosId(value = "order_item")
private Long orderItemId;
private Long orderId;

public Long getOrderItemId() {
return orderItemId;
}

public void setOrderItemId(Long orderItemId) {
this.orderItemId = orderItemId;
}

public Long getOrderId() {
return orderId;
}

public void setOrderId(Long orderId) {
this.orderId = orderId;
}

}

+ 46
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/DateLogEntity.java View File

@@ -0,0 +1,46 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity.interval;

import java.util.Date;

/**
* create table t_friendly_table
* (
* id varchar(25) not null primary key
* );
*
* @author ahoo wang
*/
public class DateLogEntity {

private long id;
private Date createTime;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

+ 40
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/LocalDateTimeLogEntity.java View File

@@ -0,0 +1,40 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity.interval;

import java.time.LocalDateTime;

/**
* @author ahoo wang
*/
public class LocalDateTimeLogEntity {
private long id;
private LocalDateTime createTime;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public LocalDateTime getCreateTime() {
return createTime;
}

public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
}

+ 32
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/SnowflakeLogEntity.java View File

@@ -0,0 +1,32 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity.interval;

import me.ahoo.cosid.annotation.CosId;

/**
* @author ahoo wang
*/
public class SnowflakeLogEntity {
@CosId("snowflake")
private long id;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}
}

+ 41
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/entity/interval/TimestampLogEntity.java View File

@@ -0,0 +1,41 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.entity.interval;

import java.util.Date;

/**
* @author ahoo wang
*/
public class TimestampLogEntity {

private long id;
private long createTime;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public long getCreateTime() {
return createTime;
}

public void setCreateTime(long createTime) {
this.createTime = createTime;
}
}

+ 33
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/repository/EntityRepository.java View File

@@ -0,0 +1,33 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.repository;

import me.ahoo.cosid.example.entity.FriendlyIdEntity;
import me.ahoo.cosid.example.entity.LongIdEntity;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

/**
* @author ahoo wang
*/
@Mapper
public interface EntityRepository {

@Insert("insert into t_table (id) value (#{id});")
void insert(LongIdEntity entity);

@Insert("insert into t_friendly_table (id) value (#{id});")
void insertFriendly(FriendlyIdEntity entity);
}

+ 36
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/repository/IntervalShardingAlgorithmRepository.java View File

@@ -0,0 +1,36 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.repository;

import me.ahoo.cosid.example.entity.interval.DateLogEntity;
import me.ahoo.cosid.example.entity.interval.LocalDateTimeLogEntity;
import me.ahoo.cosid.example.entity.interval.SnowflakeLogEntity;
import me.ahoo.cosid.example.entity.interval.TimestampLogEntity;

import org.apache.ibatis.annotations.Mapper;

/**
* @author ahoo wang
*/
@Mapper
public interface IntervalShardingAlgorithmRepository {

void insertDate(DateLogEntity log);

void insertTimestamp(TimestampLogEntity log);

void insertDateTime(LocalDateTimeLogEntity log);

void insertSnowflake(SnowflakeLogEntity log);
}

+ 41
- 0
examples/cosid-example/src/main/java/me/ahoo/cosid/example/repository/OrderRepository.java View File

@@ -0,0 +1,41 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosid.example.repository;

import me.ahoo.cosid.example.entity.Order;
import me.ahoo.cosid.example.entity.OrderItem;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
* Order Repository
*
* @author ahoo wang
*/
@Mapper
public interface OrderRepository {

void insert(Order order);

void insertItem(OrderItem orderItem);

Order getById(@Param("orderId") long orderId);

List<Order> query();

List<Order> getByIds(@Param("ids") List<Long> ids);
}

+ 220
- 0
examples/cosid-example/src/main/resources/application.yaml View File

@@ -0,0 +1,220 @@
server:
port: 8008
spring:
application:
name: ${service.name:cosid-example}
redis:
url: redis://localhost:6379
shardingsphere:
# mode:
# type: standalone
# repository:
# type: file
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_friendly_table:
actual-data-nodes: ds0.t_friendly_table
t_order:
actual-data-nodes: ds$->{0..1}.t_order
database-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-db-inline
key-generate-strategy:
column: order_id
key-generator-name: order
t_order_item:
actual-data-nodes: ds$->{0..1}.t_order_item
database-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-db-inline
t_date_log:
actual-data-nodes: ds0.t_date_log_202201
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: data-log-interval
t_date_time_log:
actual-data-nodes: ds0.t_date_time_log_202201
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: data-time-log-interval
t_timestamp_log:
actual-data-nodes: ds0.t_timestamp_log_202201
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: timestamp-log-interval
t_snowflake_log:
actual-data-nodes: ds0.t_snowflake_log_202201
table-strategy:
standard:
sharding-column: id
sharding-algorithm-name: snowflake-log-interval
sharding-algorithms:
table-inline:
type: COSID_MOD
props:
mod: "2"
logic-name-prefix: t_table_
order-db-inline:
type: COSID_MOD
props:
mod: "2"
logic-name-prefix: ds
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
data-time-log-interval:
type: COSID_INTERVAL
props:
logic-name-prefix: t_date_time_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
timestamp-log-interval:
type: COSID_INTERVAL
props:
logic-name-prefix: t_timestamp_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
snowflake-log-interval:
type: COSID_INTERVAL_SNOWFLAKE
props:
logic-name-prefix: t_snowflake_log_
id-name: snowflake
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
order:
type: COSID
props:
id-name: order


cosid:
namespace: ${spring.application.name}
machine:
enabled: true
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
# stable: true
# machine-bit: 10
# instance-id: ${HOSTNAME}
distributor:
type: redis
# manual:
# machine-id: 0
state-storage:
local:
state-location: ./cosid-machine-state/
guarder:
enabled: true
snowflake:
enabled: true
# epoch: 1577203200000
share:
clock-sync: true
friendly: true
provider:
short_id:
converter:
prefix: cosid_
type: radix
radix:
char-size: 11
pad-start: false
order_item:
sequence-bit: 12
snowflake:
sequence-bit: 12
safeJs:
machine-bit: 3
sequence-bit: 9
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:
order:
offset: 10000
step: 100
longId:
offset: 10000
step: 100


mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:/mappers/*.xml

logging:
level:
me.ahoo.cosid: debug

+ 12
- 0
examples/cosid-example/src/main/resources/bootstrap.yaml View File

@@ -0,0 +1,12 @@
spring:
application:
name: ${service.name:cosid-example}
cloud:
cosky:
redis:
mode: ${cosky.redis.mode:standalone}
url: ${cosky.redis.uri:redis://localhost:6379}

logging:
file:
name: logs/${spring.application.name}.log

+ 32
- 0
examples/cosid-example/src/main/resources/mappers/IntervalShardingAlgorithmRepository.xml View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.ahoo.cosid.example.repository.IntervalShardingAlgorithmRepository">

<insert id="insertDate" useGeneratedKeys="true" keyProperty="id">
insert into t_date_log (create_time) value (#{createTime});
</insert>

<insert id="insertDateTime" useGeneratedKeys="true" keyProperty="id">
insert into t_date_time_log (create_time) value (#{createTime});
</insert>

<insert id="insertTimestamp" useGeneratedKeys="true" keyProperty="id">
insert into t_timestamp_log (create_time) value (#{createTime});
</insert>
<insert id="insertSnowflake">
insert into t_snowflake_log (id) value (#{id});
</insert>
</mapper>

+ 47
- 0
examples/cosid-example/src/main/resources/mappers/OrderMapper.xml View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.ahoo.cosid.example.repository.OrderRepository">

<!--me.ahoo.cosid.shardingsphere.sharding.key.CosIdKeyGenerateAlgorithm-->
<insert id="insert" useGeneratedKeys="true" keyProperty="orderId">
insert into t_order (user_id) value (#{userId});
</insert>
<!--me.ahoo.cosid.annotation.CosId-->
<insert id="insertItem">
insert into t_order_item (order_item_id, order_id) value (#{orderItemId},#{orderId});
</insert>

<select id="getById" resultType="me.ahoo.cosid.example.entity.Order">
select *
from t_order
where order_id = #{orderId}
</select>

<select id="query" resultType="me.ahoo.cosid.example.entity.Order">
select *
from t_order
where order_id > 92301
</select>
<select id="getByIds" resultType="me.ahoo.cosid.example.entity.Order">
select *
from t_order
where order_id in
<foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</select>

</mapper>

+ 23
- 0
gradle.properties View File

@@ -0,0 +1,23 @@
#
# Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

group=me.ahoo.cosid
version=1.16.9

description=Universal, flexible, high-performance distributed ID generator.
website=https://github.com/Ahoo-Wang/CosId
issues=https://github.com/Ahoo-Wang/CosId/issues
vcs=https://github.com/Ahoo-Wang/CosId.git

license_name=The Apache Software License, Version 2.0
license_url=https://www.apache.org/licenses/LICENSE-2.0.txt

BIN
gradle/wrapper/gradle-wrapper.jar View File


+ 5
- 0
gradle/wrapper/gradle-wrapper.properties View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

+ 244
- 0
gradlew View File

@@ -0,0 +1,244 @@
#!/bin/sh

#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################

# Attempt to set APP_HOME

# Resolve links: $0 may be a link
app_path=$0

# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done

# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

warn () {
echo "$*"
} >&2

die () {
echo
echo "$*"
echo
exit 1
} >&2

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi

# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.

# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )

JAVACMD=$( cygpath --unix "$JAVACMD" )

# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi

# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.

set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"

# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi

# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#

eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'

exec "$JAVACMD" "$@"

+ 92
- 0
gradlew.bat View File

@@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega

+ 12
- 0
lombok.config View File

@@ -0,0 +1,12 @@
# Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

lombok.addLombokGeneratedAnnotation = true

+ 5
- 0
renovate.json View File

@@ -0,0 +1,5 @@
{
"extends": [
"config:base"
]
}

+ 63
- 0
settings.gradle.kts View File

@@ -0,0 +1,63 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

rootProject.name = "CosId"

include(":cosid-bom")
include(":cosid-dependencies")
include(":cosid-core")
include(":cosid-spring-boot-starter")
include(":cosid-redis")
include(":cosid-spring-redis")
include(":cosid-jdbc")
include(":cosid-mybatis")
include(":cosid-jackson")
include(":cosid-shardingsphere")
include(":cosid-zookeeper")
include(":cosid-test")
include(":cosid-proxy")
include(":cosid-proxy-server")
include(":cosid-axon")

include("cosid-example")
project(":cosid-example").projectDir = file("examples/cosid-example")

include("cosid-example-proxy")
project(":cosid-example-proxy").projectDir = file("examples/cosid-example-proxy")

include("cosid-example-redis")
project(":cosid-example-redis").projectDir = file("examples/cosid-example-redis")

include("cosid-example-redis-cosid")
project(":cosid-example-redis-cosid").projectDir = file("examples/cosid-example-redis-cosid")

include("cosid-example-zookeeper")
project(":cosid-example-zookeeper").projectDir = file("examples/cosid-example-zookeeper")

include("cosid-example-jdbc")
project(":cosid-example-jdbc").projectDir = file("examples/cosid-example-jdbc")

include("cosid-example-shardingsphere")
project(":cosid-example-shardingsphere").projectDir = file("examples/cosid-example-shardingsphere")


buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath("me.champeau.jmh:jmh-gradle-plugin:0.6.8")
classpath("io.github.gradle-nexus:publish-plugin:1.1.0")
classpath("com.github.spotbugs.snom:spotbugs-gradle-plugin:5.0.13")
}
}

+ 199
- 0
wiki/Distributed-ID.zh-CN.md View File

@@ -0,0 +1,199 @@
# 分布式ID生成器([CosId](https://github.com/Ahoo-Wang/CosId))设计与实现

## [CosId](https://github.com/Ahoo-Wang/CosId) 简介

*[CosId](https://github.com/Ahoo-Wang/CosId)* 旨在提供通用、灵活、高性能的分布式 ID 生成器。

- `SnowflakeId` : *单机 TPS 性能:409W/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark) , 主要解决 *时钟回拨问题* 、*机器号分配问题* 并且提供更加友好、灵活的使用体验。
- `SegmentId`: 每次获取一段 (`Step`) ID,来降低号段分发器的网络IO请求频次提升性能。
- `IdSegmentDistributor`: 号段分发器(号段存储器)
- `RedisIdSegmentDistributor`: 基于 *Redis* 的号段分发器。
- `JdbcIdSegmentDistributor`: 基于 *Jdbc* 的号段分发器,支持各种关系型数据库。
- `SegmentChainId`(**推荐**):`SegmentChainId` (*lock-free*) 是对 `SegmentId` 的增强。性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark) 。
- `PrefetchWorker` 维护安全距离(`safeDistance`), 并且支持基于饥饿状态的动态`safeDistance`扩容/收缩。

## 背景(为什么需要*分布式ID*)

在软件系统演进过程中,随着业务规模的增长,我们需要进行集群化部署来分摊计算、存储压力,应用服务我们可以很轻松做到无状态、弹性伸缩。
但是仅仅增加服务副本数就够了吗?显然不够,因为性能瓶颈往往是在数据库层面,那么这个时候我们就需要考虑如何进行数据库的扩容、伸缩、集群化,通常使用分库、分表的方式来处理。
那么我如何分片(水平分片,当然还有垂直分片不过不是本文需要讨论的内容)呢,分片得前提是我们得先有一个ID,然后才能根据分片算法来分片。(比如比较简单常用的ID取模分片算法,这个跟Hash算法的概念类似,我们得先有key才能进行Hash取得插入槽位。)

> 当然还有很多分布式场景需要*分布式ID*,这里不再一一列举。

## 分布式ID方案的核心指标

- **全局(相同业务)唯一性**:唯一性保证是**ID**的必要条件,假设ID不唯一就会产生主键冲突,这点很容易可以理解。
- 通常所说的全局唯一性并不是指所有业务服务都要唯一,而是相同业务服务不同部署副本唯一。
比如 Order 服务的多个部署副本在生成`t_order`这张表的`Id`时是要求全局唯一的。至于`t_order_item`生成的`ID`与`t_order`是否唯一,并不影响唯一性约束,也不会产生什么副作用。
不同业务模块间也是同理。即唯一性主要解决的是ID冲突问题。
- **有序性**:有序性保证是面向查询的数据结构算法(除了Hash算法)所必须的,是**二分查找法**(分而治之)的前提。
- MySq-InnoDB B+树是使用最为广泛的,假设 Id 是无序的,B+ 树 为了维护 ID 的有序性,就会频繁的在索引的中间位置插入而挪动后面节点的位置,甚至导致频繁的页分裂,这对于性能的影响是极大的。那么如果我们能够保证ID的有序性这种情况就完全不同了,只需要进行追加写操作。所以 ID 的有序性是非常重要的,也是ID设计不可避免的特性。
- **吞吐量/性能(ops/time)**:即单位时间(每秒)能产生的ID数量。生成ID是非常高频的操作,也是最为基本的。假设ID生成的性能缓慢,那么不管怎么进行系统优化也无法获得更好的性能。
- 一般我们会首先生成ID,然后再执行写入操作,假设ID生成缓慢,那么整体性能上限就会受到限制,这一点应该不难理解。
- **稳定性(time/op)**:稳定性指标一般可以采用**每个操作的时间进行百分位采样**来分析,比如 *[CosId](https://github.com/Ahoo-Wang/CosId)* 百分位采样 **P9999=0.208 us/op**,即 **0% ~ 99.99%** 的单位操作时间小于等于 **0.208 us/op**。
- [百分位数 WIKI](https://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E4%BD%8D%E6%95%B0) :统计学术语,若将一组数据从小到大排序,并计算相应的累计百分点,则某百分点所对应数据的值,就称为这百分点的百分位数,以Pk表示第k百分位数。百分位数是用来比较个体在群体中的相对地位量数。
- 为什么不用平均*每个操作的时间*:马老师的身价跟你的身价能平均么?平均后的值有意义不?
- 可以使用最小*每个操作的时间*、最大*每个操作的时间*作为参考吗?因为最小、最大值只说明了零界点的情况,虽说可以作为稳定性的参考,但依然不够全面。而且*百分位数*已经覆盖了这俩个指标。
- **自治性(依赖)**:主要是指对外部环境有无依赖,比如**号段模式**会强依赖第三方存储中间件来获取`NexMaxId`。自治性还会对可用性造成影响。
- **可用性**:分布式ID的可用性主要会受到自治性影响,比如**SnowflakeId**会受到时钟回拨影响,导致处于短暂时间的不可用状态。而**号段模式**会受到第三方发号器(`NexMaxId`)的可用性影响。
- [可用性 WIKI](https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%94%A8%E6%80%A7) :在一个给定的时间间隔内,对于一个功能个体来讲,总的可用时间所占的比例。
- MTBF:平均故障间隔
- MDT:平均修复/恢复时间
- Availability=MTBF/(MTBF+MDT)
- 假设MTBF为1年,MDT为1小时,即`Availability=(365*24)/(365*24+1)=0.999885857778792≈99.99%`,也就是我们通常所说对可用性4个9。
- **适应性**:是指在面对外部环境变化的自适应能力,这里我们主要说的是面对流量突发时动态伸缩分布式ID的性能,
- **SegmentChainId**可以基于**饥饿状态**进行**安全距离**的动态伸缩。
- **SnowflakeId**常规位分配方案性能恒定409.6W,虽然可以通过调整位分配方案来获得不同的TPS性能,但是位分配方法的变更是破坏性的,一般根据业务场景确定位分配方案后不再变更。
- **存储空间**:还是用MySq-InnoDB B+树来举例,普通索引(二级索引)会存储主键值,主键越大占用的内存缓存、磁盘空间也会越大。Page页存储的数据越少,磁盘IO访问的次数会增加。总之在满足业务需求的情况下,尽可能小的存储空间占用在绝大多数场景下都是好的设计原则。

### 不同分布式ID方案核心指标对比

| 分布式ID | 全局唯一性 | 有序性 | 吞吐量 | 稳定性(1s=1000,000us) | 自治性 | 可用性 | 适应性 | 存储空间 |
|:--------------:|:-----:|:---------------------------:|-----------------:|:--------------------|:----------:|:----------------------------------------:|:---:|:-------:|
| UUID/GUID | 是 | 完全无序 | 3078638(ops/s) | P9999=0.325(us/op) | 完全自治 | 100% | 否 | 128-bit |
| SnowflakeId | 是 | 本地单调递增,全局趋势递增(受全局时钟影响) | 4096000(ops/s) | P9999=0.244(us/op) | 依赖时钟 | 时钟回拨会导致短暂不可用 | 否 | 64-bit |
| SegmentId | 是 | 本地单调递增,全局趋势递增(受Step影响) | 29506073(ops/s) | P9999=46.624(us/op) | 依赖第三方号段分发器 | 受号段分发器可用性影响 | 否 | 64-bit |
| SegmentChainId | 是 | 本地单调递增,全局趋势递增(受Step、安全距离影响) | 127439148(ops/s) | P9999=0.208(us/op) | 依赖第三方号段分发器 | 受号段分发器可用性影响,但因安全距离存在,预留ID段,所以高于SegmentId | 是 | 64-bit |

### 有序性(要想分而治之·二分查找法,必须要维护我)

刚刚我们已经讨论了ID有序性的重要性,所以我们设计ID算法时应该尽可能地让ID是单调递增的,比如像表的自增主键那样。但是很遗憾,因全局时钟、性能等分布式系统问题,我们通常只能选择局部单调递增、全局趋势递增的组合(就像我们在分布式系统中不得不的选择最终一致性那样)以获得多方面的权衡。下面我们来看一下什么是单调递增与趋势递增。

#### 有序性之单调递增

![单调递增](img/monotonically-increasing.png)

单调递增:T表示全局绝对时点,假设有T<sub>n+1</sub>>T<sub>n</sub>(绝对时间总是往前进的,这里不考虑相对论、时间机器等),那么必然有F(T<sub>n+1</sub>)>F(T<sub>n</sub>),数据库自增主键就属于这一类。
另外需要特别说明的是单调递增跟连续性递增是不同的概念。 连续性递增:`F(n+1)=(F(n)+step)`即下一次获取的ID一定等于当前`ID+Step`,当`Step=1`时类似于这样一个序列:`1->2->3->4->5`。

> 扩展小知识:数据库的自增主键也不是连续性递增的,相信你一定遇到过这种情况,请思考一下数据库为什么这样设计?

#### 有序性之趋势递增

![趋势递增](img/trend-increasing.png)

趋势递增:T<sub>n</sub>>T<sub>n-s</sub>,那么大概率有F(T<sub>n</sub>)>F(T<sub>n-s</sub>)。虽然在一段时间间隔内有乱序,但是整体趋势是递增。从上图上看,是有上升趋势的(趋势线)。
- 在**SnowflakeId**中<sub>n-s</sub>受到全局时钟同步影响。
- 在号段模式(**SegmentId**)中<sub>n-s</sub>受到号段可用区间(`Step`)影响。

## 分布式ID分配方案

### UUID/GUID

- :thumbsup:不依赖任何第三方中间件
- :thumbsup:性能高
- :thumbsdown:完全无序
- :thumbsdown:空间占用大,需要占用128位存储空间。

UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据库的主键索引效率低下(为了维护索引树,频繁的索引中间位置插入数据,而不是追加写)。这也是UUID不适用于数据库主键的最为重要的原因。

### SnowflakeId

![Snowflake](../docs/Snowflake-identifier.png)

> *SnowflakeId*使用`Long`(64-bit)位分区来生成ID的一种分布式ID算法。
> 通用的位分配方案为:`timestamp`(41-bit)+`machineId`(10-bit)+`sequence`(12-bit)=63-bit。

- 41-bit`timestamp`=(1L<<41)/(1000/3600/365),约可以存储69年的时间戳,即可以使用的绝对时间为`EPOCH`+69年,一般我们需要自定义`EPOCH`为产品开发时间,另外还可以通过压缩其他区域的分配位数,来增加时间戳位数来延长可用时间。
- 10-bit`machineId`=(1L<<10)=1024,即相同业务可以部署1024个副本(在Kubernetes概念里没有主从副本之分,这里直接沿用Kubernetes的定义)。一般情况下没有必要使用这么多位,所以会根据部署规模需要重新定义。
- 12-bit`sequence`=(1L<<12)*1000=4096000,即单机每秒可生成约409W的ID,全局同业务集群可产生`4096000*1024=419430W=41.9亿(TPS)`。

从 *SnowflakeId* 设计上可以看出:

- :thumbsup:`timestamp`在高位,单实例*SnowflakeId*是会保证时钟总是向前的(校验本机时钟回拨),所以是本机单调递增的。受全局时钟同步/时钟回拨影响*SnowflakeId*是全局趋势递增的。
- :thumbsup:*SnowflakeId*不对任何第三方中间件有强依赖关系,并且性能也非常高。
- :thumbsup:位分配方案可以按照业务系统需要灵活配置,来达到最优使用效果。
- :thumbsdown:强依赖本机时钟,潜在的时钟回拨问题会导致ID重复、处于短暂的不可用状态。
- :thumbsdown:`machineId`需要手动设置,实际部署时如果采用手动分配`machineId`,会非常低效。

#### SnowflakeId之机器号分配问题

在**SnowflakeId**中根据业务设计的位分配方案确定了基本上就不再有变更了,也很少需要维护。但是`machineId`总是需要配置的,而且集群中是不能重复的,否则分区原则就会被破坏而导致ID唯一性原则破坏,当集群规模较大时`machineId`的维护工作是非常繁琐,低效的。
> 有一点需要特别说明的,**SnowflakeId**的**MachineId**是逻辑上的概念,而不是物理概念。
> 想象一下假设**MachineId**是物理上的,那么意味着一台机器拥有只能拥有一个**MachineId**,那会产生什么问题呢?

> 目前 *[CosId](https://github.com/Ahoo-Wang/CosId)* 提供了以下三种 `MachineId` 分配器。

- ManualMachineIdDistributor: 手动配置`machineId`,一般只有在集群规模非常小的时候才有可能使用,不推荐。
- StatefulSetMachineIdDistributor: 使用`Kubernetes`的`StatefulSet`提供的稳定的标识ID(HOSTNAME=service-01)作为机器号。
- RedisMachineIdDistributor: 使用**Redis**作为机器号的分发存储,同时还会存储`MachineId`的上一次时间戳,用于**启动时时钟回拨**的检查。

![RedisMachineIdDistributor](../docs/RedisMachineIdDistributor.png)

#### SnowflakeId之时钟回拨问题

时钟回拨的致命问题是会导致ID重复、冲突(这一点不难理解),ID重复显然是不能被容忍的。
在**SnowflakeId**算法中,按照**MachineId**分区ID,我们不难理解的是不同**MachineId**是不可能产生相同ID的。所以我们解决的时钟回拨问题是指当前**MachineId**的时钟回拨问题,而不是所有集群节点的时钟回拨问题。

**MachineId**时钟回拨问题大体可以分为俩种情况:

- 运行时时钟回拨:即在运行时获取的当前时间戳比上一次获取的时间戳小。这个场景的时钟回拨是很容易处理的,一般**SnowflakeId**代码实现时都会存储`lastTimestamp`用于运行时时钟回拨的检查,并抛出时钟回拨异常。
- 时钟回拨时直接抛出异常是不太好地实践,因为下游使用方几乎没有其他处理方案(噢,我还能怎么办呢,等吧),时钟同步是唯一的选择,当只有一种选择时就不要再让用户选择了。
- `ClockSyncSnowflakeId`是`SnowflakeId`的包装器,当发生时钟回拨时会使用`ClockBackwardsSynchronizer`主动等待时钟同步来重新生成ID,提供更加友好的使用体验。
- 启动时时钟回拨:即在启动服务实例时获取的当前时钟比上次关闭服务时小。此时的`lastTimestamp`是无法存储在进程内存中的。当获取的外部存储的**机器状态**大于当前时钟时钟时,会使用`ClockBackwardsSynchronizer`主动同步时钟。
- LocalMachineStateStorage:使用本地文件存储`MachineState`(机器号、最近一次时间戳)。因为使用的是本地文件所以只有当实例的部署环境是稳定的,`LocalMachineStateStorage`才适用。
- RedisMachineIdDistributor:将`MachineState`存储在**Redis**分布式缓存中,这样可以保证总是可以获取到上次服务实例停机时**机器状态**。

#### SnowflakeId之JavaScript数值溢出问题

`JavaScript`的`Number.MAX_SAFE_INTEGER`只有53-bit,如果直接将63位的`SnowflakeId`返回给前端,那么会产生值溢出的情况(所以这里我们应该知道后端传给前端的`long`值溢出问题,**迟早**会出现,只不过SnowflakeId出现得更快而已)。
很显然溢出是不能被接受的,一般可以使用以下俩种处理方案:
- 将生成的63-bit`SnowflakeId`转换为`String`类型。
- 直接将`long`转换成`String`。
- 使用`SnowflakeFriendlyId`将`SnowflakeId`转换成比较友好的字符串表示:`{timestamp}-{machineId}-{sequence} -> 20210623131730192-1-0`
- 自定义`SnowflakeId`位分配来缩短`SnowflakeId`的位数(53-bit)使 `ID` 提供给前端时不溢出
- 使用`SafeJavaScriptSnowflakeId`(`JavaScript` 安全的 `SnowflakeId`)

## 号段模式(SegmentId)

![SegmentId](../docs/SegmentId.png)

从上面的设计图中,不难看出**号段模式**基本设计思路是通过每次获取一定长度(Step)的可用ID(Id段/号段),来降低网络IO请求次数,提升性能。

- :thumbsdown:强依赖第三方号段分发器,可用性受到第三方分发器影响。
- :thumbsdown:每次号段用完时获取`NextMaxId`需要进行网络IO请求,此时的性能会比较低。
- 单实例ID单调递增,全局趋势递增。
- 从设计图中不难看出**Instance 1**每次获取的`NextMaxId`,一定比上一次大,意味着下一次的号段一定比上一次大,所以从单实例上来看是单调递增的。
- 多实例各自持有的不同的号段,意味着同一时刻不同实例生成的ID是乱序的,但是整体趋势的递增的,所以全局趋势递增。
- ID乱序程度受到Step长度以及集群规模影响(从趋势递增图中不难看出)。
- 假设集群中只有一个实例时**号段模式**就是单调递增的。
- `Step`越小,乱序程度越小。当`Step=1`时,将无限接近单调递增。需要注意的是这里是无限接近而非等于单调递增,具体原因你可以思考一下这样一个场景:
- 号段分发器T<sub>1</sub>时刻给**Instance 1**分发了`ID=1`,T<sub>2</sub>时刻给**Instance 2**分发了`ID=2`。因为机器性能、网络等原因,`Instance 2`网络IO写请求先于`Instance 1`到达。那么这个时候对于数据库来说,ID依然是乱序的。

## 号段链模式(SegmentChainId)

![SegmentChainId](../docs/SegmentChainId.png)

**SegmentChainId**是**SegmentId**增强版,相比于**SegmentId**有以下优势:

- 稳定性:**SegmentId**的稳定性问题(P9999=46.624(us/op))主要是因为号段用完之后同步进行`NextMaxId`的获取导致的(会产生网络IO)。
- **SegmentChainId** (P9999=0.208(us/op))引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取,性能可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark) 。
- 适应性:从**SegmentId**介绍中我们知道了影响**ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。
- `Step`应该近可能小才能使得**ID单调递增**的可能性增大。
- `Step`太小会影响吞吐量,那么我们如何合理设置`Step`呢?答案是我们无法准确预估所有时点的吞吐量需求,那么最好的办法是吞吐量需求高时,Step自动增大,吞吐量低时Step自动收缩。
- **SegmentChainId**引入了**饥饿状态**的概念,**PrefetchWorker**会根据**饥饿状态**检测当前**安全距离**是否需要膨胀或者收缩,以便获得吞吐量与有序性之间的权衡,这便是**SegmentChainId**的自适应性。

### SegmentChainId-吞吐量 (ops/s)

#### RedisChainIdBenchmark-Throughput

![RedisChainIdBenchmark-Throughput](../docs/jmh/RedisChainIdBenchmark-Throughput.png)

#### MySqlChainIdBenchmark-Throughput

![MySqlChainIdBenchmark-Throughput](../docs/jmh/MySqlChainIdBenchmark-Throughput.png)

### SegmentChainId-每次操作耗时的百分位数(us/op)

#### RedisChainIdBenchmark-Percentile

![RedisChainIdBenchmark-Sample](../docs/jmh/RedisChainIdBenchmark-Sample.png)

#### MySqlChainIdBenchmark-Percentile

![MySqlChainIdBenchmark-Sample](../docs/jmh/MySqlChainIdBenchmark-Sample.png)

## 基准测试报告运行环境说明

- 基准测试运行环境:笔记本开发机(MacBook-Pro-(M1))
- 所有基准测试都在开发笔记本上执行。

+ 107
- 0
wiki/SegmentChainId.zh-CN.md View File

@@ -0,0 +1,107 @@
# 分布式ID(CosId)之号段链模式性能(1.2亿/s)解析

> 上一篇文章[《分布式ID生成器(CosId)设计与实现》](https://github.com/Ahoo-Wang/CosId/blob/main/wiki/Distributed-ID.zh-CN.md)我们已经简单讨论过[CosId](https://github.com/Ahoo-Wang/CosId)的设计与实现全貌。
>
> 但是有很多同学有一些疑问:CosId的号段链模式(SegmentChainId)性能有些难以置信(TPS峰值性能1.2亿/s),甚至是不同性能级别号段分发器(**RedisIdSegmentDistributor**、**JdbcIdSegmentDistributor**)均能够达到这样的性能级别。
>
> 所以本篇文章将深度解析CosId的号段链模式(SegmentChainId)的设计思路与实现优化。
>
> 新同学最好先查阅上一篇文章[《分布式ID生成器(CosId)设计与实现》](https://github.com/Ahoo-Wang/CosId/blob/main/wiki/Distributed-ID.zh-CN.md)。

## 背景(为什么需要*SegmentChainId*)

通过上一篇文章[《分布式ID生成器(CosId)设计与实现》](https://github.com/Ahoo-Wang/CosId/blob/main/wiki/Distributed-ID.zh-CN.md)我们知道号段模式(SegmentId)主要有以下问题:

- 稳定性:**SegmentId**的稳定性问题主要是因为号段用完之后获取ID的线程需要同步进行`NextMaxId`导致的(会产生网络IO)。
- 步长(Step):设置多大的步长是一个权衡问题,设置太小会影响整体性能,设置太大会导致全局ID乱序的程度增加。
- 本机单调递增、全局趋势递增: 本地单调递增是我们期望看到的,但是如何理解/衡量全局趋势递增呢。下面我们来解释一下什么是全局趋势递增:

![号段模式](../document/docs/.vuepress/public/assets/design/SegmentId.png)

从上图的号段模式设计中我们可以看出:

- **Instance 1**每次获取的`NextMaxId`,一定比上一次大,意味着下一次的号段一定比上一次大,即F(T<sub>n+1</sub>)>F(T<sub>n</sub>),所以从单实例上来看是单调递增的。
- **Instance 1**、**Instance 2**实例各自持有的不同的号段,意味着在一个时间段内不同实例生成的ID是乱序的。但是多实例在获取`NextMaxId`时是单调递增的,所以整体趋势的递增,即全局趋势递增。

---

![趋势递增](../document/docs/.vuepress/public/assets/design/trend-increasing.png)

> 全局趋势递增反向说明的是ID在一个时间周期内是会乱序的,所以我们要尽可能让ID的乱序程度降低。这是一个优化点。

号段模式生成的ID,在数据库视角可以近似的理解为上面的趋势递增图(T<sub>n</sub>>T<sub>n-s</sub>)。ID乱序的程度受到步长(Step)影响,步长越小ID乱序的程度越小。这里我们使用边界值(`Step=1`)作一下说明。

- 当`Step=1`时,即每次都获取`NextMaxId`,将无限接近单调递增(数据库视角)。需要注意的是这里是无限接近而非等于单调递增,具体原因你可以思考一下这样一个场景:
- 号段分发器T<sub>1</sub>时刻给**Instance 1**分发了`ID=1`,T<sub>2</sub>时刻给**Instance 2**分发了`ID=2`。因为机器性能、网络等原因,`Instance 2`网络IO写请求先于`Instance 1`到达。那么这个时候对于数据库来说,ID依然是乱序的。

所以不难理解的是影响**全局ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。

所以*SegmentChainId*就是在这样的背景下诞生的。

## 号段链模式(SegmentChainId)的优势

![SegmentChainId](../document/docs/.vuepress/public/assets/design/SegmentChainId.png)

通过**SegmentChainId**设计图中我们可以看到,号段链模式新增了一个角色**PrefetchWorker**。
**PrefetchWorker**主要的职责是维护和保证号段链头部到尾部的**安全距离**,也可以近似理解为缓冲距离。
有了安全距离的保障不难得出的结论是所有获取ID的线程只要从进程内存的号段里边获取下次ID即可,理想情况下不需要再进行`NextMaxId`(向号段分发器请求`NextMaxId`,网络IO)的,所以性能可以达到近似`AtomicLong` 的 *TPS 性能:12743W+/s*的级别。

**SegmentChainId**是**SegmentId**的增强版,相比于**SegmentId**有以下优势:

- TPS性能:可达到近似 `AtomicLong` 的 *TPS 性能:12743W+/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark)。通过引入了新的角色**PrefetchWorker**用以维护和保证**安全距离**,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待`NextMaxId`获取。
- 稳定性:P9999=0.208(us/op),通过上面的TPS性能描述中我们可以看到,**SegmentChainId**消除了同步等待的问题,所以稳定性问题也因此迎刃而解。
- 适应性:从**SegmentId**介绍中我们知道了影响**ID乱序**的因素有俩个:集群规模、`Step`大小。集群规模是我们不能控制的,但是`Step`是可以调节的。
- `Step`应该尽可能小才能使得**ID单调递增**的可能性增大。
- `Step`太小会影响吞吐量,那么我们如何合理设置`Step`呢?答案是我们无法准确预估所有时点的吞吐量需求,那么最好的办法是吞吐量需求高时,Step自动增大,吞吐量低时Step自动收缩。
- **SegmentChainId**引入了**饥饿状态**的概念,**PrefetchWorker**会根据**饥饿状态**检测当前**安全距离**是否需要膨胀或者收缩,以便获得吞吐量与有序性之间的权衡,这便是**SegmentChainId**的自适应性。
- 所以在使用**SegmentChainId**时我们可以配置一个比较小的`Step`步长,然后由**PrefetchWorker**根据吞吐量需求自动调节**安全距离**,来自动伸缩步长。

## 常见问题

### RedisIdSegmentDistributor、JdbcIdSegmentDistributor 均能够达到TPS=1.2亿/s?

#### RedisChainIdBenchmark-Throughput

![RedisChainIdBenchmark-Throughput](../docs/jmh/RedisChainIdBenchmark-Throughput.png)

#### MySqlChainIdBenchmark-Throughput

![MySqlChainIdBenchmark-Throughput](../docs/jmh/MySqlChainIdBenchmark-Throughput.png)

上面的两张图给许多同学带来了困扰,为什么在`Step=1000`的时候*RedisChainIdBenchmark*、*MySqlChainIdBenchmark*TPS性能几乎一致(TPS=1.2亿/s)。
*RedisIdSegmentDistributor*应该要比*JdbcIdSegmentDistributor*性能更高才对啊,为什么都能达到*AtomicLong*性能上限呢?
如果我说当`Step=1`时,只要基准测试的时间够长,那么他们依然能够达到*AtomicLong*性能级别(TPS=1.2亿/s),你会不会更加困惑。
其实这里的*障眼法*是**PrefetchWorker**的**饥饿膨胀**导致的,*SegmentChainId*的极限性能跟分发器的TPS性能没有直接关系,因为最终都可以因饥饿膨胀到性能上限,只要给足够的时间膨胀。
而为什么在上图的`Step=1`时TPS差异还是很明显的,这是因为*RedisIdSegmentDistributor*膨胀得更快,而基准测试又没有给足测试时间而已。

**SegmentChainId**基准测试*TPS极限性能*可以近似使用以下的公式的表示:

`TPS(SegmentChainId)极限值=(Step*Expansion)*TPS(IdSegmentDistributor)*T/s<=TPS(AtomicLong)`

1. `<=TPS(AtomicLong)`:因为*SegmentChainId*的内部号段就是使用的`AtomicLong`,所以这是性能上限。
2. `Step*Expansion`:*Expansion*可以理解为饥饿膨胀系数,默认的饥饿膨胀系数是2。在*MySqlChainIdBenchmark*、*MySqlChainIdBenchmark*基准测试中这个值是一样的。
3. `TPS(IdSegmentDistributor)`: 这是公式中唯一的不同。指的是请求号段分发器`NextMaxId`的TPS。
4. `T`: 可以理解为基准测试运行时常。

从上面的公式中不难看出*RedisChainIdBenchmark*、*MySqlChainIdBenchmark*主要差异是分发器的TPS性能。
分发器的`TPS(IdSegmentDistributor)`越大,达到`TPS(AtomicLong)`所需的`T`就越少。但只要`T`足够长,那么任何分发器都可以达到近似`TPS(AtomicLong)`。
这也就解释了为什么不同TPS性能级别的号段分发器(**IdSegmentDistributor**)都可以达到TPS=1.2亿/s。

### CosId需要部署服务端吗?

[CosId](https://github.com/Ahoo-Wang/CosId)是以本地SDK的形式存在的,用户只需要安装一下**CosId**的依赖包做一些简单配置([快速开始](wiki/getting-started.md)、[DEMO](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-rest-api))即可。

> 分布式ID是不适合使用服务端部署模式的(C/S)。使用服务端部署模式,必然会产生网络IO(*Client*通过远程过程调用*Server*,获取ID),你想想我们费了那么大劲消除网络IO是为了什么?

### PrefetchWorker 是如何维护安全距离的?

- 定时维护:每隔一段时间**PrefetchWorker**会主动检测安全距离是否满足配置要求,如果不满足则执行`NextMaxId`预取,保证安全距离。
- 被动饥饿唤醒:当获取ID的线程获取ID时没有可用号段,会尝试获取新的号段,并主动唤醒**PrefetchWorker**并告诉他你太慢了,被唤醒的**PrefetchWorker**会检测安全距离是否需要膨胀,然后进行安全距离的维护。

### 本机单调、全局趋势递增-为什么还要尽可能保证单调递增?

从上文的论述中我们不难理解本机单调递增,全局趋势递增是权衡后的设计结果。
但是全局趋势递增的背面是周期内ID乱序,所以尽可能向单调递增优化(降低ID乱序程度)是优化目标,这俩点并不冲突。

> 如果各位同学还有其他问题请至 [Issues](https://github.com/Ahoo-Wang/CosId/issues) 提交你的疑问。


+ 696
- 0
wiki/getting-started.md View File

@@ -0,0 +1,696 @@
# [CosId](https://cosid.ahoo.me/) 通用、灵活、高性能的分布式 ID 生成器

> [English Document](https://github.com/Ahoo-Wang/CosId/blob/main/README.md)

## 介绍

*[CosId](https://github.com/Ahoo-Wang/CosId)* 旨在提供通用、灵活、高性能的分布式 ID 生成器。

- `SnowflakeId` : *单机 TPS
性能:409W/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark) , 主要解决 *时钟回拨问题* 、*
机器号分配问题* 并且提供更加友好、灵活的使用体验。
- `SegmentId`: 每次获取一段 (`Step`) ID,来降低号段分发器的网络IO请求频次提升性能。
- `IdSegmentDistributor`: 号段分发器(号段存储器)
- `RedisIdSegmentDistributor`: 基于 *Redis* 的号段分发器。
- `JdbcIdSegmentDistributor`: 基于 *Jdbc* 的号段分发器,支持各种关系型数据库。
- `SegmentChainId`(**推荐**):`SegmentChainId`(*lock-free*) 是对`SegmentId`的增强。性能可达到近似`AtomicLong`的*TPS性能:12743W+/s* [JMH 基准测试](https://github.com/Ahoo-Wang/CosId/blob/main/README.zh-CN.md#jmh-benchmark) 。
- `PrefetchWorker` 维护安全距离(`safeDistance`), 并且支持基于饥饿状态的动态`safeDistance`扩容/收缩。

## SnowflakeId

![Snowflake](../document/docs/.vuepress/public/assets/design/Snowflake-identifier.png)

> *SnowflakeId*使用`Long`(64-bit)位分区来生成ID的一种分布式ID算法。
> 通用的位分配方案为:`timestamp`(64-bit)+`machineId`(10-bit)+`sequence`(12-bit)=63bit。

- 41-bit `timestamp` = (1L<<41)/(1000/3600/365) 约可以存储 69 年的时间戳,即可以使用的绝对时间为 `EPOCH` + 69 年,一般我们需要自定义 `EPOCH`
为产品开发时间,另外还可以通过压缩其他区域的分配位数,来增加时间戳位数来延长可用时间。
- 10-bit `machineId` = (1L<<10) = 1024 即相同业务可以部署 1024 个副本 (在 Kubernetes 概念里没有主从副本之分,这里直接沿用 Kubernetes 的定义)
实例,一般情况下没有必要使用这么多位,所以会根据部署规模需要重新定义。
- 12-bit `sequence` = (1L<<12) * 1000 = 4096000 即单机每秒可生成约 409W 的 ID,全局同业务集群可产生 `4096000*1024=4194304000=41.9亿(TPS)`。

从 *SnowflakeId* 设计上可以看出:

- :thumbsup: `timestamp` 在高位,所以 *SnowflakeId* 是本机单调递增的,受全局时钟同步影响 *SnowflakeId* 是全局趋势递增的。
- :thumbsup: *SnowflakeId* 不对任何第三方中间件有强依赖关系,并且性能也非常高。
- :thumbsup: 位分配方案可以按照业务系统需要灵活配置,来达到最优使用效果。
- :thumbsdown: 强依赖本机时钟,潜在的时钟回拨问题会导致 ID 重复。
- :thumbsdown: `machineId` 需要手动设置,实际部署时如果采用手动分配 `machineId`,会非常低效。

---

*[CosId-SnowflakeId](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-core/src/main/java/me/ahoo/cosid/snowflake)*
主要解决 *SnowflakeId* 俩大问题:机器号分配问题、时钟回拨问题。 并且提供更加友好、灵活的使用体验。

### MachineIdDistributor (MachineId 分配器)

> 目前 *[CosId](https://github.com/Ahoo-Wang/CosId)* 提供了以下三种 `MachineId` 分配器。

#### ManualMachineIdDistributor

```yaml
cosid:
snowflake:
machine:
distributor:
type: manual
manual:
machine-id: 0
```

> 手动分配 `MachineId`。

#### StatefulSetMachineIdDistributor

```yaml
cosid:
snowflake:
machine:
distributor:
type: stateful_set
```

> 使用 `Kubernetes` 的 `StatefulSet` 提供的稳定的标识 ID 作为机器号。

#### RedisMachineIdDistributor

![RedisMachineIdDistributor](../document/docs/.vuepress/public/assets/design/RedisMachineIdDistributor.png)

```yaml
cosid:
snowflake:
machine:
distributor:
type: redis
```

> 使用 `Redis` 作为机器号的分发存储。

### ClockBackwardsSynchronizer (时钟回拨同步器)

```yaml
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);
}
}
```

```yaml
cosid:
snowflake:
machine:
state-storage:
local:
state-location: ./cosid-machine-state/
```

默认提供的 `LocalMachineStateStorage` 本地机器状态存储,使用本地文件存储机器号、最近一次时间戳,用作 `MachineState` 缓存。

### ClockSyncSnowflakeId (主动时钟同步 `SnowflakeId`)

```yaml
cosid:
snowflake:
share:
clock-sync: true
```

默认 `SnowflakeId` 当发生时钟回拨时会直接抛出 `ClockBackwardsException` 异常,而使用 `ClockSyncSnowflakeId` 会使用 `ClockBackwardsSynchronizer`
主动等待时钟同步来重新生成 ID,提供更加友好的使用体验。

### SafeJavaScriptSnowflakeId (`JavaScript` 安全的 `SnowflakeId`)

```java
SnowflakeId snowflakeId=SafeJavaScriptSnowflakeId.ofMillisecond(1);
```

`JavaScript` 的 `Number.MAX_SAFE_INTEGER` 只有 53 位,如果直接将 63 位的 `SnowflakeId` 返回给前端,那么会值溢出的情况,通常我们可以将`SnowflakeId`
转换为 `String` 类型或者自定义 `SnowflakeId` 位分配来缩短 `SnowflakeId` 的位数 使 `ID` 提供给前端时不溢出。

### SnowflakeFriendlyId (可以将 `SnowflakeId` 解析成可读性更好的 `SnowflakeIdState` )

```yaml
cosid:
snowflake:
share:
friendly: true
```

```java
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;
}
```

```java
public interface SnowflakeFriendlyId extends SnowflakeId {

SnowflakeIdState friendlyId(long id);

SnowflakeIdState ofFriendlyId(String friendlyId);

default SnowflakeIdState friendlyId() {
long id = generate();
return friendlyId(id);
}
}
```

```java
SnowflakeFriendlyId snowflakeFriendlyId=new DefaultSnowflakeFriendlyId(snowflakeId);
SnowflakeIdState idState=snowflakeFriendlyId.friendlyId();
idState.getFriendlyId(); //20210623131730192-1-0
```

## SegmentId (号段模式)

![SegmentId](../document/docs/.vuepress/public/assets/design/SegmentId.png)

### RedisIdSegmentDistributor (使用`Redis`作为号段分发后端存储)

```yaml
cosid:
segment:
enabled: true
distributor:
type: redis
```

### JdbcIdSegmentDistributor (使用关系型数据库`Db`作为号段分发后端存储)

> 初始化 `cosid` table

```mysql
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;

```

```yaml
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` 是主键。

```mysql
insert into cosid
(name, last_max_id, last_fetch_time)
value
('namespace.name', 0, unix_timestamp());
```

### SegmentChainId (号段链模式)

![SegmentChainId](../document/docs/.vuepress/public/assets/design/SegmentChainId.png)

```yaml
cosid:
segment:
enabled: true
mode: chain
chain:
safe-distance: 5
prefetch-worker:
core-pool-size: 2
prefetch-period: 1s
```

## IdGeneratorProvider

```yaml
cosid:
snowflake:
provider:
bizA:
# epoch:
# timestamp-bit:
sequence-bit: 12
bizB:
# epoch:
# timestamp-bit:
sequence-bit: 12
```

```java
IdGenerator idGenerator=idGeneratorProvider.get("bizA");
```

在实际使用中我们一般不会所有业务服务使用同一个`IdGenerator`而是不同的业务使用不同的`IdGenerator`,那么`IdGeneratorProvider`就是为了解决这个问题而存在的,他是 `IdGenerator`
的容器,可以通过业务名来获取相应的`IdGenerator`。

### CosIdPlugin(MyBatis 插件)

> Kotlin DSL

``` kotlin
implementation("me.ahoo.cosid:cosid-mybatis:${cosidVersion}")
```

```java

@Target({ElementType.FIELD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CosId {
String value() default IdGeneratorProvider.SHARE;

boolean friendlyId() default false;
}
```

```java
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;
}
}
```

```java

@Mapper
public interface OrderRepository {
@Insert("insert into t_table (id) value (#{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;
```

### ShardingSphere 插件

> Kotlin DSL

``` kotlin
implementation("me.ahoo.cosid:cosid-shardingsphere:${cosidVersion}")
```

#### CosIdKeyGenerateAlgorithm (分布式主键)

```yaml
spring:
shardingsphere:
rules:
sharding:
key-generators:
cosid:
type: COSID
props:
id-name: __share__
```

#### 基于间隔的时间范围分片算法

![CosIdIntervalShardingAlgorithm](../document/docs/.vuepress/public/assets/design/CosIdIntervalShardingAlgorithm.png)

- 易用性: 支持多种数据类型 (`Long`/`LocalDateTime`/`DATE`/ `String` / `SnowflakeId`),而官方实现是先转换成字符串再转换成`LocalDateTime`,转换成功率受时间格式化字符影响。
- 性能 : 相比于 `org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm` 性能高出 *1200~4000* 倍。

| **PreciseShardingValue** | **RangeShardingValue** |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Throughput Of IntervalShardingAlgorithm - PreciseShardingValue](../document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-IntervalShardingAlgorithm-PreciseShardingValue.png) | ![Throughput Of IntervalShardingAlgorithm - RangeShardingValue](../document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-IntervalShardingAlgorithm-RangeShardingValue.png) |

- CosIdIntervalShardingAlgorithm
- type: COSID_INTERVAL
- SnowflakeIntervalShardingAlgorithm
- type: COSID_INTERVAL_SNOWFLAKE

```yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_INTERVAL_{type_suffix}
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
```

#### 取模分片算法

![CosIdModShardingAlgorithm](../document/docs/.vuepress/public/assets/design/CosIdModShardingAlgorithm.png)

- 性能 : 相比于 `org.apache.shardingsphere.sharding.algorithm.sharding.mod.ModShardingAlgorithm` 性能高出 *1200~4000* 倍。并且稳定性更高,不会出现严重的性能退化。

| **PreciseShardingValue** | **RangeShardingValue** |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Throughput Of ModShardingAlgorithm - PreciseShardingValue](../document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-ModShardingAlgorithm-PreciseShardingValue.png) | ![Throughput Of ModShardingAlgorithm - RangeShardingValue](../document/docs/.vuepress/public/assets/perf/sharding/Throughput-Of-ModShardingAlgorithm-RangeShardingValue.png) |

```yaml
spring:
shardingsphere:
rules:
sharding:
sharding-algorithms:
alg-name:
type: COSID_MOD
props:
mod: 4
logic-name-prefix: t_table_
```

## Examples

[CosId-Examples](https://github.com/Ahoo-Wang/CosId/tree/main/cosid-example)

> http://localhost:8008/swagger-ui/index.html#/

## 安装

### Gradle

> Kotlin DSL

``` kotlin
val cosidVersion = "1.8.6";
implementation("me.ahoo.cosid:cosid-spring-boot-starter:${cosidVersion}")
```

### Maven

```xml
<?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.8.6</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_friendly_table:
actual-data-nodes: ds0.t_friendly_table
t_order:
actual-data-nodes: ds$->{0..1}.t_order
database-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-db-inline
key-generate-strategy:
column: order_id
key-generator-name: order
t_order_item:
actual-data-nodes: ds$->{0..1}.t_order_item
database-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-db-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
t_date_time_log:
actual-data-nodes: ds0.t_date_time_log_202112
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: data-time-log-interval
t_timestamp_log:
actual-data-nodes: ds0.t_timestamp_log_202112
key-generate-strategy:
column: id
key-generator-name: snowflake
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: timestamp-log-interval
t_snowflake_log:
actual-data-nodes: ds0.t_snowflake_log_202112
table-strategy:
standard:
sharding-column: id
sharding-algorithm-name: snowflake-log-interval
sharding-algorithms:
table-inline:
type: COSID_MOD
props:
mod: 2
logic-name-prefix: t_table_
order-db-inline:
type: COSID_MOD
props:
mod: 2
logic-name-prefix: ds
data-log-interval:
type: COSID_INTERVAL_DATE
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
data-time-log-interval:
type: COSID_INTERVAL_LDT
props:
logic-name-prefix: t_date_time_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
timestamp-log-interval:
type: COSID_INTERVAL_TS
props:
logic-name-prefix: t_timestamp_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
snowflake-log-interval:
type: COSID_INTERVAL_SNOWFLAKE
props:
logic-name-prefix: t_snowflake_log_
id-name: snowflake
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
order:
type: COSID
props:
id-name: order

cosid:
namespace: ${spring.application.name}
snowflake:
enabled: true
# epoch: 1577203200000
clock-backwards:
spin-threshold: 10
broken-threshold: 2000
machine:
# stable: true
# machine-bit: 10
# instance-id: ${HOSTNAME}
distributor:
type: redis
# manual:
# machine-id: 0
state-storage:
local:
state-location: ./cosid-machine-state/
share:
clock-sync: true
friendly: true
provider:
order_item:
# timestamp-bit:
sequence-bit: 12
snowflake:
sequence-bit: 12
safeJs:
machine-bit: 3
sequence-bit: 9
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:
order:
offset: 10000
step: 100
longId:
offset: 10000
step: 100
```

BIN
wiki/img/Throughput-Of-IntervalShardingAlgorithm-PreciseShardingValue.png View File

Before After
Width: 1155  |  Height: 651  |  Size: 2.7 MiB

BIN
wiki/img/Throughput-Of-IntervalShardingAlgorithm-RangeShardingValue.png View File

Before After
Width: 1161  |  Height: 638  |  Size: 2.6 MiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save