【问题标题】:Laravel model accessor fetching from cache - performance enhancementsLaravel 模型访问器从缓存中获取 - 性能增强
【发布时间】:2017-07-31 02:42:56
【问题描述】:

我在数据库中有一个项目列表,每个项目都可以选择否决或赞成。这些投票与其他项目字段一起存储在 MySql 中。比如这样的:

Schema::create('items', function ($table) {
    $table->increments('id');
    $table->text('message');
    $table->integer('up_votes')->unsigned()->default(0);
    $table->integer('down_votes')->unsigned()->default(0);
    $table->timestamps();
});

用户可以每天投反对票/反对票。当用户决定投票时,我将他的决定存储在 memcached 中一天,并相应地增加其中一个字段(up_votes 或 down_votes)。

$voteKey = sprintf('%s-%s', $request->ip(), $item->id);

if (!Cache::has($voteKey)) {
    $vote = $request->get('vote');

    $this->item->increment($vote ? 'up_votes' : 'down_votes');
    Cache::put($voteKey, $vote, (60*24));
}

接下来我想了解某些用户如何投票的信息。我在模型中创建了访问器:

public function getVoteAttribute($value)
{
    $voteKey = sprintf('%s-%s', Request::ip(), $this->id);

    return $this->attributes['vote'] = Cache::get($voteKey);
}

protected $appends = ['vote'];

这样做是否明智,或者长列表可能存在一些性能问题?如果返回 100 个项目,则每个用户有 100 个到 memcached 的连接。我怎样才能改进这一点,或者这是我不应该太担心的事情,因为缓存服务器可以毫无问题地处理这么多的连接。

【问题讨论】:

    标签: php mysql laravel caching eloquent


    【解决方案1】:

    缓存和数据库的当前使用情况

    1. 您使用 IP 地址 来识别用户,而不是像 user_id 这样简单的东西。这是故意的吗?如果同一用户从另一个 IP 再次登录,您要显示不同的号码吗?
    2. 在 DB 中,您将 # up-votes & down-votes 存储在每个 item 中,但在缓存中,您通过 itemIP address (or user id) 的组合存储 type of vote(上票/下票)。此外,缓存会在 24 小时后过期。

      因此,当您说Cache::get($voteKey) 时,它将返回赞成票或反对票,但前提是用户在过去 24 小时内对该项目进行了投票(否则返回 null)。这是故意的吗?

    何时使用 Cache v/s DB

    通常情况下,您会为频繁查询使用缓存(当您需要频繁执行特定的读取操作但写入频率不高时)。如果不是这种情况,您通常会回退到 DB。

    现在假设您实际上想要存储# up-votes/down-votes by itemtype of vote by combination of user and item。想一想,哪个查询会更频繁? # 每个项目的赞成票/反对票或用户和项目组合的投票类型?当然,这将是第一种情况(如果有的话)。然而,你做的恰恰相反。

    您将访问频率较高的查询存储在 DB 中,而将访问频率较低的查询存储在数据库中 缓存中经常访问的查询

    这实际上会降低您应用的整体性能!

    正确的方法是什么?

    嗯,这取决于用例。例如,假设您想按项目 ID 存储用户 ID 和投票类型(典型用例,因为您不希望在重新投票时对每个项目多次计算任何用户的投票)。然后,我将把它存储在数据库中,并在缓存中按项目存储总的 #up-votes/down-votes(仅在经常访问的情况下 - 例如,您可以选择不存储所有项目的 # 票但仅适用于浏览次数至少为 X 的更受欢迎的项目)

    对于上述用例,我建议如下:

    数据库架构

    Schema::create('item_user', function ($table) {
        $table->increments('id');
        $table->integer('user_id')->unsigned();
        $table->integer('item_id')->unsigned();
        $table->enum('vote_type', ['up_vote', 'down_vote']);
        $table->unique(['user_id', 'item_id']);
        $table->timestamps();
    });
    

    投票控制器逻辑

    $user = Auth::user();
    $vote = $request->get('vote');
    $voteType = $vote ? 'up_vote' : 'down_vote';
    $voteKey = "{$voteType}_{$item->id}";
    
    $item->users()->updateExistingPivot($user->id, ['vote_type' => $voteType]);
    Cache::increment($voteKey);
    

    原始问题

    至于您最初的问题,Laravel 使用单个连接实例对 RedisMemcached 进行缓存查询。因此,如果同一个请求获取 100 个不同的缓存项,它不会启动 100 个连接 - 它会在单个缓存连接中完成这项工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-28
      • 2013-07-05
      • 2016-06-27
      • 2021-09-08
      • 2018-03-16
      • 1970-01-01
      • 2019-02-20
      • 2019-03-09
      相关资源
      最近更新 更多