Trong chu trình phát triển phần mềm, người ta thường tập trung vào hiệu suất của ứng dụng. Có nhiều cách để cải thiện hiệu suất và một trong những mẫu được sử dụng phổ biến nhất để cải thiện hiệu suất trong các ứng dụng đám mây hiện đại là mẫu dành riêng cho bộ đệm. Trong bài đăng này, tôi sẽ mô tả ngắn gọn về mẫu cache-aside và cách triển khai nó bằng ASP.NET Core.
Giới thiệu
Mẫu này khá đơn giản và mục đích duy nhất của nó là tải dữ liệu theo yêu cầu vào bộ đệm từ nguồn dữ liệu. Điều này giúp duy trì tính nhất quán giữa dữ liệu trong bộ đệm và nguồn dữ liệu cơ bản của nó.
Sau đây là các đặc điểm của mẫu.
- Khi ứng dụng cần dữ liệu, đầu tiên ứng dụng sẽ xem xét bộ đệm.
- Trong trường hợp dữ liệu có trong bộ đệm thì ứng dụng sẽ sử dụng dữ liệu từ bộ đệm.
- Nếu không, dữ liệu sẽ được truy xuất từ nguồn dữ liệu.
Dưới đây là sơ đồ minh họa

Đối tượng bộ đệm phải bị vô hiệu hóa khi ứng dụng thay đổi giá trị.

Thứ tự vô hiệu hóa bộ đệm là quan trọng. Cập nhật nguồn dữ liệu trước khi xóa mục khỏi bộ đệm. Trong trường hợp bạn xóa mục đó khỏi bộ đệm trước, có khả năng khách hàng có thể tìm nạp mục đó trước khi kho dữ liệu được cập nhật. Điều đó sẽ dẫn đến sự không thống nhất về dữ liệu giữa kho lưu trữ dữ liệu và bộ nhớ đệm.
Khi nào nên sử dụng mẫu này?
- Mẫu này cho phép chúng tôi tải dữ liệu theo yêu cầu và có thể được sử dụng khi nhu cầu tài nguyên không thể đoán trước được
- Bộ đệm không cung cấp thao tác đọc qua và ghi qua.
Lưu ý
- Đọc qua: Đó là bộ đệm nằm phù hợp với cơ sở dữ liệu và trong trường hợp thiếu bộ đệm, nó có thể tải dữ liệu từ cơ sở dữ liệu và điền vào bộ đệm.
- Viết qua: Bộ đệm nằm song song với cơ sở dữ liệu và dữ liệu luôn đi qua bộ đệm để đến cơ sở dữ liệu chính.
Tạo tài nguyên Azure
Như minh họa ở trên, chúng ta cần cơ sở dữ liệu (Azure SQL Server) và Cache (Azure Redis Cache). Bạn có thể chọn cơ sở dữ liệu và bộ nhớ đệm một cách thuận tiện.
$resourceGroup="<Resource Group>"
$location="<location>"
$redisCacheName="<Redis cache name>"
$sqlServerName="<Azure SQL Server Name>"
$sqlDBName="<Azure SQL DB Name>"
$adminName="<admin name of SQL server>"
$adminPassword="<admin password of SQL Server>"
# Creating a resource group
az group create --name $resourceGroup --location $location
# Create Redis Cache with SKU as Basic
az redis create --name $redisCacheName --resource-group $resourceGroup --location $location --sku Basic --vm-size c0
# Create SQL Server
az sql server create -l $location -g $resourceGroup -n $sqlServerName -u $adminName -p $adminPassword
# Create SQL database with SKU as Basic
az sql db create -g $resourceGroup -s $sqlServerName -n $sqlDBName --service-objective Basic
Triển khai
Hãy bắt đầu với việc triển khai bằng cách tạo dự án API ASP.NET Core Web và thêm các gói Nuget cần thiết cho bộ đệm Redis và Entity Framework Core.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
Đầu tiên, hãy tạo một lớp mô hình quốc gia.
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
Bây giờ, hãy đăng ký các phần phụ thuộc của EF Core và Redis cache trong phương thức configureServices của lớp Startup.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<CountryContext>(optionsAction =>
optionsAction.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddStackExchangeRedisCache(setupAction =>
{
setupAction.Configuration = Configuration.GetConnectionString("RedisConnectionString");
});
}
Bây giờ hãy sửa đổi tệp appsinstall.json để chứa các chuỗi kết nối của Redis Cache và SQL Database.
"ConnectionStrings": {
"RedisConnectionString": "<Redis Cache ConnectionString>",
"DefaultConnection": "<SQL Server Connection string>"
}
Hãy thêm lớp DbContext.
public class CountryContext : DbContext
{
public DbSet<Country> Countries { get; set; }
public CountryContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{
}
}
Phương thức GetCountries cố gắng truy xuất một mục từ bộ nhớ đệm bằng một khóa. Nếu trận đấu được tìm thấy, nó sẽ được trả về. Nếu không, dữ liệu sẽ được lấy từ cơ sở dữ liệu và được đưa vào bộ đệm. Mục được lưu trong bộ nhớ đệm được định cấu hình để hết hạn sau 5 phút.
[Route("api/[controller]")]
[ApiController]
public class CountryController : ControllerBase
{
private readonly IDistributedCache cache;
private readonly CountryContext countryContext;
public CountryController(IDistributedCache cache, CountryContext countryContext)
{
this.cache = cache;
this.countryContext = countryContext;
}
// GET: api/<CountryController>
[HttpGet]
public async Task<IEnumerable<Country>> GetCountries()
{
var countriesCache = await cache.GetStringAsync("countries");
var value = (countriesCache == null) ? default : JsonConvert.DeserializeObject<IEnumerable<Country>>(countriesCache);
if (value == null)
{
var countries = countryContext.Countries.ToList();
if (countries != null && countries.Any())
{
await cache.SetStringAsync("Countries", JsonConvert.SerializeObject(countries), new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
});
return countries;
}
}
return value;
}
}
Phương thức AddCountries minh họa cách bộ đệm có thể bị vô hiệu hóa khi thêm/cập nhật dữ liệu vào cơ sở dữ liệu.
// POST api/<CountryController>
[HttpPost]
public async Task<ActionResult<string>> AddCountries([FromBody] Country country, CancellationToken cancellationToken)
{
if (country == null)
return BadRequest("country is null");
await countryContext.AddAsync(country);
await countryContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
await cache.RemoveAsync("countries", cancellationToken).ConfigureAwait(false);
return Ok("cache has been invalidated");
}
Kết luận
Trong bài viết này, tôi đã mô tả mẫu Cache-Aside và cách triển khai chính của nó bằng cách sử dụng ASP.NET Core và Azure Redis Cache. Chúc bạn lưu vào bộ nhớ đệm vui vẻ!
Tôi hy vọng bạn thích bài viết. Nếu thấy bài viết hay thì hãy like và chia sẻ nhé.