مرحبًا، صديقي!
اليوم، دعنا نتحدث عن ماهية ترحيل قواعد البيانات ولماذا هي مهمة جدًا. في عالم اليوم، ليس من المستغرب أن أي تغييرات في قاعدة البيانات يجب أن تتم بعناية وفقًا لعملية محددة. من المثالي أن يتم دمج هذه الخطوات في خط أنابيب CI/CD الخاص بنا بحيث يتم تشغيل كل شيء تلقائيًا.
إليك جدول أعمالنا:
- ما هي المشكلة؟
- كيف يمكننا إصلاحها؟
- مثال بسيط
- مثال أكثر تعقيدًا
- توصيات
- نتائج
- استنتاج
ما هي المشكلة؟
إذا لم تتعامل فريقك مع ترحيل قواعد البيانات من قبل ولست متأكدًا تمامًا لماذا هي ضرورية، دعنا نوضح ذلك. إذا كنت تعرف الأساسيات بالفعل، فلا تتردد في تخطي إلى الأمام.
التحدي الرئيسي
عندما نقوم بإجراء تغييرات “مخطط لها” و”سلسة” على قاعدة البيانات، نحتاج إلى الحفاظ على توافر الخدمة وتلبية متطلبات SLA (حتى لا يعاني المستخدمون من انقطاع الخدمة أو التأخير). تخيل أنك تريد تغيير نوع عمود في جدول يحتوي على 5 ملايين مستخدم. إذا قمت بذلك “بشكل مباشر” (على سبيل المثال، تشغيل ALTER TABLE
ببساطة دون إعداد)، قد يتم قفل الجدول لفترة زمنية كبيرة – وسيترك مستخدموك بدون خدمة.
لتجنب مثل هذه المشاكل، اتبع قاعدتين:
- قم بتطبيق الترحيلات بطريقة لا تقفل الجدول (أو على الأقل تقلل من الأقفال).
- إذا كنت بحاجة إلى تغيير نوع العمود، فمن الأسهل غالبًا إنشاء عمود جديد بالنوع الصحيح أولاً ثم التخلص من العمود القديم بعد ذلك.
مشكلة أخرى: التحكم بالإصدارات والتراجع
أحيانًا تحتاج إلى التراجع عن تهجير.
القيام بذلك يدويًا — الدخول إلى قاعدة البيانات الإنتاجية والتلاعب بالبيانات — ليس فقط خطيرًا ولكن من المحتمل أيضًا أن يكون غير ممكن إذا لم يكن لديك وصولًا مباشرًا. هنا تأتي أدوات التهجير المخصصة للمساعدة. تتيح لك تطبيق التغييرات بنظافة وإلغائها إذا لزم الأمر.
كيف نصلح الأمر؟ استخدم الأدوات الصحيحة
لكل لغة وبيئة نظام لها أدوات تهجيرها الخاصة:
- بالنسبة لجافا، Liquibase أو Flyway شائعة.
- بالنسبة لـGo، الاختيار الشائع هو goose (التي سنلقي نظرة عليها هنا).
- وهكذا.
Goose: ما هو ولماذا هو مفيد
Goose هو أداة خفيفة لـ Go تساعدك على إدارة التهجيرات تلقائيًا. إنها تقدم:
- البساطة. قليلة الاعتمادات وهيكل ملف شفاف للتهجيرات.
- التنوع. تدعم محركات قواعد البيانات المختلفة (PostgreSQL، MySQL، SQLite، إلخ).
- المرونة. كتابة التهجيرات بلغة SQL أو Go.
تثبيت Goose
go install github.com/pressly/goose/v3/cmd/goose@latest
كيفية العمل: هيكل الهجرة
بشكل افتراضي، يبحث Goose عن ملفات الهجرة في db/migrations
. كل هجرة تتبع هذا التنسيق:
NNN_migration_name.(sql|go)
NNN
هو رقم الهجرة (مثلاً،001
،002
، إلخ).- بعد ذلك، يمكنك وضع أي اسم وصفي، مثل
init_schema
. - امتداد الملف يمكن أن يكون
.sql
أو.go
.
مثال على هجرة SQL
ملف: 001_init_schema.sql
:
-- +goose Up
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now()
);
-- +goose Down
DROP TABLE users;
مثالنا الأول
تغيير نوع العمود (سلسلة → عدد صحيح)
لنفترض أن لدينا جدول users
بعمود يسمى age
من نوع VARCHAR(255)
. الآن نريد تغييره إلى INTEGER
. هكذا قد تبدو الهجرة (ملف 005_change_column_type.sql
):
-- +goose Up
ALTER TABLE users ALTER COLUMN age TYPE INTEGER USING (age::INTEGER);
-- +goose Down
ALTER TABLE users ALTER COLUMN age TYPE VARCHAR(255) USING (age::TEXT);
ما الذي يحدث هنا:
-
هجرة الصعود
- نقوم بتغيير عمود
age
إلىINTEGER
. جملةUSING (age::INTEGER)
تخبر PostgreSQL كيفية تحويل البيانات الحالية إلى النوع الجديد. - يرجى ملاحظة أن هذه الهجرة ستفشل إذا كانت هناك بيانات في
age
ليست رقمية. في هذه الحالة، ستحتاج إلى استراتيجية أكثر تعقيداً (انظر أدناه).
- نقوم بتغيير عمود
-
الهجرة التراجعية
- إذا قمنا بالتراجع، نعيد
age
إلىVARCHAR(255)
. - نستخدم مرة أخرى
USING (age::TEXT)
للتحويل منINTEGER
إلى نص.
- إذا قمنا بالتراجع، نعيد
الحالات الثانية والمعقدة: الهجرات متعددة الخطوات
إذا كانت عمود age
قد يحتوي على بيانات غير منظمة (ليست أرقام فقط)، فمن الأكثر أمانًا القيام بذلك على عدة خطوات:
- إضافة عمود جديد (
age_int
) من نوعINTEGER
. - نسخ البيانات الصالحة إلى العمود الجديد، مع التعامل مع أو إزالة الإدخالات غير الصالحة.
- حذف العمود القديم.
-- +goose Up
-- Step 1: Add a new column
ALTER TABLE users ADD COLUMN age_int INTEGER;
-- Step 2: Try to move data over
UPDATE users
SET age_int = CASE
WHEN age ~ '^[0-9]+$' THEN age::INTEGER
ELSE NULL
END;
-- (optional) remove rows where data couldn’t be converted
-- DELETE FROM users WHERE age_int IS NULL;
-- Step 3: Drop the old column
ALTER TABLE users DROP COLUMN age;
-- +goose Down
-- Step 1: Recreate the old column
ALTER TABLE users ADD COLUMN age VARCHAR(255);
-- Step 2: Copy data back
UPDATE users
SET age = age_int::TEXT;
-- Step 3: Drop the new column
ALTER TABLE users DROP COLUMN age_int;
للسماح بالتراجع السليم، فإن قسم Down
يعكس ببساطة الإجراءات في الاتجاه المعاكس.
الأتمتة هي المفتاح
للتوفير في الوقت، من الملائم حقًا إضافة أوامر الهجرة إلى ملف Makefile (أو أي نظام بناء آخر). أدناه مثال على Makefile مع الأوامر الرئيسية لـ PostgreSQL.
دعنا نفترض:
- أن DSN لقاعدة البيانات هو
postgres://user:password@localhost:5432/dbname?sslmode=disable
. - تتواجد ملفات الترحيل في
db/migrations
.
# File: Makefile
DB_DSN = "postgres://user:password@localhost:5432/dbname?sslmode=disable"
MIGRATIONS_DIR = db/migrations
# Install Goose (run once)
install-goose:
go install github.com/pressly/goose/v3/cmd/goose@latest
# Create a new SQL migration file
new-migration:
ifndef NAME
$(error Usage: make new-migration NAME=your_migration_name)
endif
goose -dir $(MIGRATIONS_DIR) create $(NAME) sql
# Apply all pending migrations
migrate-up:
goose -dir $(MIGRATIONS_DIR) postgres $(DB_DSN) up
# Roll back the last migration
migrate-down:
goose -dir $(MIGRATIONS_DIR) postgres $(DB_DSN) down
# Roll back all migrations (be careful in production!)
migrate-reset:
goose -dir $(MIGRATIONS_DIR) postgres $(DB_DSN) reset
# Check migration status
migrate-status:
goose -dir $(MIGRATIONS_DIR) postgres $(DB_DSN) status
كيفية استخدامها؟
1. إنشاء ترحيل جديد (ملف SQL). يقوم هذا بإنشاء ملف db/migrations/002_add_orders_table.sql
.
make new-migration NAME=add_orders_table
2. تطبيق جميع الترحيلات. ستقوم Goose بإنشاء جدول schema_migrations
في قاعدة البيانات الخاصة بك (إذا لم تكن موجودة بالفعل) وتطبيق أي ترحيلات جديدة بترتيب تصاعدي.
make migrate-up
3. إلغاء ترحيل الأخير. فقط إلغاء آخر واحد.
make migrate-down
4. إلغاء جميع الترحيلات (استخدم بحذر في الإنتاج). إعادة تعيين كاملة.
make migrate-reset
5. فحص حالة الترحيل.
make migrate-status
مثال على الناتج:
$ goose status
$ Applied At Migration
$ =======================================
$ Sun Jan 6 11:25:03 2013 -- 001_basics.sql
$ Sun Jan 6 11:25:03 2013 -- 002_next.sql
$ Pending -- 003_and_again.go
الملخص
من خلال استخدام أدوات الترحيل وملف Makefile، يمكننا:
- تقييد الوصول المباشر إلى قاعدة البيانات الإنتاجية، وإجراء التغييرات فقط من خلال الترحيلات.
- تتبع إصدارات قاعدة البيانات بسهولة وإلغاؤها إذا حدث خطأ ما.
- الحفاظ على تاريخ واحد وثابت لتغييرات قاعدة البيانات.
- تنفيذ ترحيلات “سلسة” التي لن تؤدي إلى كسر بيئة الإنتاج الحالية في عالم الخدمات المصغرة.
- الحصول على تحقق إضافي — كل تغيير سيمر بعملية PR ومراجعة الكود (بشرط وجود هذه الإعدادات).
ميزة أخرى هي أنه من السهل دمج جميع هذه الأوامر في خط أنابيب CI/CD الخاص بك. وتذكر — الأمان فوق كل شيء.
على سبيل المثال:
jobs
migrate
runs-on ubuntu-latest
steps
name Install Goose
run
make install-goose
name Run database migrations
env
DB_DSN $ secrets.DATABASE_URL
run
make migrate-up
الختام والنصائح
الأفكار الرئيسية بسيطة جدًا:
- حافظ على تحركاتك صغيرة ومتكررة. فهي أسهل في المراجعة والاختبار، ويمكن التراجع عنها إذا لزم الأمر.
- استخدم نفس الأداة في جميع البيئات حتى تكون التطوير والمرحلة التجريبية والإنتاج متزامنة.
- دمج التحركات في CI/CD حتى لا تعتمد على شخص واحد يقوم بتشغيلها يدويًا.
بهذه الطريقة، ستكون لديك عملية موثوقة ومُراقبة لتغيير هيكل قاعدة البيانات الخاصة بك — عملية لا تتسبب في كسر الإنتاج وتتيح لك الاستجابة بسرعة إذا حدث خطأ ما.
حظًا سعيدًا في تحريكاتك!
شكرًا على القراءة!
Source:
https://dzone.com/articles/goose-as-crucial-tool-for-your-service