In-memory caching – Liệu bạn có nên lựa chọn nó và cách sử dụng nó như thế nào

In-memory caching – Liệu bạn có nên lựa chọn nó và cách sử dụng nó như thế nào

December 27, 2021 2 By Nam Vu

Bài viết tước mình đã đi tổng quan về caching, bài viết này mình sẽ đi cụ thể hơn về một loại cache phổ biến trong các ứng dụng monolithic.

Bắt đầu nhé!

In-memory cache là gì?

In-memory cache sử dụng bộ nhớ (RAM) của máy server để lưu trữ data. RAM cho chúng ta tốc độ đọc ghi đáng kinh ngạc tùy vào vào từng loại nhưng thường cho tốc độ hàng chục Gigabyte trên giây so với tốc độ từ khoảng 50 ~ 250MB/s của SSD. Nhìn vào những con số đó ta thấy rõ ràng tốc đọc và ghi giữa 2 vùng lưu trữ là rất lớn.

In-memory cache tham chiếu tới bộ nhớ đệm và dùng nó để lưu trữ dữ liệu trong bộ nhớ của server khi mà server đang chạy ứng dụng.

Điều này đồng nghĩa với việc khi server khởi động lại hoặc bị tèo thì dữ liệu được lưu trong cache sẽ bị mất đi.

Vậy nên lựa chọn loại dữ liệu như thế nào khi lưu vào In-memory cache

Những data nào thường được lưu cache thỏa mãn 2 yếu tố: data được truy cập thường xuyên và ít thay đổi.

– Nếu lựa chọn giải pháp lưu dữ liệu được tạo mới tạm thời trên RAM thì nên tiến hành ghi vào ổ cứng ngay khi có thể.

– Nếu bạn đang lên kế hoạch lưu tạm trên RAM khoảng vài ba giây rồi mình mới lưu thực sự xuống ổ cứng thì bạn phải chấp nhận có thể mất dữ liệu người dùng, pha vỡ tính toàn vẹn dữ liệu của ứng dụng.

Ưu điểm của In-memory cache

– Ưu điểm đầu tiên chắc chắn là về mặt tốc độ rồi, RAM xử lý với tốc độ cực nhanh nên có thể đây là ưu điểm sáng nhất của In-memory cache

– Ưu điểm thứ 2 là cấu hình nhanh, không phải cài cắm gì vì nó chạy ngay trên ứng dụng của bạn. Điều này giúp bạn triển khai ứng dụng nhanh chóng và dễ dàng.

Hạn chế của In-memory cache

Nếu đọc định nghĩa bên trên bạn sẽ thấy rõ 2 vấn đề mà In-memory cache gặp phải khi sử dụng đó là:

– RAM thường có dung lượng hạn chế nên chúng ta phải quản lý được cache và khi bộ nhớ gần đầy.

– Dữ liệu sẽ bị mất khi tắt máy, khởi động lại hay sập server nên chúng ta nên ưu tiên cho việc lưu tạm những dữ liệu đã được lưu trong ổ cứng trước khi khởi động lại server.

– In-memory cache chỉ phù hợp cho 1 server duy nhất hoặc nhiều server có sử dụng sticky sessions. Chính vì nó lưu trên chính server của bạn nên nếu ứng dụng của bạn có nhiều server có lẽ sẽ ảnh hưởng lớn đến tính chính xác và xung đột của dữ liệu. Các dữ liệu lúc này có thể khác nhau vì nó nằm trên các server khác nhau và bạn không dễ dàng gì xử lý.

– Do sự hạn chế của bộ nhớ nên memory cache nên dẫn đến server bị chậm. Các chức năng sẽ bị ảnh hưởng rất nhiều khi gặp tình trạng này.

Cách sử dụng memory cache trong .net core

Trong .net core có nhiều tính năng cũng đã sử dụng caching với cơ chế memory để cải thiện hiệu năng ứng dụng. Bạn có thể thấy như Cache Tag Helper, Distributed Cache Tag Helper, and Response Caching Middleware và ResponseCache

Ngoài ra net core còn hỗ trợ sẵn memory cache (IMemoryCache) giúp bạn thực hiện hóa caching vậy nên ta không cần phải xài third party.

Mặc định một In-memory caching được mở tự động hay có thể sử dụng phương thức AddMemoryCache(). Chúng cho phép ứng dụng của bạn có thể inject IMemoryCache interface và bạn có thể sử dụng nó trong services hay controllers. IMemoryCache interface là một interface đơn giản với 3 phương thức cơ bản sau:

CreateEntry(Object): Tạo và ghi đè một thực thể trong cache.
Remove(Object): Xóa đối tượng và giá trị trong cache với key.
TryGetValue(Object, Object): Lấy giá trị trong cache với key.

Chúng ta cũng có thể mở rộng các phương thức khác trong interface cơ bản này với nhiều options để cấu hình cơ chế cache sử dụng cho ứng dụng của bạn.

Dưới đây là các bước để bạn sử dụng In-memory cache

Bước 1: Cấu hình service memory cache để sử dụng

services.AddMemoryCache();

Bước 2: Sử dụng dependence injection để inject IMemoryCache

public class IndexModel : PageModel
{
        private readonly ILogger<IndexModel> _logger;
        private readonly IMemoryCache _cache;
        private readonly DataContext _dataContext;

        public IndexModel(ILogger<IndexModel> logger, IMemoryCache cache, DataContext  dataContext)
        {
            _logger = logger;
            _cache = cache;
            _dataContext = dataContext;
        }
}

Bước 3: Set data cho cache bao gồm key và value(value có thể là kiểu int, string, double, object…)

_cache.Set(cacheKey, products);

Bước 4: Lấy dữ liệu từ cache ta dùng phương thức get

_cache.Get<string>(cacheKey);

Bước 5: Sử dụng TryGetValue để kiểm tra sự tồn tại của key

var cacheKey = "GET_ALL_PRODUCTS";

// If data found in cache, return cached data
if (_cache.TryGetValue(cacheKey, out List<Product> products))
{
    Product = products;
    return Page();
}

Bước 6: Sử dụng GetOrCreate để tạo 1 cache mới nếu chưa tồn tại

var cacheGetOrCreate =_cache.GetOrCreate<string>(cacheKey, entry =>
{
    return new List<Product>(){ newProduct };
});

Bước 7: Xóa cache

_cache.Remove(cacheKey);

Ngoài những bước cơ bản bên trên như mình đã nói bên trên mình có thể cấu hình những options khác khi sử dụng cache. Cụ thể như sau:

– Khi thêm giá trị dữ liệu vào cache, chúng không tồn tại ở bộ nhớ mãi mãi. Sau một thời gian dữ liệu này sẽ ở trạng thái ngầm (stale) và khi này nếu ứng dụng muốn truy vấn thì chúng phải nạp lại và tính toán dữ liệu mới lại và lưu ngược lại và bộ nhớ đệm. Nếu bạn muốn control được vấn đề này thì bạn sẽ phải set cache expiration cho In-memory cache. Khi này bạn phải sử dụng đến MemoryCacheEntryOptions, đây sẽ là một đối tượng sẽ cho phép bạn cấu hình cache expiration và các cache options khác.

– Để thời gian sống của dữ liệu trong cache một thời gian chính xác nào đó bạn sử dụng AbsoluteExpiration để cấu hình.

Ví dụ bạn set thời gian AbsoluteExpiration là 5 phút kể từ thời điểm hiện tại thì sau 5 phút dữ liệu trong cache key của bạn set thêm option này sẽ bị mất. Khi này dù bạn có truy vấn get dữ liệu này ra sẽ không còn tồn tại nữa.

var cacheOptions = new MemoryCacheEntryOptions()
{
    AbsoluteExpiration = DateTime.Now.AddMinutes(5)
};
_cache.Set(cacheKey, products, cacheOptions);

– Bạn cũng có thể sử dụng SlidingExpiration, cấu hình này cho phép xóa dữ liệu cache khi mà nó không thường xuyên truy cập. Nghĩa là nếu bạn set SlidingExpiration là 5 phút thì nếu trong vòng 5 phút đó dữ liệu cache key này không được tác động (không get không set) thì dữ liệu này nó mới mất đi.

var cacheOptions = new MemoryCacheEntryOptions()
{
    SlidingExpiration = TimeSpan.FromMinutes(5)
};
_cache.Set(cacheKey, products, cacheOptions);

– Vậy nếu dữ liệu trong cache của bạn được truy cập thường xuyên thì sao, khi này nếu bạn sử dụng SlidingExpiration thì dữ liệu của bạn có lẽ sẽ không bao giờ mất. Đơn giản vì nó được renew liên tục và không bao giờ hết hạn. Điều này lại gây nguy hiểm cho tính toàn vẹn dữ liệu khiến dữ liệu có thể bị sai khi thời gian sống quá lâu. Bạn có thể giải quyết vấn đề này bằng cách kết hợp cả 2 options AbsoluteExpiration và SlidingExpiration để vừa đảm bảo thời gian sống một cách tương đối vừa đảm bảo dữ liệu lâu không được truy cập sẽ được dọn sạch.

var cacheOptions = new MemoryCacheEntryOptions()
{
    SlidingExpiration = TimeSpan.FromMinutes(5),
    AbsoluteExpiration = DateTime.Now.AddMinutes(60)
};
_cache.Set(cacheKey, products, cacheOptions);

– Ngoài ra bạn cũng có thể set độ ưu tiên cho cache items của bạn. Đây chính là độ ưu tiên để trigger xóa dữ liệu khi không sử dụng của bạn. In-memory cache có 4 mức độ ưu tiên: Low, Normal, High, và NeverRemove. Mặc định là độ ưu tiên Normal.

var cacheOptions = new MemoryCacheEntryOptions()
{
    AbsoluteExpiration = DateTime.Now.AddMinutes(60),
    Priority = CacheItemPriority.High
};
_cache.Set(cacheKey, products, cacheOptions);

Bạn có thể tham khảo project demo tại link github của mình nhé!
https://github.com/ntechdevelopers/ntech.caching

Trên đây là bài viết về In-memory cache cùng với cách sử dụng của nó với AspNet Core. Hi vọng bài viết sẽ giúp ích được bạn hiểu được khi nào nên hay không nên sử dụng loại cache này và demo sử dụng nó.

#ntechdevelopers