مقدمة في التخزين المؤقت في Kotlin
تطبيقات Kotlin الحديثة، سواء كانت حلول خلفية (backend) أو عملاء Android، غالباً ما تواجه مشكلة في الأداء عند العمل مع البيانات. الاستعلامات المتكررة إلى قواعد البيانات، واجهات برمجة التطبيقات الخارجية (API)، أو العمليات الحسابية الثقيلة يمكن أن تبطئ النظام بشكل كبير. التخزين المؤقت (Caching) هو وسيلة فعالة لتقليل الحمل وتسريع استجابات التطبيق. في هذه المقالة، سنستعرض ثلاثة أساليب رئيسية للتخزين المؤقت في بيئة Kotlin: استخدام Redis و Memcached والحلول المدمجة في الذاكرة (in-memory). ستتعلم كيفية اختيار الأداة المناسبة وتنفيذها في مشروعك.
ما هو التخزين المؤقت ولماذا هو مهم في Kotlin؟
التخزين المؤقت هو عملية حفظ البيانات التي يتم طلبها بشكل متكرر في مخزن مؤقت سريع الوصول. بدلاً من تنفيذ عملية مكلفة في كل مرة (استعلام قاعدة بيانات، اتصال شبكة)، يتحقق التطبيق أولاً من التخزين المؤقت. إذا كانت البيانات موجودة (cache hit)، يتم إرجاعها فوراً. إذا لم تكن موجودة (cache miss)، يتم تنفيذ العملية الأساسية، ويتم حفظ النتيجة في التخزين المؤقت للطلبات المستقبلية.
الفوائد الرئيسية للتخزين المؤقت في Kotlin:
- تقليل وقت الاستجابة — يتم إرجاع البيانات من التخزين المؤقت خلال أجزاء من الثانية (ملي ثانية).
- تقليل الحمل على قاعدة البيانات والخدمات الخارجية.
- توفير الموارد — عمليات أقل على المعالج (CPU) والشبكة.
- قابلية التوسع — التخزين المؤقت الموزع (Redis, Memcached) يمكن توسيعه بسهولة.
في Kotlin، بفضل توافقه مع Java وكوروتينات القوية، يتم تنفيذ التخزين المؤقت بشكل أنيق بشكل خاص.
الحلول المدمجة للتخزين المؤقت في Kotlin
للمشاريع الصغيرة أو التخزين المؤقت المحلي ضمن عملية JVM واحدة، يمكن استخدام الأدوات المدمجة. لا تتطلب هذه الأدوات تثبيت خدمات خارجية وهي مثالية للنماذج الأولية.
تخزين مؤقت بسيط في الذاكرة باستخدام ConcurrentHashMap
أبسط طريقة هي استخدام ConcurrentHashMap مع دعم TTL (وقت الحياة). إليك مثال على التنفيذ:
import java.util.concurrent.ConcurrentHashMapimport java.util.concurrent.TimeUnit
class SimpleCache<K, V>(private val ttlMillis: Long) { private val cache = ConcurrentHashMap<K, CacheEntry<V>>()
private data class CacheEntry<V>(val value: V, val expiryTime: Long)
fun put(key: K, value: V) { val expiryTime = System.currentTimeMillis() + ttlMillis cache[key] = CacheEntry(value, expiryTime) }
fun get(key: K): V? { val entry = cache[key] ?: return null if (System.currentTimeMillis() > entry.expiryTime) { cache.remove(key) return null } return entry.value }
fun invalidate(key: K) { cache.remove(key) }
fun clear() { cache.clear() }}
// الاستخدامfun main() { val cache = SimpleCache<String, String>(TimeUnit.MINUTES.toMillis(5)) cache.put("user_42", "{\\"name\\":\\"Alice\\"}") val user = cache.get("user_42") println(user) // سيطبع سلسلة JSON}هذا الأسلوب بسيط، لكنه غير مناسب للأنظمة الموزعة.
استخدام Caffeine — مكتبة متقدمة
للسيناريوهات الأكثر تعقيداً، يُوصى باستخدام مكتبة Caffeine. توفر هذه المكتبة تخزيناً مؤقتاً عالي الأداء مع إزالة تلقائية، إحصائيات، وتحميل غير متزامن. أضف التبعية في ملف build.gradle.kts:
implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")مثال على استخدام Caffeine مع الكوروتينات:
import com.github.benmanes.caffeine.cache.Caffeineimport kotlinx.coroutines.runBlockingimport java.util.concurrent.TimeUnit
data class Product(val id: Int, val name: String, val price: Double)
class ProductService { private val ca
```