You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决合并数组/集合内存溢出问题,重构代码并保证订单号唯一

代码重构:优化内存占用并保证order_number唯一性,避免JSONB存储内存溢出

问题描述

如何重构以下代码以优化内存使用,同时确保整个列表中的order_number保持唯一?此外,希望将最终结果保存到JSONB列中,且不会遇到内存不足错误?

原代码

public function generateRandomOrders(int $numbers): array
{
    $faker = \Faker\Factory::create();
    $orders = [];
    for($i=0; $i < $numbers;$i++) {
        $order = [
            'discount' => $faker->randomFloat(2, 0, 100),
            'order_amount' => $faker->randomFloat(2, 10, 1000),
            'order_amount_no_vat' => $faker->randomFloat(2, 10, 1000),
            'order_name' => $faker->words(3, true),
            'order_number' => $faker->unique()->randomNumber(),
            'payment_method' => $faker->creditCardType(),
            'shipping_cost' => $faker->randomFloat(2, 0, 50),
            'status' => $faker->randomElement(['pending', 'processing', 'completed']),
            'tag_date' => $faker->date(),
            'tag_time' => $faker->time(),
            'vat_amount' => $faker->randomFloat(2, 0, 50),
            'vat_percent' => $faker->randomFloat(2, 0, 25),
        ];
        $orders[] = $order;
    }
    return $orders;
}

public function test(): JsonResponse
{
    ini_set('memory_limit','32M'); // 故意降低内存限制以测试内存溢出情况
    $orders = collect([]);
    for($i=0; $i < 20;$i++) {
        $orders = $orders->merge($this->generateRandomOrders(5000)); // 生成5000条随机订单
    }

    $cachedList = CachedList::firstOrNew([
        'company_id' => $this->company_id,
        'class' => get_class($this)
    ]);

    $cachedList->company_id = $this->company_id;
    $cachedList->data = $orders->unique('order_number')->values()->toArray(); // 将最终数据保存到jsonb列
    $cachedList->class = get_class($this);
    $cachedList->save();
    return response()->json($orders);
}

原代码内存问题分析

  1. 重复数据浪费内存:每次调用generateRandomOrders都会新建Faker实例,unique()仅在当前批次生效,跨批次可能生成重复order_number,最后需要全量去重,导致内存中同时存储大量重复数据。
  2. Collection合并开销高:每次merge都会创建新的Collection对象,10万条数据会产生大量临时对象,内存占用飙升。
  3. 全量数据加载风险:所有订单先全部加载到内存再去重,32M内存限制下必然溢出。

重构方案

核心优化点

  • 共享Faker实例:保证order_number全局唯一,避免跨批次重复,无需事后去重。
  • 增量去重+原生数组:用order_number作为数组键,生成订单时直接写入关联数组,天然去重,避免存储重复数据。
  • 避免全量加载:不维护全量未去重数据,每生成一条就直接进入最终数据集,内存占用可控。
  • 移除Collection依赖:原生数组比Collection内存开销更低,操作更高效。

重构后代码

/**
 * 生成随机订单,直接写入传入的关联数组(按order_number去重)
 * @param int $numbers 生成数量
 * @param \Faker\Generator $faker 共享的Faker实例
 * @param array $orders 存储订单的关联数组(键为order_number)
 */
public function generateRandomOrders(int $numbers, \Faker\Generator $faker, array &$orders): void
{
    for($i=0; $i < $numbers; $i++) {
        $orderNumber = $faker->unique()->randomNumber();
        // 利用数组键唯一性自动去重(即使Faker的unique失效也能兜底)
        if (!isset($orders[$orderNumber])) {
            $orders[$orderNumber] = [
                'discount' => $faker->randomFloat(2, 0, 100),
                'order_amount' => $faker->randomFloat(2, 10, 1000),
                'order_amount_no_vat' => $faker->randomFloat(2, 10, 1000),
                'order_name' => $faker->words(3, true),
                'order_number' => $orderNumber,
                'payment_method' => $faker->creditCardType(),
                'shipping_cost' => $faker->randomFloat(2, 0, 50),
                'status' => $faker->randomElement(['pending', 'processing', 'completed']),
                'tag_date' => $faker->date(),
                'tag_time' => $faker->time(),
                'vat_amount' => $faker->randomFloat(2, 0, 50),
                'vat_percent' => $faker->randomFloat(2, 0, 25),
            ];
        } else {
            // 极端情况:Faker生成重复(理论上unique()不会出现),重新生成一条
            $i--;
        }
    }
}

public function test(): JsonResponse
{
    ini_set('memory_limit','32M');
    // 用关联数组存储,键为order_number,自动去重
    $orders = [];
    // 只创建一次Faker实例,保证order_number全局唯一
    $faker = \Faker\Factory::create();

    for($i=0; $i < 20; $i++) {
        // 直接写入共享数组,无需返回大数组
        $this->generateRandomOrders(5000, $faker, $orders);
    }

    $cachedList = CachedList::firstOrNew([
        'company_id' => $this->company_id,
        'class' => get_class($this)
    ]);

    // 转成索引数组存入JSONB
    $cachedList->company_id = $this->company_id;
    $cachedList->data = array_values($orders);
    $cachedList->class = get_class($this);
    $cachedList->save();

    // 避免返回全量数据导致内存溢出,返回成功状态即可
    return response()->json([
        'status' => 'success',
        'count' => count($orders)
    ]);
}

重构效果说明

  1. 内存占用大幅降低:不再存储重复订单,也没有Collection的额外开销,32M限制下可轻松处理10万条数据。
  2. 绝对唯一的order_number:共享Faker实例的unique()约束全局生效,再加上数组键的兜底,彻底避免重复。
  3. 无内存溢出风险:增量式构建数据集,内存始终只存储最终的去重后数据。
  4. 写入JSONB更高效:直接用array_values()转成索引数组,无需先处理Collection再转数组。

内容的提问来源于stack exchange,提问作者Marcos DaSilva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 12:26:16