php方案 PHP的数据仓库ETL
·
用 https://github.com/flow-php/etl,基于 Generator,大文件也不会撑爆内存。
composer require flow-php/flow flow-php/etl-adapter-csv flow-php/etl-adapter-doctrine
场景:CSV → 清洗 → 写入数据库
<?php
require 'vendor/autoload.php';
use function Flow\ETL\Adapter\CSV\from_csv;
use function Flow\ETL\Adapter\Doctrine\to_dbal_table;
use Flow\ETL\Flow;
use Flow\ETL\Row;
use Doctrine\DBAL\DriverManager;
$db = DriverManager::getConnection(['url' => 'mysql://root:pass@localhost/warehouse']);
(new Flow())
// E:从 CSV 抽数据(逐行读,不会一次性全加载进内存)
->read(from_csv('orders.csv'))
// T:清洗和转换
->filter(fn(Row $r) => $r->valueOf('amount') > 0) // 过滤无效行
->map(fn(Row $r) => $r
->rename('order_id', 'id') // 改列名
->set('amount', (float) $r->valueOf('amount')) // 类型转换
->set('created', new \DateTimeImmutable($r->valueOf('created_at'))) // 格式化日期
->remove('raw_notes') // 删掉不要的列
)
// L:写入数据仓库
->write(to_dbal_table($db, 'dw_orders'))
->run();
echo "done\n";
多源合并(两张表 JOIN 后入库)
use function Flow\ETL\DSL\{from_array, join};
$orders = (new Flow())->read(from_csv('orders.csv'));
$customers = (new Flow())->read(from_csv('customers.csv'));
(new Flow())
->read(from_array([])) // 起点
->join($orders, join()->left('customer_id'))
->join($customers, join()->left('id'))
->write(to_dbal_table($db, 'dw_order_detail'))
->run();
大批量性能优化
(new Flow())
->read(from_csv('big_file.csv'))
->map(fn(Row $r) => /* transform */)
->batchSize(500) // 每500行批量写一次,减少 DB 往返
->write(to_dbal_table($db, 'dw_orders'))
->run();
监控进度
(new Flow())
->read(from_csv('orders.csv'))
->map(fn(Row $r) => $r)
->each(function (Row $r) {
static $i = 0;
if (++$i % 1000 === 0) echo "已处理 {$i} 行\n";
})
->write(to_dbal_table($db, 'dw_orders'))
->run();
---
核心就三步:read() 抽、map()/filter() 转、write() 载。Generator 架构保证不管文件多大内存都稳定,batchSize()
控制写库频率。
一句话终极结论
flow-php/etl 是 PHP 的轻量化ETL工具,靠 Generator(生成器)逐行读取大文件,不会一次性把整个文件塞进内存,所以多大的CSV都不会撑爆内存;还能轻松做数据清洗、多文件关联、批量入库,三步搞定数据搬运。
下面全程大白话,把原理、代码、用法拆到完全易懂。
一、核心原理:为啥大文件不爆内存?
普通读文件是一次性把整个文件加载进内存,文件10GB内存就直接炸。
这个库用了 Generator(生成器):
- 读文件时一行一行读,读完一行处理一行,处理完就扔掉
- 内存里永远只存当前一行数据,内存占用始终稳稳的
- 不管文件是100MB还是10GB,内存占用几乎不变
二、基础场景:CSV → 清洗 → 入库(ETL核心三步)
ETL 就是数据的抽 → 转 → 载,代码完全对应这三步:
1. read():抽取数据(读CSV)
php
->read(from_csv('orders.csv'))
大白话:
从订单CSV文件里逐行读数据,不加载全文件,内存无压力。
2. map/filter:转换清洗(数据加工)
php
->filter(fn(Row $r) => $r->valueOf('amount') > 0) // 过滤金额≤0的无效行
->map(fn(Row $r) => $r
->rename('order_id', 'id') // 列名order_id改成id
->set('amount', (float)$r->valueOf('amount')) // 金额转成数字类型
->set('created', new \DateTimeImmutable($r->valueOf('created_at'))) // 日期格式化
->remove('raw_notes') // 删掉没用的列
)
大白话:
对每一行数据修修改改:
- 扔掉垃圾数据
- 改列名、转数据类型
- 删掉不需要的字段
- 格式化时间
3. write():加载入库(写数据库)
php
->write(to_dbal_table($db, 'dw_orders'))
大白话:
把清洗好的数据,写入数据库的 dw_orders 表。
三、多源合并:两个CSV关联(JOIN)
php
$orders = (new Flow())->read(from_csv('orders.csv')); // 订单表
$customers = (new Flow())->read(from_csv('customers.csv')); // 客户表
(new Flow())
->read(from_array([]))
->join($orders, join()->left('customer_id')) // 按customer_id左关联订单
->join($customers, join()->left('id')) // 按id左关联客户
->write(to_dbal_table($db, 'dw_order_detail'))
->run();
大白话:
像数据库连表查询一样,把订单CSV和客户CSV按关联字段拼在一起,合并后再入库。
四、大批量性能优化
php
->batchSize(500) // 每500行批量写一次
大白话:
不每一行都插一次数据库(太频繁、太慢),攒够500行再批量插入,减少数据库往返次数,速度快几十倍。
五、进度监控:看处理了多少行
php
->each(function (Row $r) {
static $i = 0;
if (++$i % 1000 === 0) echo "已处理 {$i} 行\n";
})
大白话:
每处理1000行就打印一次进度,实时看处理到哪了,不会黑屏干等。
六、终极极简总结
1. 核心优势:Generator逐行读大文件,内存不爆炸
2. 核心逻辑:read读数据 → map/filter洗数据 → write写数据库
3. 进阶用法:多CSV关联合并、批量入库提速、实时监控进度
4. 适用场景:超大CSV清洗入库、数据同步、数据仓库导入
更多推荐
所有评论(0)