
GraphRAG:基于PolarDB+通義千問api+LangChain的知識圖譜定制實踐
請注意,這種特定的機制被稱為該高速緩存旁路模式,它通常用于讀重應用程序。
在這篇文章中,我們將詳細討論內存緩存
。
在ASP.NET Core中,內存緩存允許您將數據存儲在應用程序的內存中。這意味著數據被緩存在服務器的實例上,這通過減少重復獲取相同數據的需求來顯著提高應用程序的性能。內存緩存是在應用程序中實現緩存的最簡單、最有效的方法之一。
在ASP.NETCore中設置緩存非常簡單。只需幾行代碼,就可以顯著提高應用程序的響應能力,通常提高50-75%或更多。
讓我們創建一個新的ASP.NET Core 8 Web API。我將使用Visual Studio 2022社區版進行此演示
要啟用內存中緩存,首先需要在應用程序的服務容器中注冊緩存服務。然后我們將看到如何使用依賴注入。
導航到Program.cs
文件,并添加以下內容。
builder.Services.AddMemoryCache();
這將向應用程序的IServiceCollection
添加一個非分布式的內存中實現。
就是這樣。您的應用程序現在支持內存緩存托管。現在,為了更好地理解緩存是如何工作的,我們將創建幾個CRUD端點。
我將使用EF核心代碼優先方法,并與本地計算機上托管的PostgreSQL數據庫進行交互。
讓我們先安裝所需的EF Core包。
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Npgsql.EntityFrameworkCore.PostgreSQL
如前所述,我們將構建一個ASP.NET Core 8 Web API,它具有端點,
像往常一樣,我們將有一個相當簡單的產品模型。
public class Product
{
public Guid Id { get; set; }
public string Name { get; set; } = default!;
public string Description { get; set; } = default!;
public decimal Price { get; set; }
// Parameterless constructor for EF Core
private Product() { }
public Product(string name, string description, decimal price)
{
Id = Guid.NewGuid();
Name = name;
Description = description;
Price = price;
}
}
我還添加了一個ProductCreationDTO,我們將在ProductService
類中使用它。
public record ProductCreationDto(string Name, string Description, decimal Price);
我不打算一步一步地介紹如何設置DB Context類以及如何生成和應用遷移。您可以在本文末尾找到完整的源代碼。
但是,下面是我用來連接到本地PGSQL實例的連接字符串。
"ConnectionStrings": {"dotnetSeries": "Host=localhost;Database=dotnetSeries;Username=postgres;Password=admin;Include Error Detail=true"}
我還使用MockarooWeb應用程序為產品生成了1000條假記錄。我將此數據生成為SQL插入腳本,并在遷移的數據庫中運行它。因此,我們的“產品”表中有1,000條樣本記錄可用。SQL腳本包含在解決方案的SQL文件
夾中,以防您也需要它。
這是產品服務類,它為我們做了所有繁重的工作。讓我們一個方法一個方法地檢查代碼。
請注意,我已經將AppDbContext
、IMemoryCache
和ILogger<ProductService>
實例注入到產品服務類的主構造函數中,以便我們可以在整個類中訪問它們。
public class ProductService(AppDbContext context, IMemoryCache cache, ILogger<ProductService> logger) : IProductService
{
public async Task Add(ProductCreationDto request)
{
var product = new Product(request.Name, request.Description, request.Price);
await context.Products.AddAsync(product);
await context.SaveChangesAsync();
// invalidate cache for products, as new product is added
var cacheKey = "products";
logger.LogInformation("invalidating cache for key: {CacheKey} from cache.", cacheKey);
cache.Remove(cacheKey);
}
public async Task<Product> Get(Guid id)
{
var cacheKey = $"product:{id}";
logger.LogInformation("fetching data for key: {CacheKey} from cache.", cacheKey);
if (!cache.TryGetValue(cacheKey, out Product? product))
{
logger.LogInformation("cache miss. fetching data for key: {CacheKey} from database.", cacheKey);
product = await context.Products.FindAsync(id);
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(30))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(300))
.SetPriority(CacheItemPriority.Normal);
logger.LogInformation("setting data for key: {CacheKey} to cache.", cacheKey);
cache.Set(cacheKey, product, cacheOptions);
}
else
{
logger.LogInformation("cache hit for key: {CacheKey}.", cacheKey);
}
return product;
}
public async Task<List<Product>> GetAll()
{
var cacheKey = "products";
logger.LogInformation("fetching data for key: {CacheKey} from cache.", cacheKey);
if (!cache.TryGetValue(cacheKey, out List<Product>? products))
{
logger.LogInformation("cache miss. fetching data for key: {CacheKey} from database.", cacheKey);
products = await context.Products.ToListAsync();
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(30))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(300))
.SetPriority(CacheItemPriority.NeverRemove)
.SetSize(2048);
logger.LogInformation("setting data for key: {CacheKey} to cache.", cacheKey);
cache.Set(cacheKey, products, cacheOptions);
}
else
{
logger.LogInformation("cache hit for key: {CacheKey}.", cacheKey);
}
return products;
}
}
從第36行到第57行,我們有一個從數據庫/緩存返回所有產品列表的方法。
我們首先設置一個緩存鍵,在當前情況下是products
。此鍵將用作標識符,用于將數據存儲在該高速緩存中。正如你所知道的,緩存在技術上是存儲在數據結構中的鍵值對,就像字典一樣。
接下來,在第40行中,我們首先嘗試檢查該高速緩存中是否有針對關鍵產品的
可用數據。如果存在某些數據,則將其提取到List<Product>
中。否則,控件進入if語句,我們將從數據庫中獲取數據。請注意,只有當緩存未命中時才會出現這種情況,或者換句話說,只有當數據不存在于我們的緩存存儲中時才會出現這種情況。
一旦我們從數據庫獲得響應,我們就根據該高速緩存鍵將該數據設置到我們的緩存存儲中,并將其返回給客戶機。
Get(Guid id)
方法的實現也幾乎類似,我們在其中傳遞Product ID。然而,主要區別是,這一次,我們有一個更動態的緩存,即product:{productId}
,基于請求的Product ID。
配置該高速緩存選項允許您根據需要自定義緩存行為。
var cacheOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(20)).SetSlidingExpiration(TimeSpan.FromMinutes(2)).SetPriority(CacheItemPriority.NeverRemove);
MemoryCacheEntryOptions
-這個類用于定義相關緩存技術的關鍵屬性。我們將創建這個類的一個實例,并將其傳遞給IMemoryCache
實例。但在此之前,讓我們了解MemoryCacheEntryOptions
的屬性。
在ASP.NET Core中使用緩存時,可以配置幾個設置來優化性能和資源管理。以下是一些關鍵設置及其用途:
SetPriority()
)正常
(默認)高
低
NeverRemove
SetSize()
)SetSlidingList()
)cacheEntry.SetSlidingExpiration(TimeSpan.FromMinutes(2));
SetAbsoluteArray()
)cacheEntry.SetAbsoluteExpiration(TimeSpan.FromMinutes(20));
關于ASP.NET Core中的內存緩存,在構建應用程序時,必須仔細考慮Priority、Size、Sliding Size和Absolute Size屬性。
緩存并不像聽起來那么簡單。雖然它可以通過減少重復獲取數據的需要來顯著提高應用程序性能,但有效管理該高速緩存涉及確保緩存的數據保持準確和最新。這就是緩存失效發揮作用的地方。
緩存失效是至關重要的,因為它可以確保陳舊或過期的數據不會在該高速緩存中持久存在,從而導致應用程序中的不一致和潛在的錯誤行為。
這里有一個場景。如果用戶創建了一個新產品,然后試圖獲取所有產品的列表,該怎么辦?我們的應用程序很可能會提供產品創建之前的陳舊數據。當基礎數據發生更改時,必須更新該高速緩存以反映這些更改。
對于我們的演示,我們將使用手動緩存無效。每當添加新產品時,我們調用cache.Remove(cacheKey);
確保產品列表從該高速緩存中刪除。
您可以通過更新該高速緩存來進一步增強此機制,而不是完全清除它。例如,當創建新產品時,您可以從該高速緩存中獲取產品列表,將新創建的產品追加到列表中,并再次將更新后的列表重新添加到該高速緩存內存中。這可能會增加往返于該高速緩存內存的次數,但根據您的系統設計,這也可能是高效的。
您還可以將整個緩存失效過程作為后臺任務運行,這樣應用程序就不必等待該高速緩存調用完成才發送響應
現在我們已經實現了所需的服務方法,讓我們將產品服務連接到.NET 8 Web API中的實際API端點。打開Program.cs
,并添加以下最小API端點。
app.MapGet("/products", async (IProductService service) =>
{
var products = await service.GetAll();
return Results.Ok(products);
});
app.MapGet("/products/{id:guid}", async (Guid id, IProductService service) =>
{
var product = await service.Get(id);
return Results.Ok(product);
});
app.MapPost("/products", async (ProductCreationDto product, IProductService service) =>
{
await service.Add(product);
return Results.Created();
});
您可能還必須將IProductService
服務注冊到具有Transient作用域的依賴注入(DI)容器中。
builder.Services.AddTransient<IProductService, ProductService>();
讓我們構建并運行應用程序。我將使用Postman來測試我們的實現。
首先,讓我們訪問GetAll
端點。由于該高速緩存中沒有數據,因此我們希望從數據庫中獲取數據。
如您所見,返回1000個產品的列表的響應時間接近800 ms。理想情況下,產品列表應該被緩存,如果我們再次訪問端點,我們希望從該高速緩存中獲取它。
我們再來一次。
現在,響應時間大大降低,降至40毫秒以下!
日志和我們預期的一樣。
下面是實現緩存時需要考慮的一些要點。
此外,作為一項改進,您可以使用后臺作業定期更新該高速緩存。如果“絕對緩存”設置為5分鐘,則可以每6分鐘運行一次重復作業,以將該高速緩存條目更新為其最新版本。您可以使用Hangfire在ASP.NET核心應用程序中實現相同的功能。
讓我們在這里結束這篇文章。
在下一篇文章中,我們將討論更高級的緩存概念,如分布式緩存,設置Redis,Redis緩存,PostBack調用等等。
在這篇詳細的文章中,我們探討了緩存的各個方面,包括基礎知識、內存中緩存以及如何在ASP.NET Core中實現內存中緩存。
文章轉自微信公眾號@科控物聯