ASP.NET Core 분산 캐싱Distributed caching in ASP.NET Core

By Luke Latham 문자Steve SmithBy Luke Latham and Steve Smith

분산 캐시는 여러 앱 서버에서 공유 하는 캐시로, 일반적으로 액세스 하는 앱 서버에 외부 서비스로 유지 관리 됩니다.A distributed cache is a cache shared by multiple app servers, typically maintained as an external service to the app servers that access it. 분산 캐시는 특히 앱이 클라우드 서비스 또는 서버 팜에서 호스트 되는 경우 ASP.NET Core 앱의 성능과 확장성을 향상 시킬 수 있습니다.A distributed cache can improve the performance and scalability of an ASP.NET Core app, especially when the app is hosted by a cloud service or a server farm.

분산 캐시는 개별 앱 서버에 캐시 된 데이터가 저장 되는 다른 캐싱 시나리오에 비해 여러 가지 이점을 제공 합니다.A distributed cache has several advantages over other caching scenarios where cached data is stored on individual app servers.

캐시 된 데이터를 배포 하는 경우 데이터는 다음과 같습니다.When cached data is distributed, the data:

  • 는 여러 서버에 대 한 여러 요청 에서 일관 되 고 일관 됩니다.Is coherent (consistent) across requests to multiple servers.
  • 서버를 다시 시작 하 고 앱을 배포 합니다.Survives server restarts and app deployments.
  • 로컬 메모리를 사용 하지 않습니다.Doesn't use local memory.

분산 캐시 구성은 구현 별로 다릅니다.Distributed cache configuration is implementation specific. 이 문서에서는 SQL Server 및 Redis 분산 캐시를 구성 하는 방법을 설명 합니다.This article describes how to configure SQL Server and Redis distributed caches. NCache (GitHub의 NCache)와 같은 타사 구현도 사용할 수 있습니다.Third party implementations are also available, such as NCache (NCache on GitHub). 선택한 구현과 관계 없이 앱은 인터페이스를 IDistributedCache 사용 하 여 캐시와 상호 작용 합니다.Regardless of which implementation is selected, the app interacts with the cache using the IDistributedCache interface.

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

필수 구성 요소Prerequisites

SQL Server 분산 캐시를 사용 하려면 패키지 참조를 Microsoft 확장명이 . i n t. i n t. i n t 패키지에 추가 합니다.To use a SQL Server distributed cache, add a package reference to the Microsoft.Extensions.Caching.SqlServer package.

Redis 분산 캐시를 사용 하려면 StackExchangeRedis 패키지에 패키지 참조를 추가 합니다.To use a Redis distributed cache, add a package reference to the Microsoft.Extensions.Caching.StackExchangeRedis package.

SQL Server 분산 캐시를 사용 하려면 AspNetCore 메타 패키지 를 참조 하거나 패키지 참조를 패키지에 추가 합니다.To use a SQL Server distributed cache, reference the Microsoft.AspNetCore.App metapackage or add a package reference to the Microsoft.Extensions.Caching.SqlServer package.

Redis 분산 캐시를 사용 하려면 AspNetCore 메타 패키지 를 참조 하 고 패키지 참조를 StackExchangeRedis 패키지에 추가 합니다.To use a Redis distributed cache, reference the Microsoft.AspNetCore.App metapackage and add a package reference to the Microsoft.Extensions.Caching.StackExchangeRedis package. Redis 패키지는 Microsoft.AspNetCore.App 패키지에 포함 되지 않으므로 프로젝트 파일에서 개별적으로 Redis 패키지를 참조 해야 합니다.The Redis package isn't included in the Microsoft.AspNetCore.App package, so you must reference the Redis package separately in your project file.

SQL Server 분산 캐시를 사용 하려면 AspNetCore 메타 패키지 를 참조 하거나 패키지 참조를 패키지에 추가 합니다.To use a SQL Server distributed cache, reference the Microsoft.AspNetCore.App metapackage or add a package reference to the Microsoft.Extensions.Caching.SqlServer package.

Redis 분산 캐시를 사용 하려면 AspNetCore 메타 패키지 를 참조 하 고 패키지 참조를 Redis 패키지에 추가 합니다.To use a Redis distributed cache, reference the Microsoft.AspNetCore.App metapackage and add a package reference to the Microsoft.Extensions.Caching.Redis package. Redis 패키지는 Microsoft.AspNetCore.App 패키지에 포함 되지 않으므로 프로젝트 파일에서 개별적으로 Redis 패키지를 참조 해야 합니다.The Redis package isn't included in the Microsoft.AspNetCore.App package, so you must reference the Redis package separately in your project file.

IDistributedCache 인터페이스IDistributedCache interface

인터페이스 IDistributedCache 는 분산 캐시 구현에서 항목을 조작 하는 다음과 같은 메서드를 제공 합니다.The IDistributedCache interface provides the following methods to manipulate items in the distributed cache implementation:

  • Get는 문자열 키를 byte[] 수락 하 고 캐시 된 항목을 캐시에 있는 경우 배열로 검색 합니다. – GetAsyncGet, GetAsync – Accepts a string key and retrieves a cached item as a byte[] array if found in the cache.
  • Set는 문자열 키를 사용 하 byte[] 여 캐시에 항목 (배열)을 추가합니다.– SetAsyncSet, SetAsync – Adds an item (as byte[] array) to the cache using a string key.
  • Refresh는 해당 키에 따라 캐시에서 항목을 새로고치고해당하는슬라이딩만료시간제한을다시설정합니다(있는경우).– RefreshAsyncRefresh, RefreshAsync – Refreshes an item in the cache based on its key, resetting its sliding expiration timeout (if any).
  • Remove는 문자열 키에 따라 캐시 항목을 제거합니다.– RemoveAsyncRemove, RemoveAsync – Removes a cache item based on its string key.

분산 캐싱 서비스 설정Establish distributed caching services

IDistributedCache 에서Startup.ConfigureServices의 구현을 등록 합니다.Register an implementation of IDistributedCache in Startup.ConfigureServices. 이 항목에서 설명 하는 프레임 워크 제공 구현에는 다음이 포함 됩니다.Framework-provided implementations described in this topic include:

분산 메모리 캐시Distributed Memory Cache

분산 메모리 캐시 (AddDistributedMemoryCache)는 메모리에 항목을 저장 IDistributedCache 하는 프레임 워크에서 제공 하는 구현입니다.The Distributed Memory Cache (AddDistributedMemoryCache) is a framework-provided implementation of IDistributedCache that stores items in memory. 분산 메모리 캐시는 실제 분산 된 캐시가 아닙니다.The Distributed Memory Cache isn't an actual distributed cache. 캐시 된 항목은 앱이 실행 되 고 있는 서버의 앱 인스턴스에 의해 저장 됩니다.Cached items are stored by the app instance on the server where the app is running.

분산 메모리 캐시는 다음과 같은 유용한 구현입니다.The Distributed Memory Cache is a useful implementation:

  • 개발 및 테스트 시나리오에서In development and testing scenarios.
  • 프로덕션 환경에서 단일 서버를 사용 하는 경우 메모리 소비가 문제가 되지 않습니다.When a single server is used in production and memory consumption isn't an issue. 분산 메모리 캐시를 구현 하면 캐시 된 데이터 저장소를 추상화 합니다.Implementing the Distributed Memory Cache abstracts cached data storage. 이를 통해 여러 노드 또는 내결함성이 필요한 경우 나중에 진정한 분산 캐싱 솔루션을 구현할 수 있습니다.It allows for implementing a true distributed caching solution in the future if multiple nodes or fault tolerance become necessary.

샘플 앱은 앱이의 Startup.ConfigureServices개발 환경에서 실행 될 때 분산 메모리 캐시를 사용 합니다.The sample app makes use of the Distributed Memory Cache when the app is run in the Development environment in Startup.ConfigureServices:

services.AddDistributedMemoryCache();
services.AddDistributedMemoryCache();

분산 SQL Server 캐시Distributed SQL Server Cache

분산 SQL Server 캐시 구현 (AddDistributedSqlServerCache)을 사용 하면 분산 캐시에서 SQL Server 데이터베이스를 백업 저장소로 사용할 수 있습니다.The Distributed SQL Server Cache implementation (AddDistributedSqlServerCache) allows the distributed cache to use a SQL Server database as its backing store. SQL Server 인스턴스에서 캐시 된 SQL Server 항목 테이블을 만들려면 sql-cache 도구를 사용할 수 있습니다.To create a SQL Server cached item table in a SQL Server instance, you can use the sql-cache tool. 이 도구는 사용자가 지정한 이름 및 스키마를 사용 하 여 테이블을 만듭니다.The tool creates a table with the name and schema that you specify.

sql-cache create 명령을 실행 하 여 SQL Server에서 테이블을 만듭니다.Create a table in SQL Server by running the sql-cache create command. SQL Server 인스턴스 (Data Source), 데이터베이스 (Initial Catalog), dbo스키마 (예:) 및 테이블 이름 (예: TestCache)을 제공 합니다.Provide the SQL Server instance (Data Source), database (Initial Catalog), schema (for example, dbo), and table name (for example, TestCache):

dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache

도구가 성공 했음을 나타내는 메시지가 기록 됩니다.A message is logged to indicate that the tool was successful:

Table and index were created successfully.

sql-cache 도구에서 만든 테이블에는 다음 스키마가 있습니다.The table created by the sql-cache tool has the following schema:

SqlServer 캐시 테이블

참고

앱은가 IDistributedCache SqlServerCache아닌 인스턴스를 사용 하 여 캐시 값을 조작 해야 합니다.An app should manipulate cache values using an instance of IDistributedCache, not a SqlServerCache.

샘플 앱은 Startup.ConfigureServices다음과 SqlServerCache 같은 개발 이외의 환경에서을 구현 합니다.The sample app implements SqlServerCache in a non-Development environment in Startup.ConfigureServices:

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = 
        _config["DistCache_ConnectionString"];
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});
services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = 
        _config["DistCache_ConnectionString"];
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

참고

( ConnectionString 및 필요에 SchemaName 따라 및 TableName)는 일반적으로 소스 제어 외부에 저장 됩니다 (예: 암호 관리자 또는 appsettings/에서 저장 한 경우 ). 환경}. json 파일).A ConnectionString (and optionally, SchemaName and TableName) are typically stored outside of source control (for example, stored by the Secret Manager or in appsettings.json/appsettings.{ENVIRONMENT}.json files). 연결 문자열에는 원본 제어 시스템에서 유지 되어야 하는 자격 증명이 포함 될 수 있습니다.The connection string may contain credentials that should be kept out of source control systems.

분산 Redis CacheDistributed Redis Cache

Redis는 분산 캐시로 흔히 사용되는 오픈 소스 메모리 내 데이터 저장소입니다.Redis is an open source in-memory data store, which is often used as a distributed cache. Redis를 로컬로 사용할 수 있으며, Azure에서 호스트 되는 ASP.NET Core 앱에 대 한 Azure Redis Cache 를 구성할 수 있습니다.You can use Redis locally, and you can configure an Azure Redis Cache for an Azure-hosted ASP.NET Core app.

앱은 Startup.ConfigureServices다음과 같은 개발 이외의 환경에서 RedisCache 인스턴스 (AddStackExchangeRedisCache)를 사용 하 여 캐시 구현을 구성 합니다.An app configures the cache implementation using a RedisCache instance (AddStackExchangeRedisCache) in a non-Development environment in Startup.ConfigureServices:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost";
    options.InstanceName = "SampleInstance";
});

앱은 Startup.ConfigureServices다음과 같은 개발 이외의 환경에서 RedisCache 인스턴스 (AddStackExchangeRedisCache)를 사용 하 여 캐시 구현을 구성 합니다.An app configures the cache implementation using a RedisCache instance (AddStackExchangeRedisCache) in a non-Development environment in Startup.ConfigureServices:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost";
    options.InstanceName = "SampleInstance";
});

앱은 인스턴스 ( RedisCache AddDistributedRedisCache)를 사용 하 여 캐시 구현을 구성 합니다.An app configures the cache implementation using a RedisCache instance (AddDistributedRedisCache):

services.AddDistributedRedisCache(options =>
{
    options.Configuration = "localhost";
    options.InstanceName = "SampleInstance";
});

로컬 컴퓨터에 Redis를 설치 하려면 다음을 수행 합니다.To install Redis on your local machine:

분산 캐시 사용Use the distributed cache

IDistributedCache 인터페이스를 사용 하려면 앱의 모든 생성자에서 IDistributedCache 의 인스턴스를 요청 합니다.To use the IDistributedCache interface, request an instance of IDistributedCache from any constructor in the app. 인스턴스는 DI (종속성 주입)에 의해 제공 됩니다.The instance is provided by dependency injection (DI).

샘플 앱 IDistributedCache 이 시작 되 면가에 Startup.Configure삽입 됩니다.When the sample app starts, IDistributedCache is injected into Startup.Configure. 현재 시간은를 사용 하 여 IHostApplicationLifetime 캐시 됩니다. 자세한 내용은 제네릭 호스트를 참조 하십시오. IHostApplicationLifetime):The current time is cached using IHostApplicationLifetime (for more information, see Generic Host: IHostApplicationLifetime):

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
    IHostApplicationLifetime lifetime, IDistributedCache cache)
{
    lifetime.ApplicationStarted.Register(() =>
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
    });

샘플 앱 IDistributedCache 이 시작 되 면가에 Startup.Configure삽입 됩니다.When the sample app starts, IDistributedCache is injected into Startup.Configure. 현재 시간은를 사용 하 여 IApplicationLifetime 캐시 됩니다. 자세한 내용은 웹 호스트를 참조 하십시오. IApplicationLifetime 인터페이스):The current time is cached using IApplicationLifetime (for more information, see Web Host: IApplicationLifetime interface):

public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
    IApplicationLifetime lifetime, IDistributedCache cache)
{
    lifetime.ApplicationStarted.Register(() =>
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
    });

샘플 앱은 인덱스 IDistributedCache 페이지에서 IndexModel 사용 하기 위해를에 삽입 합니다.The sample app injects IDistributedCache into the IndexModel for use by the Index page.

인덱스 페이지가 로드 될 때마다에서 OnGetAsync캐시 된 시간에 대 한 캐시를 확인 합니다.Each time the Index page is loaded, the cache is checked for the cached time in OnGetAsync. 캐시 된 시간이 만료 되지 않은 경우에는 시간이 표시 됩니다.If the cached time hasn't expired, the time is displayed. 캐시 된 시간에 마지막으로 액세스 한 후 20 초가 경과한 경우 (이 페이지가 마지막으로 로드 된 시간) 페이지에는 캐시 된 시간 만료가 표시 됩니다.If 20 seconds have elapsed since the last time the cached time was accessed (the last time this page was loaded), the page displays Cached Time Expired.

캐시 된 시간 다시 설정 단추를 선택 하 여 캐시 된 시간을 현재 시간으로 즉시 업데이트 합니다.Immediately update the cached time to the current time by selecting the Reset Cached Time button. 단추는 처리기 메서드 OnPostResetCachedTime 를 트리거합니다.The button triggers the OnPostResetCachedTime handler method.

public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string CachedTimeUTC { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}
public class IndexModel : PageModel
{
    private readonly IDistributedCache _cache;

    public IndexModel(IDistributedCache cache)
    {
        _cache = cache;
    }

    public string CachedTimeUTC { get; set; }

    public async Task OnGetAsync()
    {
        CachedTimeUTC = "Cached Time Expired";
        var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");

        if (encodedCachedTimeUTC != null)
        {
            CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
        }
    }

    public async Task<IActionResult> OnPostResetCachedTime()
    {
        var currentTimeUTC = DateTime.UtcNow.ToString();
        byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
        var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
        await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);

        return RedirectToPage();
    }
}

참고

IDistributedCache 인스턴스를 Singleton이나 Scoped 수명으로 사용할 필요는 없습니다(적어도 기본 구현일 경우).There's no need to use a Singleton or Scoped lifetime for IDistributedCache instances (at least for the built-in implementations).

DI를 사용 하는 IDistributedCache 대신 인스턴스를 만들 수도 있지만, 코드에서 인스턴스를 만들면 코드를 테스트 하는 것이 어렵고 명시적 종속성 원칙을 위반 하 게 될 수 있습니다.You can also create an IDistributedCache instance wherever you might need one instead of using DI, but creating an instance in code can make your code harder to test and violates the Explicit Dependencies Principle.

권장 사항Recommendations

앱에 가장 적합 한 IDistributedCache 구현을 결정할 때 다음 사항을 고려 합니다.When deciding which implementation of IDistributedCache is best for your app, consider the following:

  • 기존 인프라Existing infrastructure
  • 성능 요구 사항Performance requirements
  • 비용Cost
  • 팀 환경Team experience

캐싱 솔루션은 일반적으로 메모리 내 저장소를 사용 하 여 캐시 된 데이터를 신속 하 게 검색 하지만 메모리는 제한 된 리소스 이며 확장 하는 데 비용이 많이 듭니다.Caching solutions usually rely on in-memory storage to provide fast retrieval of cached data, but memory is a limited resource and costly to expand. 일반적으로 사용 되는 데이터를 캐시에만 저장 합니다.Only store commonly used data in a cache.

일반적으로 Redis cache는 SQL Server 캐시 보다 높은 처리량과 짧은 대기 시간을 제공 합니다.Generally, a Redis cache provides higher throughput and lower latency than a SQL Server cache. 그러나 일반적으로 벤치마킹은 캐싱 전략의 성능 특성을 결정 하는 데 필요 합니다.However, benchmarking is usually required to determine the performance characteristics of caching strategies.

SQL Server를 분산 캐시 백업 저장소로 사용 하는 경우 캐시에 대해 동일한 데이터베이스를 사용 하 고 앱의 일반 데이터 저장 및 검색을 사용 하면 두 성능에 부정적인 영향을 줄 수 있습니다.When SQL Server is used as a distributed cache backing store, use of the same database for the cache and the app's ordinary data storage and retrieval can negatively impact the performance of both. 분산 캐시 백업 저장소에 전용 SQL Server 인스턴스를 사용 하는 것이 좋습니다.We recommend using a dedicated SQL Server instance for the distributed cache backing store.

추가 자료Additional resources