
很多团队的开发工作止步于"能跑就行"。Jira tickets 关闭了,功能上线了,就万事大吉。但对于真正的工程师来说,这只是起点——真正的挑战在于代码不再只是文本,而是开始与基础设施产生真实的交互那一刻。
我决定开一个 ENGINEERING 专栏,因为我坚信:性能不是上线前临时抱佛脚的"调优",也不是在死线前慌慌张张加个缓存。性能是架构的根基。
开发经济学:一个能在 200ms 内返回响应的系统,CPU 和内存消耗会显著降低。这直接映射到更低的云服务账单和更长的项目生命周期。
用户体验:我们处于"即时互联网"时代。每多 100ms 的等待,都在增加用户关掉页面的风险。加载快的网站,是对访客时间的尊重。
可扩展性:用工程思维设计的系统,不会一遇到流量高峰就"塌方"。它是可预测的、稳定的、容易诊断的。
这不是玄学也不是运气。这是深思熟虑的工具选型、对底层机制(从 SQL 查询到网络延迟)的理解,以及对数据流的严格管理。在这个专栏里,我会分享如何把一团乱麻的代码打磨成流畅的高并发系统。不只是讲"怎么做",更要讲清楚"为什么能长期稳定运行"。
在现代 Laravel 架构里,数据库(MySQL/PostgreSQL)是"真理之源"。但每次用户请求页面都去读数据库,这就很浪费了。磁盘 I/O 和复杂的 SQL 联表在大流量场景下很快就会成为瓶颈。
Redis 把这个负担卸载到 RAM(内存数据结构存储)上。通过把它设为 CACHE_STORE 和 SESSION_DRIVER,我们构建了一个两层架构:
快速层(内存):存放频繁访问的数据,比如博客页面、复杂计算结果、会话信息。
可靠层(磁盘):存放需要事务完整性的长期数据。
安装是五分钟的事,但得清楚自己在做什么。不只是搭个服务,而是启动一个独立进程,把服务器的一部分 RAM "划拨"给应用使用。
# 更新包索引并安装 Redis
sudo apt update && sudo apt install redis-server
# 安装 PHP-Redis 连接驱动
sudo apt install php-redis
装完之后,需要在 .env 里告诉 Laravel 用这个缓存层:
# 切换缓存和会话驱动
CACHE_STORE=redis
SESSION_DRIVER=redis
生产服务器经常同时跑多个应用。如果不做命名区分,不同项目的缓存必然会冲突。prefix 是你的"数字卫生"习惯。
在 config/database.php 里定义严格的 key 结构:
'redis' => [ 'options' => [ // 使用项目名 slug 保证唯一性 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_'), ],
],
整洁(隔离):你的 key 比如 oleant_blog_post_1 永远不会和第三方应用的 key 打架。
方便调试:用 redis-cli 时看到的是清晰的结构,而不是一堆乱码 key。找"丢失"的数据或者清理某个模块的缓存时能省不少时间。
我们选用了业界标准的 spatie/laravel-responsecache。它允许我们在应用生命周期的最早期"截断"请求——在 Laravel 加载重型服务、解析控制器、执行 SQL 查询之前就把响应返回。
安装包:
composer require spatie/laravel-responsecache
发布配置文件:如果需要细粒度调优(比如排除某些不应该被缓存的页面),就发布配置文件:
php artisan vendor:publish --provider="Spatie\ResponseCache\ResponseCacheServiceProvider"
执行完之后会得到 config/responsecache.php,在这里定义排除逻辑。
在我们的场景里,缓存在本地化分组里全局生效。这是一个很优雅的方案——自动覆盖所有公开页面,同时不影响在这个分组外部定义的系统路由或 API 路由。
顺便说一句,深刻理解请求生命周期对性能优化至关重要。可以看看这篇关于如何在后端响应发送后执行重型任务的方法:"How I Built My Own Laravel Analytics Package (and Almost Didn't Crash Production)"
Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => [ 'localizationRedirect', 'localeViewPath', 'cacheResponse' // <- 这里 Laravel 直接拦截响应 ]], function () { // 分组内所有路由都会被自动缓存
});
所有"游戏规则"都在 config/responsecache.php 里定义。在 cache_profile 键里指定一个继承自 BaseCacheProfile 的类,缓存决策逻辑都集中在这里。
继承带来的灵活性:标准的 CacheAllSuccessfulGetRequests 类默认只缓存 GET 请求和成功响应。如果想改变这个行为,创建一个继承自 BaseCacheProfile 的类,然后覆盖 shouldCacheRequest 和 shouldCacheResponse 方法即可。
特定异常:在 shouldCacheRequest 方法里可以定义路由匹配规则(比如 /contact 或 /admin/*),让这些路由始终保持"鲜活"。这对于包含 CSRF token 的表单很关键——状态必须对每个用户都是独立的。
php artisan optimize:clear用缓存需要纪律。缓存是应用的"冻结状态"。部署时或者修改逻辑时(比如更新翻译),缓存必须保持最新。
我们把 php artisan optimize:clear 作为 CI/CD 流水线的必经步骤,或者作为部署的最后一步。这个命令"清理一切":配置、路由、视图,当然还有让 ResponseCache 失效。这样用户看到的永远是最新版本的产品,而不是旧代码的"幽灵"。
工程思维建立在可测量性之上。如果无法衡量结果,就无法管理它。redis-cli monitor 是我们的"X 光片",能实时看到应用和存储层到底是怎么通信的。
在终端里跑 redis-cli monitor,你会看到 Laravel 发给 Redis 的命令流。在缓存架构里,我们找的是一个证明效率的模式:
Cache Hit 时:会看到针对特定 key(符合项目 prefix 的)的一组快速 GET 命令。这意味着 Laravel 根本没走到控制器执行或 MySQL 查询——数据直接从 RAM "截获"了。这就是我们梦寐以求的 200ms 响应时间的来源。
Cache Miss 时:如果页面加载很慢,你在 monitor 里看到一连串 SQL 数据库查询(或者通过 DB general log),或者一连串 SET 命令,说明系统正在被迫"预热"缓存。
没有这个工具,开发就变成"猜谜游戏"。monitor 让你能够:
验证架构:确认 cacheResponse 中间件真的在拦截请求。
定位瓶颈:如果只是更新一个简单页面却看到 50+ 个 Redis 请求,说明你的缓存架构粒度太细了,需要优化。
提供证据:当业务方问"网站怎么变快了?",你能展示的不只是 TTFB 图表,而是一段实时的命令流,证明数据库完全被卸掉了。
工程纪律:在大改之后的测试环节用 redis-cli monitor。这是验证缓存失效机制没有被意外破坏、应用没有从 RAM 里 serving 过时数据的最佳方式。
想实时检查项目性能?用我们的页面加载时间审计工具获取详细的 TTFB 分析,识别架构中的隐藏瓶颈。
用 Filament 做内容管理,开发速度确实起飞,但问题来了:怎么保证前端内容实时更新?总不能每次发布文章都跑去终端或者后台手动清缓存吧。工程思维是事件自动化。
我们给 Post 模型实现了一个 Observer。现在,只要在后台更新了内容,Laravel 就能在模型层面"感知"到,并自动清空缓存。
namespace App\Observers;
use App\Models\Post;
use Spatie\ResponseCache\Facades\ResponseCache;
class PostObserver
{ public function saved(Post $post): void { // 文章创建或更新时自动重置缓存 ResponseCache::clear(); } public function deleted(Post $post): void { ResponseCache::clear(); }
}
一致性:用户看到的永远是新鲜内容。编辑之后不会出现"卡住"的旧标题或者过时的文字碎片。
编辑体验:管理员不需要担心点"保存"按钮会有什么"技术后果"。系统自动处理全部失效逻辑。
完整性:我们把数据库事件(Eloquent Events)和基础设施层(Redis)打通了。这正是"系统工程师"的意义所在——构建一个闭环,其中一层的变更会自动、正确地反映到另一层。
总结:Filament 后台不再只是写入数据库的界面,它现在是"真理之源",能自主管理自己在前端的表现生命周期。
工程思维永远要用事实说话。以下是引入 Redis 和 responsecache 后的性能基准测试结果。我们聚焦在 TTFB(Time to First Byte,首字节时间),因为它精确反映了应用绕过重型后端逻辑、快速"点火"响应的能力。
{ "homepage": { "url": "https://oleant.dev", "http_code": 200, "dns_lookup_ms": 21.46, "tcp_connection_ms": 8.26, "time_to_first_byte_ms": 195.41, "total_time_ms": 225.94 }, "blog_post": { "url": "https://oleant.dev/blog/jagd-auf-digitale-chamaleons...", "http_code": 200, "dns_lookup_ms": 1.24, "tcp_connection_ms": 17.23, "time_to_first_byte_ms": 220.32, "total_time_ms": 256.69 }
}
数据分析:
TTFB ~200ms:这是 Laravel 应用的标杆指标。我们已经做到 TCP 连接建立后服务器几乎即时开始推送数据。
开销极小:time_to_first_byte 和 total_time 的差值很小,说明内容传输过程中没有"卡顿"(内容传输效率很高)。
稳定性:即使在重型文章页面(DOM 元素很多),响应时间依然保持在用户友好的 250ms 以内。
结论:通过 Redis 做缓存不是什么"黑科技"或者 workaround,这是基本的架构卫生。项目现在可以应对流量增长而不牺牲用户体验。我们打下了一个基础,让系统能够随着业务扩张而扩展,而不是被数据库服务器的容量上限卡死。