- 读取 Excel 文件内的数据,拼接成 商品id 为key 的数组。
- 过滤掉库存为空的数据
- 直接丢进异步消息队列执行插入,同时开多个进程去跑。
注意事项: 因为队列中仅做插入操作,如果在推入队列和执行队列中发生了库存清空的场景,需要自行做处理。
你这个需求本质上是如何快速计算出待插入数据。 至于mysql的插入速度,反而是最简单的,用批量插入就可以。 快速计算出待插入数据的逻辑里面,如果计算过程有io,就用协程,如果纯cpu计算,那就多开线、进程。
使用多线程或多进程:可以将插入操作分配到多个线程或进程中,以提高插入速度。这需要您在应用程序中实现多线程或多进程的代码逻辑,以确保线程/进程之间的同步和协作。
批量插入:可以使用 Laravel 提供的批量插入功能,将多个记录一次性插入到数据库中。这可以显著降低每个插入操作的开销,并提高插入速度。例如,使用 Eloquent ORM 提供的 insert 方法,可以将一个包含多个记录的数组一次性插入到数据库中,而不是使用循环插入单个记录。
还可以使用 Eloquent ORM 提供的 insert 批量插入方法并使用laravel 8或以上的任务批处理
写入速度往往跟数据库服务器配置有关,但应该先从代码逻辑上进行优化,对数据进行分组加载到缓冲区,再分批写入:
class BatchImport
{
public const BUFFER_SIZE = 1000;
protected $file;
protected $buffer = [];
public function __construct()
{
}
public function handle($file)
{
$this->file = $file;
\Illuminate\Support\Facades\DB::transaction(function () {
$this->provider();
});
}
public function provider(): void
{
fastexcel()->import($this->file, fn ($line) =>
$this->validate($line) && $this->import($line)
);
}
protected function validate(array $line): bool
{
// TODO: 校验逻辑
return true;
}
protected function import(array $line): array
{
if (count($this->buffer) >= self::BUFFER_SIZE) {
$this->save();
$this->buffer = [];
}
return $this->buffer[] = $this->transform($line);
}
protected function transform(array $line): array
{
return [
// TODO: 字段值映射列的转换关系
];
}
protected function save(): void
{
Trade::insert($this->buffer);
// TODO: 如果需要触发事件,可以在这里触发
}
}
这里使用到事务批量写入来保证批次完整性,如果不需要可以移除事务来提高效率。
数据库方面可以采用多主库的方式进行连接,当然这样成本就提高了。
- 准备个临时表
temp_excel
格式跟要插入的目标表格式一致。 - 读出
excel
内容,整理成laravel
的批量插入格式的数据。并无脑批量插入 - 使用
insert into 目标表 select temp_excel.字段1,temp_excel.字段2... from temp_excel left join 库存表 on temp_excel.货品ID = 库存表.货品ID where 库存表.库存 > 0
批量更新到目标表 - 使用
truncate table temp_excel
清空临时表
那些使用队列的,你只是能快速返回看起来象是插入了,但实际上并没有,如果后续需要依赖插入的数据做其他处理,就可能发生找不到数据的麻烦。异步同时带来了错误处理上的麻烦。 简单粗暴的方案也许更好用。
1、你在意服务执行性能的话,可以先将数据记录到消息队列中,真实的处理流程异步处理。后台开几个常驻内存的cli进程,并行来完成存储数据库逻辑的操作。redis也是可以的,内存操作写入性能更好。不过就怕数据体量大了,redis数据容易丢。 2、可以调研存储的MongoDB中,对于字段不固定的数据,文档数据库的存储更加友好。扩展/并发较MySQL更好些。
推荐文章: