• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

PHP使用array_merge导致内存不足的反思

武飞扬头像
juejin
帮助95

故事背景

  1. 从用户喜欢表分批拿到数据,通过array_merge()组装,再批量插入到数据分析表。
  2. 测试的时候因为数据量小,没有出现问题。随着业务增长,在查询范围内已经超过3万条数据。
  3. 3万条数据在8核32G的单机上已经提示内存溢出了。

解决问题的思路

  1. 设计初衷是不全量分析数据,只取查询范围内有喜欢动作的用户
  2. 尽量减少DB操作,把计算和拼接数据的操作交给程序
  3. 因为没有考虑到程序的计算也是有上限的,所有解决问题的思路在于上面的1、2保持不变,需要找到一个平衡点。

优化后的思路是:

分批取的同时分批插入,每次插入休眠10毫秒

核心代码如下:

//最近7天喜欢的数据
public static function likeBetweenDuration($begin, $end)
{
    $limit = 1000;
    $offset = 0;
    $users = [];
    do {
        $sponsorUserIds = self::query()
            ->selectRaw('userid,createtime')
            ->distinct()
            ->whereBetween('createtime', [$begin, $end])
            ->orderBy('createtime')
            ->offset($offset)
            ->limit($limit)
            ->get()
            ->toArray();

        $beLikedUserIds = self::query()
            ->selectRaw('"otherUserid",createtime')
            ->distinct()
            ->whereBetween('createtime', [$begin, $end])
            ->orderBy('createtime')
            ->offset($offset)
            ->limit($limit)
            ->get()
            ->toArray();

        $sponsorUserIds = array_column($sponsorUserIds, 'userid');
        $beLikedUserIds = array_column($beLikedUserIds, 'otherUserid');
        $likesUserIds = array_unique(array_merge($sponsorUserIds, $beLikedUserIds));
        $userIds = array_map(function ($value) {
            return ['userid' => $value];
        }, $likesUserIds);

        UserActionRecord::recordBatch($userIds);
        echo "推荐算法需要的喜欢\n";
        echo 'arrayCount:' . count($userIds) . "\n";
        $offset = $offset   $limit;
        echo '偏移量:' . $offset . "\n";
        usleep(10); //休眠10毫秒
    } while ($userIds);
    return $users;
}

优化前的思路是:

分批从DB中读取,通过array_merge()拼接所有数据,将所有数据通过一条sql批量插入数据库。

核心代码如下:

//分批取值的方法
public static function likeBetweenDuration($begin, $end, $select = 'userid,"otherUserid"')
{
    $limit = 200;
    $offset = 0;
    $users = [];
    do {
        $thisUsers = self::query()
            ->selectRaw($select)
            ->whereBetween('createtime', [$begin, $end])
            ->orderBy('createtime')
            ->offset($offset)
            ->limit($limit)
            ->get()
            ->toArray();
        $users = array_merge($users, $thisUsers);
        $offset = $offset   $limit;
    } while ($thisUsers);
    return $users;
}

//获得所有数据,再去重,插入数据库
$likes = UserRelationSingle::likeBetweenDuration(Utility::recommendCalcTimestamp(), Utility::recommendCalcEndTimestamp());
$sponsorUserIds = array_column($likes, 'userid');
$beLikedUserIds = array_column($likes, 'otherUserid');
$likesUserIds = array_unique(array_merge($sponsorUserIds, $beLikedUserIds));
$userIds = array_map(function ($value) {
    return ['userid' => $value];
}, $likesUserIds);

UserActionRecord::recordBatch($userIds);
echo "UserActionRecord 批量记录有喜欢行为的用户:" .
    json_encode($userIds) . "\n";

总结

  1. 优化前的array_merge()一定会随着数据的增多出现内存不足的情况,而优化后的代码就不会。

  2. 优化后的思路array_merge()每次最多只会处理2千条数据。

思路对比:

  1. 优化前的思路尝试使用尽量少的sql,减少DB操作,把压力交给程序(PHP函数)去处理,忽略了内存问题。

  2. 优化后的思路较好的平衡了DB操作和程序之间的平衡关系,分配读取的sql没有变; 之前的一次写入改成了多次写入,规避了内存问题,同时每次DB插入之后休眠10毫秒,减轻DB压力。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfbbbj
系列文章
更多 icon
同类精品
更多 icon
继续加载