php 实现无限级读取百万数据的几种思路

【最常用的递归方法】

先给出递归数据库

create table student
(
   id int primary key comment '主键id',
   name varchar(50) default '会员姓名',
   pid int(11) not null comment '上级id'
)

给出递归Model层代码(这里只包含读取方法,插入省略..  自行生成):

<?php

namespace app\index\model;

use think\Model;

class Member extends Model
{
    /*
    *  根据参数$id 获取其下级所有会员
    *  @param int 参数id
    *  @return array 结果数组
    */
    public function get_member(int $id):array
    {
       $member=new self();
       return self::get_next($member->field('id,pid')->select(),$id);
    }
    
    /*
    *  获取下一级元素
    *  @param array 全部数据
    *  @param int 上级id
    *  @return array 返回数组
    */
    public static function get_next(array $data,int $id):array
    {
       static $arr;
       foreach($data as $k => $v)
       {
          if($v['pid']==$id)
          {
             $arr[]=$v['id'];
          }
          $this->get_next($data,$v['id']);
       }
       return $arr;
    }
}

 

优缺点分析 

优点: 可增加level字段实现层级分明、可实现统一排序。

缺点: 需要比较长io时间,一次性拉取大量数据可能会导致数据库崩溃。

总结: 该方法只适用于小型层级,并且有需要排序的需求。

 

 

【设置数据库缓存字段思路】

思路的来源于分散读取,如果我们在插入的时候就给予一定的标记,可轻松实现层级的查找。

示例数据库:

create table student
(
   id int primary key comment '主键id',
   name varchar(50) default '会员姓名',
   lcache text not null comment '下级id缓存'
)

Model层代码示例:

<?php

namespace app\index\model;

use think\Model;

class Member extends Model
{
    /*
    * 获取参数id 的所有下级会员
    * @param int id值
    * @return array 结果数组
    */
    public function get_member(int $id):array
    {
       $lcache=self::find($id)['lcache'];
       return self::get_next(json_decode($lcache,true));
    }


    /*
    * 获取缓存字段内的所有下级会员
    * @param array 缓存数组
    * @return array 结果数组
    */
    public static function get_next(array $lcache):array
    {
       static $arr;
       foreach($lcache as $v)
       {
          $arr[]=$v;
          $lcache_child=self::find($v)['lcache'];
          if(!(bool)$lcache_child)return continue;
          self::get_next(json_decode($lcache_child,true));    
       }
       return $arr;
    }
}

 

优缺点分析 

优点: 可以配合数据库索引,最快获得下级,分散数据库一次性读取的压力,读取当前id的下一级 有着超高的效率。

缺点: 频繁读取数据库在多次 tcl 消耗和io 消耗上 比较严重。并且大量数据的情况下读取全部层级同样较为费时,同样由于分散了读取的压力导致在插入、修改、删除数据库的时候需多次observer的执行。

总结: 该方法要求快速获取下级时 非常实用,适合分批次加载。

 

 

【id 链路实现快速读取】

实现思路同样是分散读取压力,在写入时 做适当的标记

先贴出数据库的设计

create table student
(
   id int primary key comment '主键id',
   name varchar(50) default '会员姓名',
   pid int(11) not null comment '上级id',
   chain text not null comment 'id链'
)

在贴出代码的设计

<?php

namespace app\index\model;
use app\exception\ModException;
use think\model;

class Member extends model
{

    /**
     * 新增一个会员
     * @param string $name 插入会员姓名
     * @param int $id  插入会员归属 id
     * @return array 返回结果数组
     */
    public function insert_member(string $name,int $id):bool
    {
        $mem = new self();
        $chain= ($mem->find($id))['chain'];
        if ($mem->insert([
            'name' => $name,
            'pid' => $id,
            'chain' => $chain. '-' . $id
        ])return true;
    }



    /**
     * 读取会员
     * @param $id  读取参数id 会员的下属会员
     * @param bool  是否只读取下一级
     * @return bool 插入状态
     */
    public function get_member(int $id,bool $is_one=true):array
    {
       $member=new self();
       //读取下一级
       if($is_one)return ($member->where('pid',$id)->select())->toArray();
       //读取下属所有会员
       $where_str=$member->find($id)['chain'].'-'.$id.'%';
       return ($member->where('chain','like',$where_str)->select)->toArray();
    }
}

 

优缺点分析 

优点: 该方法综合以上两种思路,既可以实现快速读取下一层,也可以快速读取所有下属层级,也可以实现分页加载效果,最重要的是数据库压力小便于优化。

缺点: 同样需要在修改删除更新等操作时 使用observer 修改id 链路。

总结: 该方法在要求大量数据,并且需要快速读取时很实用,虽然需要observer协助,但是并不是过于复杂,适合大量数据使用。

 

 

【总结一下】

方法一适合小型的层级系统,比如树形结构,目录加载等等。通过内置的逻辑基本不需要额外的逻辑操作。

方法二适用于对下级查询要求极高的情况,性价比是三个方法里比较低的一种。

方法三适用于大量数据查询的情况,既分散了数据读取的压力,也不会给写入修改 造成太大的麻烦,大量数据情况下的优选。

转载于:https://my.oschina.net/u/4173863/blog/3089440

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值