はじめに
キャッシュとは、高速なデータ交換が可能なメモリで、メインメモリより先にCPUとデータを交換するため、非常に高速です。CPUがメモリからデータを読み取る速度はディスクから読み取るよりも数桁速く、またメモリ上に存在するためデータベースアクセスの負荷が軽減されます。そのため、ほぼすべてのプロジェクトでキャッシュが使用されます。一般的によく使われるのは MemoryCache や Redis ですが、今回は MemoryCache の使い方をご紹介します!
種類
メモリキャッシュの有効期限には次の4種類があります。
- 無期限
- 絶対有効期限
- 現在からの相対有効期限
- スライディング有効期限
もちろん、これらの3種類の期限を組み合わせて、スライディングウィンドウ+絶対有効期限などを作成することも可能です。
公式サイト
公式ドキュメントを確認して MemoryCache を詳しく知ることもできます。ここでは詳しく説明しません。
- MemoryCache アドレス: https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.caching.memorycache?view=dotnet-plat-ext-6.0
使用法
先ほどの質問に戻り、有効期限の設定方法を紹介します!
無期限
プログラムを公開した後、キャッシュを明示的にクリアしない限り、キャッシュは永久に有効です。
/// <summary>
/// 無期限
/// </summary>
static void NeverExpire()
{
_cache.Set("NeverExpire", "1");
}
絶対有効期限
特定の絶対時刻を指定します。「締切日」と考えることができます。
static void AbsoluteExpiration()
{
DateTime time = new DateTime(2022, 04, 01, 23, 59, 59);
_cache.Set("AbsoluteExpiration", "20220401235959", time);
}
現在からの相対有効期限
キャッシュ設定後、例えば1分間だけ有効にするものです。よくある例としては、SMSログインが挙げられます。バックエンドでランダムな確認コードを生成し、Redisに保存してキーに有効期限を設定し、その後電話番号と確認コードを受け取り、Redisから確認コードを取得して検証します。正しければ確認コードを削除し、複数回の検証を防ぎます。
static void ExpirationTimeRelativeToThePresent()
{
_cache.Set("AbsoluteExpiration", "123456", new TimeSpan(0, 0, 60));
}
スライディング有効期限
設定された時間内にキャッシュが使用されなければ無効になります。使用されると、キャッシュの有効期限がリセットされます。
static void SlidingExpirationTime()
{
_cache.Set("SlidingExpirationTime", "3", new MemoryCacheEntryOptions()
{
SlidingExpiration = new TimeSpan(0, 0, 2),
AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(1000)
});
}
公式の定義を以下の図で確認してください。

次に、2番目のパラメータ MemoryCacheEntryOptions について説明します。キャッシュエントリの絶対有効期限を設定するものです:現在のキャッシュ設定から1000秒後(実際は分?冗談です。通常は5分や10分など、実際のビジネスに応じて設定します)。例えば、私たちがよく遊ぶ「League of Legends: Wild Rift」では、1日ログインしなければキャッシュされたトークンが失効し、再ログインして新しいトークンを取得する必要があります。毎日プレイしていればスライディング有効期限がトリガーされ、アプリ起動のたびにログインする必要はありません。しかし、しばらく遊んでいると、再ログインが必要になることがあります。これがスライディング有効期限における絶対有効期限の役割です!
キャッシュ値の取得
ConcurrentDictionary<object, CacheEntry> _entries:マルチスレッドセーフな辞書型です。実はキャッシュの本質はこの辞書であり、すべてのキャッシュをこの辞書に格納し、辞書のキー(辞書のキーはキャッシュエンティティ CacheEntry のキーと同じ)を使って CacheEntry エンティティ(CacheEntry エンティティは key と value を含み、コードで設定した key と value に相当)を取得します。
static void GetCache()
{
// 方法1
_cache.Get("NeverExpire").ToString();
// 方法2
string value = "";
if (!_cache.TryGetValue("NeverExpire", out value))
{
throw new Exception("キャッシュが存在しないか、既に期限切れです");
}
}
キャッシュ値の削除
static void GetCache()
{
string value = "";
if (_cache.TryGetValue("NeverExpire", out value))
{
_cache.Remove("NeverExpire");
}
}
削除するときに value 値が不要なのに、一時変数を使うのは面倒だと思われるかもしれません。
C# ではこの問題が考慮されており、C# 7.0 からは破棄(discard)がサポートされています。破棄は記述や意味上の改善だけでなく、メモリ割り当ての削減にも役立ちます。
上記のコードを簡略化すると次のようになります。
static void GetCache()
{
if (_cache.TryGetValue("NeverExpire", out _))
{
_cache.Remove("NeverExpire");
}
}
完全なコード
class Program
{
public static IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
static void Main(string[] args)
{
_cache.Get("NeverExpire").ToString();
string value = "";
if (!_cache.TryGetValue("NeverExpire", out value))
{
throw new Exception("キャッシュが存在しないか、既に期限切れです");
}
if (_cache.TryGetValue("NeverExpire", out value))
{
_cache.Remove("NeverExpire");
}
if (_cache.TryGetValue("NeverExpire", out _))
{
_cache.Remove("NeverExpire");
}
}
/// <summary>
/// 無期限
/// </summary>
static void NeverExpire()
{
_cache.Set("NeverExpire", "1");
}
/// <summary>
/// 絶対有効期限
/// </summary>
static void AbsoluteExpiration()
{
DateTime time = new DateTime(2022, 04, 01, 23, 59, 59);
_cache.Set("AbsoluteExpiration", "20220401235959", time);
}
/// <summary>
/// 現在からの相対有効期限
/// </summary>
///
static void ExpirationTimeRelativeToThePresent()
{
_cache.Set("AbsoluteExpiration", "123456", new TimeSpan(0, 0, 60));
}
/// <summary>
/// スライディング有効期限
/// </summary>
static void SlidingExpirationTime()
{
_cache.Set("key3", "3", new MemoryCacheEntryOptions()
{
SlidingExpiration = new TimeSpan(0, 0, 2),
AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(1000)
});
}
}
最後に、この記事が気に入っていただけましたら、ぜひフォローといいねをお願いします。.NET エコシステムがますます良くなることを願っています!