Tidak ada antipola Penembolokan
Antipattern adalah kelemahan desain umum yang dapat merusak perangkat lunak atau aplikasi Anda dalam situasi stres dan tidak boleh diabaikan. Antipola tanpa penembolokan terjadi saat aplikasi cloud yang menangani banyak permintaan serentak, berulang kali mengambil data yang sama. Hal ini dapat mengurangi performa dan skalabilitas.
Ketika tidak di-cache, data dapat menyebabkan sejumlah perilaku yang tidak diinginkan, termasuk:
- Mengambil informasi yang sama secara berulang dari sumber daya yang mahal untuk diakses, dalam hal overhead atau latensi I/O.
- Berulang kali membangun objek atau struktur data yang sama untuk beberapa permintaan.
- Melakukan panggilan berlebihan ke layanan jarak jauh yang memiliki kuota layanan dan membatasi klien melewati batas tertentu.
Pada gilirannya, masalah ini dapat menyebabkan waktu respons yang buruk, peningkatan pertentangan di penyimpanan data, dan skalabilitas yang buruk.
Contoh antipola tanpa penembolokan
Contoh berikut menggunakan Entity Framework untuk menyambungkan ke database. Setiap permintaan klien menghasilkan panggilan ke database, meskipun beberapa permintaan mengambil data yang sama persis. Biaya permintaan berulang, dalam hal overhead I/O dan biaya akses data, dapat terakumulasi dengan cepat.
public class PersonRepository : IPersonRepository
{
public async Task<Person> GetAsync(int id)
{
using (var context = new AdventureWorksContext())
{
return await context.People
.Where(p => p.Id == id)
.FirstOrDefaultAsync()
.ConfigureAwait(false);
}
}
}
Anda dapat menemukan sampel lengkap di sini.
Anti pola ini biasanya terjadi karena:
- Tidak menggunakan cache lebih mudah diterapkan, dan berfungsi dengan baik di bawah beban rendah. Penembolokan membuat kode lebih rumit.
- Keuntungan dan kerugian menggunakan cache tidak dipahami dengan jelas.
- Ada kekhawatiran tentang overhead menjaga akurasi dan kesegaran data cache.
- Aplikasi dimigrasikan dari sistem lokal, tempat latensi jaringan tidak menjadi masalah, dan sistem berjalan pada perangkat keras berperforma tinggi yang mahal, jadi penembolokan tidak dipertimbangkan dalam desain aslinya.
- Pengembang tidak menyadari bahwa penembolokan adalah kemungkinan dalam skenario tertentu. Misalnya, pengembang mungkin tidak berpikir untuk menggunakan ETag saat mengimplementasikan API web.
Bagaimana cara memperbaiki antipola tanpa penembolokan
Strategi penembolokan yang paling populer adalah strategi on-demand atau cache-aside.
- Saat dibaca, aplikasi mencoba membaca data dari cache. Jika data tidak ada dalam cache, aplikasi mengambilnya dari sumber data dan menambahkannya ke cache.
- Saat menulis, aplikasi menulis perubahan langsung ke sumber data dan menghapus nilai lama dari cache. Aplikasi ini akan diambil dan ditambahkan ke cache saat diperlukan lagi.
Pendekatan ini cocok untuk data yang sering berubah. Berikut adalah contoh sebelumnya yang diperbarui untuk menggunakan pola Cache-Aside.
public class CachedPersonRepository : IPersonRepository
{
private readonly PersonRepository _innerRepository;
public CachedPersonRepository(PersonRepository innerRepository)
{
_innerRepository = innerRepository;
}
public async Task<Person> GetAsync(int id)
{
return await CacheService.GetAsync<Person>("p:" + id, () => _innerRepository.GetAsync(id)).ConfigureAwait(false);
}
}
public class CacheService
{
private static ConnectionMultiplexer _connection;
public static async Task<T> GetAsync<T>(string key, Func<Task<T>> loadCache, double expirationTimeInMinutes)
{
IDatabase cache = Connection.GetDatabase();
T value = await GetAsync<T>(cache, key).ConfigureAwait(false);
if (value == null)
{
// Value was not found in the cache. Call the lambda to get the value from the database.
value = await loadCache().ConfigureAwait(false);
if (value != null)
{
// Add the value to the cache.
await SetAsync(cache, key, value, expirationTimeInMinutes).ConfigureAwait(false);
}
}
return value;
}
}
Perhatikan bahwa metode GetAsync
sekarang memanggil kelas CacheService
, daripada memanggil database secara langsung. Kelas CacheService
pertama-tama mencoba mendapatkan item dari Azure Cache for Redis. Jika nilai tidak ditemukan dalam cache, CacheService
akan memanggil fungsi lambda yang diteruskan oleh pemanggil. Fungsi lambda bertanggung jawab untuk mengambil data dari database. Implementasi ini memisahkan repositori dari solusi penembolokan tertentu, dan memisahkan CacheService
dari database.
Pertimbangan untuk strategi penembolokan
Jika cache tidak tersedia, mungkin karena kegagalan sementara, jangan kembalikan kesalahan ke klien. Sebagai gantinya, ambil data dari sumber data asli. Namun, perlu diketahui bahwa sementara cache sedang dipulihkan, penyimpanan data asli dapat dibanjiri dengan permintaan, mengakibatkan waktu tunggu dan koneksi gagal. (Bagaimanapun, ini adalah salah satu motivasi untuk menggunakan cache di tempat pertama.) Gunakan teknik seperti pola Circuit Breaker untuk menghindari kewalahan sumber data.
Aplikasi yang menyimpan data dinamis harus dirancang untuk mendukung konsistensi akhirnya.
Untuk API web, Anda dapat mendukung penembolokan sisi klien dengan menyertakan header Cache-Control dalam pesan permintaan dan respons, dan menggunakan ETag untuk mengidentifikasi versi objek. Untuk informasi selengkapnya, lihat implementasi API.
Anda tidak perlu men-cache seluruh entitas. Jika sebagian besar entitas statis tetapi hanya sebagian kecil yang sering berubah, cache elemen statis dan ambil elemen dinamis dari sumber data. Pendekatan ini dapat membantu mengurangi volume I/O yang dilakukan terhadap sumber data.
Dalam beberapa kasus, jika bersifat volatile berumur pendek, data akan berguna untuk menyimpannya dalam cache. Misalnya, pertimbangkan perangkat yang terus mengirimkan pembaruan status. Mungkin masuk akal untuk men-cache informasi ini saat tiba, dan tidak menulisnya ke penyimpanan persisten sama sekali.
Untuk mencegah data menjadi basi, banyak solusi penembolokan mendukung periode kedaluwarsa yang dapat dikonfigurasi, sehingga data dihapus secara otomatis dari cache setelah interval tertentu. Anda mungkin perlu menyetel waktu kedaluwarsa untuk skenario Anda. Data yang sangat statis dapat tetap berada di cache untuk waktu yang lebih lama daripada data volatile yang dapat cepat usang.
Jika solusi penembolokan tidak menyediakan kedaluwarsa bawaan, Anda mungkin perlu menerapkan proses latar belakang yang sesekali menghapus cache, untuk mencegahnya berkembang tanpa batas.
Selain penembolokan data dari sumber data eksternal, Anda bisa menggunakan penembolokan untuk menyimpan hasil komputasi yang kompleks. Namun, sebelum Anda melakukannya, instrumen aplikasi untuk menentukan apakah aplikasi tersebut benar-benar terikat CPU.
Mungkin berguna untuk melakukan prime cache saat aplikasi dimulai. Isi cache dengan data yang kemungkinan besar akan digunakan.
Selalu sertakan instrumentasi yang mendeteksi hit singgahan dan cache miss. Gunakan informasi ini untuk menyesuaikan kebijakan penembolokan, seperti data apa yang akan di-cache, dan berapa lama untuk menyimpan data dalam cache sebelum kedaluwarsa.
Jika kurangnya penembolokan adalah hambatan, menambahkan penembolokan dapat meningkatkan volume permintaan sedemikian rupa sehingga frontend web menjadi kelebihan beban. Klien mungkin mulai menerima kesalahan HTTP 503 (Layanan Tidak Tersedia). Ini adalah indikasi bahwa Anda harus menskalakan frontend.
Bagaimana cara mendeteksi antipola tanpa penembolokan
Anda dapat melakukan langkah-langkah berikut untuk membantu mengidentifikasi apakah kurangnya penembolokan menyebabkan masalah performa:
Meninjau desain aplikasi. Ambil inventaris semua penyimpanan data yang digunakan aplikasi. Untuk masing-masing inventaris, tentukan apakah aplikasi menggunakan cache. Jika memungkinkan, tentukan seberapa sering data berubah. Kandidat awal yang baik untuk penembolokan mencakup data yang berubah perlahan, dan data referensi statis yang sering dibaca.
Instrumen aplikasi dan pantau sistem langsung untuk mengetahui seberapa sering aplikasi mengambil data atau menghitung informasi.
Profil aplikasi di lingkungan pengujian untuk menangkap metrik tingkat rendah tentang overhead yang terkait dengan operasi akses data atau perhitungan lain yang sering dilakukan.
Lakukan pengujian beban di lingkungan pengujian untuk mengidentifikasi bagaimana sistem merespons di bawah beban kerja normal dan di bawah beban berat. Pengujian beban harus menyimulasikan pola akses data yang diamati di lingkungan produksi menggunakan beban kerja yang realistis.
Periksa statistik akses data untuk penyimpanan data yang mendasarinya dan tinjau seberapa sering permintaan data yang sama diulang.
Contoh diagnosis
Bagian berikut menerapkan langkah-langkah ini ke aplikasi contoh yang dijelaskan sebelumnya.
Melengkapi aplikasi dan memantau sistem langsung
Melengkapi aplikasi dan memantau untuk mendapatkan informasi tentang permintaan spesifik yang dibuat pengguna saat aplikasi dalam produksi.
Gambar berikut menunjukkan data pemantauan yang diambil oleh New Relic selama pengujian beban. Dalam hal ini, satu-satunya operasi HTTP GET yang dilakukan adalah Person/GetAsync
. Namun, dalam lingkungan produksi langsung, mengetahui frekuensi relatif yang dilakukan setiap permintaan dapat memberi Anda wawasan tentang sumber daya mana yang harus di-cache.
Jika Anda memerlukan analisis yang lebih dalam, Anda dapat menggunakan profiler untuk menangkap data performa tingkat rendah di lingkungan pengujian (bukan sistem produksi). Lihat metrik seperti tingkat permintaan I/O, penggunaan memori, dan pemanfaatan CPU. Metrik ini mungkin menunjukkan sejumlah besar permintaan ke penyimpanan data atau layanan, atau pemrosesan berulang yang melakukan penghitungan yang sama.
Menguji beban aplikasi
Grafik berikut menunjukkan hasil pengujian beban aplikasi contoh. Pengujian beban menyimulasikan beban langkah hingga 800 pengguna yang melakukan serangkaian operasi biasa.
Jumlah pengujian yang berhasil dilakukan setiap detik meningkat, dan sebagai akibatnya permintaan tambahan diperlambat. Waktu pengujian rata-rata terus meningkat seiring dengan beban kerja. Tingkat waktu respons turun setelah puncak pemuatan pengguna.
Memeriksa statistik akses data
Statistik akses data dan informasi lain yang disediakan oleh penyimpanan data dapat memberikan informasi yang berguna, seperti kueri mana yang paling sering diulang. Misalnya, di Microsoft SQL Server, tampilan pengelolaan sys.dm_exec_query_stats
memiliki informasi statistik untuk kueri yang baru saja dijalankan. Teks untuk setiap kueri tersedia dalam tampilan sys.dm_exec-query_plan
. Anda dapat menggunakan alat seperti SQL Server Management Studio untuk menjalankan kueri SQL berikut dan menentukan seberapa sering kueri dilakukan.
SELECT UseCounts, Text, Query_Plan
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
CROSS APPLY sys.dm_exec_query_plan(plan_handle)
Kolom UseCount
dalam hasil menunjukkan seberapa sering setiap kueri dijalankan. Gambar berikut menunjukkan bahwa kueri ketiga dijalankan lebih dari 250.000 kali, jauh lebih banyak daripada kueri lainnya.
Berikut adalah kueri SQL yang menyebabkan begitu banyak permintaan database:
(@p__linq__0 int)SELECT TOP (2)
[Extent1].[BusinessEntityId] AS [BusinessEntityId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [Person].[Person] AS [Extent1]
WHERE [Extent1].[BusinessEntityId] = @p__linq__0
Ini adalah kueri yang dihasilkan Entity Framework dalam metode GetByIdAsync
yang ditampilkan sebelumnya.
Menerapkan solusi strategi cache dan memverifikasi hasilnya
Setelah Anda memasukkan cache, ulangi pengujian beban dan bandingkan hasilnya dengan pengujian beban sebelumnya tanpa cache. Berikut adalah hasil pengujian beban setelah menambahkan cache ke aplikasi sampel.
Volume pengujian yang berhasil masih mencapai puncaknya, tetapi pada beban pengguna yang lebih tinggi. Tingkat permintaan pada beban ini secara signifikan lebih tinggi dari sebelumnya. Waktu pengujian rata-rata masih meningkat seiring dengan beban, tetapi waktu respons maksimum adalah 0,05 mdtk, dibandingkan dengan 1 mdtk sebelumnya—peningkatan 20×.