לדלג לתוכן

איך מעצבים מקצר URLs - מקרה בוחן

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

שלב 1 - מה בעצם צריך לבנות

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

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

שלב 2 - איך יוצרים את הקוד הקצר

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

הגישה הנקייה יותר היא להשתמש במונה עולה (auto-increment counter) שמייצר מספר ייחודי לכל URL חדש, ואז להמיר את המספר הזה לבסיס 62 - כלומר שילוב של אותיות גדולות, קטנות, וספרות (a-z, A-Z, 0-9). מספר כמו 125 בבסיס 10 הופך למחרוזת קצרה בבסיס 62, בדיוק כמו שהמספר 10 בבסיס עשרוני הופך ל-A בבסיס הקסדצימלי. עם מחרוזת של שבעה תווים בבסיס 62, יש כ-3.5 טריליון קומבינציות אפשריות - יותר מספיק לכל שימוש סביר, בלי סיכון להתנגשויות.

שלב 3 - עיצוב מסד הנתונים

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

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

שלב 4 - קאשינג הוא לא אופציונלי, הוא הליבה

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

שלב 5 - איזו הפניה - 301 או 302

זו נקודה קטנה שמראיינים אוהבים לבדוק, כי היא בודקת הבנה, לא רק שינון. הפניית 301 (Permanent Redirect) אומרת לדפדפן שההפניה קבועה, והדפדפן שומר אותה בקאש שלו - בפעם הבאה הוא בכלל לא יפנה לשרת שלכם, אלא ילך ישר לכתובת המקורית. זה חוסך עומס על השרת, אבל זה אומר שאתם מאבדים נתוני שימוש, כי אתם לא רואים את הלחיצות הבאות.

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

שלב 6 - מה עוד כדאי להזכיר

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

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

רוצים לתרגל שאלת system design דומה עם אנשים נוספים? בואו לדיסקורד, יש שם קבוצות שמתרגלות בדיוק את סוג התרגילים האלה.

לסיכום

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