ציין את כל מקשי Redis הזמינים

ג'אווה טופ

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

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

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

במדריך זה נלמד כיצד אנו יכולים לקרוא ביעילות את כל מקשי ה- Redis הזמינים התואמים לדפוס מסוים.

2. חקור אוספים

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

  • כדור קריקט במשקל 160 גרם
  • כדורגל במשקל 450 גרם
  • כדורעף במשקל 270 גרם

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

3. גישה נאיבית באמצעות redis-cli

לפני שנתחיל לכתוב קוד ג'אווה כדי לחקור את האוספים, עלינו לקבל מושג הוגן כיצד אנו עושים זאת באמצעות ה- רדיס-קלי מִמְשָׁק. נניח שמופע Redis שלנו זמין בכתובת 127.0.0.1 בנמל 6379, כדי שנחקור כל סוג אוסף בעזרת ממשק שורת הפקודה.

3.1. רשימה מקושרת

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

% redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> כדורי RPUSH "cricket_160" (מספר שלם) 1 127.0.0.1:6379> כדורי RPUSH "כדורגל_450" (מספר שלם) 2 127.0.0.1:6379> כדורי RPUSH "כדורעף_270" (מספר שלם) 3

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

127.0.0.1:6379> כדורי llen (מספר שלם) 3

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

127.0.0.1:6379> כדורי סדר 0 2 1) "קריקט_160" 2) "כדורגל_450" 3) "כדורעף_270"

3.2. מַעֲרֶכֶת

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

127.0.0.1:6379> כדורי סאד "קריקט_160" "כדורגל_450" "כדורעף_270" "קריקט_160" (מספר שלם) 3

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

עכשיו, אנחנו יכולים למנף את סובר פקודה לראות את כל חברי הקבוצה:

127.0.0.1:6379> smembers כדורים 1) "כדורעף_270" 2) "קריקט_160" 3) "כדורגל_450"

3.3. בְּלִיל

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

127.0.0.1:6379> כדורי המסית קריקט 160 כדורגל 450 כדורעף 270 בסדר

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

127.0.0.1:6379> כדורי hgetall 1) "קריקט" 2) "160" 3) "כדורגל" 4) "450" ​​5) "כדורעף" 6) "270"

3.4. סט ממוין

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

127.0.0.1:6379> כדורי זאד 160 קריקט 450 כדורגל 270 כדורעף (מספר שלם) 3

כעת, ראשית נוכל להשתמש ב- zcard פקודה למצוא את אורך הקבוצה הממוינת, ואחריה zrange פקודה לחקור את הסט השלם:

127.0.0.1:6379> כדורי zcard (מספר שלם) 3 127.0.0.1:6379> כדורי zrange 0 2 1) "קריקט" 2) "כדורעף" 3) "כדורגל"

3.5. מיתרים

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

127.0.0.1:6379> כדורי mset: קריקט 160 כדורים: כדורגל 450 כדורים: כדורעף 270 בסדר

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

127.0.0.1:6379> כדורי מפתחות * 1) "כדורים: קריקט" 2) "כדורים: כדורעף" 3) "כדורים: כדורגל"

4. יישום Java נאיבי

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

4.1. תלות של Maven

בחלק זה נהיה משתמש ב ג'דיס ספריית לקוחות לרדיס ביישום שלנו:

 redis.clients jedis 3.2.0 

4.2. לקוח רדיס

ספריית Jedis מגיעה עם השיטות של Redis-CLI. עם זאת, מומלץ לנו צור לקוח Redis עטיפה, אשר יפעיל קריאות פונקציה של Jedis באופן פנימי.

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

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

ראשית, בואו ליצור קונסטרוקטור פרטי עבור הלקוח שלנו שיאתחל את ה- JedisPool כאשר מופע של RedisClient הכיתה נוצרת:

סטטי פרטי JedisPool jedisPool; RedisClient פרטי (מחרוזת IP, יציאת int) {נסה {if (jedisPool == null) {jedisPool = חדש JedisPool (URI חדש ("//" + ip + ":" + יציאה)); }} לתפוס (URISyntaxException e) {log.error ("כתובת שרת שגויה", e); }}

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

מופע סטטי נדיף סטטי פרטי = null; RedisClient סטטי ציבורי getInstance (IP מחרוזת, יציאת int סופי) {if (מופע == null) {מסונכרן (RedisClient.class) {if (מופע == null) {instance = RedisClient חדש (ip, port); }}} להחזיר מופע; }

לבסוף, בואו נראה כיצד נוכל ליצור שיטת עטיפה על גבי זו של ג'דיס שיטת סדר:

תחום רשימה ציבורי (מפתח מחרוזת סופי, התחלה ארוכה סופית, עצירה ארוכה סופית) {נסה (Jedis jedis = jedisPool.getResource ()) {החזר jedis.lrange (מפתח, התחל, עצור); } לתפוס (Exception ex) {log.error ("Exception caught in lrange", ex); } להחזיר קישור חדש (); }

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

4.3. אָנָלִיזָה

כל פקודות Redis שאנו יכולים להשתמש בהן לחקור אוסף בבת אחת יהיה באופן טבעי מורכבות O (n) במקרה הטוב.

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

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

5. יסודות המחזיקים

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

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

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

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

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

6. סריקת רדיס

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

6.1. אסטרטגיות סריקה

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

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

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

בואו נשתמש ב- לִסְרוֹק פקודה לסרוק מעל מקשים מסוג מחרוזת. כדי להתחיל בסריקה, עלינו להשתמש בערך הסמן כ- "0", התאמת מחרוזת תבנית כ"כדור * ":

127.0.0.1:6379> כדורי mset: קריקט 160 כדורים: כדורגל 450 כדורים: כדורעף 270 OK 127.0.0.1:6379> סריקה 0 כדור התאמה * ספירה 1 1) "2" 2) 1) "כדורים: קריקט" 127.0.0.1 : 6379> כדור סריקה 2 התאמה * ספירה 1 1) "3" 2) 1) "כדורים: כדורעף" 127.0.0.1:6379> סריקת כדור משחק 3 * ספירה 1 1) "0" 2) 1) "כדורים: כדורגל "

עם כל סריקה שהושלמה, נקבל את הערך הבא של הסמן שישמש באיטרציה הבאה. בסופו של דבר, אנו יודעים שסרקנו את כל האוסף כאשר ערך הסמן הבא הוא "0".

7. סריקה עם Java

עד עכשיו יש לנו מספיק הבנה לגבי הגישה שלנו כדי שנוכל להתחיל ליישם אותה בג'אווה.

7.1. אסטרטגיות סריקה

אם נציץ בפונקציונליות הסריקה המרכזית שמציעה ג'דיס בכיתה, אנו נמצא אסטרטגיות לסריקת סוגי אוספים שונים:

סריקת ScanResult פומבית (סמן מחרוזת סופי, פראמרי ScanParams סופיים); סריקה סריקה ציבורית של ScanResult (מקש מחרוזת סופי, סמן מחרוזת סופי, מסגרות ScanParams סופיות); ScanResult ציבורי hscan (מפתח מחרוזת סופי, סמן מחרוזת סופי, פרמטים סופיים של ScanParams); ScanResult zscan ציבורי (מקש מחרוזת סופי, סמן מחרוזת סופי, פראמרי ScanParams סופיים);

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

התאמה ציבורית של ScanParams (תבנית מחרוזת סופית); ספירת ScanParams ציבורית (ספירת מספר שלם שלם);

עכשיו שספגנו את הידע הבסיסי בנושא של ג'דיס בגישת סריקה, בואו לדגם אסטרטגיות אלה באמצעות ScanStrategy מִמְשָׁק:

ממשק ציבורי ScanStrategy {ScanResult scan (Jedis jedis, סמן מחרוזת, ScanParams scanParams); }

ראשית, בואו נעבוד על הפשוטה ביותר לִסְרוֹק אסטרטגיה, שאינה תלויה בסוג האוסף וקוראת את המפתחות, אך לא ערך המפתחות:

class class סריקה מיישמת את ScanStrategy {public ScanResult scan (Jedis jedis, String cursor, ScanParams scanParams) {return jedis.scan (cursor, scanParams); }}

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

מחלקה ציבורית Hscan מיישמת את ScanStrategy {מפתח מחרוזת פרטי; @ עקוב על ScanResult ציבורי סריקה (Jedis jedis, סמן מחרוזת, ScanParams scanParams) {החזר jedis.hscan (מפתח, סמן, scanParams); }}

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

Sscan בכיתה ציבורית מיישם את ScanStrategy {מפתח מחרוזת פרטי; סריקת סריקה ציבורית פומבית (Jedis jedis, סמן מחרוזת, ScanParams scanParams) {החזר jedis.sscan (מפתח, סמן, scanParams); }} מחלקה ציבורית Zscan מיישמת את ScanStrategy {מפתח מחרוזת פרטי; @ עקוף סריקת ScanResult ציבורית (Jedis jedis, סמן מחרוזת, ScanParams scanParams) {החזר jedis.zscan (מפתח, סמן, scanParams); }}

7.2. רדיס איטרטור

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

  • סמן מבוסס מחרוזת
  • אסטרטגיית סריקה כגון לִסְרוֹק, ססקאן, hscan, zscan
  • מציין מיקום לסריקת פרמטרים
  • גישה ל JedisPool לקבל ג'דיס מַשׁאָב

כעת נוכל להמשיך ולהגדיר את החברים הללו אצלנו RedisIterator מעמד:

גמר פרטי JedisPool jedisPool; פרטי ScanParams scanParams; סמן מחרוזת פרטי; אסטרטגיית ScanStrategy פרטית;

הבמה שלנו מוגדרת להגדרת הפונקציונליות הספציפית לאיטרציה לאיטרטור שלנו. בשביל זה, שלנו RedisIterator המחלקה חייבת ליישם את איטרטור מִמְשָׁק:

מחלקה ציבורית מיישמת את איטרטור { }

מטבע הדברים אנו נדרשים לעקוף את hasNext () ו הַבָּא() שיטות שעוברות בירושה מה- איטרטור מִמְשָׁק.

ראשית, בואו נבחר את הפירות התלויים נמוך - hasNext () שיטה - כיוון שההיגיון הבסיסי הוא ישר קדימה. ברגע שערך הסמן הופך להיות "0", אנו יודעים שסיימנו עם הסריקה. אז בואו נראה איך נוכל ליישם זאת בשורה אחת בלבד:

@ Override בוליאני ציבורי hasNext () {return! "0" .equals (cursor); }

לאחר מכן, בואו נעבוד על ה- הַבָּא() שיטה שעושה הרמה כבדה של סריקה:

@ עקוב ברשימה ציבורית הבא () {if (cursor == null) {cursor = "0"; } נסה (Jedis jedis = jedisPool.getResource ()) {ScanResult scanResult = strategy.scan (jedis, cursor, scanParams); סמן = scanResult.getCursor (); להחזיר scanResult.getResult (); } לתפוס (Exception ex) {log.error ("Exception שנתפס ב- next ()", ex); } להחזיר LinkedList חדש (); }

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

לבסוף, אנו יכולים לאפשר לפונקציונליות ליצור שלנו RedisIterator בתוך ה RedisClient מעמד:

איטרטור RedisIterator ציבורי (int initialScanCount, דפוס מחרוזת, אסטרטגיית ScanStrategy) {להחזיר RedisIterator חדש (jedisPool, initialScanCount, דפוס, אסטרטגיה); }

7.3. קרא עם רדיס איטרטור

כפי שתכננו את איטרטור Redis שלנו בעזרת ה- איטרטור ממשק, זה די אינטואיטיבי ל- קרא את ערכי האוסף בעזרת הַבָּא() שיטה כל עוד hasNext () החזרות נָכוֹן.

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

@ מבחן הריק הציבורי @ ScannStrategy () {Hash hashap = HashMap חדש (); hash.put ("קריקט", "160"); hash.put ("כדורגל", "450"); hash.put ("כדורעף", "270"); redisClient.hmset ("כדורים", hash); Hscan scanStrategy = Hscan חדש ("כדורים"); int iterationCount = 2; איטרטור RedisIterator = redisClient.iterator (iterationCount, "*", scanStrategy); רשימה תוצאות = קישור רשימה חדשה(); בעוד (iterator.hasNext ()) {results.addAll (iterator.next ()); } Assert.assertEquals (hash.size (), results.size ()); }

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

8. מסקנה

התחלנו הדרכה זו בכוונה ללמוד כיצד אנו יכולים לקרוא את כל המקשים התואמים ב- Redis.

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

כמו תמיד, קוד המקור השלם ליישום Java המשמש במאמר זה זמין ב- GitHub.

תחתית Java

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

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