hyperf/docs/zh-hk/snowflake.md
沈唁 bb3ed4815c
Add translate docs action (#3276)
* Add translate docs action

* Remove push

Co-authored-by: sy-records <sy-records@users.noreply.github.com>
2021-02-19 12:53:50 +08:00

177 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Snowflake
## 算法介紹
`Snowflake` 是由 Twitter 提出的一個分佈式全局唯一 ID 生成算法,算法生成 `ID` 的結果是一個 `64bit` 大小的長整,標準算法下它的結構如下圖:
![snowflake](imgs/snowflake.jpeg)
- `1 位`,不用。
- 二進制中最高位為符號位,我們生成的 `ID` 一般都是正整數,所以這個最高位固定是 0。
- `41 位`,用來記錄時間戳(毫秒)。
- `41 位` 可以表示 `2^41 - 1` 個數字。
- 也就是説 `41 位` 可以表示 `2^41 - 1` 個毫秒的值,轉化成單位年則是 `(2^41 - 1) / (1000 * 60 * 60 * 24 * 365)` 約為 `69` 年。
- `10 位`,用來記錄工作機器 `ID`
- 可以部署在 `2^10``1024` 個節點,包括 `5``DatacenterId``5``WorkerId`
- `12 位`,序列號,用來記錄同毫秒內產生的不同 `id`
- `12 位` 可以表示的最大正整數是 `2^12 - 1``4095` 個數字,來表示同一機器同一時間截(毫秒)內產生的 `4095``ID` 序號。
`Snowflake` 可以保證:
- 所有生成的 `ID` 按時間趨勢遞增。
- 整個分佈式系統內不會產生重複 `ID`(因為有 `DatacenterId (5 bits)``WorkerId (5 bits)` 來做區分)。
Hyperf 的 [hyperf/snowflake](https://github.com/hyperf/snowflake) 組件在設計上提供了很好的可擴展性,允許您通過簡單的擴展就能實現其它基於 Snowflake 的變體算法。
## 安裝
```
composer require hyperf/snowflake
```
## 使用
框架提供了 `MetaGeneratorInterface``IdGeneratorInterface``MetaGeneratorInterface` 會生成 `ID``Meta` 文件,`IdGeneratorInterface` 則會根據對應的 `Meta` 文件生成 `分佈式 ID`
框架默認使用的 `MetaGeneratorInterface` 是基於 `Redis` 實現的 `毫秒級別生成器`
配置文件位於 `config/autoload/snowflake.php`,如配置文件不存在可通過執行 `php bin/hyperf.php vendor:publish hyperf/snowflake` 命令創建默認配置,配置文件內容如下:
```php
<?php
declare(strict_types=1);
use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
use Hyperf\Snowflake\MetaGenerator\RedisSecondMetaGenerator;
use Hyperf\Snowflake\MetaGeneratorInterface;
return [
'begin_second' => MetaGeneratorInterface::DEFAULT_BEGIN_SECOND,
RedisMilliSecondMetaGenerator::class => [
// Redis Pool
'pool' => 'default',
// 用於計算 WorkerId 的 Key 鍵
'key' => RedisMilliSecondMetaGenerator::DEFAULT_REDIS_KEY
],
RedisSecondMetaGenerator::class => [
// Redis Pool
'pool' => 'default',
// 用於計算 WorkerId 的 Key 鍵
'key' => RedisMilliSecondMetaGenerator::DEFAULT_REDIS_KEY
],
];
```
框架中使用 `Snowflake` 十分簡單,只需要從 `DI` 中取出 `IdGeneratorInterface` 對象即可。
```php
<?php
use Hyperf\Snowflake\IdGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
$container = ApplicationContext::getContainer();
$generator = $container->get(IdGeneratorInterface::class);
$id = $generator->generate();
```
當知道 `ID` 需要反推對應的 `Meta` 時,只需要調用 `degenerate` 即可。
```php
<?php
use Hyperf\Snowflake\IdGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
$container = ApplicationContext::getContainer();
$generator = $container->get(IdGeneratorInterface::class);
$meta = $generator->degenerate($id);
```
## 重寫 `Meta` 生成器
`分佈式全局唯一 ID` 的實現方式多種多樣,也有很多基於 `Snowflake` 算法的變體算法,雖然都是 `Snowflake` 算法,但也不盡相同。比如有人可能會根據 `UserId` 生成 `Meta`,而非 `WorkerId`。接下來,讓我們實現一個簡單的 `MetaGenerator`
簡單的來講,`UserId` 絕對會超過 `10 bit`,所以默認的 `DataCenterId``WorkerId` 肯定是裝不過來的,所以就需要對 `UserId` 取模。
```php
<?php
declare(strict_types=1);
use Hyperf\Snowflake\IdGenerator;
class UserDefinedIdGenerator
{
/**
* @var IdGenerator\SnowflakeIdGenerator
*/
protected $idGenerator;
public function __construct(IdGenerator\SnowflakeIdGenerator $idGenerator)
{
$this->idGenerator = $idGenerator;
}
public function generate(int $userId)
{
$meta = $this->idGenerator->getMetaGenerator()->generate();
return $this->idGenerator->generate($meta->setWorkerId($userId % 31));
}
public function degenerate(int $id)
{
return $this->idGenerator->degenerate($id);
}
}
use Hyperf\Utils\ApplicationContext;
$container = ApplicationContext::getContainer();
$generator = $container->get(UserDefinedIdGenerator::class);
$userId = 20190620;
$id = $generator->generate($userId);
```
## 在數據庫模型中應用
配置好 Snowflake 以後,我們可以讓數據庫模型直接使用雪花 id 作為主鍵。
```php
<?php
use Hyperf\Database\Model\Model;
use Hyperf\Snowflake\Concern\Snowflake;
class User extends Model {
use Snowflake;
}
```
上述 User 模型在創建時便會默認使用 Snowflake 算法生成主鍵。
因為 Snowflake 中會複寫 `creating` 方法,而用户有需要自己設置 `creating` 方法時,就會出現無法生成 `ID` 的問題。這裏需要用户按照以下方式自行處理即可
```php
<?php
use Hyperf\Database\Model\Model;
use Hyperf\Snowflake\Concern\Snowflake;
class User extends Model {
use Snowflake {
creating as create;
}
public function creating()
{
$this->create();
// Do something ...
}
}
```