本文目录导读:

我来详细介绍Laravel中雪花算法的实现方式。
安装依赖包
推荐使用 godruoyi/php-snowflake 包:
composer require godruoyi/php-snowflake
创建雪花ID服务提供者
<?php
namespace App\Providers;
use Godruoyi\Snowflake\Snowflake;
use Godruoyi\Snowflake\SequenceResolver;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Redis;
class SnowflakeServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('snowflake', function ($app) {
$snowflake = new Snowflake();
// 设置数据中心ID和工作节点ID
$datacenterId = config('snowflake.datacenter_id', 1);
$workerId = config('snowflake.worker_id', 1);
$snowflake->setDatacenterId($datacenterId)
->setWorkerId($workerId);
// 可选:设置序列号解析器(支持Redis、Laravel缓存等)
$snowflake->setSequenceResolver($this->getSequenceResolver());
return $snowflake;
});
}
/**
* 获取序列号解析器
*/
protected function getSequenceResolver()
{
// 使用Redis作为序列号解析器(推荐)
return app(RedisSequenceResolver::class);
}
}
Redis序列号解析器实现
<?php
namespace App\Providers;
use Godruoyi\Snowflake\SequenceResolver;
use Illuminate\Support\Facades\Redis;
class RedisSequenceResolver implements SequenceResolver
{
/**
* 获取序列号
*/
public function sequence(int $currentTime)
{
$key = sprintf('snowflake:%s', $currentTime);
// 使用Redis INCR命令获取自增序列
$sequence = Redis::incr($key);
// 设置过期时间,防止key无限增长
Redis::expire($key, 10);
// 最大序列号限制(通常为4095)
if ($sequence > 4095) {
// 如果达到上限,等待下一毫秒
usleep(1000);
return $this->sequence(time() * 1000);
}
return $sequence;
}
}
Laravel缓存序列号解析器(可选)
<?php
namespace App\Providers;
use Godruoyi\Snowflake\SequenceResolver;
use Illuminate\Support\Facades\Cache;
class CacheSequenceResolver implements SequenceResolver
{
/**
* 使用Laravel缓存作为序列号解析器
*/
public function sequence(int $currentTime)
{
$key = sprintf('snowflake:%s', $currentTime);
// 使用Cache的increment方法
if (!Cache::has($key)) {
Cache::put($key, 0, 10);
}
$sequence = Cache::increment($key);
if ($sequence > 4095) {
usleep(1000);
return $this->sequence(time() * 1000);
}
return $sequence;
}
}
创建配置文件和辅助函数
配置文件 config/snowflake.php
<?php
return [
// 数据中心ID(0-31)
'datacenter_id' => env('SNOWFLAKE_DATACENTER_ID', 1),
// 工作节点ID(0-31)
'worker_id' => env('SNOWFLAKE_WORKER_ID', 1),
// 开始时间戳(毫秒)
'start_time' => strtotime('2020-01-01') * 1000,
// 是否启用缓存序列号解析器
'use_cache_resolver' => env('SNOWFLAKE_USE_CACHE', false),
];
辅助函数
// 在 app/helpers.php 中
if (!function_exists('snowflake_id')) {
function snowflake_id()
{
return app('snowflake')->id();
}
}
创建Trait(可选)
<?php
namespace App\Models\Traits;
use Godruoyi\Snowflake\Snowflake;
trait HasSnowflakeId
{
public static function bootHasSnowflakeId()
{
static::creating(function ($model) {
if (!$model->getKey()) {
$model->{$model->getKeyName()} = app('snowflake')->id();
}
});
}
}
在模型中使用
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Traits\HasSnowflakeId;
class Order extends Model
{
use HasSnowflakeId;
// 关闭自增ID
public $incrementing = false;
// 设置ID类型为string(如果是数字ID可以去掉)
protected $keyType = 'string';
// 其他模型代码...
}
简单实现示例
如果不想使用第三方包,也可以自己实现简单的雪花算法:
<?php
namespace App\Services;
class SnowflakeService
{
// 时间戳起始值(毫秒)
const EPOCH = 1577808000000; // 2020-01-01
// 各部分位数
const TIME_BITS = 41;
const DATACENTER_BITS = 5;
const WORKER_BITS = 5;
const SEQUENCE_BITS = 12;
// 最大值
const MAX_DATACENTER_ID = 31;
const MAX_WORKER_ID = 31;
const MAX_SEQUENCE = 4095;
// 位移量
const TIME_SHIFT = 22;
const DATACENTER_SHIFT = 17;
const WORKER_SHIFT = 12;
protected $datacenterId;
protected $workerId;
protected $lastTimestamp = -1;
protected $sequence = 0;
public function __construct($datacenterId = 1, $workerId = 1)
{
if ($datacenterId > self::MAX_DATACENTER_ID || $datacenterId < 0) {
throw new \InvalidArgumentException('数据中心ID范围错误');
}
if ($workerId > self::MAX_WORKER_ID || $workerId < 0) {
throw new \InvalidArgumentException('工作节点ID范围错误');
}
$this->datacenterId = $datacenterId;
$this->workerId = $workerId;
}
public function generateId()
{
$timestamp = $this->getTimestamp();
if ($timestamp < $this->lastTimestamp) {
throw new \RuntimeException('时钟回拨异常');
}
if ($timestamp === $this->lastTimestamp) {
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
if ($this->sequence === 0) {
$timestamp = $this->waitNextMillis($timestamp);
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
return (($timestamp - self::EPOCH) << self::TIME_SHIFT)
| ($this->datacenterId << self::DATACENTER_SHIFT)
| ($this->workerId << self::WORKER_SHIFT)
| $this->sequence;
}
protected function getTimestamp()
{
return (int) (microtime(true) * 1000);
}
protected function waitNextMillis($lastTimestamp)
{
$timestamp = $this->getTimestamp();
while ($timestamp <= $lastTimestamp) {
$timestamp = $this->getTimestamp();
}
return $timestamp;
}
}
使用示例
// 生成ID
$id = app('snowflake')->id();
// 或
$id = snowflake_id();
// 解析ID
$snowflake = app('snowflake');
$parsed = $snowflake->parseId($id);
// 返回数组包含:timestamp, sequence, workerid, datacenterid
注意事项
- 时钟回拨问题:生产环境建议使用NTP时间同步
- 序列号溢出:当同一毫秒生成超过4095个ID时,需要等待下一毫秒
- 数据中心ID:如果是单机部署,设为1即可;分布式系统需要确保唯一性
- 性能考虑:使用Redis作为序列号解析器性能更好
这样你就得到了一个完整的Laravel雪花算法实现方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播