מתי יש צורך בהחלפת DBMS: תרחישים אמיתיים
נתקלנו בפרויקטים שבהם חברה מחליטה לבצע הגירה מ-MySQL ל-PostgreSQL עקב חוסר בפונקציות חלון, או מ-MongoDB ל-DBMS רלציוני עבור עסקאות ACID. מקרה נפוץ נוסף הוא הגירה מ-MySQL ל-PostgreSQL כדי לעבוד עם נתונים גיאוגרפיים באמצעות PostGIS. שינוי סוג מסד הנתונים אינו רק ייצוא טבלאות: נדרשים טרנספורמציית סכמה, בדיקות תקינות וכיוונון ביצועים. הניסיון שלנו כולל למעלה מ-50 הגירות ב-5 שנים בעבודה עם מסדי נתונים מ-10 GB עד 5 TB. אנו מכירים את המלכודות האופייניות וכיצד להימנע מהן.
מהם הסיכונים בהגירה בין DBMS שונים?
הקשיים העיקריים הם הבדלים בסוגי נתונים, רגישות לאותיות רישיות, טיפול ב-NULL ובתאריכים. להלן טבלת מיפוי סוגים לשלושה DBMS פופולריים:
| סוג MySQL | סוג PostgreSQL | סוג MongoDB | הערה |
|---|---|---|---|
tinyint(1) |
boolean |
bool |
המרה אוטומטית |
enum |
text + CHECK |
אין | יש ליצור domain או CHECK |
datetime |
timestamptz |
Date |
אזור זמן |
varchar |
text |
string |
כמעט ללא שינויים |
geometry |
geography |
אין | PostGIS דורש הגדרה נפרדת |
בעיה אופיינית נוספת היא תאריכי אפס (0000-00-00): PostgreSQL אינו מקבל אותם, ויש להחליפם ב-NULL. כמו כן, שאילתות עם GROUP BY לא סטנדרטי דורשות שכתוב, ויש להסיר גרשיים הפוכים (backticks).
כיצד אנו מבצעים הגירת נתונים: טכנולוגיה וכלים
אנו משתמשים בכלי ETL ייעודיים ובסקריפטים מותאמים אישית לאוטומציה. נבחן שני תרחישים פופולריים.
MySQL → PostgreSQL עם pgloader
pgloader הוא הבחירה הטובה ביותר להעברה ישירה. הוא ממיר פי 3 מהר יותר מגישה ידנית ומטפל אוטומטית באינדקסים, מפתחות זרים ורצפים. דוגמת תצורה:
LOAD DATABASE FROM mysql://user:pass@mysql-host/myapp
INTO postgresql://user:pass@pg-host/myapp
WITH include no drop, create tables, create indexes, reset sequences
SET work_mem to '256MB', maintenance_work_mem to '512MB'
CAST type datetime to timestamptz using midnight-in-utc,
type tinyint(1) to boolean using tinyint-to-boolean,
type enum to text,
column orders.status to text
ALTER SCHEMA 'myapp' RENAME TO 'public'
EXCLUDING TABLE NAMES MATCHING 'cache_*', 'sessions' ;pgloader מאפשר casting גמיש והחרגה של טבלאות מיותרות. השוואה עם ETL ידני:
| פרמטר | pgloader | ETL ידני |
|---|---|---|
| מהירות | עד 100 MB/s | 20-30 MB/s |
| אוטומציית אינדקסים | כן | לא |
| תצורת casting ידנית | מינימלית | גבוהה |
MongoDB → PostgreSQL עם נורמליזציה
MongoDB מאחסן מסמכים מקוננים, שבמודל הרלציוני דורשים טבלאות נפרדות. הסקריפט שלנו ב-Python מעבד אוספים בקבוצות, תוך שימוש ב-LOAD DATABASE FROM mysql://user:pass@mysql-host/myapp INTO postgresql://user:pass@pg-host/myapp WITH include no drop, create tables, create indexes, reset sequences SET work_mem to '256MB', maintenance_work_mem to '512MB' CAST type datetime to timestamptz using midnight-in-utc, type tinyint(1) to boolean using tinyint-to-boolean, type enum to text, column orders.status to text ALTER SCHEMA 'myapp' RENAME TO 'public' EXCLUDING TABLE NAMES MATCHING 'cache_*', 'sessions' ; לשדה מטא-דאטה גמיש:
from pymongo import MongoClient
import psycopg2
from psycopg2.extras import execute_batch
import json
mongo = MongoClient('mongodb://localhost:27017')
pg = psycopg2.connect('host=pg-host dbname=myapp user=app')
source = mongo.myapp.users
cursor = pg.cursor()
batch = []
for doc in source.find():
batch.append((
str(doc['_id']),
doc.get('email'),
doc.get('name'),
json.dumps(doc.get('metadata', {})),
doc.get('created_at')
))
if len(batch) >= 1000:
execute_batch(cursor, """INSERT INTO users (id, email, name, metadata, created_at) VALUES (%s, %s, %s, %s::jsonb, %s) ON CONFLICT (id) DO NOTHING""", batch)
pg.commit()
batch = []
if batch:
execute_batch(cursor, query, batch)
pg.commit()
עבור מערכים מקוננים (לדוגמה, כתובות) אנו יוצרים טבלה נפרדת עם מפתח זר ומעבירים נתונים בלולאה.
מדוע zero-downtime הוא הסטנדרט למערכות קריטיות לעסק?
כדי למנוע השבתה, אנו מיישמים את תבנית dual-write. כל כתיבה משוכפלת לשני ה-DBMS, והקריאות נשארות על הישן עד שנתונים היסטוריים מסונכרנים. לאחר מעבר הקריאות ושבוע של ניטור, אנו מפרקים את מסד הנתונים הישן. קוד המאגר:
class DualWriteRepository:
def __init__(self, primary, secondary):
self.primary = primary
self.secondary = secondary
def create_user(self, data):
result = self.primary.create_user(data)
try:
self.secondary.create_user(data)
except Exception as e:
logger.error(f"Secondary write failed: {e}")
queue.put(('create_user', data))
return resultגישה זו מפחיתה את סיכון אובדן הנתונים ל-0.01% ומאפשרת rollback בכל רגע. אנו מבטיחים 99.99% שלמות בפעולת dual-write תקינה.
כיצד להבטיח שלמות נתונים?
אנו בודקים ספירות שורות וסכומי ביקורת (checksums) בכל הטבלאות. עבור PostgreSQL אנו משתמשים ב-jsonb על נתונים ממוינים:
SELECT md5(array_agg(md5(id::text || email))::text) FROM (SELECT id, email FROM users ORDER BY id) t; MySQL מפיק hash דומה, ולאחר ההגירה הם חייבים להתאים. בנוסף, אנו מבצעים השוואת מדגם אקראי של 10%.
כיצד אנו בודקים את ההגירה?
בדיקות הן המפתח. אנו פורסים עותק מלא של מסד הנתונים בסביבת staging, מריצים סקריפטים, משווים hashes ומבצעים בדיקות עומס. רק לאחר בדיקות מוצלחות אנו מתחילים dual-write על הייצור. אם משהו משתבש, אנו חוזרים ל-DB המקורי.
סקירת תהליך
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח | 1-2 ימים | מסמך ביקורת של סכמה ותלויות |
| תכנון | 1-3 ימים | מיפוי סוגים, תוכנית dual-write |
| יישום | 3-10 ימים | סקריפטי הגירה ו-rollback |
| בדיקות | 2-5 ימים | השוואת hashes, בדיקות עומס |
| פריסה | 1-2 ימים | התחלת dual-write, מעבר קריאות |
מה כלול ואחריות
- תיעוד של הסכמה הסופית ומיפוי הסוגים.
- סקריפטי הגירה ו-rollback.
- ריצת בדיקה על עותק מלא של מסד הנתונים.
- הכשרת הצוות על ה-DBMS החדש.
- תמיכה למשך שבועיים לאחר הפריסה.
- אחריות ל-99.99% שלמות נתונים.
דוגמה: הגירת חנות מסחר אלקטרוני מ-MySQL ל-PostgreSQL
ללקוח היה מסד נתונים של 120 GB עם סוגי ENUM מותאמים אישית ותאריכי אפס. הגדרנו pgloader עם 12 casts וביצענו dual-write תוך 4 ימים. המעבר היה ללא השבתה. חיסכון ברישיונות Oracle (במעבר מהם) — 40% בשנה.
לוח זמנים ועלות
עבור מסדי נתונים עד 100 GB, ההגירה אורכת מ-3 ימי עבודה (MySQL ל-PostgreSQL) עד שבועיים (MongoDB ל-PostgreSQL עם נורמליזציה). העלות מחושבת באופן אישי לאחר הערכת נפח הנתונים ומורכבות הטרנספורמציה. צרו קשר לייעוץ וקבלו תוכנית הגירה אישית ללא התחייבות. הזמינו ביקורת מקדימה של מסד הנתונים שלכם!







