
最近在负责一个快速增长的 Laravel 项目,随着用户和数据量的激增,我们开始频繁收到数据库性能下降的告警。经过一番排查,我发现一个主要瓶颈竟然是那些我们引以为傲的 UUID!
我们知道,UUID(Universally Unique Identifier)在分布式系统和无主键场景下非常有用,能有效避免ID冲突。在 Laravel 中,我们通常会使用 uuid() 方法在迁移文件中创建 UUID 字段,并在模型中自动生成。然而,Laravel 默认会将 UUID 存储为 char(36) 类型的字符串。这意味着每个 UUID 都需要 36 个字符的存储空间,并且在创建索引和执行查询时,数据库需要处理更长的字符串,这无疑增加了额外的开销。
想象一下,当你的 posts 表有数百万条记录,每条记录都有一个 char(36) 的 uuid 字段,并且你经常需要根据这个 uuid 进行查询和关联时,数据库的负担可想而知。我的困境是:既想享受 UUID 带来的便利,又不想牺牲数据库性能。手动将 UUID 转换为二进制存储固然可行,但那会增加大量重复且易错的代码,降低开发效率。
dyrynda/laravel-efficient-uuid 登场!正当我一筹莫展之际,我发现了 dyrynda/laravel-efficient-uuid 这个 Composer 包。它提供了一个优雅且高效的解决方案,通过将 UUID 存储为 binary(16) 类型,极大地优化了存储空间和查询性能,同时又保持了开发体验的流畅性。
那么,它是如何解决问题的呢?
这个包的核心思想是利用数据库的 binary(16) 类型来存储 UUID。一个标准的 UUID 实际上是一个 128 位的数字,将其存储为 binary(16) 只需要 16 个字节,相比 char(36) 的 36 个字节,足足节省了一半多的空间!更重要的是,数据库在处理二进制数据时效率更高,尤其是在索引和比较操作上。
dyrynda/laravel-efficient-uuid 通过扩展 Laravel 的数据库迁移语法和提供自定义模型类型转换器(Casts),实现了 UUID 在应用程序和数据库之间的无缝转换。
安装与配置:
首先,通过 Composer 安装这个包:
<code class="bash">composer require dyrynda/laravel-efficient-uuid</code>
由于这个包会对 MySQL schema 文件进行修改,它故意没有使用 Laravel 的包自动发现功能。你需要手动在 config/app.php 中注册其服务提供者:
<pre class="brush:php;toolbar:false;">// config/app.php
'providers' => [
// ...
Dyrynda\Database\LaravelEfficientUuidServiceProvider::class,
],在迁移中使用高效 UUID:
现在,你可以在迁移文件中使用 efficientUuid() 方法来创建高效的 UUID 字段了。这个方法会自动创建 binary(16) 类型的字段。
<pre class="brush:php;toolbar:false;">use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id(); // 或者 $table->increments('id');
$table->efficientUuid('uuid')->unique(); // 使用 efficientUuid
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('posts');
}
}模型中的类型转换(Casting):
为了让 Laravel 模型能够正确地读写 binary(16) 字段,你需要为 UUID 字段添加一个自定义的类型转换器 EfficientUuid。同时,如果你希望模型自动生成 UUID,可以结合 dyrynda/laravel-model-uuid 包(与此包作者是同一人,且功能已合并)。
<pre class="brush:php;toolbar:false;"><?php
namespace App\Models;
use Dyrynda\Database\Casts\EfficientUuid;
use Dyrynda\Database\Support\GeneratesUuid; // 如果你使用 laravel-model-uuid 自动生成 UUID
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use GeneratesUuid; // 引入自动生成 UUID 的 Trait
protected $casts = [
'uuid' => EfficientUuid::class, // 使用 EfficientUuid cast
];
// ... 其他模型代码
}这样一来,你在模型中操作 uuid 属性时,它依然是一个可读的字符串 UUID,而底层数据库则高效地存储着二进制数据,所有转换都由 EfficientUuid 自动处理。
便捷的查询与验证:
这个包还提供了一些额外的便利功能:
查询: 你可以直接使用 whereUuid() 作用域来通过字符串 UUID 查询记录,无需手动转换。
<code class="php">$post = Post::whereUuid('25b112a9-499a-4627-9ea0-72cd8694aee3')->first();</code>验证: 对于 UUID 字段的验证,你可以使用 EfficientUuidExists 规则。
<pre class="brush:php;toolbar:false;">use Dyrynda\Database\Rules\EfficientUuidExists;
public function update(Request $request, Post $post)
{
$request->validate([
'uuid' => [new EfficientUuidExists(Post::class)],
// 或指定自定义列名
// 'custom_uuid' => [new EfficientUuidExists(Post::class, 'custom_uuid')],
]);
// ...
}通过引入 dyrynda/laravel-efficient-uuid,我的项目获得了显著的提升:
char(36) 变为 binary(16),数据库文件体积明显减小,尤其是在大型表中。如果你也在 Laravel 项目中大量使用 UUID,并且正为数据库性能担忧,那么 dyrynda/laravel-efficient-uuid 绝对值得一试。它不仅能解决你的燃眉之急,还能为你的应用带来长远的性能效益。
Treeware 倡议:
值得一提的是,dyrynda/laravel-efficient-uuid 的作者推行 Treeware 理念:如果你在生产环境中使用这个包并从中受益,鼓励你为世界购买一棵树。这是一个非常有意义的倡议,让我们在享受开源便利的同时,也为环境保护贡献一份力量。
以上就是如何解决Laravel中UUID存储效率低下问题,使用dyrynda/laravel-efficient-uuid提升数据库性能的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号