【问题标题】:Laravel API with large datasets very slow in loading具有大型数据集的 Laravel API 加载速度非常慢
【发布时间】:2020-08-30 19:49:04
【问题描述】:

我开发了一个Laravel 应用程序,它带有从mysql db 加载数据的api 路由,控制器动作执行一个嵌套的数据库查询并返回一个集合;默认情况下,Laravel 将返回的集合转换为 JSON,下面是我的控制器操作:

public function index()
    {
        $admin0 = 2;

        return DB::table('commodities_monthly_prices as CMP')
            ->join('locations', 'CMP.location_id', '=', 'locations.id')
            ->where('locations.admin0', $admin0)
            ->join('global_admins', 'locations.admin0', '=', 'global_admins.admin0_code')
            ->join('commodities', 'CMP.commodity_id', '=', 'commodities.id')
            ->join('price_types', 'CMP.price_type_id', '=', 'price_types.id')
            ->join('measure_units', 'CMP.measure_unit_id', '=', 'measure_units.id')
            ->select('commodities.name AS commodity_name', 'price_types.name AS type_name',
                'measure_units.name AS unit_name', 'global_admins.admin0_code AS admin0',
                'global_admins.admin0_name AS country_name', 'global_admins.admin1_code AS admin1',
                'global_admins.admin1_name AS governorate_name')
            ->selectRaw('date(concat_WS(\'-\', CMP.price_year, CMP.price_month, \'01\')) as date, ROUND(AVG(CMP.price) / 1000, 3) as price')
            ->groupBy('date', 'commodity_name', 'type_name', 'unit_name', 'global_admins.admin0_name',
                'global_admins.admin0_code', 'global_admins.admin1_name', 'global_admins.admin1_code')
            ->orderBy('date')
            ->orderBy('commodity_name')
            ->get();
    }

返回的 JSON 对象数量超过 50000 个对象,大约需要 90 秒才能完成加载,我唯一想到的是将响应从 JSON 更改为 CSV(这会有所帮助),因为前端库接受 JSON和 CSV http 响应,如果这有帮助,请告诉我该怎么做?如果我需要第三方库?

请告诉我如何使动作加载更快。

感谢您的帮助,

SELECT `commodities`.`name` AS `commodity_name`,
       `price_types`.`name` AS `type_name`,
       `measure_units`.`name` AS `unit_name`,
       `global_admins`.`admin0_code` AS `admin0`,
       `global_admins`.`admin0_name` AS `country_name`,
       `global_admins`.`admin1_code` AS `admin1`,
       `global_admins`.`admin1_name` AS `governorate_name`,
       ROUND(AVG(CMP.price) / 1000, 3) AS price
FROM `commodities_monthly_prices` AS `CMP`
INNER JOIN `locations` ON `CMP`.`location_id` = `locations`.`id`
INNER JOIN `global_admins` ON `locations`.`admin0` = `global_admins`.`admin0_code`
INNER JOIN `commodities` ON `CMP`.`commodity_id` = `commodities`.`id`
INNER JOIN `price_types` ON `CMP`.`price_type_id` = `price_types`.`id`
INNER JOIN `measure_units` ON `CMP`.`measure_unit_id` = `measure_units`.`id`
WHERE `locations`.`admin0` = ?
GROUP BY `CMP`.`price_year`,
         `CMP`.`price_month`,
         `commodity_name`,
         `type_name`,
         `unit_name`,
         `global_admins`.`admin0_name`,
         `global_admins`.`admin0_code`,
         `global_admins`.`admin1_name`,
         `global_admins`.`admin1_code`
ORDER BY `commodity_name` ASC

【问题讨论】:

  • 优化/索引您的数据库?使用分页返回 500 而不是 50000?
  • 获取原始查询。包括EXPLAIN {query}SHOW CREATE TABLE {tablename} 用于涉及的表。
  • 而不是group by date,使用groupBy CMP.price_year, CMP.price_month
  • 我不能在前端使用分页,因为我使用的是PivotTable.jspivottable.js.org/examples,它需要所有记录来进行分析;
  • @danblack 这是一个gist 包含表格和原始查询:gist.github.com/yabasha/d36acf539f32af70b72f394b8e45404a 谢谢你的帮助,

标签: mysql laravel performance api


【解决方案1】:

对于初学者,您需要 INDEX(admin0) 上的 Locations

您不需要为此花费数十亿的数据类型:price_month int(10) unsigned DEFAULT NULL。 INT 占用 4 个字节; TINYINT 只需要 1。而且许多其他列都比必要的大。这会影响磁盘空间和查询速度。

如果这是一个“年”,我希望你不要将它与一个 INT 进行比较:str_year0 varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,

commodities_monthly_prices 既有价格又有商品描述(市场、货币等)。描述可能是不变的,所以可能应该在一个单独的表中。

您的问题是关于加载速度。它是来自LOAD DATA 的 CSV 文件吗?如果是这样,我建议您将其加载到根据传入数据的外观定制的表中。然后对数据进行处理——规范化、清理、修复拼写错误等。最后将数据复制到要分析的表中。 这些表不一定与原始数据采用相同的形式。 例如,这会给你一个拆分commodities_monthly_prices 的机会。

架构设计中的一个禁忌是让多个列表示一个数组。我正在考虑 str_year、admin_name 等。由于我不了解它们的目的,所以我没有具体的建议,只是说我上面描述的重组是解决这个问题的时候了。

您提到了 JSON,但我在 Schema 中没有看到这样的内容。

【讨论】:

  • 感谢您的建议,我所说的 JSON 是从 laravel 控制器返回到视图,当 laravel 读取查询时,它将查询读取到集合数组中,laravel 默认为任何数组或集合它将响应转换为 json 对象数组
  • @YaBasha - 我会期望到 JSON 的转换不相关。您可以通过删除对 JSON 的转换来验证这一点。
  • 我认为原因是转换,因为在检查响应时,chrome 网络面板中的大小为 12mb
  • @YaBasha - 你的 JSON 字符串有那么大吗?
  • 国家不同商品价格的数据集,前端库需要整套分析
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-07
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 2017-01-04
  • 1970-01-01
相关资源
最近更新 更多