V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
chenhua19940128
V2EX  ›  问与答

苛求各位开发大佬来指导

  •  
  •   chenhua19940128 · 2023-02-09 12:19:25 +08:00 · 871 次点击
    这是一个创建于 678 天前的主题,其中的信息可能已经有所发展或是发生改变。

    遇到一个面试题:

    题目:假设有 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
         * -------------
    
    第 1 条附言  ·  2023-02-09 20:12:57 +08:00

    想出用这个方法。不知道各位大佬还有没有其他方案

       /**
         * 假设有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;
        }
    
    3 条回复    2023-02-09 14:00:38 +08:00
    chenhua19940128
        1
    chenhua19940128  
    OP
       2023-02-09 12:21:59 +08:00
    如果直接用 order by ,效率会非常低下
    yxisenx
        2
    yxisenx  
       2023-02-09 13:41:13 +08:00
    用 es
    sss15
        3
    sss15  
       2023-02-09 14:00:38 +08:00
    就单表来说,百万级别根本不是事,且表字段也很少,连回表都不用,直接索引查出 goodsId 就好了,顶天一页 100 条数据,100 个 goodsId 去 goods 表中查询,此时可以引入 redis ,查过的 goodsId 就不用查了。

    不知道这样子面试官是否满意
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5356 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 08:18 · PVG 16:18 · LAX 00:18 · JFK 03:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.