التخزين المؤقت في Java: Redis، Caffeine، Ehcache و JCache
التخزين المؤقت هو أحد الأدوات الرئيسية لتحسين أداء تطبيقات Java. فهو يسمح بتقليل وقت الاستجابة، وتخفيف الحمل على قواعد البيانات وواجهات API الخارجية. في هذه المقالة، سنستعرض ثلاثة حلول شائعة: Redis (تخزين مؤقت موزع خارجي)، Caffeine (تخزين مؤقت عالي الأداء في الذاكرة)، و Ehcache (تخزين مؤقت هجين مع دعم القرص). كما سننظر في المعيار JCache (JSR 107) الذي يوحد العمل مع التخزين المؤقت.
لماذا نحتاج إلى التخزين المؤقت في Java؟
سيناريوهات الاستخدام الرئيسية:
- تقليل التأخير — يتم إرجاع البيانات من التخزين المؤقت في ميكروثانية، وليس ملي ثانية (كما هو الحال عند الاستعلام من قاعدة البيانات).
- تقليل الحمل — يحمي التخزين المؤقت قاعدة البيانات من الاستعلامات المتكررة.
- توفير الموارد — استدعاءات أقل للخدمات الخارجية → تكاليف أقل على البنية التحتية.
1. Redis — تخزين مؤقت موزع للخدمات المصغرة
Redis هو مخزن بيانات في الذاكرة يُستخدم غالبًا كتخزين مؤقت موزع. وهو يدعم هياكل بيانات مختلفة: سلاسل، تجزئات، قوائم، مجموعات. بالنسبة لـ Java، هناك عميلان رئيسيان: Jedis و Lettuce (موصى به لـ Spring Boot).
مثال على الاتصال عبر Spring Data Redis:
@Configurationpublic class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; }}
@Servicepublic class UserService { @Autowired private RedisTemplate<String, Object> redisTemplate;
public User getUserById(String id) { String key = "user:" + id; User cached = (User) redisTemplate.opsForValue().get(key); if (cached != null) return cached;
User user = database.findUserById(id); // استعلام مكلف redisTemplate.opsForValue().set(key, user, 10, TimeUnit.MINUTES); return user; }}مزايا Redis: التوزيع، دعم TTL، pub/sub، الثبات. عيوبه: تأخير الشبكة، استهلاك الذاكرة على الخادم.
2. Caffeine — تخزين مؤقت فائق السرعة في الذاكرة
Caffeine هي مكتبة للتخزين المؤقت المحلي، تعتمد على خوارزمية W-TinyLFU. وهي توفر معدل إصابة يقارب 100% مع الحد الأدنى من استهلاك الذاكرة. مثالية للتطبيقات أحادية الخيوط ومتعددة الخيوط.
مثال على استخدام Caffeine:
Cache<String, User> cache = Caffeine.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .maximumSize(10_000) .recordStats() .build();
public User getUser(String id) { return cache.get(id, key -> database.findUserById(key));}
// الحصول على الإحصائياتCacheStats stats = cache.stats();System.out.println("Hit rate: " + stats.hitRate());مزايا Caffeine: تأخير ضئيل (نانو ثانية)، إحصائيات مدمجة، إزالة تلقائية للسجلات القديمة. عيوبه: تخزين مؤقت محلي فقط، غير مناسب للأنظمة الموزعة.
3. Ehcache — تخزين مؤقت هجين مع الكتابة على القرص
Ehcache هي واحدة من أقدم مكتبات التخزين المؤقت لـ Java. وهي تدعم التخزين المؤقت في الذاكرة وعلى القرص، بالإضافة إلى التجميع عبر Terracotta. تُستخدم غالبًا مع Hibernate.
مثال على تكوين Ehcache 3 (XML):
<config xmlns:xsi="http://www.w3.org