「High-Performance Distributed Caching with .NET and Postgres on Azure」は、Azure上の.NETアプリで、Postgresを分散キャッシュとして使いながら、HybridCacheでメモリ内キャッシュも組み合わせる実装パターンを整理した公式ウォークスルーです。結論から言うと、これは「Redisを置き換える新標準」というより、すでにAzure Database for PostgreSQLを使っている.NETアプリに、低遅延・負荷削減・運用のシンプルさを足す現実的な選択肢として読むべき更新です。
特に、外部API呼び出し、重い集計処理、マスターデータ取得、複数インスタンス構成のWeb APIを運用している開発者や管理者にとって、今回の内容はそのまま設計レビューに使えます。Microsoftの公式記事は2026年4月28日に公開され、.NET 10のコンソールアプリを題材に、Azure Database for PostgreSQLへ分散キャッシュを保存し、HybridCacheでメモリ内キャッシュと組み合わせる流れを示しています。(Microsoft for Developers)
High-Performance Distributed Caching with .NET and Postgres on Azureで何が変わったか
今回の更新で重要なのは、「Postgresをキャッシュの保存先として使える」という一点だけではありません。ポイントは、.NETアプリのキャッシュ設計を、より実務に近い形で段階的に組み立てていることです。
公式ウォークスルーでは、次の流れが示されています。
| 観点 | 内容 |
|---|---|
| アプリ構成 | .NET Generic Hostを使い、構成、DI、ログ、バックグラウンドサービスを整理 |
| キャッシュ保存先 | Azure Database for PostgreSQLに分散キャッシュ値を保存 |
| キャッシュ方式 | Microsoft.Extensions.Caching.PostgresとMicrosoft.Extensions.Caching.Hybridを使用 |
| パフォーマンス確認 | Stopwatchでキャッシュなし、メモリ内ヒット、分散キャッシュヒットの差を確認 |
| 運用面 | appsettings、User Secrets、環境変数、Key Vaultなどを意識した構成 |
公式記事では、Microsoft.Extensions.Caching.PostgresがPostgresに分散キャッシュを永続化し、Microsoft.Extensions.Caching.Hybridが高速なメモリ内キャッシュと分散キャッシュを組み合わせる役割として紹介されています。(Microsoft for Developers)
つまり、今回の読みどころは「キャッシュを入れると速くなる」という一般論ではなく、Azure上の.NETアプリで、キャッシュ層をどこに置き、どのようにDIへ登録し、どのように期限切れや接続情報を扱うかまで具体化されている点です。
仕組みを簡単に整理する:HybridCacheとPostgresの役割
今回の構成では、HybridCacheがキャッシュ利用の入口になります。アプリケーションコードからはHybridCacheに問い合わせ、裏側ではメモリ内キャッシュとPostgresベースの分散キャッシュが使われます。
処理の流れは次のように考えると理解しやすくなります。
| 状態 | 何が起きるか | 期待できる効果 |
|---|---|---|
| 初回アクセス | キャッシュに値がないため、外部APIやDBなど本来のデータ取得元を呼び出す | 通常の処理時間がかかる |
| メモリ内キャッシュヒット | アプリプロセス内のキャッシュから返す | 最も高速。頻繁なアクセスに向く |
| メモリ期限切れ、分散キャッシュ有効 | Postgres上の分散キャッシュから返す | アプリ再起動や複数インスタンスでも値を共有しやすい |
| 両方期限切れ | 再び本来のデータ取得元を呼び出す | 最新データを再取得できる |
公式記事のデモでは、ローカルのメモリ内キャッシュを3秒、Postgres側の分散キャッシュを6秒で期限切れにする設定が使われています。これは挙動を観察しやすくするための短い値であり、本番環境ではデータの更新頻度や業務要件に合わせて設計する必要があります。(Microsoft for Developers)
重要なのは、メモリ内キャッシュだけに依存しないことです。メモリ内キャッシュは非常に速い一方で、アプリの再起動やスケールアウト時にインスタンス間で共有されません。分散キャッシュは複数アプリサーバーで共有され、クラウドやサーバーファーム環境でパフォーマンスとスケーラビリティを高める用途に向いています。(Microsoft Learn)
どのキャッシュ方式を選ぶべきか
Postgresを分散キャッシュに使えるからといって、すべてのシステムでPostgresキャッシュが最適とは限りません。実務では、速度、運用負荷、既存構成、コスト、可用性を見て選びます。
| 選択肢 | 向いているケース | 注意点 |
|---|---|---|
| メモリ内キャッシュ | 単一インスタンス、短時間だけ保持したいデータ、極低遅延が必要な処理 | 再起動で消える。複数インスタンス間で共有できない |
| Postgres分散キャッシュ | すでにPostgresを使っている.NETアプリ、運用サービス数を増やしたくない構成 | キャッシュアクセスがPostgresの負荷になるため監視が必要 |
| HybridCache + Postgres | メモリの速さと分散キャッシュの共有性を両立したいAPIやバックエンド | キー設計、期限設計、シリアル化設計が重要 |
| Azure Cache for Redis | 高頻度アクセス、低遅延重視、大規模な専用キャッシュ基盤が必要な場合 | 別サービスとしての運用、コスト、接続管理が必要 |
Microsoft Learnでは、分散キャッシュは複数サーバーで共有されるキャッシュとして説明されており、クラウドやサーバーファームで特に有効とされています。また、実稼働アプリで高性能な分散キャッシュが必要な場合の選択肢としてRedisも示されています。(Microsoft Learn)
そのため、今回のPostgresキャッシュは「Redis不要論」ではなく、Postgresをすでに中核データ基盤として使っているチームが、追加サービスを増やさずに分散キャッシュを導入する選択肢と捉えるのが自然です。
実装の流れ:まず何をすればよいか
公式記事の実装は、コンソールアプリを題材にしていますが、考え方はWeb API、バックグラウンドワーカー、非同期処理基盤にも応用できます。
必要なパッケージを追加する
まず、Postgres分散キャッシュとHybridCacheのパッケージを追加します。
dotnet add package Microsoft.Extensions.Caching.Postgres
dotnet add package Microsoft.Extensions.Caching.Hybrid
Microsoft.Extensions.Caching.PostgresはPostgreSQLを使った.NET向け分散キャッシュ実装として公開されており、READMEでもNuGetパッケージの追加方法と構成例が示されています。(GitHub)
appsettingsでキャッシュ設定を分離する
キャッシュ設定はコードに直書きせず、環境ごとに差し替えられる形にします。
{
"PostgresCache": {
"SchemaName": "public",
"TableName": "cache",
"CreateIfNotExists": true,
"UseWAL": false,
"ExpiredItemsDeletionInterval": "00:30:00",
"DefaultSlidingExpiration": "00:20:00"
}
}
Postgresキャッシュの設定項目には、スキーマ名、テーブル名、テーブル作成有無、WAL利用、期限切れアイテムの削除間隔、既定のスライド有効期限などがあります。(GitHub)
本番環境では、CreateIfNotExistsを安易に有効化するより、マイグレーションやIaCでテーブル作成を管理する方が安全です。開発環境では便利ですが、本番でアプリ起動時にスキーマ変更が走る設計は、権限管理やリリース管理の観点で問題になりやすいためです。
接続文字列はソースコードに入れない
公式記事では開発時にUser Secretsを使う例が示されていますが、本番環境では環境変数やAzure Key Vaultなどのシークレット管理を使うことが推奨されています。接続文字列、パスワード、キーをリポジトリへコミットしないことは必須です。(Microsoft for Developers)
開発環境では次のようにUser Secretsへ保存できます。
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:PostgresCache" "Host=...;Port=5432;Username=...;Password=...;Database=...;"
運用では、少なくとも次の分離を意識します。
| 環境 | 推奨される扱い |
|---|---|
| ローカル開発 | User Secrets、ローカル用の限定権限ユーザー |
| 検証環境 | 環境変数、Key Vault、検証用Postgres |
| 本番環境 | Key Vault、マネージドID、最小権限、監査ログ |
DIにPostgres分散キャッシュとHybridCacheを登録する
サービス登録では、Postgresの分散キャッシュを登録したうえで、HybridCacheを追加します。
builder.Services.AddDistributedPostgresCache(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache");
options.SchemaName = builder.Configuration.GetValue<string>("PostgresCache:SchemaName", "public");
options.TableName = builder.Configuration.GetValue<string>("PostgresCache:TableName", "cache");
options.CreateIfNotExists = builder.Configuration.GetValue<bool>("PostgresCache:CreateIfNotExists", false);
});
builder.Services.AddHybridCache();
公式記事では、services.AddHybridCache()を追加することで、メモリ内キャッシュと分散キャッシュを組み合わせる構成が紹介されています。(Microsoft for Developers)
GetOrCreateAsyncでキャッシュミス時の処理を書く
実際の利用では、GetOrCreateAsyncを使うと「キャッシュにあれば返す。なければ取得して保存する」という流れを簡潔に書けます。
var result = await cache.GetOrCreateAsync(
$"weather:forecast:{region}",
async token => await weatherService.GetForecastAsync(region, token),
cancellationToken: cancellationToken
);
HybridCacheのドキュメントでも、GetOrCreateAsyncの主なオーバーロードが一般的なシナリオで推奨されており、キャッシュキーは対象データを一意に識別できる必要があると説明されています。(Microsoft Learn)
キャッシュキー設計で失敗しやすいポイント
キャッシュ導入で最も多い失敗は、「保存するかどうか」ではなく「何をキーにするか」です。キーが曖昧だと、別ユーザーのデータを返す、古い条件の検索結果を返す、キャッシュが増えすぎるといった問題が起きます。
避けるべきキーの例は次の通りです。
// 悪い例:条件が曖昧
"search-result"
// 悪い例:外部入力をそのまま使う
$"search:{rawQuery}"
改善例は次のようになります。
$"search:v1:tenant:{tenantId}:locale:{locale}:hash:{queryHash}"
キーには、少なくとも次の要素を検討します。
| 要素 | 例 | 理由 |
|---|---|---|
| データ種別 | product, user-profile, weather | 異なるデータの衝突を防ぐ |
| バージョン | v1, v2 | 仕様変更時に古いキャッシュを自然に切り離す |
| テナント | tenant:{tenantId} | マルチテナントでの混在を防ぐ |
| ユーザー・権限 | user:{userId} | 個人化データの誤配信を防ぐ |
| 条件 | region:{region}, lang:{locale} | 検索条件や表示条件の違いを反映する |
Microsoft Learnでも、キャッシュキーには一意性が必要であり、外部ユーザー入力を直接キーに使うと、無意味なキーの氾濫やセキュリティリスクにつながる可能性があると説明されています。(Microsoft Learn)
有効期限は「短ければ安全」ではない
キャッシュの有効期限は、短すぎても長すぎても問題になります。短すぎるとキャッシュヒット率が上がらず、長すぎると古いデータを返し続けます。
実務では、データの性質ごとに考えるのが効果的です。
| データの種類 | 期限設計の目安 | 補足 |
|---|---|---|
| 変更頻度の低いマスターデータ | 数分〜数十分 | 更新時に明示削除できると安定する |
| 外部APIの取得結果 | API制限やSLAに合わせる | レート制限対策として有効 |
| 商品価格・在庫 | 短め、またはイベントで無効化 | 古い情報の影響が大きい |
| ユーザー設定 | 数十秒〜数分 | 更新後にRemoveAsyncで削除する |
| 権限・認可情報 | 慎重に短く設計 | 誤った権限判定を避ける |
HybridCacheでは、グローバルな既定値をAddHybridCacheで設定でき、個別のGetOrCreateAsync呼び出しでHybridCacheEntryOptionsを渡して上書きすることもできます。ドキュメントでは、ExpirationとLocalCacheExpirationを使った設定例も示されています。(Microsoft Learn)
注意したいのは、メモリ内キャッシュと分散キャッシュの期限を同じにする必要はないことです。たとえば、メモリ内キャッシュは短く、分散キャッシュは少し長くすることで、プロセス内では高速に返しつつ、再起動やスケールアウト時にもPostgres側のキャッシュを活用できます。
更新・削除の設計を先に決めておく
キャッシュは「保存する」より「消す」方が難しい仕組みです。データが変わったときにキャッシュをどう扱うかを決めずに導入すると、障害対応時に原因追跡が難しくなります。
HybridCacheでは、期限切れ前に元データが変わった場合、キーを指定してRemoveAsyncを呼ぶことでキャッシュエントリを明示的に削除できます。削除時にはプライマリとセカンダリの両方のキャッシュから削除されます。(Microsoft Learn)
たとえば、商品情報を更新した直後に次のような削除処理を入れます。
await cache.RemoveAsync($"product:v1:{productId}", cancellationToken);
また、HybridCacheにはタグによる無効化もあります。ただし、タグベースの無効化は論理的な操作であり、値そのものが即座に物理削除されるとは限りません。大量のタグを付けるとパフォーマンスに影響する可能性もあります。(Microsoft Learn)
Postgresをキャッシュに使うときの運用注意点
Azure Database for PostgreSQLを分散キャッシュとして使う場合、運用はシンプルになりますが、無条件に負荷が消えるわけではありません。キャッシュの読み書きはPostgresへのアクセスになるため、通常のアプリデータと同じPostgresを使う場合は、負荷の分離と監視が重要です。
特に次の項目は導入前に確認してください。
| 確認項目 | 見るべきポイント |
|---|---|
| 接続数 | アプリのスケールアウト時にPostgres接続数が増えすぎないか |
| テーブル肥大化 | 期限切れキャッシュが適切に削除されているか |
| I/O負荷 | キャッシュの高頻度アクセスで通常クエリが遅くならないか |
| 可用性 | Postgres障害時にアプリがどう振る舞うか |
| リージョン | アプリとPostgresの距離が遅延を増やしていないか |
| コスト | キャッシュ用途の読み書きでDBコストが増えないか |
ExpiredItemsDeletionIntervalは期限切れアイテムの削除間隔に関わります。短くすれば掃除は早くなりますが、削除処理の頻度が増えます。長くすれば掃除の負荷は下がりますが、不要なデータが残りやすくなります。サービスのアクセス量や保存件数を見ながら調整する項目です。
UseWALについても、単にオン・オフで決めるのではなく、耐久性、レプリケーション、障害復旧、キャッシュデータの扱いを踏まえて検証する必要があります。キャッシュは「失われても再取得できるデータ」であるべきですが、障害時の動作はアプリ側で確認しておくべきです。
キャッシュすべきデータ、してはいけないデータ
キャッシュは万能ではありません。むしろ、向かないデータをキャッシュすると、障害や情報漏えいの原因になります。
キャッシュに向いているデータ
次のようなデータは、キャッシュ効果が出やすい対象です。
| データ | 理由 |
|---|---|
| 外部APIの応答 | レイテンシ削減とレート制限対策になる |
| 変更頻度の低いマスターデータ | 古い値のリスクが低く、ヒット率が高い |
| 重い集計結果 | DB負荷を下げやすい |
| 設定値・Feature Flagの一部 | 短いTTLなら反映遅延を制御しやすい |
| 同一条件で繰り返される検索結果 | 条件をキー化しやすい |
キャッシュに慎重になるべきデータ
一方で、次のデータは慎重に扱います。
| データ | 注意点 |
|---|---|
| 認可・権限情報 | 古い権限を返すと重大な問題になる |
| 個人情報を含むレスポンス | キー設計ミスで別ユーザーへ返るリスクがある |
| 在庫・決済・残高 | 正確性が最優先。TTLだけに頼らない |
| 巨大なオブジェクト | シリアル化コストやサイズ制限に注意 |
| 一度しか読まれないデータ | キャッシュしても効果が薄い |
HybridCacheにはサイズやキー長の制限もあります。ドキュメントでは、既定の最大ペイロードが1MB、最大キー長が1,024文字と説明されています。制限を超える値はキャッシュに保存されないため、大きなレスポンスをそのまま保存する設計は避けるべきです。(Microsoft Learn)
実務で導入する前のチェックリスト
Postgres分散キャッシュとHybridCacheを導入する前に、次の質問に答えられる状態にしておくと、設計の失敗を避けやすくなります。
| チェック項目 | 判断基準 |
|---|---|
| 何を高速化したいか | 外部API、DB集計、検索、マスターデータなど対象を明確にする |
| キャッシュミス時の処理時間はどれくらいか | 遅い処理ほど効果が出やすい |
| 同じキーで何回読まれるか | 再利用されないデータはキャッシュ効果が薄い |
| どれくらい古くても許容できるか | TTL設計の根拠になる |
| 更新時に削除できるか | RemoveAsyncやタグ無効化の設計が必要 |
| キーにテナント・ユーザー・条件を含めたか | データ混在を防ぐ |
| Postgresへの負荷を監視できるか | CPU、I/O、接続数、テーブルサイズを見る |
| 障害時の動作を決めたか | キャッシュなしで本来の取得元へ戻れるか |
| シークレット管理は安全か | Key Vaultや環境変数を使う |
| Redisとの比較をしたか | 超低遅延や大規模用途では専用キャッシュも検討する |
このチェックリストで半分以上が曖昧なら、まず小さな対象から始めるのが安全です。たとえば「天気情報」「商品カテゴリ」「外部APIの参照結果」のように、更新頻度が低く、キャッシュミス時の処理が重く、誤配信リスクが低いデータを選びます。
Microsoft ecosystem readersが注目すべき意味
今回の公式更新は、Azure、.NET、Postgres、HybridCacheが実務的に接続された点で重要です。Microsoft ecosystemの開発者や管理者にとっては、単なるサンプルではなく、次のような判断材料になります。
まず、.NETの標準的なDI、構成、ログの流れに沿ってキャッシュを組み込めます。特別な独自フレームワークを覚えるのではなく、Generic HostやMicrosoft.Extensions.*の流儀で実装できます。
次に、Azure Database for PostgreSQLをすでに使っている環境では、キャッシュ用に新しい専用サービスを増やす前に、Postgres分散キャッシュを検証する余地があります。もちろん、アクセス頻度が非常に高い場合や専用キャッシュ基盤が必要な場合はRedisなどの比較が必要ですが、運用のシンプルさを重視するチームには有力な選択肢です。
さらに、HybridCacheにより、アプリ内の高速なメモリキャッシュと、複数インスタンスで共有しやすい分散キャッシュを同じ入口から扱えるようになります。これは、Web API、バックグラウンドジョブ、マイクロサービス、非同期処理のいずれにも応用しやすい考え方です。
まずは小さく試し、数値で判断する
High-Performance Distributed Caching with .NET and Postgres on Azureを読むときは、「Postgresキャッシュを入れるべきか」ではなく、自分のアプリのどの処理に、どの期限で、どのキー設計で入れると効果が出るかを考えることが大切です。
最初の一歩としては、次の順番がおすすめです。
- 遅い処理をログやAPMで特定する
- 同じ条件で繰り返し読まれるデータを選ぶ
- キャッシュキーとTTLを設計する
- HybridCacheで
GetOrCreateAsyncを使う - Stopwatchやメトリクスでヒット時・ミス時の差を測る
- Postgres側の接続数、I/O、テーブルサイズを監視する
- 効果が確認できたら対象範囲を広げる
キャッシュは、入れるだけで自動的に速くなる魔法ではありません。しかし、対象データ、キー、期限、削除、監視を正しく設計すれば、レイテンシ削減、下流システムの負荷軽減、スケール時の安定性向上に直結します。今回の公式更新は、Azure上の.NETアプリでその設計を始めるための、実用的な出発点として活用できます。

コメント