מצב שינה של מטמון ברמה שנייה

1. סקירה כללית

אחד היתרונות של שכבות הפשטה של ​​מסדי נתונים כגון מסגרות ORM (מיפוי התייחסות-עצמים) הוא שלהן יכולת לשמור במטמון נתונים באופן שקוף אוחזר מהחנות הבסיסית. זה עוזר לחסל את עלויות הגישה למסד הנתונים עבור נתונים שהגישה אליהם נפוצה.

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

במאמר זה אנו חוקרים מטמון ברמה שנייה במצב שינה.

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

2. מהו מטמון מדרגה שנייה?

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

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

מצד שני, מטמון מדרגה שנייה הוא SessionFactory-scoped, כלומר הוא משותף לכל ההפעלות שנוצרו באותו מפעל הפעלות. כאשר מופע של ישות נבדק על ידי המזהה שלו (או על ידי לוגיקת היישום או על ידי מצב שינה פנימי, לְמָשָׁל כאשר הוא טוען שינויים לאותו ישות מגופים אחרים), ואם מופעל מטמון ברמה שנייה עבור ישות זו, קורה הדבר הבא:

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

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

3. מפעל אזור

מטמון במטמון ברמה שנייה במצב שינה תרדמה נועד להיות לא מודע לספק המטמון בפועל. למצב שינה יש לספק רק יישום של org.hibernate.cache.spi.RegionFactory ממשק המכיל את כל הפרטים הספציפיים לספקי המטמון בפועל. ביסודו של דבר, הוא משמש כגשר בין ספקי שינה למטמון.

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

אנו מוסיפים את יישום המפעל של אזור Ehcache למסלול הכיתה עם התלות הבאה ב- Maven:

 org.hibernate hibernate-ehcache 5.2.2.Final 

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

ה שינה-אה-מטמון לחפץ יש תלות ביישום ה- Ehcache עצמו, שנכלל באופן מעברי גם בכיתה.

4. הפעלת זיכרון מטמון ברמה שנייה

עם שני המאפיינים הבאים אנו אומרים ל- Hibernate שמטמון L2 מופעל ואנחנו נותנים לו את שם מחלקת המפעל באזור:

hibernate.cache.use_second_level_cache = hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.EhCacheRegionFactory 

לדוגמה, ב התמדה.קסמל זה ייראה כמו:

 ...   ... 

כדי להשבית מטמון ברמה שנייה (למטרות איתור באגים למשל), פשוט הגדר hibernate.cache.use_second_level_cache רכוש לשקר.

5. הפיכת ישות למטמון

כדי הפוך ישות כשירה למטמון במטמון ברמה שנייה, אנו מציינים זאת בספציפית למצב שינה @ org.hibernate.annotations.Cache ביאור וציין אסטרטגיית מקבילות מטמון.

יש מפתחים הרואים שזה מוסכם טוב להוסיף את התקן @ javax.persistence.Cacheable הערה גם כן (אם כי אינה נדרשת על ידי מצב שינה), לכן יישום מחלקת ישויות עשוי להיראות כך:

@Entity @ Cacheable @ org.hibernate.annotations.Cache (use = CacheConcurrencyStrategy.READ_WRITE) Foo class class {@Id @GeneratedValue (strategy = GenerationType.AUTO) @Column (name = "ID") מזהה ארוך פרטי @Column (name = "NAME") שם מחרוזת פרטי; // גטרים וקובעים}

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

לדוגמה, פו מופעים נשמרים במטמון בשם com.baeldung.hibernate.cache.model.Foo ב- Ehcache.

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

Foo foo = Foo חדש (); fooService.create (foo); fooService.findOne (foo.getId ()); int size = CacheManager.ALL_CACHE_MANAGERS.get (0) .getCache ("com.baeldung.hibernate.cache.model.Foo"). getSize (); assertThat (גודל, גדול יותר (0));

כאן אנו משתמשים ב- Ehcache API ישירות כדי לאמת זאת com.baeldung.hibernate.cache.model.Foo המטמון אינו ריק לאחר טעינת a פו למשל.

אתה יכול גם לאפשר רישום של SQL שנוצר על ידי מצב שינה ולהפעיל fooService.findOne (foo.getId ()) פעמים רבות במבחן כדי לוודא שה- בחר הצהרה לטעינה פו מודפס פעם אחת בלבד (בפעם הראשונה), כלומר בשיחות הבאות מופע הישות נלקח מהמטמון.

6. אסטרטגיית זמירות במטמון

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

  • לקריאה בלבד: משמש רק עבור ישויות שלעולם לא משתנות (יוצא מהכלל אם נעשה ניסיון לעדכן ישות כזו). זה מאוד פשוט וביצועי. מתאים מאוד לכמה נתוני הפניה סטטיים שאינם משתנים
  • NONSTRICT_READ_WRITE: המטמון מעודכן לאחר שבוצעה עסקה ששינתה את הנתונים המושפעים. לפיכך, לא מובטחת עקביות חזקה ויש חלון זמן קטן בו ניתן להשיג נתונים מעופשים מהמטמון. סוג זה של אסטרטגיה מתאים למקרי שימוש שיכולים לסבול עקביות בסופו של דבר
  • קרוא וכתוב: אסטרטגיה זו מבטיחה עקביות חזקה בה היא משיגה באמצעות נעילות 'רכות': כאשר ישות במטמון מתעדכנת, נעילה רכה נשמרת במטמון גם עבור אותה ישות, אשר משוחררת לאחר ביצוע העסקה. כל העסקאות בו זמנית שניגשות לערכים נעולים רכים יביאו את הנתונים המתאימים ישירות ממסד הנתונים
  • טרנזאקציה: שינויים במטמון נעשים בעסקאות XA מבוזרות. שינוי בישות במטמון מתחייב או מוחזר גם במסד הנתונים וגם במטמון באותה עסקת XA

7. ניהול מטמון

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

לדוגמה, נוכל להגדיר את תצורת ה- Ehcache הבאה כדי להגביל את המספר המרבי של מטמון פו מקרים עד 1000:

8. מטמון אוסף

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

@Entity @ Cacheable @ org.hibernate.annotations.Cache (use = CacheConcurrencyStrategy.READ_WRITE) Foo class class {... @Cacheable @ org.hibernate.annotations.Cache (use = CacheConcurrencyStrategy.READ_WRITE) @OneToMany ברים אוספים; // גטרים וקובעים}

9. ייצוג פנימי של המדינה במטמון

ישויות אינן מאוחסנות במטמון ברמה שנייה כמופעי Java, אלא במצבן המפורק (לחות):

  • מזהה (מפתח ראשי) לא נשמר (הוא נשמר כחלק ממפתח המטמון)
  • מאפיינים חולפים אינם מאוחסנים
  • אוספים לא נשמרים (ראה להלן לפרטים נוספים)
  • ערכי נכסים שאינם שייכים מאוחסנים בצורתם המקורית
  • שמור רק id (מפתח זר) לאחד עמותות

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

9.1. ייצוג פנימי של אוספים במטמון

כבר הזכרנו שעלינו לציין במפורש כי אוסף (אחד לרבים אוֹ ManyToMany אסוציאציה) ניתן למטמון, אחרת הוא לא נשמר במטמון.

למעשה, מצב שינה מאחסן אוספים באזורי מטמון נפרדים, אחד לכל אוסף. שם האזור הוא שם מחלקה מוסמך לחלוטין ושם נכס האוסף, למשל: com.baeldung.hibernate.cache.model.Foo.bars. זה נותן לנו את הגמישות להגדיר פרמטרי מטמון נפרדים לאוספים, לְמָשָׁל מדיניות פינוי / תפוגה.

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

10. אימות מטמון עבור שאילתות בסגנון HQL DML ושאילתות מקוריות

כשמדובר ב- HQL בסגנון DML (לְהַכנִיס, עדכון ו לִמְחוֹק הצהרות HQL), מצב שינה יכול לקבוע אילו ישויות מושפעות מפעולות כאלה:

entityManager.createQuery ("עדכן Foo להגדיר ... איפה ..."). executeUpdate ();

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

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

session.createNativeQuery ("עדכן FOO להגדיר ... איפה ..."). executeUpdate ();

זה כנראה לא מה שאתה רוצה! הפיתרון הוא להגיד למצב שינה את הישויות המושפעות מהצהרות DML מקוריות, כך שהיא תוכל לפנות רק ערכים הקשורים פו ישויות:

שאילתה nativeQuery = entityManager.createNativeQuery ("עדכן ערכת FOO ... היכן ..."); nativeQuery.unwrap (org.hibernate.SQLQuery.class) .addSynchronizedEntityClass (Foo.class); nativeQuery.executeUpdate ();

יש לנו גם לחזור אל הילידים במצב שינה SQLQuery API, מכיוון שתכונה זו אינה מוגדרת (עדיין) ב- JPA.

שים לב שהאמור לעיל חל רק על הצהרות DML (לְהַכנִיס, עדכון, לִמְחוֹק ושיחות פונקציה / הליך מקומי). יָלִיד בחר שאילתות אינן מבטלות את המטמון.

11. מטמון שאילתות

ניתן לשמור במטמון תוצאות של שאילתות HQL. זה שימושי אם אתה מבצע לעתים קרובות שאילתה על ישויות שמשתנות לעתים רחוקות.

כדי להפעיל מטמון שאילתה, הגדר את הערך של hibernate.cache.use_query_cache נכס ל נָכוֹן:

hibernate.cache.use_query_cache = נכון

לאחר מכן, עבור כל שאילתה עליך לציין במפורש שהשאילתה ניתנת לשמירה (באמצעות org.hibernate.cacheable רמז לשאילתות):

entityManager.createQuery ("בחר f מ Foo f") .setHint ("org.hibernate.cacheable", נכון) .getResultList ();

11.1. שיטות עבודה מומלצות לשאילתת מטמון שאילתות

הנה כמה הנחיות ושיטות עבודה מומלצות הקשורות למטמון שאילתות:

  • כמו במקרה של אוספים, נשמרים במטמון רק מזהים של ישויות שהוחזרו כתוצאה משאילתה הניתנת למטמון, ולכן מומלץ מאוד להפעיל מטמון ברמה השנייה עבור ישויות כאלה.
  • יש ערך מטמון אחד לכל שילוב של ערכי פרמטר שאילתה (משתני כריכה) עבור כל שאילתה, לכן שאילתות שאתה מצפה להן שילובים שונים של ערכי פרמטרים אינן מועמדות טובות למטמון.
  • שאילתות הכוללות מחלקות ישויות שיש שינויים תכופים עבורן במסד הנתונים אינן מועמדות טובות למטמון, משום שהן לא יופסלו בכל פעם שיש שינוי הקשור לאחת מהישויות המסווגות המשתתפות בשאילתה, בין אם המקרים שהשתנו הם נשמר במטמון כחלק מתוצאת השאילתה או לא.
  • כברירת מחדל, כל תוצאות מטמון השאילתות נשמרות ב org.hibernate.cache.internal.StandardQueryCache אזור. כמו במטמון ישויות / אוספים, באפשרותך להתאים אישית פרמטרי מטמון לאזור זה כדי להגדיר מדיניות פינוי ותוקף לפי הצרכים שלך. עבור כל שאילתה ניתן גם לציין שם אזור מותאם אישית על מנת לספק הגדרות שונות לשאילתות שונות.
  • בכל הטבלאות שנחקרו כחלק משאילתות הניתנות למטמון, מצב שינה שומר על חותמות זמן של העדכון האחרון באזור נפרד בשם org.hibernate.cache.spi.UpdateTimestampsCache. להיות מודע לאזור זה חשוב מאוד אם אתה משתמש במטמון שאילתות, מכיוון ש- Hibernate משתמש בו כדי לוודא שתוצאות השאילתות במטמון אינן מעופשות. אין לפנות / לפוג את הערכים במטמון זה כל עוד ישנן תוצאות שאילתות במטמון עבור הטבלאות המתאימות באזורי תוצאות השאילתות. עדיף לכבות פינוי ותפוגה אוטומטיים לאזור המטמון הזה, מכיוון שהוא ממילא לא צורך הרבה זיכרון.

12. מסקנה

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

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