ในวงจรการพัฒนาซอฟต์แวร์ มักมุ่งเน้นไปที่ประสิทธิภาพของแอปพลิเคชัน มีหลายวิธีในการปรับปรุงประสิทธิภาพ และรูปแบบหนึ่งที่ใช้บ่อยที่สุดในการปรับปรุงประสิทธิภาพในแอปพลิเคชันระบบคลาวด์สมัยใหม่ก็คือรูปแบบที่ไม่ใช้แคช ในโพสต์นี้ ผมจะอธิบายโดยย่อเกี่ยวกับรูปแบบการยกเว้นแคชและการนำไปใช้งานโดยใช้ ASP.NET Core
บทนำ
รูปแบบนี้ค่อนข้างตรงไปตรงมา และจุดประสงค์เพียงอย่างเดียวคือเพื่อโหลดข้อมูลตามความต้องการลงในแคชจากแหล่งข้อมูล ซึ่งช่วยในการรักษาความสอดคล้องระหว่างข้อมูลในแคชและแหล่งข้อมูลที่สำคัญ
ต่อไปนี้เป็นลักษณะของรูปแบบ
- เมื่อแอปพลิเคชันต้องการข้อมูล อันดับแรกแอปพลิเคชันจะตรวจสอบแคช
- ในกรณีที่ข้อมูลมีอยู่ในแคช แอปพลิเคชันจะใช้ข้อมูลจากแคช
- มิฉะนั้น ข้อมูลจะถูกดึงมาจากแหล่งข้อมูล
ด้านล่างนี้คือภาพประกอบไดอะแกรม

วัตถุแคชจะต้องใช้งานไม่ได้เมื่อมีการเปลี่ยนแปลงค่าโดยแอปพลิเคชัน

ลำดับการทำให้แคชใช้ไม่ได้เป็นสิ่งสำคัญ อัปเดตแหล่งข้อมูลก่อนที่จะลบรายการออกจากแคช ในกรณีที่คุณลบรายการออกจากแคชก่อน มีโอกาสที่ไคลเอ็นต์อาจดึงข้อมูลรายการก่อนที่ที่เก็บข้อมูลจะได้รับการอัปเดต ซึ่งจะส่งผลให้ข้อมูลไม่สอดคล้องกันระหว่างที่เก็บข้อมูลและแคช
เมื่อใดจึงจะใช้รูปแบบนี้
- รูปแบบนี้ช่วยให้เราสามารถโหลดข้อมูลตามความต้องการ และสามารถใช้ได้เมื่อความต้องการทรัพยากรไม่สามารถคาดเดาได้
- แคชที่ไม่มีการดำเนินการอ่านและเขียนผ่าน
หมายเหตุ
- การอ่านผ่าน: เป็นแคชที่อยู่ในแนวเดียวกับฐานข้อมูล และในกรณีที่แคชหายไป ก็สามารถโหลดข้อมูลจากฐานข้อมูลและเติมแคชได้
- เขียนผ่าน: แคชอยู่ในแนวเดียวกับฐานข้อมูลและข้อมูลจะผ่านแคชไปยังฐานข้อมูลหลักเสมอ
สร้างทรัพยากร Azure
ดังที่แสดงไว้ข้างต้น เราจำเป็นต้องมีฐานข้อมูล (Azure SQL Server) และแคช (Azure Redis Cache) คุณสามารถเลือกฐานข้อมูลและแคชตามความสะดวกของคุณ
$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
การใช้งาน
เริ่มต้นด้วยการใช้งานโดยการสร้างโปรเจ็กต์ ASP.NET Core Web API และเพิ่มแพ็คเกจ Nuget ที่จำเป็นสำหรับแคช Redis และ Entity Framework Core
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
ก่อนอื่น มาสร้างคลาสโมเดลประเทศกันก่อน
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
ตอนนี้ มาลงทะเบียนการขึ้นต่อกันของแคช EF Core และ Redis ในวิธี ConfigureServices ของคลาส 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");
});
}
ตอนนี้แก้ไขไฟล์ appsettings.json เพื่อรองรับสตริงการเชื่อมต่อของ Redis Cache และฐานข้อมูล SQL
"ConnectionStrings": {
"RedisConnectionString": "<Redis Cache ConnectionString>",
"DefaultConnection": "<SQL Server Connection string>"
}
มาเพิ่มคลาส DbContext กันดีกว่า
public class CountryContext : DbContext
{
public DbSet<Country> Countries { get; set; }
public CountryContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{
}
}
วิธีการGetCountriesพยายามที่จะดึงข้อมูลสินค้าจากแคชโดยใช้คีย์ หากพบการจับคู่ก็จะถูกส่งคืน มิฉะนั้น ข้อมูลจะถูกดึงมาจากฐานข้อมูลและเติมลงในแคช รายการที่แคชไว้ได้รับการกำหนดค่าให้หมดอายุหลังจาก 5 นาที
[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;
}
}
วิธีการ AddCountries แสดงให้เห็นว่าแคชสามารถทำให้ใช้งานไม่ได้เมื่อมีการเพิ่ม/อัปเดตข้อมูลลงในฐานข้อมูลได้อย่างไร
// 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");
}
บทสรุป
ในบทความนี้ ฉันได้อธิบายรูปแบบ Cache-Aside และการใช้งานหลักโดยใช้ ASP.NET Core และ Azure Redis Cache ขอให้มีความสุขในการแคช!
ฉันหวังว่าคุณจะชอบบทความนี้ ในกรณีที่คุณพบบทความที่น่าสนใจ กรุณากดไลค์และแชร์