Laravel 数量统计优化

Laravel 数量统计优化

有很多如下类似的需求,在一个博客系统,博客有分类,分类有个博客数量的字段 post_count,通常会在博客被发布后统计并存入这个字段。

我不清楚大佬们怎么做的,反正我一般是在分类模型中定义一个这样的方法:

public function freshPostCount()
{
  $this->post_count = $this->posts()->count();

  return $this->save();
}

非常清晰,非常简单!

但是有个问题,如果有并发存在,可能会导致数据不一致,或者说不准确。作为一个有追求的渣渣,我想做点什么来尽量降低不一致性的发生。
首先想到的是,用原生 SQL:

public function freshPostCount()
{
  \DB::update('UPDATE `pre_cates` cate SET `post_count` = (
    SELECT count( * ) FROM `pre_posts` post WHERE `post`.`cate_id` = `cate`.`id` AND `post`.`is_published` = 1 )
  WHERE `id` = '. $this->getKey());
}

看上去能解决点问题,但看着有点恶心,格格不入,毫无优雅可言。而且所有东西都写死了,非常没有弹性,那就让他弹起来。

从上面的解决方案来看,统计和赋值只要执行一次就可以,那么如此:

public function freshPostCount()
{
  $this->update([
    'post_count' => 'SELECT count( * ) FROM `pre_posts` WHERE `cate_id` = '. $this->getKey() .' `is_published` = 1'
  ]);
}

但是执行不起作用,发现框架把 SQL 语句绑定成参数,作为字符串赋值了。于此同时我发现 Illuminate\Database\Query\Expression 类的存在,稍作修改:

public function freshPostCount()
{
  $this->update([
    'post_count' => new Expression('(SELECT count( * ) FROM `pre_posts` WHERE `cate_id` = '. $this->getKey() .' `is_published` = 1)'),
  ]);
}

可以执行,并且还把 updated_at 自动带上了,但依然留有遗憾,能否利用 posts() 这个关联关系,答案是可以的,不过比较麻烦:

public function freshPostCount()
{
    $count_sql = $this->posts()->published()->selectRaw('count(*)')->toRawSql();

    return $this->update([
        'post_count' => new Expression("($count_sql)"),
    ]);
}

toRawSql() 是一个自定义的方法,获取完整的执行 SQL:

\Illuminate\Database\Query\Builder::macro('toRawSql', function(){
    return array_reduce($this->getBindings(), function($sql, $binding){
        return preg_replace('/\?/', is_numeric($binding) ? $binding : "'{$binding}'", $sql, 1);
    }, $this->toSql());
});

\Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){
    return $this->getQuery()->toRawSql();
});

参考:https://gist.github.com/JesseObrien/741898...

不知道各位大佬都是怎么操作的,一起交流下啊

本作品采用《CC 协议》,转载必须注明作者和本文链接
? 我的导航网站已经可以公开使用啦:Cootab
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4

队列

4年前 评论
chuoke (楼主) 4年前
pigzzz (作者) 4年前

你这个方法改用「sharedLock」 可以吗?我没用过。

4年前 评论
chuoke (楼主) 4年前
  1. saving 或者 updating 事件中来维护这个字段 sql写成 update tables set count= count +1 (or count-1)
  2. Redis 维护
4年前 评论
jcc123

Redis 有个 HyperLogLog结构

PFADD category_key  "1"
PFADD category_key  "2"
PFADD category_key  "1"

PFCOUNT category_key  //输出2

可以把这个结合进去

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!