מבוא לקפאין

1. הקדמה

במאמר זה אנו נסתכל על קפאין - א ספריית מטמון עם ביצועים גבוהים עבור Java.

הבדל מהותי אחד בין מטמון ל- a מַפָּה הוא שמטמון מפנה פריטים מאוחסנים.

An מדיניות פינוי מחליטה אילו אובייקטים יש למחוק בכל זמן נתון. מדיניות זו משפיע ישירות על קצב ההיט של המטמון - מאפיין מכריע בספריות המטמון.

קפאין משתמש ב חלון TinyLfu מדיניות פינוי, המספקת א קצב פגיעה כמעט אופטימלי.

2. תלות

עלינו להוסיף את קָפֵאִין תלות שלנו pom.xml:

 com.github.ben-manes.קפאין קפאין 2.5.5 

תוכל למצוא את הגרסה האחרונה של קָפֵאִין במרכז מייבן.

3. אוכלוסיית מטמון

בואו נתמקד בקפאין שלוש אסטרטגיות לאוכלוסיית המטמון: טעינה ידנית, סינכרונית וטעינה אסינכרונית.

ראשית, בוא נכתוב מחלקה לסוגי הערכים שנאחסן במטמון שלנו:

class DataObject {נתוני מחרוזת פרטיים סופיים; private static int objectCounter = 0; // קונסטרוקטורים / גטרים סטנדרטיים ציבורי סטטי DataObject לקבל (נתוני מחרוזת) {objectCounter ++; להחזיר DataObject חדש (נתונים); }}

3.1. אכלוס ידני

באסטרטגיה זו, אנו מכניסים ידנית ערכים למטמון ושולפים אותם מאוחר יותר.

בוא נתחיל את המטמון שלנו:

מטמון מטמון = Caffeine.newBuilder () .expireAfterWrite (1, TimeUnit.MINUTES) .maximumSize (100) .build ();

עַכשָׁיו, אנו יכולים לקבל ערך מהמטמון באמצעות ה- getIfPresent שיטה. שיטה זו תחזור ריק אם הערך אינו קיים במטמון:

מפתח מחרוזת = "A"; DataObject dataObject = cache.getIfPresent (מפתח); assertNull (dataObject);

אנחנו יכולים אכלס את המטמון באופן ידני באמצעות לָשִׂים שיטה:

cache.put (מפתח, dataObject); dataObject = cache.getIfPresent (מפתח); assertNotNull (dataObject);

אנו יכולים גם לקבל את הערך באמצעות ה- לקבל שיטה, שלוקח א פוּנקצִיָה יחד עם מפתח כטיעון. פונקציה זו תשמש למתן ערך החזרה אם המפתח אינו קיים במטמון, שיוכנס למטמון לאחר החישוב:

dataObject = מטמון .get (מפתח, k -> DataObject.get ("נתונים עבור A")); assertNotNull (dataObject); assertEquals ("נתונים עבור A", dataObject.getData ());

ה לקבל השיטה מבצעת את החישוב באופן אטומי. המשמעות היא שהחישוב יתבצע פעם אחת בלבד - גם אם מספר שרשורים מבקשים את הערך בו זמנית. בגלל זה באמצעות לקבל עדיף על פני getIfPresent.

לפעמים אנחנו צריכים בטל כמה ערכים במטמון באופן ידני:

cache.invalidate (מפתח); dataObject = cache.getIfPresent (מפתח); assertNull (dataObject);

3.2. טעינה סינכרונית

שיטה זו לטעינת המטמון נמשכת פוּנקצִיָה, המשמש לאתחול ערכים, בדומה ל- לקבל שיטת האסטרטגיה הידנית. בואו נראה איך נוכל להשתמש בזה.

קודם כל, עלינו לאתחל את המטמון:

טעינת מטמון מטמון = Caffeine.newBuilder () .maximumSize (100) .expireAfterWrite (1, TimeUnit.MINUTES) .build (k -> DataObject.get ("נתונים עבור" + k));

כעת אנו יכולים לאחזר את הערכים באמצעות ה- לקבל שיטה:

DataObject dataObject = cache.get (מפתח); assertNotNull (dataObject); assertEquals ("נתונים עבור" + מקש, dataObject.getData ());

אנו יכולים גם להשיג קבוצה של ערכים באמצעות ה- קבל הכל שיטה:

Map dataObjectMap = cache.getAll (Arrays.asList ("A", "B", "C")); assertEquals (3, dataObjectMap.size ());

ערכים נלקחים מהאתחול הבסיסי של ה- back-end פוּנקצִיָה שהועבר ל לִבנוֹת שיטה. זה מאפשר להשתמש במטמון כחזית העיקרית לגישה לערכים.

3.3. טעינה אסינכרונית

אסטרטגיה זו עובד זהה לקודם אך מבצע פעולות בצורה לא סינכרונית ומחזיר a העתיד החזקת הערך בפועל:

מטמון AsyncLoadingCache = Caffeine.newBuilder () .maximumSize (100) .expireAfterWrite (1, TimeUnit.MINUTES) .buildAsync (k -> DataObject.get ("נתונים עבור" + k));

אנחנו יכולים להשתמש ב לקבל ו קבל הכל שיטות, באותו אופן, תוך התחשבות בעובדה שהם חוזרים העתיד:

מפתח מחרוזת = "A"; cache.get (מפתח) .thenAccept (dataObject -> {assertNotNull (dataObject); assertEquals ("נתונים עבור" + מפתח, dataObject.getData ());}); cache.getAll (Arrays.asList ("A", "B", "C")) .thenAccept (dataObjectMap -> assertEquals (3, dataObjectMap.size ()));

העתיד יש API עשיר ושימושי, עליו תוכלו לקרוא עוד במאמר זה.

4. פינוי ערכים

לקפאין יש שלוש אסטרטגיות לפינוי ערכים: מבוסס גודל, מבוסס זמן ומבוסס על ייחוס.

4.1. פינוי מבוסס גודל

סוג זה של פינוי מניח זאת פינוי מתרחש כאשר חורגת ממגבלת הגודל המוגדרת של המטמון. יש שתי דרכים להשיג את הגודל - ספירת אובייקטים במטמון, או השגת משקליהם.

בואו נראה איך יכולנו לספור אובייקטים במטמון. כאשר המטמון מאותחל, גודלו שווה לאפס:

מטמון LoadingCache = Caffeine.newBuilder () .maximumSize (1) .build (k -> DataObject.get ("נתונים עבור" + k)); assertEquals (0, cache.estimatedSize ());

כשאנחנו מוסיפים ערך, הגודל כמובן עולה:

cache.get ("A"); assertEquals (1, cache.estimatedSize ());

אנו יכולים להוסיף את הערך השני למטמון, מה שמוביל להסרת הערך הראשון:

cache.get ("B"); cache.cleanUp (); assertEquals (1, cache.estimatedSize ());

ראוי להזכיר שאנחנו תתקשר ל לנקות שיטה לפני קבלת גודל המטמון. הסיבה לכך היא שפינוי המטמון מבוצע באופן אסינכרוני ושיטה זו עוזר להמתין לסיום הפינוי.

אנחנו יכולים גם לעבור א שוקלפוּנקצִיָהכדי לקבל את גודל המטמון:

טעינת מטמון מטמון = Caffeine.newBuilder () .maximumWeight (10). Weigher ((k, v) -> 5) .build (k -> DataObject.get ("Data for" + k)); assertEquals (0, cache.estimatedSize ()); cache.get ("A"); assertEquals (1, cache.estimatedSize ()); cache.get ("B"); assertEquals (2, cache.estimatedSize ());

הערכים מוסרים מהמטמון כאשר המשקל עולה על 10:

cache.get ("C"); cache.cleanUp (); assertEquals (2, cache.estimatedSize ());

4.2. פינוי מבוסס זמן

אסטרטגיית פינוי זו היא בהתבסס על זמן התפוגה של הערך ויש לו שלושה סוגים:

  • תפוג לאחר הגישה - תוקף הכניסה פג לאחר שעברה תקופה מאז קריאה או כתיבה אחרונה
  • תפוג לאחר כתיבה - הכניסה תוקפה לאחר שעברה תקופה מאז הכתיבה האחרונה
  • מדיניות מותאמת אישית זמן התפוגה מחושב עבור כל ערך בנפרד על ידי ה- תְפוּגָה יישום

בואו להגדיר את אסטרטגיית התפוגה לאחר הגישה באמצעות ה- expireAfterAccess שיטה:

טעינת מטמון מטמון = Caffeine.newBuilder () .expireAfterAccess (5, TimeUnit.MINUTES) .build (k -> DataObject.get ("נתונים עבור" + k));

כדי להגדיר אסטרטגיית תפוגה לאחר כתיבה, אנו משתמשים ב- expireAfterWrite שיטה:

מטמון = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .weakKeys () .weakValues ​​() .build (k -> DataObject.get ("נתונים עבור" + k));

כדי לאתחל מדיניות מותאמת אישית, עלינו ליישם את ה- תְפוּגָה מִמְשָׁק:

cache = Caffeine.newBuilder (). expireAfter (Expiry new () {@Override public long expireAfterCreate (Key String, DataObject value, long currentTime) {return value.getData (). length () * 1000;} @Override public expireAfterUpdate (מפתח מחרוזת, ערך DataObject, זמן הנוכחי ארוך, זמן הנוכחי ארוך) {החזר הנוכחי משך זמן;} @ העבר לציבור ארוך expireAfterRead (מפתח מחרוזת, ערך DataObject, זמן הנוכחי זמן ארוך, זמן ארוך הנוכחי) {החזור הנוכחי משך;}}). לבנות (k -> DataObject .get ("נתונים עבור" + k));

4.3. פינוי מבוסס הפניה

אנו יכולים להגדיר את המטמון כך שיאפשר איסוף אשפה של מקשי מטמון ו / או ערכים. לשם כך, הגדר את השימוש ב- הגנה חלשה הן למפתחות והן לערכים, ונוכל להגדיר את ה- SoftReference לאיסוף ערכי אשפה בלבד.

ה הגנה חלשה השימוש מאפשר איסוף אשפה של חפצים כאשר אין התייחסויות חזקות לאובייקט. SoftReference מאפשר לאסוף חפצים על בסיס האסטרטגיה הגלובלית של ה- JVM העולמית. ניתן למצוא פרטים נוספים על הפניות ב- Java כאן.

עלינו להשתמש קפאין.חלש מפתחות (), Caffeine.weakValues ​​(), ו Caffeine.softValues ​​() כדי להפעיל כל אפשרות:

מטמון LoadingCache = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .weakKeys () .weakValues ​​() .build (k -> DataObject.get ("נתונים עבור" + k)); מטמון = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .softValues ​​() .build (k -> DataObject.get ("נתונים עבור" + k));

5. מרענן

ניתן להגדיר את המטמון כך שירענן ערכים לאחר תקופה מוגדרת באופן אוטומטי. בואו נראה איך לעשות זאת באמצעות ה- refreshAfterWrite שיטה:

Caffeine.newBuilder () .refreshAfterWrite (1, TimeUnit.MINUTES) .build (k -> DataObject.get ("נתונים עבור" + k));

כאן עלינו להבין א הבדל בין יפוג לאחר ו לרענן אחרי. כאשר מתבקש הערך שפג תוקפו, חסימת ביצוע עד שהערך החדש היה מחושב על ידי ה- build פוּנקצִיָה.

אך אם הערך כשיר לרענון, המטמון יחזיר ערך ישן ו טען מחדש את הערך באופן אסינכרוני.

6. סטטיסטיקה

לקפאין יש אמצעי הקלטת נתונים סטטיסטיים אודות השימוש במטמון:

טעינת מטמון מטמון = Caffeine.newBuilder () .maximumSize (100) .recordStats () .build (k -> DataObject.get ("נתונים עבור" + k)); cache.get ("A"); cache.get ("A"); assertEquals (1, cache.stats (). hitCount ()); assertEquals (1, cache.stats (). missCount ());

אנו עשויים גם לעבור recordStats הספק, שיוצר יישום של StatsCounter. אובייקט זה יידחק בכל שינוי הקשור לסטטיסטיקה.

7. מסקנה

במאמר זה התוודענו לספריית המטמון לקפאין עבור Java. ראינו כיצד להגדיר ולאחסן מטמון, כמו גם כיצד לבחור מדיניות תפוגה או רענון מתאימה בהתאם לצרכים שלנו.

קוד המקור המוצג כאן זמין ב- Github.


$config[zx-auto] not found$config[zx-overlay] not found