当前位置:首页 > 技术 > 正文内容

在 .NET 中利用 PostgreSQL 与 HybridCache 构建高性能分布式缓存

访客 技术 2026年5月8日 09:09 5

背景:为什么需要两级缓存

单机内存缓存(IMemoryCache)在单实例场景下表现良好,但在水平扩展时会出现以下问题:

  • 每个节点独立维护缓存,导致缓存命中率低。
  • 节点重启后缓存全部失效,产生"缓存雪崩"。
  • 多个实例同时回源,数据库瞬时压力激增。

解决思路:引入分布式缓存,但纯分布式缓存(如 Redis)又会带来额外的运维与成本。若系统已深度依赖 PostgreSQL,可直接复用其作为共享缓存存储,降低基础设施复杂度。

两级缓存架构

HybridCache 提供"本地内存 + 分布式存储"两层结构:

  1. L1(本地内存):进程内高速访问,纳秒级延迟。
  2. L2(PostgreSQL 缓存表):跨节点共享,毫秒级延迟,持久化。

访问顺序:

请求 → 本地内存命中 → 直接返回
        └─ 未命中 → PostgreSQL 命中 → 回填本地内存 → 返回
                   └─ 未命中 → 回源 → 写入两级缓存 → 返回

快速上手

1. 安装 NuGet 包

dotnet add package Microsoft.Extensions.Caching.Hybrid
dotnet add package Microsoft.Extensions.Caching.Postgres

2. 配置服务

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDistributedPostgresCache(opts =>
{
    opts.ConnectionString = builder.Configuration.GetConnectionString("PgCache");
    opts.SchemaName = "public";
    opts.TableName = "cache_store";
    opts.CreateIfNotExists = true;
});

builder.Services.AddHybridCache();

3. 定义缓存实体与键

public record ProductCatalog(Guid TenantId, IReadOnlyList<ProductItem> Items);

private static string CacheKey(Guid tenantId) => $"tenant:{tenantId}:catalog:v2";

4. 业务层封装

public sealed class CatalogService(
    HybridCache cache,
    ICatalogApiClient apiClient,
    ILogger<CatalogService> logger)
{
    private static readonly HybridCacheEntryOptions Options = new()
    {
        LocalCacheExpiration = TimeSpan.FromSeconds(15),
        Expiration = TimeSpan.FromMinutes(5)
    };

    public async ValueTask<ProductCatalog> GetCatalogAsync(Guid tenant, CancellationToken ct = default)
    {
        var key = CacheKey(tenant);
        return await cache.GetOrCreateAsync(
            key,
            async token =>
            {
                logger.LogInformation("回源拉取租户 {Tenant} 的商品目录", tenant);
                return await apiClient.FetchCatalogAsync(tenant, token);
            },
            Options,
            ct);
    }
}

关键设计要点

缓存键设计

  • 包含作用域、版本号,避免串扰。
  • 避免把可变参数(如时间戳、随机数)拼入键。

过期策略

数据类型 本地过期 分布式过期
静态配置 5 min 1 h
业务字典 30 s 10 min
实时库存 5 s 30 s

缓存雪崩防护

HybridCache 内置并发合并:当多个请求同时 miss 同一 key 时,只触发一次回源,其余等待结果。

失效策略

  • 被动失效:依赖 TTL。
  • 主动失效:数据变更后发布事件,消费者调用 RemoveAsync(key)
  • 版本化失效:修改键后缀,如 v1v2,实现平滑迁移。

可观测性

public sealed class CacheMetrics
{
    public int HitLocal { get; set; }
    public int HitDistributed { get; set; }
    public int Miss { get; set; }
}

// 在回源处埋点
logger.LogInformation("CacheMiss Key={Key} Duration={ElapsedMs}ms", key, sw.ElapsedMilliseconds);

何时选用 PostgreSQL 作为分布式缓存

场景特征 推荐
已托管 Azure Database for PostgreSQL
延迟要求 < 1 ms ❌ 选 Redis
写吞吐极高 ❌ 选 Redis
运维团队熟悉 PostgreSQL
预算敏感,需减少组件

示例:完整 Minimal API 集成

var app = builder.Build();

app.MapGet("/tenants/{tenantId:guid}/catalog", 
    async (Guid tenantId, CatalogService svc) => 
        Results.Ok(await svc.GetCatalogAsync(tenantId)));

app.Run();

请求流程:

  1. 首次请求 → 回源 → 写入两级缓存 → 返回 200(~200 ms)。
  2. 后续 15 s 内 → 本地内存命中 → 返回 200(< 1 ms)。
  3. 15 s–5 min → PostgreSQL 命中 → 回填本地内存 → 返回 200(~5 ms)。

小结

通过 HybridCache + PostgreSQL,可在不引入额外缓存组件的前提下,为 .NET 应用提供:

  • 本地极速访问。
  • 跨节点共享。
  • 缓存雪崩与并发保护。
  • 与现有 PostgreSQL 运维体系无缝整合。

若系统已深度依赖 PostgreSQL,且对缓存延迟要求并非极端苛刻,这套组合提供了简单、可维护、成本友好的分布式缓存方案。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。