ASP.NET Core 메모리 내 캐시Cache in-memory in ASP.NET Core

작성자, Rick Anderson, John 루 오 어Steve SmithBy Rick Anderson, John Luo, and Steve Smith

예제 코드 살펴보기 및 다운로드 (다운로드 방법)View or download sample code (how to download)

캐싱 기본 사항Caching basics

캐싱은 콘텐츠를 생성 하는 데 필요한 작업을 줄여 응용 프로그램의 성능과 확장성을 크게 향상 시킬 수 있습니다.Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. 캐싱은 자주 변경 되지 않으며 생성 하는 데 비용이 많이 드는 데이터에서 가장 잘 작동 합니다.Caching works best with data that changes infrequently and is expensive to generate. 캐싱은 원본에서 보다 훨씬 빠르게 반환 될 수 있는 데이터 복사본을 만듭니다.Caching makes a copy of data that can be returned much faster than from the source. 캐시 된 데이터에 종속 되지 않도록 앱을 작성 하 고 테스트 해야 합니다.Apps should be written and tested to never depend on cached data.

ASP.NET Core는 여러 캐시를 지원 합니다.ASP.NET Core supports several different caches. 가장 간단한 캐시는 IMemoryCache을 기반으로 합니다.The simplest cache is based on the IMemoryCache. IMemoryCache 웹 서버의 메모리에 저장 된 캐시를 나타냅니다.IMemoryCache represents a cache stored in the memory of the web server. 서버 팜 (여러 서버)에서 실행 되는 앱은 메모리 내 캐시를 사용할 때 세션이 고정 되어 있는지 확인 해야 합니다.Apps running on a server farm (multiple servers) should ensure sessions are sticky when using the in-memory cache. 고정 세션은 클라이언트의 후속 요청이 모두 동일한 서버로 이동 하는지 확인 합니다.Sticky sessions ensure that subsequent requests from a client all go to the same server. 예를 들어 Azure Web apps는 ARR ( 응용 프로그램 요청 라우팅 )을 사용 하 여 모든 후속 요청을 동일한 서버에 라우팅합니다.For example, Azure Web apps use Application Request Routing (ARR) to route all subsequent requests to the same server.

웹 팜의 고정 되지 않은 세션은 캐시 일관성 문제를 방지 하기 위해 분산 캐시가 필요 합니다.Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. 일부 앱의 경우 분산 캐시는 메모리 내 캐시 보다 더 높은 확장을 지원할 수 있습니다.For some apps, a distributed cache can support higher scale-out than an in-memory cache. 분산 캐시를 사용 하면 캐시 메모리가 외부 프로세스로 오프 로드 됩니다.Using a distributed cache offloads the cache memory to an external process.

메모리 내 캐시는 모든 개체를 저장할 수 있습니다.The in-memory cache can store any object. 분산 캐시 인터페이스는로 제한 됩니다 byte[] .The distributed cache interface is limited to byte[]. 메모리 내 및 분산 캐시 저장소는 키-값 쌍으로 항목을 캐시 합니다.The in-memory and distributed cache store cache items as key-value pairs.

시스템 런타임 캐싱/MemoryCacheSystem.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (NuGet 패키지)는와 함께 사용할 수 있습니다.System.Runtime.Caching/MemoryCache (NuGet package) can be used with:

  • 2.0 이상 .NET Standard 합니다..NET Standard 2.0 or later.
  • .NET Standard 2.0 이상을 대상으로 하는 .net 구현Any .NET implementation that targets .NET Standard 2.0 or later. 예를 들어 ASP.NET Core 2.0 이상입니다.For example, ASP.NET Core 2.0 or later.
  • .NET Framework 4.5 이상.NET Framework 4.5 or later.

/ IMemoryCache 이 문서에 설명 된 대로, System.Runtime.Caching / MemoryCache ASP.NET Core에 더 잘 통합 되기 때문에이 문서에 설명 되어 있는 것이 좋습니다.Microsoft.Extensions.Caching.Memory/IMemoryCache (described in this article) is recommended over System.Runtime.Caching/MemoryCache because it's better integrated into ASP.NET Core. 예를 들어은 IMemoryCache ASP.NET Core 종속성 주입을 기본적으로 사용 합니다.For example, IMemoryCache works natively with ASP.NET Core dependency injection.

System.Runtime.Caching / MemoryCache ASP.NET 4.x에서 ASP.NET Core로 코드를 이식할 때 호환성 브리지로를 사용 합니다.Use System.Runtime.Caching/MemoryCache as a compatibility bridge when porting code from ASP.NET 4.x to ASP.NET Core.

캐시 지침Cache guidelines

  • 코드에는 항상 데이터를 인출 하는 대체 (fallback) 옵션이 있으며 사용 가능한 캐시 된 값에 의존 하지 않아야 합니다.Code should always have a fallback option to fetch data and not depend on a cached value being available.
  • 캐시는 부족 한 리소스 인 메모리를 사용 합니다.The cache uses a scarce resource, memory. 캐시 증가 제한:Limit cache growth:
    • 외부 입력을 캐시 키로 사용 하지 마십시오.Do not use external input as cache keys.
    • 캐시 증가를 제한 하려면 만료를 사용 합니다.Use expirations to limit cache growth.
    • SetSize, Size 및 SizeLimit를 사용 하 여 캐시 크기를 제한합니다.Use SetSize, Size, and SizeLimit to limit cache size. ASP.NET Core 런타임은 메모리 압력에 따라 캐시 크기를 제한 하지 않습니다.The ASP.NET Core runtime does not limit cache size based on memory pressure. 캐시 크기를 제한 하는 것은 개발자에 게 있습니다.It's up to the developer to limit cache size.

IMemoryCache 사용Use IMemoryCache

경고

종속성 주입 에서 공유 메모리 캐시를 사용 하 고, 또는를 호출 하 여 SetSize Size SizeLimit 캐시 크기를 제한 하면 앱에 오류가 발생할 수 있습니다.Using a shared memory cache from Dependency Injection and calling SetSize, Size, or SizeLimit to limit cache size can cause the app to fail. 캐시에 크기 제한이 설정 된 경우 모든 항목은 추가 될 때 크기를 지정 해야 합니다.When a size limit is set on a cache, all entries must specify a size when being added. 이로 인해 개발자가 공유 캐시를 사용 하는 항목을 완전히 제어할 수 없기 때문에 문제가 발생할 수 있습니다.This can lead to issues since developers may not have full control on what uses the shared cache. , 또는를 사용 하 여 SetSize Size SizeLimit 캐시를 제한 하는 경우 캐싱에 대 한 캐시 singleton을 만듭니다.When using SetSize, Size, or SizeLimit to limit cache, create a cache singleton for caching. 자세한 내용 및 예제는 SetSize, Size 및 SizeLimit를 사용 하 여 캐시 크기 제한을 참조 하세요.For more information and an example, see Use SetSize, Size, and SizeLimit to limit cache size. 공유 캐시는 다른 프레임 워크 또는 라이브러리에서 공유 하는 캐시입니다.A shared cache is one shared by other frameworks or libraries.

메모리 내 캐싱은 종속성 주입을 사용 하 여 앱에서 참조 되는 서비스 입니다.In-memory caching is a service that's referenced from an app using Dependency Injection. IMemoryCache생성자에서 인스턴스를 요청 합니다.Request the IMemoryCache instance in the constructor:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

다음 코드에서는 TryGetValue 를 사용 하 여 시간이 캐시에 있는지 확인 합니다.The following code uses TryGetValue to check if a time is in the cache. 시간이 캐시 되지 않으면 새 항목이 만들어지고 Set을 사용 하 여 캐시에 추가 됩니다.If a time isn't cached, a new entry is created and added to the cache with Set. CacheKeys클래스는 다운로드 샘플의 일부입니다.The CacheKeys class is part of the download sample.

public static class CacheKeys
{
    public static string Entry { get { return "_Entry"; } }
    public static string CallbackEntry { get { return "_Callback"; } }
    public static string CallbackMessage { get { return "_CallbackMessage"; } }
    public static string Parent { get { return "_Parent"; } }
    public static string Child { get { return "_Child"; } }
    public static string DependentMessage { get { return "_DependentMessage"; } }
    public static string DependentCTS { get { return "_DependentCTS"; } }
    public static string Ticks { get { return "_Ticks"; } }
    public static string CancelMsg { get { return "_CancelMsg"; } }
    public static string CancelTokenSource { get { return "_CancelTokenSource"; } }
}
public IActionResult CacheTryGetValueSet()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Set cache options.
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Save data in cache.
        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}

현재 시간과 캐시 된 시간이 표시 됩니다.The current time and the cached time are displayed:

@model DateTime?

<div>
    <h2>Actions</h2>
    <ul>
        <li><a asp-controller="Home" asp-action="CacheTryGetValueSet">TryGetValue and Set</a></li>
        <li><a asp-controller="Home" asp-action="CacheGet">Get</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreate">GetOrCreate</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAsynchronous">CacheGetOrCreateAsynchronous</a></li>
        <li><a asp-controller="Home" asp-action="CacheRemove">Remove</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAbs">CacheGetOrCreateAbs</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAbsSliding">CacheGetOrCreateAbsSliding</a></li>

    </ul>
</div>

<h3>Current Time: @DateTime.Now.TimeOfDay.ToString()</h3>
<h3>Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())</h3>

다음 코드에서는 집합 확장명 메서드를 사용 하 여 개체를 만들지 않고 상대 시간에 대 한 데이터를 캐시 합니다 MemoryCacheEntryOptions .The following code uses the Set extension method to cache data for a relative time without creating the MemoryCacheEntryOptions object.

public IActionResult SetCacheRelativeExpiration()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Save data in cache and set the relative expiration time to one day
        _cache.Set(CacheKeys.Entry, cacheEntry, TimeSpan.FromDays(1));
    }

    return View("Cache", cacheEntry);
}

DateTime제한 시간 내에 요청이 있는 동안 캐시 된 값은 캐시에 남아 있습니다.The cached DateTime value remains in the cache while there are requests within the timeout period.

다음 코드는 GetorcreateGetorcreateasync 를 사용 하 여 데이터를 캐시 합니다.The following code uses GetOrCreate and GetOrCreateAsync to cache data.

public IActionResult CacheGetOrCreate()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return Task.FromResult(DateTime.Now);
        });

    return View("Cache", cacheEntry);
}

다음 코드는 Get 을 호출 하 여 캐시 된 시간을 인출 합니다.The following code calls Get to fetch the cached time:

public IActionResult CacheGet()
{
    var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
    return View("Cache", cacheEntry);
}

다음 코드는 절대 만료를 사용 하는 캐시 된 항목을 가져오거나 만듭니다.The following code gets or creates a cached item with absolute expiration:

public IActionResult CacheGetOrCreateAbs()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

슬라이딩 만료가 있는 캐시 된 항목 집합은 부실 해질 수 있습니다.A cached item set with a sliding expiration only is at risk of becoming stale. 슬라이딩 만료 간격 보다 더 자주 액세스 하는 경우 항목은 만료 되지 않습니다.If it's accessed more frequently than the sliding expiration interval, the item will never expire. 절대 만료 시간이 지날 때 항목이 만료 되도록 하려면 슬라이딩 만료를 절대 만료와 결합 합니다.Combine a sliding expiration with an absolute expiration to guarantee that the item expires once its absolute expiration time passes. 절대 만료는 항목을 캐시할 수 있는 기간에 대 한 상한을 설정 하 고, 해당 항목이 슬라이딩 만료 간격 내에 요청 되지 않은 경우에도 이전에 만료 될 수 있도록 합니다.The absolute expiration sets an upper bound to how long the item can be cached while still allowing the item to expire earlier if it isn't requested within the sliding expiration interval. 절대 및 상대 (sliding) 만료가 모두 지정 된 경우 만료는 논리적으로 ORed 됩니다.When both absolute and sliding expiration are specified, the expirations are logically ORed. 슬라이딩 만료 간격이 절대 만료 시간을 전달 하는 경우 항목은 캐시에서 제거 됩니다.If either the sliding expiration interval or the absolute expiration time pass, the item is evicted from the cache.

다음 코드는 슬라이딩 절대 만료를 모두 사용 하 여 캐시 된 항목을 가져오거나 만듭니다.The following code gets or creates a cached item with both sliding and absolute expiration:

public IActionResult CacheGetOrCreateAbsSliding()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SetSlidingExpiration(TimeSpan.FromSeconds(3));
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

위의 코드는 데이터가 절대 시간 보다 오래 캐시 되지 않도록 보장 합니다.The preceding code guarantees the data will not be cached longer than the absolute time.

GetOrCreate, GetOrCreateAsyncGet 는 클래스의 확장 메서드입니다 CacheExtensions .GetOrCreate, GetOrCreateAsync, and Get are extension methods in the CacheExtensions class. 이러한 메서드는의 기능을 확장 IMemoryCache 합니다.These methods extend the capability of IMemoryCache.

MemoryCacheEntryOptions

다음 샘플입니다.The following sample:

  • 슬라이딩 만료 시간을 설정 합니다.Sets a sliding expiration time. 이 캐시 된 항목에 액세스 하는 요청은 슬라이딩 만료 시간을 다시 설정 합니다.Requests that access this cached item will reset the sliding expiration clock.
  • 캐시 우선 순위를 Cacheitempriority. NeverRemove로 설정 합니다.Sets the cache priority to CacheItemPriority.NeverRemove.
  • 항목이 캐시에서 제거 된 후에 호출 되는 PostEvictionDelegate 을 설정 합니다.Sets a PostEvictionDelegate that will be called after the entry is evicted from the cache. 콜백은 캐시에서 항목을 제거 하는 코드와 다른 스레드에서 실행 됩니다.The callback is run on a different thread from the code that removes the item from the cache.
public IActionResult CreateCallbackEntry()
{
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        // Pin to cache.
        .SetPriority(CacheItemPriority.NeverRemove)
        // Add eviction callback
        .RegisterPostEvictionCallback(callback: EvictionCallback, state: this);

    _cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);

    return RedirectToAction("GetCallbackEntry");
}

public IActionResult GetCallbackEntry()
{
    return View("Callback", new CallbackViewModel
    {
        CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
        Message = _cache.Get<string>(CacheKeys.CallbackMessage)
    });
}

public IActionResult RemoveCallbackEntry()
{
    _cache.Remove(CacheKeys.CallbackEntry);
    return RedirectToAction("GetCallbackEntry");
}

private static void EvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

SetSize, Size 및 SizeLimit를 사용 하 여 캐시 크기 제한Use SetSize, Size, and SizeLimit to limit cache size

MemoryCache인스턴스는 필요에 따라 크기 제한을 지정 하 고 적용할 수 있습니다.A MemoryCache instance may optionally specify and enforce a size limit. 캐시 크기 제한에는 항목의 크기를 측정 하는 메커니즘이 없기 때문에 정의 된 측정 단위가 없습니다.The cache size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. 캐시 크기 제한이 설정 된 경우 모든 항목의 크기를 지정 해야 합니다.If the cache size limit is set, all entries must specify size. ASP.NET Core 런타임은 메모리 압력에 따라 캐시 크기를 제한 하지 않습니다.The ASP.NET Core runtime does not limit cache size based on memory pressure. 캐시 크기를 제한 하는 것은 개발자에 게 있습니다.It's up to the developer to limit cache size. 지정 된 크기는 개발자가 선택 하는 단위입니다.The size specified is in units the developer chooses.

예:For example:

  • 웹 앱이 주로 문자열을 캐싱하는 경우 각 캐시 엔트리 크기는 문자열 길이가 될 수 있습니다.If the web app was primarily caching strings, each cache entry size could be the string length.
  • 앱은 모든 항목의 크기를 1로 지정 하 고, 크기 제한은 항목 수를 지정 합니다.The app could specify the size of all entries as 1, and the size limit is the count of entries.

SizeLimit가 설정 되지 않은 경우 캐시는 바인딩되지 않고 증가 합니다.If SizeLimit isn't set, the cache grows without bound. 시스템 메모리가 부족할 때 ASP.NET Core 런타임은 캐시를 자르지 않습니다.The ASP.NET Core runtime doesn't trim the cache when system memory is low. 앱은 다음을 위해 설계 되어야 합니다.Apps must be architected to:

  • 캐시 증가를 제한 합니다.Limit cache growth.
  • Compact또는 Remove 사용 가능한 메모리가 제한 된 경우를 호출 합니다.Call Compact or Remove when available memory is limited:

다음 코드는 MemoryCache 종속성 주입을 통해 액세스할 수 있는 고정 크기를 만듭니다.The following code creates a unitless fixed size MemoryCache accessible by dependency injection:

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache 
{
    public MemoryCache Cache { get; set; }
    public MyMemoryCache()
    {
        Cache = new MemoryCache(new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
    }
}

SizeLimit 에 단위가 없습니다.SizeLimit does not have units. 캐시 된 항목은 캐시 크기 한도가 설정 된 경우 가장 적합 한 모든 단위에서 크기를 지정 해야 합니다.Cached entries must specify size in whatever units they deem most appropriate if the cache size limit has been set. 캐시 인스턴스의 모든 사용자는 동일한 단위 시스템을 사용 해야 합니다.All users of a cache instance should use the same unit system. 캐시 된 항목 크기의 합계가에 지정 된 값을 초과 하는 경우 항목이 캐시 되지 않습니다 SizeLimit .An entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. 캐시 크기 제한을 설정 하지 않으면 항목에 설정 된 캐시 크기가 무시 됩니다.If no cache size limit is set, the cache size set on the entry will be ignored.

다음 코드는 MyMemoryCache 종속성 주입 컨테이너에 등록 됩니다.The following code registers MyMemoryCache with the dependency injection container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache 는이 크기 제한 된 캐시를 인식 하는 구성 요소에 대 한 독립 메모리 캐시로 만들어지며 캐시 엔트리 크기를 적절 하 게 설정 하는 방법을 알고 있습니다.MyMemoryCache is created as an independent memory cache for components that are aware of this size limited cache and know how to set cache entry size appropriately.

다음 코드에서는를 사용 합니다 MyMemoryCache .The following code uses MyMemoryCache:

public class SetSize : PageModel
{
    private MemoryCache _cache;
    public static readonly string MyKey = "_MyKey";

    public SetSize(MyMemoryCache memoryCache)
    {
        _cache = memoryCache.Cache;
    }

    [TempData]
    public string DateTime_Now { get; set; }

    public IActionResult OnGet()
    {
        if (!_cache.TryGetValue(MyKey, out string cacheEntry))
        {
            // Key not in cache, so get data.
            cacheEntry = DateTime.Now.TimeOfDay.ToString();

            var cacheEntryOptions = new MemoryCacheEntryOptions()
                // Set cache entry size by extension method.
                .SetSize(1)
                // Keep in cache for this time, reset time if accessed.
                .SetSlidingExpiration(TimeSpan.FromSeconds(3));

            // Set cache entry size via property.
            // cacheEntryOptions.Size = 1;

            // Save data in cache.
            _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
        }

        DateTime_Now = cacheEntry;

        return RedirectToPage("./Index");
    }
}

캐시 엔트리의 크기는 Size 또는 확장 메서드를 통해 설정할 수 있습니다 SetSize .The size of the cache entry can be set by Size or the SetSize extension methods:

public IActionResult OnGet()
{
    if (!_cache.TryGetValue(MyKey, out string cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now.TimeOfDay.ToString();

        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Set cache entry size by extension method.
            .SetSize(1)
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Set cache entry size via property.
        // cacheEntryOptions.Size = 1;

        // Save data in cache.
        _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
    }

    DateTime_Now = cacheEntry;

    return RedirectToPage("./Index");
}

MemoryCache. CompactMemoryCache.Compact

MemoryCache.Compact 다음 순서에 따라 캐시의 지정 된 비율을 제거 하려고 시도 합니다.MemoryCache.Compact attempts to remove the specified percentage of the cache in the following order:

  • 만료 된 모든 항목입니다.All expired items.
  • 우선 순위별로 항목Items by priority. 우선 순위가 가장 낮은 항목이 먼저 제거 됩니다.Lowest priority items are removed first.
  • 가장 최근에 사용한 개체입니다.Least recently used objects.
  • 가장 빠른 절대 만료를 가진 항목입니다.Items with the earliest absolute expiration.
  • 가장 빠른 슬라이딩 만료를 포함 하는 항목입니다.Items with the earliest sliding expiration.

우선 순위가 있는 고정 된 항목 NeverRemove 은 제거 되지 않습니다.Pinned items with priority NeverRemove are never removed. 다음 코드는 캐시 항목을 제거 하 고를 호출 합니다 Compact .The following code removes a cache item and calls Compact:

_cache.Remove(MyKey);

// Remove 33% of cached items.
_cache.Compact(.33);   
cache_size = _cache.Count;

자세한 내용은 GitHub의 Compact source 를 참조 하세요.See Compact source on GitHub for more information.

캐시 종속성Cache dependencies

다음 샘플에서는 종속 항목이 만료 된 경우 캐시 항목을 만료 하는 방법을 보여 줍니다.The following sample shows how to expire a cache entry if a dependent entry expires. CancellationChangeToken이 캐시 된 항목에 추가 됩니다.A CancellationChangeToken is added to the cached item. Cancel에 대해를 호출 하면 CancellationTokenSource 두 캐시 항목이 모두 제거 됩니다.When Cancel is called on the CancellationTokenSource, both cache entries are evicted.

public IActionResult CreateDependentEntries()
{
    var cts = new CancellationTokenSource();
    _cache.Set(CacheKeys.DependentCTS, cts);

    using (var entry = _cache.CreateEntry(CacheKeys.Parent))
    {
        // expire this entry if the dependant entry expires.
        entry.Value = DateTime.Now;
        entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);

        _cache.Set(CacheKeys.Child,
            DateTime.Now,
            new CancellationChangeToken(cts.Token));
    }

    return RedirectToAction("GetDependentEntries");
}

public IActionResult GetDependentEntries()
{
    return View("Dependent", new DependentViewModel
    {
        ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
        ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
        Message = _cache.Get<string>(CacheKeys.DependentMessage)
    });
}

public IActionResult RemoveChildEntry()
{
    _cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
    return RedirectToAction("GetDependentEntries");
}

private static void DependentEvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Parent entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}

를 사용 CancellationTokenSource 하면 여러 캐시 항목을 하나의 그룹으로 제거할 수 있습니다.Using a CancellationTokenSource allows multiple cache entries to be evicted as a group. using위의 코드에서 패턴을 사용 하 여 블록 내에 만들어진 캐시 항목 using 은 트리거와 만료 설정을 상속 합니다.With the using pattern in the code above, cache entries created inside the using block will inherit triggers and expiration settings.

추가적인 참고 사항Additional notes

  • 만료는 백그라운드에서 발생 하지 않습니다.Expiration doesn't happen in the background. 만료 된 항목에 대 한 캐시를 적극적으로 검색 하는 타이머가 없습니다.There is no timer that actively scans the cache for expired items. 캐시의 모든 작업 ( Get , Set ,)은 만료 된 Remove 항목에 대 한 백그라운드 검색을 트리거할 수 있습니다.Any activity on the cache (Get, Set, Remove) can trigger a background scan for expired items. CancellationTokenSource또한 ()의 타이머는 CancelAfter 항목을 제거 하 고 만료 된 항목에 대 한 검색을 트리거합니다.A timer on the CancellationTokenSource (CancelAfter) also removes the entry and triggers a scan for expired items. 다음 예에서는 등록 된 토큰에 대해 CancellationTokenSource (TimeSpan) 을 사용 합니다.The following example uses CancellationTokenSource(TimeSpan) for the registered token. 이 토큰이 발생 하면 엔트리를 즉시 제거 하 고 제거 콜백을 발생 시킵니다.When this token fires it removes the entry immediately and fires the eviction callbacks:
public IActionResult CacheAutoExpiringTryGetValueSet()
{
    DateTime cacheEntry;

    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        cacheEntry = DateTime.Now;

        var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

        var cacheEntryOptions = new MemoryCacheEntryOptions()
            .AddExpirationToken(new CancellationChangeToken(cts.Token));

        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}
  • 콜백을 사용 하 여 캐시 항목을 다시 채우는 경우:When using a callback to repopulate a cache item:

    • 콜백이 완료 되지 않아 여러 요청에서 캐시 된 키 값이 비어 있는 것을 찾을 수 있습니다.Multiple requests can find the cached key value empty because the callback hasn't completed.
    • 이로 인해 여러 스레드가 캐시 된 항목을 늘이기 수 있습니다.This can result in several threads repopulating the cached item.
  • 한 캐시 엔트리를 사용 하 여 다른 캐시 엔트리를 만드는 경우 자식는 부모 항목의 만료 토큰 및 시간 기반 만료 설정을 복사 합니다.When one cache entry is used to create another, the child copies the parent entry's expiration tokens and time-based expiration settings. 자식은 수동으로 제거 하거나 부모 항목을 업데이트 하 여 만료 되지 않습니다.The child isn't expired by manual removal or updating of the parent entry.

  • 캐시 PostEvictionCallbacks 엔트리를 캐시에서 제거한 후에 발생 하는 콜백을 설정 하는 데 사용 됩니다.Use PostEvictionCallbacks to set the callbacks that will be fired after the cache entry is evicted from the cache.

  • 대부분의 앱에서 IMemoryCache 이 사용 됩니다.For most apps, IMemoryCache is enabled. 예를 들어,,, AddMvc AddControllersWithViews AddRazorPages AddMvcCore().AddRazorViewEngine 및의 다른 여러 메서드를 호출 하면 Add{Service} ConfigureServices 가 활성화 IMemoryCache 됩니다.For example, calling AddMvc, AddControllersWithViews, AddRazorPages, AddMvcCore().AddRazorViewEngine, and many other Add{Service} methods in ConfigureServices, enables IMemoryCache. 이전 메서드 중 하나를 호출 하지 않는 앱의 경우 Add{Service} 에서를 호출 해야 할 수 있습니다 AddMemoryCache ConfigureServices .For apps that are not calling one of the preceding Add{Service} methods, it may be necessary to call AddMemoryCache in ConfigureServices.

백그라운드 캐시 업데이트Background cache update

과 같은 백그라운드 서비스 를 사용 IHostedService 하 여 캐시를 업데이트 합니다.Use a background service such as IHostedService to update the cache. 백그라운드 서비스는 항목을 다시 계산 하 고 준비 된 경우에만 캐시에 할당할 수 있습니다.The background service can recompute the entries and then assign them to the cache only when they’re ready.

추가 리소스Additional resources

작성자, Rick Anderson, John 루 오 어Steve SmithBy Rick Anderson, John Luo, and Steve Smith

예제 코드 살펴보기 및 다운로드 (다운로드 방법)View or download sample code (how to download)

캐싱 기본 사항Caching basics

캐싱은 콘텐츠를 생성 하는 데 필요한 작업을 줄여 응용 프로그램의 성능과 확장성을 크게 향상 시킬 수 있습니다.Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. 캐싱은 자주 변경 되지 않는 데이터에 가장 적합 합니다.Caching works best with data that changes infrequently. 캐싱은 원래 원본에서 보다 훨씬 빠르게 반환 될 수 있는 데이터 복사본을 만듭니다.Caching makes a copy of data that can be returned much faster than from the original source. 캐시 된 데이터에 종속 되지 않도록 코드를 작성 하 고 테스트 해야 합니다.Code should be written and tested to never depend on cached data.

ASP.NET Core는 여러 캐시를 지원 합니다.ASP.NET Core supports several different caches. 가장 간단한 캐시는 웹 서버의 메모리에 저장 된 캐시를 나타내는 IMemoryCache을 기반으로 합니다.The simplest cache is based on the IMemoryCache, which represents a cache stored in the memory of the web server. 서버 팜 (여러 서버)에서 실행 되는 앱은 메모리 내 캐시를 사용할 때 세션이 고정 되어 있는지 확인 해야 합니다.Apps that run on a server farm (multiple servers) should ensure that sessions are sticky when using the in-memory cache. 고정 세션은 클라이언트의 이후 요청이 모두 동일한 서버로 이동 하는지 확인 합니다.Sticky sessions ensure that later requests from a client all go to the same server. 예를 들어 Azure Web apps는 ARR ( 응용 프로그램 요청 라우팅 )을 사용 하 여 사용자 에이전트의 모든 요청을 동일한 서버로 라우팅합니다.For example, Azure Web apps use Application Request Routing (ARR) to route all requests from a user agent to the same server.

웹 팜의 고정 되지 않은 세션은 캐시 일관성 문제를 방지 하기 위해 분산 캐시가 필요 합니다.Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. 일부 앱의 경우 분산 캐시는 메모리 내 캐시 보다 더 높은 확장을 지원할 수 있습니다.For some apps, a distributed cache can support higher scale-out than an in-memory cache. 분산 캐시를 사용 하면 캐시 메모리가 외부 프로세스로 오프 로드 됩니다.Using a distributed cache offloads the cache memory to an external process.

메모리 내 캐시는 모든 개체를 저장할 수 있습니다.The in-memory cache can store any object. 분산 캐시 인터페이스는로 제한 됩니다 byte[] .The distributed cache interface is limited to byte[]. 메모리 내 및 분산 캐시 저장소는 키-값 쌍으로 항목을 캐시 합니다.The in-memory and distributed cache store cache items as key-value pairs.

시스템 런타임 캐싱/MemoryCacheSystem.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (NuGet 패키지)는와 함께 사용할 수 있습니다.System.Runtime.Caching/MemoryCache (NuGet package) can be used with:

  • 2.0 이상 .NET Standard 합니다..NET Standard 2.0 or later.
  • .NET Standard 2.0 이상을 대상으로 하는 .net 구현Any .NET implementation that targets .NET Standard 2.0 or later. 예를 들어 ASP.NET Core 2.0 이상입니다.For example, ASP.NET Core 2.0 or later.
  • .NET Framework 4.5 이상.NET Framework 4.5 or later.

/ IMemoryCache 이 문서에 설명 된 대로, System.Runtime.Caching / MemoryCache ASP.NET Core에 더 잘 통합 되기 때문에이 문서에 설명 되어 있는 것이 좋습니다.Microsoft.Extensions.Caching.Memory/IMemoryCache (described in this article) is recommended over System.Runtime.Caching/MemoryCache because it's better integrated into ASP.NET Core. 예를 들어은 IMemoryCache ASP.NET Core 종속성 주입을 기본적으로 사용 합니다.For example, IMemoryCache works natively with ASP.NET Core dependency injection.

System.Runtime.Caching / MemoryCache ASP.NET 4.x에서 ASP.NET Core로 코드를 이식할 때 호환성 브리지로를 사용 합니다.Use System.Runtime.Caching/MemoryCache as a compatibility bridge when porting code from ASP.NET 4.x to ASP.NET Core.

캐시 지침Cache guidelines

  • 코드에는 항상 데이터를 인출 하는 대체 (fallback) 옵션이 있으며 사용 가능한 캐시 된 값에 의존 하지 않아야 합니다.Code should always have a fallback option to fetch data and not depend on a cached value being available.
  • 캐시는 부족 한 리소스 인 메모리를 사용 합니다.The cache uses a scarce resource, memory. 캐시 증가 제한:Limit cache growth:
    • 외부 입력을 캐시 키로 사용 하지 마십시오.Do not use external input as cache keys.
    • 캐시 증가를 제한 하려면 만료를 사용 합니다.Use expirations to limit cache growth.
    • SetSize, Size 및 SizeLimit를 사용 하 여 캐시 크기를 제한합니다.Use SetSize, Size, and SizeLimit to limit cache size. ASP.NET Core 런타임은 메모리 압력에 따라 캐시 크기를 제한 하지 않습니다.The ASP.NET Core runtime does not limit cache size based on memory pressure. 캐시 크기를 제한 하는 것은 개발자에 게 있습니다.It's up to the developer to limit cache size.

IMemoryCache 사용Using IMemoryCache

경고

종속성 주입 에서 공유 메모리 캐시를 사용 하 고, 또는를 호출 하 여 SetSize Size SizeLimit 캐시 크기를 제한 하면 앱에 오류가 발생할 수 있습니다.Using a shared memory cache from Dependency Injection and calling SetSize, Size, or SizeLimit to limit cache size can cause the app to fail. 캐시에 크기 제한이 설정 된 경우 모든 항목은 추가 될 때 크기를 지정 해야 합니다.When a size limit is set on a cache, all entries must specify a size when being added. 이로 인해 개발자가 공유 캐시를 사용 하는 항목을 완전히 제어할 수 없기 때문에 문제가 발생할 수 있습니다.This can lead to issues since developers may not have full control on what uses the shared cache. 예를 들어 Entity Framework Core는 공유 캐시를 사용 하 고 크기를 지정 하지 않습니다.For example, Entity Framework Core uses the shared cache and does not specify a size. 앱이 캐시 크기 제한을 설정 하 고 EF Core를 사용 하는 경우 앱은을 throw InvalidOperationException 합니다.If an app sets a cache size limit and uses EF Core, the app throws an InvalidOperationException. , 또는를 사용 하 여 SetSize Size SizeLimit 캐시를 제한 하는 경우 캐싱에 대 한 캐시 singleton을 만듭니다.When using SetSize, Size, or SizeLimit to limit cache, create a cache singleton for caching. 자세한 내용 및 예제는 SetSize, Size 및 SizeLimit를 사용 하 여 캐시 크기 제한을 참조 하세요.For more information and an example, see Use SetSize, Size, and SizeLimit to limit cache size.

메모리 내 캐싱은 종속성 주입을 사용 하 여 앱에서 참조 되는 서비스 입니다.In-memory caching is a service that's referenced from your app using Dependency Injection. AddMemoryCache에서 호출 ConfigureServices :Call AddMemoryCache in ConfigureServices:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMemoryCache();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvcWithDefaultRoute();
    }
}

IMemoryCache생성자에서 인스턴스를 요청 합니다.Request the IMemoryCache instance in the constructor:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

IMemoryCacheMicrosoft.AspNetCore.App 메타 패키지에서 사용할 수 있는 NuGet 패키지를설치 해야 합니다.IMemoryCache requires NuGet package Microsoft.Extensions.Caching.Memory, which is available in the Microsoft.AspNetCore.App metapackage.

다음 코드에서는 TryGetValue 를 사용 하 여 시간이 캐시에 있는지 확인 합니다.The following code uses TryGetValue to check if a time is in the cache. 시간이 캐시 되지 않으면 새 항목이 만들어지고 Set을 사용 하 여 캐시에 추가 됩니다.If a time isn't cached, a new entry is created and added to the cache with Set.

public static class CacheKeys
{
    public static string Entry { get { return "_Entry"; } }
    public static string CallbackEntry { get { return "_Callback"; } }
    public static string CallbackMessage { get { return "_CallbackMessage"; } }
    public static string Parent { get { return "_Parent"; } }
    public static string Child { get { return "_Child"; } }
    public static string DependentMessage { get { return "_DependentMessage"; } }
    public static string DependentCTS { get { return "_DependentCTS"; } }
    public static string Ticks { get { return "_Ticks"; } }
    public static string CancelMsg { get { return "_CancelMsg"; } }
    public static string CancelTokenSource { get { return "_CancelTokenSource"; } }
}
public IActionResult CacheTryGetValueSet()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Set cache options.
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Save data in cache.
        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}

현재 시간과 캐시 된 시간이 표시 됩니다.The current time and the cached time are displayed:

@model DateTime?

<div>
    <h2>Actions</h2>
    <ul>
        <li><a asp-controller="Home" asp-action="CacheTryGetValueSet">TryGetValue and Set</a></li>
        <li><a asp-controller="Home" asp-action="CacheGet">Get</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreate">GetOrCreate</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAsync">GetOrCreateAsync</a></li>
        <li><a asp-controller="Home" asp-action="CacheRemove">Remove</a></li>
    </ul>
</div>

<h3>Current Time: @DateTime.Now.TimeOfDay.ToString()</h3>
<h3>Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())</h3>

DateTime제한 시간 내에 요청이 있는 동안 캐시 된 값은 캐시에 남아 있습니다.The cached DateTime value remains in the cache while there are requests within the timeout period. 다음 이미지는 현재 시간과 캐시에서 검색 된 이전 시간을 보여 줍니다.The following image shows the current time and an older time retrieved from the cache:

서로 다른 두 시간이 표시 된 인덱스 뷰

다음 코드는 GetorcreateGetorcreateasync 를 사용 하 여 데이터를 캐시 합니다.The following code uses GetOrCreate and GetOrCreateAsync to cache data.

public IActionResult CacheGetOrCreate()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

public async Task<IActionResult> CacheGetOrCreateAsync()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return Task.FromResult(DateTime.Now);
    });

    return View("Cache", cacheEntry);
}

다음 코드는 Get 을 호출 하 여 캐시 된 시간을 인출 합니다.The following code calls Get to fetch the cached time:

public IActionResult CacheGet()
{
    var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
    return View("Cache", cacheEntry);
}

GetOrCreate , GetOrCreateAsyncGet 은의 기능을 확장 하는 cacheextensions 클래스의 일부인 확장 메서드입니다 IMemoryCache .GetOrCreate , GetOrCreateAsync, and Get are extension methods part of the CacheExtensions class that extends the capability of IMemoryCache. 다른 캐시 메서드에 대 한 설명은 IMemoryCache 메서드cacheextensions 메서드 를 참조 하세요.See IMemoryCache methods and CacheExtensions methods for a description of other cache methods.

MemoryCacheEntryOptionsMemoryCacheEntryOptions

다음 샘플입니다.The following sample:

  • 슬라이딩 만료 시간을 설정 합니다.Sets a sliding expiration time. 이 캐시 된 항목에 액세스 하는 요청은 슬라이딩 만료 시간을 다시 설정 합니다.Requests that access this cached item will reset the sliding expiration clock.
  • 캐시 우선 순위를로 설정 합니다 CacheItemPriority.NeverRemove .Sets the cache priority to CacheItemPriority.NeverRemove.
  • 항목이 캐시에서 제거 된 후에 호출 되는 PostEvictionDelegate 을 설정 합니다.Sets a PostEvictionDelegate that will be called after the entry is evicted from the cache. 콜백은 캐시에서 항목을 제거 하는 코드와 다른 스레드에서 실행 됩니다.The callback is run on a different thread from the code that removes the item from the cache.
public IActionResult CreateCallbackEntry()
{
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        // Pin to cache.
        .SetPriority(CacheItemPriority.NeverRemove)
        // Add eviction callback
        .RegisterPostEvictionCallback(callback: EvictionCallback, state: this);

    _cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);

    return RedirectToAction("GetCallbackEntry");
}

public IActionResult GetCallbackEntry()
{
    return View("Callback", new CallbackViewModel
    {
        CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
        Message = _cache.Get<string>(CacheKeys.CallbackMessage)
    });
}

public IActionResult RemoveCallbackEntry()
{
    _cache.Remove(CacheKeys.CallbackEntry);
    return RedirectToAction("GetCallbackEntry");
}

private static void EvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

SetSize, Size 및 SizeLimit를 사용 하 여 캐시 크기 제한Use SetSize, Size, and SizeLimit to limit cache size

MemoryCache인스턴스는 필요에 따라 크기 제한을 지정 하 고 적용할 수 있습니다.A MemoryCache instance may optionally specify and enforce a size limit. 캐시 크기 제한에는 항목의 크기를 측정 하는 메커니즘이 없기 때문에 정의 된 측정 단위가 없습니다.The cache size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. 캐시 크기 제한이 설정 된 경우 모든 항목의 크기를 지정 해야 합니다.If the cache size limit is set, all entries must specify size. ASP.NET Core 런타임은 메모리 압력에 따라 캐시 크기를 제한 하지 않습니다.The ASP.NET Core runtime does not limit cache size based on memory pressure. 캐시 크기를 제한 하는 것은 개발자에 게 있습니다.It's up to the developer to limit cache size. 지정 된 크기는 개발자가 선택 하는 단위입니다.The size specified is in units the developer chooses.

예:For example:

  • 웹 앱이 주로 문자열을 캐싱하는 경우 각 캐시 엔트리 크기는 문자열 길이가 될 수 있습니다.If the web app was primarily caching strings, each cache entry size could be the string length.
  • 앱은 모든 항목의 크기를 1로 지정 하 고, 크기 제한은 항목 수를 지정 합니다.The app could specify the size of all entries as 1, and the size limit is the count of entries.

SizeLimit가 설정 되지 않은 경우 캐시는 바인딩되지 않고 증가 합니다.If SizeLimit is not set, the cache grows without bound. 시스템 메모리가 부족할 때 ASP.NET Core 런타임은 캐시를 자르지 않습니다.The ASP.NET Core runtime does not trim the cache when system memory is low. 앱은 다음에 맞게 설계 되었습니다.Apps much be architected to:

  • 캐시 증가를 제한 합니다.Limit cache growth.
  • Compact또는 Remove 사용 가능한 메모리가 제한 된 경우를 호출 합니다.Call Compact or Remove when available memory is limited:

다음 코드는 MemoryCache 종속성 주입을 통해 액세스할 수 있는 고정 크기를 만듭니다.The following code creates a unitless fixed size MemoryCache accessible by dependency injection:

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache 
{
    public MemoryCache Cache { get; set; }
    public MyMemoryCache()
    {
        Cache = new MemoryCache(new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
    }
}

SizeLimit 에 단위가 없습니다.SizeLimit does not have units. 캐시 된 항목은 캐시 크기 한도가 설정 된 경우 가장 적합 한 모든 단위에서 크기를 지정 해야 합니다.Cached entries must specify size in whatever units they deem most appropriate if the cache size limit has been set. 캐시 인스턴스의 모든 사용자는 동일한 단위 시스템을 사용 해야 합니다.All users of a cache instance should use the same unit system. 캐시 된 항목 크기의 합계가에 지정 된 값을 초과 하는 경우 항목이 캐시 되지 않습니다 SizeLimit .An entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. 캐시 크기 제한을 설정 하지 않으면 항목에 설정 된 캐시 크기가 무시 됩니다.If no cache size limit is set, the cache size set on the entry will be ignored.

다음 코드는 MyMemoryCache 종속성 주입 컨테이너에 등록 됩니다.The following code registers MyMemoryCache with the dependency injection container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache 는이 크기 제한 된 캐시를 인식 하는 구성 요소에 대 한 독립 메모리 캐시로 만들어지며 캐시 엔트리 크기를 적절 하 게 설정 하는 방법을 알고 있습니다.MyMemoryCache is created as an independent memory cache for components that are aware of this size limited cache and know how to set cache entry size appropriately.

다음 코드에서는를 사용 합니다 MyMemoryCache .The following code uses MyMemoryCache:

public class AboutModel : PageModel
{
    private MemoryCache _cache;
    public static readonly string MyKey = "_MyKey";

    public AboutModel(MyMemoryCache memoryCache)
    {
        _cache = memoryCache.Cache;
    }

    [TempData]
    public string DateTime_Now { get; set; }

    public IActionResult OnGet()
    {
        if (!_cache.TryGetValue(MyKey, out string cacheEntry))
        {
            // Key not in cache, so get data.
            cacheEntry = DateTime.Now.TimeOfDay.ToString();

            var cacheEntryOptions = new MemoryCacheEntryOptions() 
                // Set cache entry size by extension method.
                .SetSize(1) 
                // Keep in cache for this time, reset time if accessed.
                .SetSlidingExpiration(TimeSpan.FromSeconds(3));

            // Set cache entry size via property.
            // cacheEntryOptions.Size = 1;

            // Save data in cache.
            _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
        }

        DateTime_Now = cacheEntry;

        return RedirectToPage("./Index");
    }
}

캐시 엔트리의 크기는 크기 또는 SetSize 확장 메서드를 통해 설정할 수 있습니다.The size of the cache entry can be set by Size or the SetSize extension method:

public IActionResult OnGet()
{
    if (!_cache.TryGetValue(MyKey, out string cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now.TimeOfDay.ToString();

        var cacheEntryOptions = new MemoryCacheEntryOptions() 
            // Set cache entry size by extension method.
            .SetSize(1) 
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Set cache entry size via property.
        // cacheEntryOptions.Size = 1;

        // Save data in cache.
        _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
    }

    DateTime_Now = cacheEntry;

    return RedirectToPage("./Index");
}

MemoryCache. CompactMemoryCache.Compact

MemoryCache.Compact 다음 순서에 따라 캐시의 지정 된 비율을 제거 하려고 시도 합니다.MemoryCache.Compact attempts to remove the specified percentage of the cache in the following order:

  • 만료 된 모든 항목입니다.All expired items.
  • 우선 순위별로 항목Items by priority. 우선 순위가 가장 낮은 항목이 먼저 제거 됩니다.Lowest priority items are removed first.
  • 가장 최근에 사용한 개체입니다.Least recently used objects.
  • 가장 빠른 절대 만료를 가진 항목입니다.Items with the earliest absolute expiration.
  • 가장 빠른 슬라이딩 만료를 포함 하는 항목입니다.Items with the earliest sliding expiration.

우선 순위가 있는 고정 된 항목 NeverRemove 은 제거 되지 않습니다.Pinned items with priority NeverRemove are never removed.

_cache.Remove(MyKey);

// Remove 33% of cached items.
_cache.Compact(.33);   
cache_size = _cache.Count;

자세한 내용은 GitHub의 Compact source 를 참조 하세요.See Compact source on GitHub for more information.

캐시 종속성Cache dependencies

다음 샘플에서는 종속 항목이 만료 된 경우 캐시 항목을 만료 하는 방법을 보여 줍니다.The following sample shows how to expire a cache entry if a dependent entry expires. CancellationChangeToken이 캐시 된 항목에 추가 됩니다.A CancellationChangeToken is added to the cached item. Cancel에 대해를 호출 하면 CancellationTokenSource 두 캐시 항목이 모두 제거 됩니다.When Cancel is called on the CancellationTokenSource, both cache entries are evicted.

public IActionResult CreateDependentEntries()
{
    var cts = new CancellationTokenSource();
    _cache.Set(CacheKeys.DependentCTS, cts);

    using (var entry = _cache.CreateEntry(CacheKeys.Parent))
    {
        // expire this entry if the dependant entry expires.
        entry.Value = DateTime.Now;
        entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);

        _cache.Set(CacheKeys.Child,
            DateTime.Now,
            new CancellationChangeToken(cts.Token));
    }

    return RedirectToAction("GetDependentEntries");
}

public IActionResult GetDependentEntries()
{
    return View("Dependent", new DependentViewModel
    {
        ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
        ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
        Message = _cache.Get<string>(CacheKeys.DependentMessage)
    });
}

public IActionResult RemoveChildEntry()
{
    _cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
    return RedirectToAction("GetDependentEntries");
}

private static void DependentEvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Parent entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}

를 사용 CancellationTokenSource 하면 여러 캐시 항목을 하나의 그룹으로 제거할 수 있습니다.Using a CancellationTokenSource allows multiple cache entries to be evicted as a group. using위의 코드에서 패턴을 사용 하 여 블록 내에 만들어진 캐시 항목 using 은 트리거와 만료 설정을 상속 합니다.With the using pattern in the code above, cache entries created inside the using block will inherit triggers and expiration settings.

추가적인 참고 사항Additional notes

  • 콜백을 사용 하 여 캐시 항목을 다시 채우는 경우:When using a callback to repopulate a cache item:

    • 콜백이 완료 되지 않아 여러 요청에서 캐시 된 키 값이 비어 있는 것을 찾을 수 있습니다.Multiple requests can find the cached key value empty because the callback hasn't completed.
    • 이로 인해 여러 스레드가 캐시 된 항목을 늘이기 수 있습니다.This can result in several threads repopulating the cached item.
  • 한 캐시 엔트리를 사용 하 여 다른 캐시 엔트리를 만드는 경우 자식는 부모 항목의 만료 토큰 및 시간 기반 만료 설정을 복사 합니다.When one cache entry is used to create another, the child copies the parent entry's expiration tokens and time-based expiration settings. 자식은 수동으로 제거 하거나 부모 항목을 업데이트 하 여 만료 되지 않습니다.The child isn't expired by manual removal or updating of the parent entry.

  • PostEvictionCallbacks 를 사용 하 여 캐시 엔트리를 캐시에서 제거한 후에 발생 하는 콜백을 설정 합니다.Use PostEvictionCallbacks to set the callbacks that will be fired after the cache entry is evicted from the cache.

백그라운드 캐시 업데이트Background cache update

과 같은 백그라운드 서비스 를 사용 IHostedService 하 여 캐시를 업데이트 합니다.Use a background service such as IHostedService to update the cache. 백그라운드 서비스는 항목을 다시 계산 하 고 준비 된 경우에만 캐시에 할당할 수 있습니다.The background service can recompute the entries and then assign them to the cache only when they’re ready.

추가 리소스Additional resources