שילוב Firebase Realtime Database לאפליקציות מובייל
Firebase Realtime Database (RTDB) הוא עץ JSON עם סנכרון WebSocket. זה לא מסד נתונים רלציוני ולא מסד נתונים מסמכים קלאסי. התכונות המרכזיות שלו הן שמירה במצב לא מקוון וסנכרון בזמן אמת מהקופסה. אבל מבנה נתונים לא תקין ב-RTDB הופך את היתרונות האלה לבעיות: הרשמה לצומת מקונן עמוק מושכת את כל תת-העץ לזיכרון המכשיר. ראינו פרויקטים שבהם מבנה שטוח גרם לאפליקציה להוריד 50 MB של נתונים בכל עדכון. לצ'אטים בזמן אמת, RTDB מהיר פי 2-3 מ-Firestore בהשהיית מסירת הודעות. איך להימנע מזה? בואו נפרק את זה.
למה מבנה נתונים לא תקין הורג ביצועים
RTDB לא תומך ב-JOINs ולא יכול לבחור תת-קבוצה של צומת. אם תקננו פוסטים בתוך פרופיל משתמש, קריאת users/$uid שולפת את כל הפוסטים במלואם. עם שמירה במצב לא מקוון, הם נשמרים במטמון, תופסים מקום. הפתרון הוא דנורמליזציה ואינדקסים הפוכים. דוגמה למבנה נכון:
{
"users": {
"uid123": {
"name": "Иван",
"email": "ivan@..."
}
},
"posts": {
"postId1": {
"userId": "uid123",
"text": "...",
"createdAt": 1700000000
}
},
"userPosts": {
"uid123": {
"postId1": true,
"postId2": true
}
}
}{ "users": { "uid123": { "name": "Иван", "email": "ivan@..." } }, "posts": { "postId1": { "userId": "uid123", "text": "...", "createdAt": 1700000000 } }, "userPosts": { "uid123": { "postId1": true, "postId2": true } } } הוא אינדקס הפוך לקבלת הפוסטים של משתמש ספציפי ללא סריקת כל הצומת userPosts. זהו תבנית RTDB סטנדרטית. אנו מבטיחים שהמבנה יעוצב בהתאם לתבניות Firebase — מעל 10 פרויקטי אינטגרציה.
איך אנו משלבים Firebase RTDB
תהליך העבודה שלנו כולל שלבים, כל אחד מלווה בבדיקת קוד ובדיקות על מכשירים אמיתיים. לצוות שלנו ניסיון עם מעל 20 פרויקטי Firebase.
| שלב | תיאור | לוח זמנים משוער |
|---|---|---|
| ניתוח תרחישים | קביעה אילו נתונים דורשים זמן אמת | מיומיים |
| עיצוב מבנה נתונים | דנורמליזציה, אינדקסים הפוכים, חלוקה | מ-3 ימים |
| הגדרת כללי אבטחה | אימות, אימות זהות, בקרת גישה | מיום אחד |
| יישום הרשמות | שמירה במצב לא מקוון, בחירת posts לעומת on('value') |
מ-4 ימים |
| אופטימיזציית מטמון | on('child_added'), גודל מטמון |
מיום אחד |
| בדיקת עומסים | בדיקה עד 10k משתמשים בו-זמנית | מיומיים |
| תיעוד ומסירה | דיאגרמת ארכיטקטורה, תיאור כללים, פריסה | מיום אחד |
העלות מחושבת באופן אישי לאחר ניתוח הפרויקט שלך. צרו קשר לייעוץ.
עוד על טרנזקציות
לייקים, מונים, יתרות — כל הגדלה בו-זמנית דורשת טרנזקציות:
const likeRef = database().ref(`/posts/${postId}/likes`);
await likeRef.transaction(currentLikes => (currentLikes ?? 0) + 1);keepSynced קורא וכותב בצורה אטומית. אם לקוח אחר משנה את הערך בין קריאה לכתיבה, הטרנזקציה חוזרת אוטומטית (עד 25 פעמים). ללייקים, זו הגישה הנכונה היחידה — const likeRef = database().ref(`/posts/${postId}/likes`); await likeRef.transaction(currentLikes => (currentLikes ?? 0) + 1); גורם לתנאי מרוץ בלחיצות בו-זמניות.
איך להימנע מדליפות זיכרון עם הרשמות
import database from '@react-native-firebase/database';
useEffect(() => {
const ref = database().ref(`/userPosts/${userId}`);
const onValue = ref.on('value', snapshot => {
const postIds = Object.keys(snapshot.val() ?? {});
setPostIds(postIds);
});
const onChildAdded = ref.on('child_added', snapshot => {
setPostIds(prev => [...prev, snapshot.key!]);
});
return () => {
ref.off('value', onValue);
ref.off('child_added', onChildAdded);
};
}, [userId]);קריטי: תמיד קראו ל-transaction() בעת ביטול הרכבה. set(currentLikes + 1) ללא import database from '@react-native-firebase/database'; useEffect(() => { const ref = database().ref(`/userPosts/${userId}`); const onValue = ref.on('value', snapshot => { const postIds = Object.keys(snapshot.val() ?? {}); setPostIds(postIds); }); const onChildAdded = ref.on('child_added', snapshot => { setPostIds(prev => [...prev, snapshot.key!]); }); return () => { ref.off('value', onValue); ref.off('child_added', onChildAdded); }; }, [userId]); הוא דליפת זיכרון: המאזין חי לנצח, מרנדר מחדש רכיב שכבר לא קיים. בייצור, זה גורם לקריסה: ref.off(). לחלופין, הפשטו מאזינים ל-hook מותאם אישית עם ניקוי אוטומטי.
שמירה במצב לא מקוון
import database from '@react-native-firebase/database';
database().setPersistenceEnabled(true);
database().setPersistenceCacheSizeBytes(10 * 1024 * 1024); // 10 MBon() מאפשר מטמון SQLite במכשיר. במצב לא מקוון, האפליקציה קוראת מהמטמון. כשהרשת חוזרת, היא מסנכרנת שינויים. קראו רק פעם אחת באתחול, לפני כל חיבור למסד נתונים.
off() על צומת ספציפי טוען מראש נתונים ושומר אותם במטמון גם ללא מאזינים פעילים. זהירות: אל תחילו על צמתים גדולים — RTDB יוריד את כל העץ.
הגדרת כללי אבטחה
לפי מדריך כללי האבטחה של Firebase Realtime Database, כללי RTDB ברירת המחדל מאפשרים קריאה/כתיבה לכולם או לאף אחד. אנו תמיד מגדירים אותם לפני הייצור:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"posts": {
"$postId": {
".read": "auth != null",
".write": "auth != null && newData.child('userId').val() === auth.uid",
".validate": "newData.hasChildren(['userId', 'text', 'createdAt'])"
}
}
}
}Can't perform a React state update on an unmounted component בודק את מבנה הנתונים לפני כתיבה. ללא אימות, הלקוח יכול לכתוב JSON שרירותי.
בחירה בין RTDB ל-Firestore
| קריטריון | RTDB | Firestore |
|---|---|---|
| סוג נתונים | היררכי (JSON) | מונחה מסמכים |
| שאילתות | רק לפי מפתחות וסינון | שאילתות מורכבות, אינדקסים מרוכבים |
| קנה מידה | עד 1M בו-זמנית | גבוה יותר, קנה מידה אוטומטי |
| זמן אמת | השהיה נמוכה (WebSocket) | השהיה גבוהה יותר, אבל תכונות עשירות יותר |
| לא מקוון | שמירה תמיד מופעלת | אופציונלי |
| מחיר | $5/GB אחסון + $1/GB תעבורה | $0.06/100K פעולות |
מסקנה: RTDB מיועד לצ'אטים, נוכחות, משחקים. Firestore מיועד לשאילתות מורכבות ומספר גדול של משתמשים. הצוות שלנו עוזר לבחור את האפשרות האופטימלית.
מה כלול באינטגרציית Firebase RTDB
- דיאגרמת ארכיטקטורת נתונים: דנורמליזציה, אינדקסים הפוכים, חלוקה.
- יישום הרשמות עם שמירה במצב לא מקוון: קוד ל-React Native / Flutter / iOS / Android.
- הגדרת כללי אבטחה: אימות, אימות זהות, אופטימיזציה.
- בדיקת עומסים: אימות עד 10k משתמשים בו-זמנית.
- תיעוד: תיאור API, כללים, מדריך פריסה.
- תמיכה לאחר השקה: אחריות ל-3 חודשים.
נעריך את הפרויקט שלך. הזמינו אינטגרציית Firebase RTDB היום.







