遇到一个面试题:
题目:假设有 100 个分类,每个分类下有至少 1000 个商品,请实现一个分页程序,要求根据客户端给定的分类 ID 和排序字段,返回下一页的数据
代码使用 php 实现,可以依赖 Laravel 实现。其他组件如 Mysql 、Redis 等等没有限制。如果实现困难,排序字段可以假设只有一个。
要求:程序运行效率较高,在商品数达到百万级别的情况下,也能高效运行
提示:直接使用一行 sql 查询到结果的方式是无法满足高效运行的,会出现慢 sql
方法示例:
/**
* @param int $categoryId 分类 ID
* @param string $sortField 排序字段
* @param int $page 当前第几页
* @param int $perPage 每页数量
*/
public function nextPage(int $categoryId, string $sortField = 'price', int $page =1, int $perPage=20)
{
....
}
现在能想到的是先给字段加特定的索引,但是也是一条 sql 就出来了,redis 其他的没用到,请问下如何更高的提高效率
假设表结构是这样的
* --------------
* id | goods_id | category_id | price | created_at | updated_at
* -------------
想出用这个方法。不知道各位大佬还有没有其他方案
/**
* 假设有100个分类,每个分类下有至少1000个商品,请实现一个分页程序,要求根据客户端给定的分类ID和排序字段,返回下一页的数据
* 表字段
* --------------
* id | goods_id | category_id | price | created_at | updated_at
* -------------
*
* 对于字段进行添加索引(category_id, price)暂定只有这两个字段,如果有其他字段也需要加联合索引
*
* @param int $categoryId 分类ID
* @param string $sortField 排序字段
* @param int $page 当前第几页
* @param int $perPage 每页数量
* @param string $sort 排序规则
*
* @return void
*/
public function nextPage(int $categoryId, string $sortField = 'price', int $page =1, int $perPage=20, string $sort = 'desc')
{
// 查询是否有该列别的缓存 category => time 格式的缓存
// 这个key 在商品更改的时候,就进行删除
$cacheTime = Redis::get($categoryId);
$cacheKey = "{$categoryId}:{$cacheTime}:{$sortField}:{$sort}:{$page}:{$perPage}";
if ($cacheTime) {
// 存在,则去缓存中查找
$res = Redis::get($cacheKey);
// 存在直接返回缓存的数据
if (!empty($res)) {
$goods = json_decode($res);
return $goods;
}
}
// 使用组合索引category_id 和 sort_field
$query = \DB::table('goods as sub_goods')
->select('id')
->where('category_id', $categoryId)
->orderBy($sortField, $sort)
->skip($page * $perPage)
->take($perPage);
$goods = \DB::table('goods')->joinSub($query, 'sub_goods', function (Builder $join) {
$join->on('goods.id', '=', 'sub_goods.id');
})->select(['id, goods_id, category_id', 'price'])->get();
// 将查询结果缓存
Redis::set($cacheKey, json_encode($goods));
return $goods;
}
1
chenhua19940128 OP 如果直接用 order by ,效率会非常低下
|
2
yxisenx 2023-02-09 13:41:13 +08:00
用 es
|
3
sss15 2023-02-09 14:00:38 +08:00
就单表来说,百万级别根本不是事,且表字段也很少,连回表都不用,直接索引查出 goodsId 就好了,顶天一页 100 条数据,100 个 goodsId 去 goods 表中查询,此时可以引入 redis ,查过的 goodsId 就不用查了。
不知道这样子面试官是否满意 |