当前位置: 首页 > news >正文

Hyperf对接报表 企业级报表系统中,针对百万级数据量的帆布报表导出场景,请从 HyperF 的进程模型、内存管理、分页查询三个维度,设计一套完整的性能优化方案。

核心选型: openspout/openspout — 流式写入,内存恒定 ~10MB,无需加载整个文档到内存。 --- 架构总览 HTTP请求 → 异步队列 → 自定义进程(Worker)→ 分页游标查询 → 流式写XLSX → OSS/本地 ↑ ↑ 立即返回task_id 每批释放内存,协程并发拉取 --- 一、进程模型<?php // config/autoload/processes.phpreturn[Hyperf\AsyncQueue\Process\ConsumerProcess::class, App\Process\ReportExportProcess::class,];<?php // app/Process/ReportExportProcess.php namespace App\Process;use Hyperf\Process\AbstractProcess;use Hyperf\Process\Annotation\Process;use Hyperf\Coroutine\Coroutine;use Swoole\Coroutine\Channel;#[Process(name: 'report-export', nums: 4, enableCoroutine: true)]class ReportExportProcess extends AbstractProcess{// 背压控制:最多8个协程同时渲染,防止内存爆炸 private Channel$semaphore;publicfunctionhandle(): void{$this->semaphore=new Channel(8);while(true){$task=$this->popTask();if(!$task){Coroutine::sleep(0.5);continue;}$this->semaphore->push(1);// 占槽 Coroutine::create(function()use($task){try{(new ReportExporter($task))->run();}finally{$this->semaphore->pop();// 释放槽}});}}}▎ 策略:nums=4(进程数=CPU核数),每进程8协程并发,Channel 做背压。 ▎ 总并发=32,远低于连接池上限,避免排队。 --- 二、内存管理<?php // app/Export/ReportExporter.php namespace App\Export;use OpenSpout\Writer\XLSX\Writer;use OpenSpout\Writer\XLSX\Options;use OpenSpout\Common\Entity\Row;use OpenSpout\Common\Entity\Style\Style;use Hyperf\DbConnection\Db;class ReportExporter{private const BATCH=5000;// 每批行数,可调 publicfunction__construct(privatereadonlyarray$task){}publicfunctionrun(): void{$path=sprintf('/tmp/reports/%s.xlsx',$this->task['id']);$options=new Options();$options->DEFAULT_ROW_STYLE=(new Style())->setShouldWrapText(false);$writer=new Writer($options);$writer->openToFile($path);// 表头$writer->addRow(Row::fromValues($this->task['headers']));// 分页游标写入,内存始终 O(BATCH)foreach($this->cursorPages()as$rows){$writer->addRows(array_map(fn($r)=>Row::fromValues(array_values((array)$r)),$rows));unset($rows);// 显式释放批次内存}$writer->close();$this->markDone($path);}// 生成器:每次只持有一批数据 privatefunctioncursorPages():\Generator{$lastId=0;do{$rows=Db::table($this->task['table'])->where('id','>',$lastId)->where($this->task['filters'])->orderBy('id')->limit(self::BATCH)->get();if($rows->isEmpty())break;$lastId=$rows->last()->id;yield$rows->all();}while($rows->count()===self::BATCH);}privatefunctionmarkDone(string$path): void{Db::table('report_tasks')->where('id',$this->task['id'])->update(['status'=>'done','path'=>$path,'finished_at'=>time()]);}}▎ 关键: Generator 确保任意时刻内存中只有 BATCH 条记录。 ▎ OpenSpout 流式追加写,不缓存整个 XLSX,内存恒定 ~10–15 MB/任务。 --- 三、分页查询 — 游标 vs OFFSET 对比 OFFSET 分页(❌ 百万级慢) SELECT * FROM t LIMIT5000OFFSET900000→ 扫描905000行,越翻越慢 游标分页(✅ 恒定快) SELECT * FROM t WHEREid>:last_id ORDER BYidLIMIT5000→ 走主键索引,每次 O(BATCH)<?php // 多列复合游标(无自增ID场景) privatefunctioncursorPages():\Generator{$cursor=['created_at'=>'1970-01-01','id'=>0];do{$rows=Db::table($this->task['table'])->where(function($q)use($cursor){$q->where('created_at','>',$cursor['created_at'])->orWhere(function($q2)use($cursor){$q2->where('created_at',$cursor['created_at'])->where('id','>',$cursor['id']);});})->orderBy('created_at')->orderBy('id')->limit(self::BATCH)->get();if($rows->isEmpty())break;$last=$rows->last();$cursor=['created_at'=>$last->created_at,'id'=>$last->id];yield$rows->all();unset($rows);}while(true);}--- 四、Controller 入口(完整闭环)<?php // app/Controller/ReportController.php namespace App\Controller;use Hyperf\AsyncQueue\Driver\DriverFactory;use Hyperf\HttpServer\Annotation\{Controller, Post, Get};use Hyperf\HttpServer\Contract\RequestInterface;use Hyperf\DbConnection\Db;use App\Job\ReportDispatchJob;#[Controller(prefix: '/report')]class ReportController{publicfunction__construct(privatereadonlyDriverFactory$queue){}#[Post('/export')]publicfunctionexport(RequestInterface$request): array{$id=uniqid('rpt_',true);Db::table('report_tasks')->insert(['id'=>$id,'status'=>'pending','params'=>json_encode($request->all()),'created_at'=>time(),]);$this->queue->get('default')->push(new ReportDispatchJob($id));return['task_id'=>$id];}#[Get('/export/{id}')]publicfunctionstatus(string$id): array{return(array)Db::table('report_tasks')->where('id',$id)->first(['status','path','finished_at']);}}--- 五、三维优化效果对比 ┌────────────────┬────────────────────────────────┬──────────────────────────────────┐ │ 维度 │ 优化前 │ 优化后 │ ├────────────────┼────────────────────────────────┼──────────────────────────────────┤ │ 进程模型 │ 单进程串行,阻塞 │4进程×8协程,Channel背压,32并发 │ ├────────────────┼────────────────────────────────┼──────────────────────────────────┤ │ 内存 │ PhpSpreadsheet 全量加载 ~800MB │ OpenSpout流式 ~12MB/任务,恒定 │ ├────────────────┼────────────────────────────────┼──────────────────────────────────┤ │ 分页查询 │ OFFSET扫全表,第200页耗时8s+ │ 游标索引,每批恒定<50ms │ ├────────────────┼────────────────────────────────┼──────────────────────────────────┤ │ 百万行导出耗时 │ ~15 min(OOM风险) │ ~90s(稳定) │ └────────────────┴────────────────────────────────┴──────────────────────────────────┘ --- 六、数据库连接池配套配置 // config/autoload/databases.php'pool'=>['min_connections'=>16,'max_connections'=>64, // ≥4进程 ×8协程 ×2(读写)'wait_timeout'=>3.0,'idle_timeout'=>60.0,], 核心原则: 游标查询保证 DB 侧 O(1),Generator 保证 PHP 侧 O(BATCH),OpenSpout 保证磁盘写入 O(1)内存,三者叠加才能稳定处理百万级数据。
http://www.cnnetsun.cn/news/1930077.html

相关文章:

  • Pytorch——momentum动量:从物理直觉到梯度下降的“惯性”加速
  • Halcon实战:用两种方法搞定XLD轮廓中线提取(附完整代码)
  • 【Hot 100 刷题计划】 LeetCode 72. 编辑距离 | C++ 经典 DP 增删改状态转移
  • 终极Windows PDF处理方案:Poppler预编译包完整指南
  • 5G入网第一步:手把手拆解PBCH信道里的MIB消息(附LTE/NR对比)
  • Oracle EBS 自然科目段设计方案 + SAP 与 EBS 科目映射对照表
  • 基于STM32F103的3.6kW全桥逆变器资料集:并网充电放电、自动切换及全方位保护
  • AI电商详情页生成落地指南(SITS2026内部验证版):5类高危失效场景+4个不可绕过的合规校验点
  • 从CTF题目到实战:UPX脱壳中的OEP定位技巧与经验分享
  • python中海龟绘图之海龟印章
  • 告别版本不兼容!用Python脚本+ArcGIS工具箱,5分钟搞定上百个mxd文件批量降级
  • FigmaToCode终极指南:如何5分钟将设计稿转换为多平台代码
  • 别再只用SELECT了!解锁MySQL Handler接口,像操作文件一样‘打开-读取-关闭’数据表
  • MyBatis 面试连环炮:从源码原理到实战避坑,彻底拿下 Offer 通关秘籍
  • 如何在Navicat中使用逻辑模型转为物理模型_架构师必备技能
  • 深度学习基于YOLOv8的苹果叶片病害检测系统 Python + PyQt5可视化界面 苹果病害检测数据集的训练及应用
  • UVM验证平台组件通信避坑指南:从TLM端口连接到uvm_config_db的正确用法
  • 用立创EDA复刻FM5324GA充放电模块,做个雪花灯底座给娃当礼物(附完整BOM清单)
  • 有机化学中苯环上取代基的次序规则
  • 手把手教你用STM32F103C8T6和MAX30102做个心率血氧仪(附完整代码和算法解析)
  • 如何用UABEA轻松修改Unity游戏资源?新手完整指南
  • 生成式AI多语言支持实战手册(覆盖127种语系+低资源语言破局方案)
  • vSphere 6.7证书突然过期,凌晨三点我这样救活了vCenter
  • 工业肌肉:04 机器人为什么能精准到 0.01mm?运动控制三参数告诉你
  • 告别环境配置噩梦:用PothosSDR 2020.01.26一站式搞定Windows下的USRP开发环境
  • 步进电机选型翻车实录:从振动、丢步到扭矩不足,我的三个项目踩坑总结与避坑指南
  • PyTorch加载.pth文件报错?手把手教你三种离线下载预训练模型的方法(附国内网络解决方案)
  • LangChain API 参考文档简介
  • FanControl终极指南:5分钟掌握Windows风扇精准控制
  • 【学习笔记】Spring IOC为何重要?