我已经制定了一个 Laravel
应用的api路由从mysql db加载数据,controller动作执行一个嵌套的DB查询,并返回一个集合;默认情况下Laravel将返回的集合转换为JSON,下面是我的contrller动作。
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
首先, 你需要 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
既有价格,也有对商品的描述(市场、货币等)。 描述可能是不变的,所以可能应该放在一个单独的表中。
你的问题是关于加载的速度。 它是否来自CSV文件,通过 LOAD DATA
? 如果是这样,我建议你将其加载到一个根据输入数据的样子而定制的表中。 然后对数据进行按摩 -- -- 归一化、清理、修正错别字等。 最后把数据复制到你要分析的表中。 这些表的形式不一定和原始数据一样。 例如,这将给你一个机会来拆解 commodities_monthly_prices
.
在模式设计中,不允许有多个列代表一个数组。 我想到的是str_year,admin_name等。 由于我不明白它们的用途,所以我没有具体的建议,只能说我上面说的重组就是解决这个问题的时候。
你提到了JSON,但我在Schema中没有看到这样的内容。