话说大家都是怎么维护查询缓存的

$data[‘top_project’] = Cache::remember(md5($request->route()->getName().’_top_project’),config(‘app.debug’) == true ? 100 : 864000,function(){
return Project::query()
->from(‘project as p’)
->where(‘p.is_del’,0)
->where(‘p.project_status’,2)
->orderBy(‘views’,’desc’)
->select([‘id’,’project_title’,’budget_upper_limit’])
->limit(10)
->get();
});
如上所示,对查询结果进行了缓存,大家都是怎么维护这个缓存的,我目前采用的方案是当数据有新增的时候删除这个缓存 key

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 13
sanders

为啥要给缓存键做哈希哪?虽然16的32次方概率很低但哈希还是会碰撞的。

1个月前 评论
yyy123456 1个月前
IT学徒 (楼主) 1个月前

就是数据同步的问题嘛,无论是先更新数据库再更新缓存,或者先更新缓存再更新数据库都不可能保证100%的准确性,不过一般正常使用,不涉及到太多的数据量,基本都够了。

但是上述可能会存在问题,假如当你删除缓存数据的同时,用户刚好访问到了缓存数据,如果没做处理,那么就会报错。如果做了处理,没有就去数据库里拿并缓存起来,当大量请求的时候,所有的请求都会落到数据库,所以这里需要思考一下怎么处理。

还有,如同上一楼的朋友所说的,为啥缓存的键还要做哈希的问题。

1个月前 评论
IT学徒 (楼主) 1个月前

laravel-model-caching

1个月前 评论
IT学徒 (楼主) 1个月前

假设列表页数据有为期3600秒的缓存,新增数据后,列表页需要同步变更为最新的数据,缓存的设计读取和删除有几种方案

在设计缓存机制以确保列表页数据的实时性和一致性时,可以采用以下几种方案:

  1. 定时刷新缓存

    • 每隔一定时间(例如每10分钟)自动刷新缓存,更新为最新的数据。
    • 优点:简单易实现。
    • 缺点:可能在两次刷新之间存在数据不一致的情况。
  2. 发布/订阅模式

    • 当数据更新时,通过发布/订阅机制通知缓存系统进行更新或删除。
    • 优点:可以实时更新数据,保证数据的一致性。
    • 缺点:实现复杂度较高,需要额外的发布/订阅系统支持。
  3. 事件驱动更新

    • 在数据更新操作时触发事件,由事件驱动缓存的更新或删除。
    • 优点:数据更新与缓存更新同步进行,减少数据不一致的风险。
    • 缺点:需要在数据更新逻辑中加入事件触发机制。
  4. 短时效性缓存

    • 设置缓存的有效期较短,例如几分钟,这样即使数据更新后,缓存也会很快过期并重新从数据库获取最新数据。
    • 优点:实现简单,对缓存系统的压力较小。
    • 缺点:在缓存过期和重新加载期间可能会有短暂的数据不一致。
  5. 条件缓存

    • 根据数据的变更频率和重要性设置不同的缓存策略,例如对频繁变更的数据采用较短的缓存时间或不缓存。
    • 优点:可以针对不同数据类型采取不同的缓存策略,提高缓存效率。
    • 缺点:需要对数据变更模式有深入的了解和分析。
  6. 分布式缓存一致性

    • 使用分布式缓存系统,如Redis集群,通过一致性哈希等机制保证缓存数据的一致性。
    • 优点:可以处理大规模数据,提高系统的扩展性和容错性。
    • 缺点:系统复杂度和维护成本较高。
  7. 缓存预热

    • 在系统启动或低峰时段预先加载数据到缓存中,减少首次访问的延迟。
    • 优点:提高用户体验,减少首次加载时间。
    • 缺点:需要额外的逻辑来处理预热过程。
  8. 缓存失效策略

    • 采用LRU(最近最少使用)或其他缓存失效策略,自动淘汰不常用的数据,为新数据腾出空间。
    • 优点:自动管理缓存空间,提高缓存效率。
    • 缺点:需要缓存系统支持复杂的失效策略。

每种方案都有其适用场景和优缺点,实际应用时需要根据具体业务需求、数据更新频率、系统资源和维护成本等因素综合考虑。

1个月前 评论

给我一个新数据产生后,列表页缓存失效策略,要求列表页读取到的数据与数据库数据始终保持一致,并且列表页缓存有效期越长越好,提供一份php语言laravel框架的代码实例

在 Laravel 框架中实现新数据产生后列表页缓存失效的策略,可以使用 Laravel 的缓存系统结合事件和监听器来完成。以下是一个基本的实现思路和示例代码:

  1. 定义事件:当数据被创建或更新时触发一个事件。
  2. 创建监听器:监听上述事件,并在事件发生时使相关缓存失效。
  3. 缓存数据:在列表页的数据获取逻辑中,先尝试从缓存读取数据,如果缓存不存在或已失效,则从数据库获取数据,并更新缓存。

首先,定义一个事件,比如 DataUpdated

// app/Events/DataUpdated.php

namespace App\Events;

use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class DataUpdated
{
    use Dispatchable, SerializesModels;

    public $data;

    public function __construct($data)
    {
        $this->data = $data;
    }
}

然后,创建一个事件监听器,当 DataUpdated 事件发生时,使相关缓存失效:

// app/Listeners/InvalidateCache.php

namespace App\Listeners;

use App\Events\DataUpdated;
use Illuminate\Contracts\Queue\ShouldQueue;

class InvalidateCache implements ShouldQueue
{
    public function handle(DataUpdated $event)
    {
        // 假设缓存键是数据ID
        $cacheKey = 'list_page_cache_' . $event->data->id;
        // 使缓存失效
        \Cache::forget($cacheKey);
    }
}

接下来,在 EventServiceProvider 中注册事件和监听器:

// app/Providers/EventServiceProvider.php

protected $listen = [
    \App\Events\DataUpdated::class => [
        \App\Listeners\InvalidateCache::class,
    ],
];

在数据模型中,当数据被创建或更新后,触发 DataUpdated 事件:

// app/Models/YourModel.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class YourModel extends Model
{
    protected $dispatchesEvents = [
        'created' => \App\Events\DataUpdated::class,
        'updated' => \App\Events\DataUpdated::class,
    ];
}

最后,实现列表页的缓存逻辑:

// 某个控制器中的方法

use App\Models\YourModel;
use Illuminate\Support\Facades\Cache;

public function index()
{
    $cacheKey = 'list_page_cache_all';
    // 尝试从缓存获取数据
    $cachedData = Cache::get($cacheKey);
    if ($cachedData) {
        return $cachedData;
    }

    // 从数据库获取数据
    $data = YourModel::all();
    // 将数据存入缓存,这里可以根据需要设置不同的过期时间
    Cache::put($cacheKey, $data, 3600); // 缓存有效期1小时

    return $data;
}

这样,每当数据被创建或更新时,相关的缓存会被自动失效,列表页在下一次请求时会从数据库获取最新数据并更新缓存,从而保证数据的一致性。同时,通过合理设置缓存有效期,可以在保证数据一致性的同时,尽可能延长缓存的有效期,减少数据库的访问压力。

1个月前 评论
晏南风 1个月前
唐章明 (作者) 1个月前

越多越难维护, 我是这么觉得,反正我没维护的多好

1个月前 评论

刚好前段时间从Google找到一个比较合适的缓存维护方案,根据我自身使用情况结合了一下,代码排版凑合看看吧

<?php

declare(strict_types=1);

namespace App\Support\Cache\V1;

use Illuminate\Support\Facades\Cache;
use Psr\SimpleCache\InvalidArgumentException;

abstract class CacheBuilder
{
    private string $key;
    private \Closure $data;
    protected int $ttl;
    private array $tags;

    public function __construct(string $key)
    {
        $this->key = $key;
    }

    public static function __callStatic($property, $arguments): self
    {
        $constant = self::getConstant($property);

        if (!$constant) {
            throw new \BadMethodCallException("Property {$property} not supported.");
        }
//        if ($constant instanceof \Closure) {
//            $key = $constant(...$arguments);
//        } else {
        $key = vsprintf($constant, $arguments);
//        }
        return new static($key);

    }

    private static function getConstant(string $property): ?string
    {
        $reflection = new \ReflectionClass(static::class);
        $constants = $reflection->getConstants();

        $matchedConstant = array_filter($constants, function ($constant, $name) use ($property) {
            $propertyName = mb_strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $property));
            return mb_strtolower($name) === $propertyName;
        }, ARRAY_FILTER_USE_BOTH);

        return current($matchedConstant);
    }

    public function getKey(): string
    {
        return $this->key;
    }

    public function setData($data): self
    {
        $this->data = $data;
        return $this;
    }

    public function getData(): \Closure
    {
        return $this->data;
    }

    public function setTtl(int $ttl): self
    {
        $this->ttl = $ttl;
        return $this;
    }

    public function setTags($tags): self
    {
        $this->tags = $tags;
        return $this;
    }

    /**
     * @throws InvalidArgumentException
     */
    public function get()
    {
        return Cache::store('redis')->get($this->key);
    }

    public function forget(): bool
    {
        return Cache::store('redis')->forget($this->key);
    }

    public function forever(): bool
    {
        return $this->performCacheOperation('forever');
    }

    public function remember()
    {
        return $this->performCacheOperation('remember', [$this->ttl, $this->data]);
    }

    public function put(): bool
    {
        return $this->performCacheOperation('put', [$this->ttl]);
    }

    /**
     * @return bool
     */
    public function flush(): bool
    {
        $cache = Cache::store('redis');
        if (empty($this->tags) || !$cache->supportsTags()) {
            throw new \BadMethodCallException("method: tags() not supported or tags cant be null");
        }
        return $cache->tags($this->tags)->flush();
    }

    protected function performCacheOperation(string $method, array $additionalArgs = [])
    {
        $cache = Cache::store('redis');

        if ($cache->supportsTags() && !empty($this->tags)) {
            $cache = $cache->tags($this->tags);
        }

        return $cache->$method($this->key, ...$additionalArgs);
    }
}

每一个缓存类继承CacheBuilder

<?php

declare(strict_types=1);

namespace App\Support\Cache\V1;

/**
 * @method static Domains()
 * @method static SiteCache Domain(string $domain)
 * @method static SiteCache Attributes(string $site_id, string $attribute_name)
 * @method static SiteCache ViewPage(string $site_machine_name, string $slug, string $language)
 */
class SiteCache extends CacheBuilder
{
    protected int $ttl = 60;


    public const DOMAINS = 'site_domains';
    public const DOMAIN = 'site_domain:%s';
    public const ATTRIBUTES = 'site:%s_%s';
    public const VIEW_PAGE = "view_page:%s:%s:%s";
}
<?php

declare(strict_types=1);

namespace App\Support\Cache\V1;

/**
 * @method static SiteCache allSuppliers(string $companyId)
 */
class SupplierCache extends CacheBuilder
{
    protected int $ttl = 60 * 10 * 60;

    public const ALL_SUPPLIERS = 'all_suppliers:%s';

    public const TAG = [
        'all_supplier'
    ];




}

试用

        $companyId = '1';
        $value = SupplierCache::allSuppliers($companyId)
            ->setData(function (){
                return [
                  'name'=>'tom'  
                ];
            })->remember();
1个月前 评论

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