使用Varnish时,如何统计基于PHP Laravel MySQL的新闻网站页面浏览量?
咱先对齐你的核心诉求:高并发场景下的新闻浏览量统计,要靠谱、不拖垮性能,还要用成熟的标准方案,别自己瞎折腾轮子。结合你用的PHP/Laravel/MySQL/Varnish技术栈,给你整理几个工业界常用的路子:
1. Varnish侧异步统计(首推,完美适配你的架构)
既然你已经用上了Varnish做前端缓存,那直接在缓存层处理统计请求是最高效的——不用让请求穿透到后端Laravel,避免给后端加压力。具体玩法:
- 在VCL的
vcl_deliver阶段,判断当前请求是新闻详情页(比如匹配/news/[0-9]+这样的URL规则),然后异步发起一个统计请求到后端的轻量接口(别阻塞主请求!) - 后端不用直接写MySQL,先把浏览量打到Redis里——Redis的
incr命令性能拉满,扛几万QPS都没问题 - 最后用Laravel的任务调度(Schedule),比如每5分钟跑一次脚本,把Redis里累计的浏览量批量同步到MySQL主表,这样既保证了性能,又能让数据落地供业务使用
举个VCL代码片段参考:
sub vcl_deliver { # 匹配新闻详情页的GET请求 if (req.url ~ "^/news/\d+" && req.method == "GET") { # 标记要统计的请求 set req.http.X-News-View = "1"; # 用异步请求发送统计数据到后端接口,不阻塞主响应 async(0, "POST", "/api/log-news-view", "Content-Type: application/json", "{\"news_id\":\"" + regsub(req.url, "^/news/(\d+)$", "\1") + "\"}"); } }
Laravel端的统计接口逻辑也很简单:
// routes/api.php Route::post('/log-news-view', function(Request $request) { $newsId = $request->input('news_id'); // 用Redis累加浏览量 Redis::incr("news:views:$newsId"); return response()->json(['status' => 'ok']); });
然后在app/Console/Kernel.php里加个定时任务:
protected function schedule(Schedule $schedule) { // 每5分钟同步一次浏览量到MySQL $schedule->call(function() { // 遍历所有有累计浏览量的新闻ID $keys = Redis::keys('news:views:*'); foreach ($keys as $key) { $newsId = str_replace('news:views:', '', $key); $count = Redis::get($key); if ($count > 0) { // 批量更新到MySQL DB::table('news')->where('id', $newsId)->increment('views', $count); // 清空Redis里的累计数 Redis::del($key); } } })->everyFiveMinutes(); }
2. Google Analytics(GA4):适合宏观分析,不适合业务实时展示
如果你的需求只是看整体流量、用户行为这类宏观数据,GA4是现成的工具,不用自己开发。但它有个硬伤:数据有延迟,而且没法直接和你的业务数据库联动——比如你想在新闻页显示“XX次浏览”,用GA数据就不太现实,因为它的API导出数据慢,而且统计逻辑和业务需求不一定匹配。所以这个方案适合做报表分析,不适合业务层面的实时统计。
3. 前端非阻塞上报(优化你之前的AJAX思路)
你之前想的随机AJAX其实可以优化成更靠谱的版本:用navigator.sendBeacon()来发送统计请求——这个API是浏览器专门为异步上报设计的,不会阻塞页面卸载,比普通AJAX更可靠。还可以加个本地存储缓存,比如用户浏览3条新闻后再批量上报,减少请求数。
但这个方案的问题也很明显:如果用户禁用JS、页面没加载完就关闭,统计数据会丢;而且高并发下前端请求太多,不如Varnish侧处理高效。所以适合流量不大的场景,你的高访问量站不太推荐。
4. MySQL批量更新:万不得已的兜底方案
如果不想用Redis,也可以直接在MySQL层面做优化:别每次请求都直接更新新闻表的views字段,而是把浏览请求放到Laravel队列里,攒够100条或者等个1分钟,再批量更新到数据库。或者用中间表过渡:先把浏览记录写到news_view_logs表,再定时把日志表的数据聚合到主表。但这个方案的性能不如Redis,高并发下还是会给DB加压力。
总结一下:结合你的高访问量场景,Varnish+Redis+Laravel定时任务是最成熟的标准方案——既保证了性能,又能准确统计,还完全适配你的技术栈,不用自己造轮子。
内容的提问来源于stack exchange,提问作者alex toader

