أنماط قواعد البيانات متعددة المستأجرين مع أمثلة في لغة Go
دليل شامل للأنماط النموذجية للقواعد البيانات متعددة المستأجرين
التنقيط المتعدد هو نمط بنائي أساسي للتطبيقات السحابية، حيث يمكن لعدد من العملاء (المستأجرين) مشاركة البنية التحتية نفسها للتطبيق مع الحفاظ على العزلة بين البيانات.
اختيار نمط قاعدة البيانات المناسب أمر حيوي للتوسع، والأمان، والكفاءة التشغيلية.

ملخص أنماط التنقيط المتعدد
عند تصميم تطبيق تنقيط متعدد، لديك ثلاث أنماط رئيسية لعمارة قاعدة البيانات للاختيار بينها:
- قاعدة بيانات مشتركة، نموذج مشترك (الأكثر شيوعًا)
- قاعدة بيانات مشتركة، نماذج منفصلة
- قاعدة بيانات منفصلة لكل مستأجر
لكل نمط خصائص مميزة، تنازلات، وحالات استخدام. دعنا نستعرض كل نمط بالتفصيل.
النمط 1: قاعدة بيانات مشتركة، نموذج مشترك
هذا هو النمط الأكثر شيوعًا في التنقيط المتعدد، حيث يشارك جميع المستأجرين نفس قاعدة البيانات والنماذج، مع استخدام عمود tenant_id لتمييز بيانات المستأجر.
البنية
┌─────────────────────────────────────┐
│ قاعدة بيانات واحدة │
│ ┌───────────────────────────────┐ │
│ │ نموذج مشترك │ │
│ │ - المستخدمين (tenant_id, ...) │ │
│ │ - الطلبات (tenant_id, ...) │ │
│ │ - المنتجات (tenant_id, ...) │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
مثال على التنفيذ
عند تنفيذ أنماط التنقيط المتعدد، من المهم فهم أساسيات SQL. للحصول على مرجع شامل حول الأوامر واللغة SQL، راجع قائمة الأوامر SQL. إليك كيفية إعداد نمط النموذج المشترك:
-- جدول المستخدمين مع tenant_id
CREATE TABLE users (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL,
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW(),
FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);
-- فهرس على tenant_id لتحسين الأداء
CREATE INDEX idx_users_tenant_id ON users(tenant_id);
-- أمان مستوى الصف (مثال PostgreSQL)
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON users
FOR ALL
USING (tenant_id = current_setting('app.current_tenant')::INTEGER);
للمزيد من الميزات وال الأوامر الخاصة بـ PostgreSQL، بما في ذلك سياسات RLS، إدارة النماذج، وتحسين الأداء، راجع قائمة PostgreSQL.
الفلترة على مستوى التطبيق
عند العمل مع تطبيقات Go، فإن اختيار ORM المناسب يمكن أن يؤثر بشكل كبير على تنفيذ التنقيط المتعدد. تستخدم الأمثلة أدناه GORM، ولكن هناك خيارات ممتازة أخرى. للمقارنة التفصيلية بين ORMs لـ Go بما في ذلك GORM، Ent، Bun، وsqlc، راجع دليل شامل لـ ORMs لـ PostgreSQL.
// مثال في Go مع GORM
func GetUserByEmail(db *gorm.DB, tenantID uint, email string) (*User, error) {
var user User
err := db.Where("tenant_id = ? AND email = ?", tenantID, email).First(&user).Error
return &user, err
}
// وسطية لتحديد سياق المستأجر
func TenantMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tenantID := extractTenantID(r) // من النطاق الفرعي، رأس، أو JWT
ctx := context.WithValue(r.Context(), "tenant_id", tenantID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
مزايا النموذج المشترك
- أدنى تكلفة: نسخة واحدة من قاعدة البيانات، بنية تحتية محدودة
- أبسط العمليات: قاعدة بيانات واحدة لنسخها، مراقبتها، وصيانتها
- تغييرات نموذج بسيطة: تطبق المهاجرات على جميع المستأجرين في وقت واحد
- أفضل لعدد كبير من المستأجرين: استخدام فعّال للموارد
- تحليلات عبر المستأجرين: سهلة لجمع البيانات عبر المستأجرين
عيوب النموذج المشترك
- عزلة أضعف: خطر تسرب البيانات إذا نسيت الاستعلام ترشيح tenant_id
- مستخدم مزعج: يمكن أن يؤثر أداء أحد المستأجرين على الآخرين
- محدودية التخصيص: يشارك جميع المستأجرين نفس النموذج
- تحديات الامتثال: أصعب لتحقيق متطلبات العزلة الصارمة
- تعقيد النسخ الاحتياطي: لا يمكن استعادة بيانات المستأجر الفردية بسهولة
أفضل لنموذج مشترك
- تطبيقات SaaS مع عدد كبير من المستأجرين الصغار والمتوسطين
- تطبيقات لا تحتاج المستأجرين إلى نماذج مخصصة
- شركات ناشئة حساسة للتكلفة
- عندما يكون عدد المستأجرين مرتفعًا (ألف أو أكثر)
النمط 2: قاعدة بيانات مشتركة، نماذج منفصلة
يحصل كل مستأجر على نموذجه الخاص داخل نفس قاعدة البيانات، مما يوفر عزلة أفضل مع مشاركة البنية التحتية.
بنية النماذج المنفصلة
┌─────────────────────────────────────┐
│ قاعدة بيانات واحدة │
│ ┌──────────┐ ┌──────────┐ │
│ │ نموذج A │ │ نموذج B │ ... │
│ │ (المستأجر 1)│ │ (المستأجر 2)│ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────┘
تنفيذ النماذج المنفصلة
تُعد نماذج PostgreSQL ميزة قوية للاستخدام في التنقيط المتعدد. للمعلومات التفصيلية حول إدارة نماذج PostgreSQL، وسلاسل الاتصال، وأوامر إدارية قاعدة البيانات، راجع قائمة PostgreSQL.
-- إنشاء نموذج للمستأجر
CREATE SCHEMA tenant_123;
-- تعيين مسار البحث للمهام الخاصة بالمستأجر
SET search_path TO tenant_123, public;
-- إنشاء جداول في نموذج المستأجر
CREATE TABLE tenant_123.users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW()
);
إدارة الاتصالات بالقاعدة البيانات
إدارة الاتصالات بالقاعدة البيانات بكفاءة أمر حيوي لتطبيقات التنقيط المتعدد. يُستخدم الكود أدناه مع GORM، ولكن قد ترغب في استكشاف خيارات ORM أخرى. للمقارنة التفصيلية بين ORMs لـ Go بما في ذلك إدارة الاتصالات، وخصائص الأداء، وحالات الاستخدام، راجع دليل مقارنة ORMs لـ Go.
// سلسلة الاتصال مع مسار البحث
func GetTenantDB(tenantID uint) *gorm.DB {
db := initializeDB()
db.Exec(fmt.Sprintf("SET search_path TO tenant_%d, public", tenantID))
return db
}
// أو استخدام سلسلة الاتصال الخاصة بـ PostgreSQL
// postgresql://user:pass@host/db?search_path=tenant_123
مزايا النماذج المنفصلة
- عزلة أفضل: تقليل خطر تسرب البيانات عبر فصل النماذج
- التخصيص: يمكن لكل مستأجر أن يكون له هيكل جداول مختلف
- تكاليف معتدلة: لا تزال قاعدة بيانات واحدة
- نسخ احتياطية لكل مستأجر: يمكن نسخ نماذج فردية
- أفضل للاستيفاء: أقوى من نمط النموذج المشترك
عيوب النماذج المنفصلة
- تعقيد إدارة النماذج: يجب تشغيل المهاجرات لكل مستأجر
- العبء الإضافي للاتصال: يجب تعيين مسار البحث لكل اتصال
- محدودية التوسع: عدد محدود من النماذج (PostgreSQL ~10k نماذج)
- الاستعلامات عبر المستأجرين: أكثر تعقيدًا، يتطلب مراجعات نماذج ديناميكية
- القيود المواردية: موارد قاعدة البيانات مشتركة
أفضل لنموذج منفصل
- SaaS متوسط الحجم (عشرات إلى مئات المستأجرين)
- عندما يحتاج المستأجرين إلى تخصيص نماذج
- تطبيقات تحتاج إلى عزلة أفضل من النموذج المشترك
- عندما تكون متطلبات الامتثال معتدلة
النمط 3: قاعدة بيانات منفصلة لكل مستأجر
يحصل كل مستأجر على نسخة كاملة من قاعدة البيانات، مما يوفر العزلة القصوى.
بنية قاعدة البيانات المنفصلة
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ قاعدة بيانات 1 │ │ قاعدة بيانات 2 │ │ قاعدة بيانات 3 │
│ (المستأجر A) │ │ (المستأجر B) │ │ (المستأجر C) │
└──────────────┘ └──────────────┘ └──────────────┘
تنفيذ قاعدة البيانات المنفصلة
-- إنشاء قاعدة بيانات للمستأجر
CREATE DATABASE tenant_enterprise_corp;
-- الاتصال بقاعدة بيانات المستأجر
\c tenant_enterprise_corp
-- إنشاء جداول (لا حاجة لـ tenant_id!)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW()
);
إدارة الاتصالات الديناميكية
// مدير حوض الاتصالات
type TenantDBManager struct {
pools map[uint]*gorm.DB
mu sync.RWMutex
}
func (m *TenantDBManager) GetDB(tenantID uint) (*gorm.DB, error) {
m.mu.RLock()
if db, exists := m.pools[tenantID]; exists {
m.mu.RUnlock()
return db, nil
}
m.mu.RUnlock()
m.mu.Lock()
defer m.mu.Unlock()
// التحقق المزدوج بعد اكتساب القفل الكتابة
if db, exists := m.pools[tenantID]; exists {
return db, nil
}
// إنشاء اتصال جديد
db, err := gorm.Open(postgres.Open(fmt.Sprintf(
"host=localhost user=dbuser password=dbpass dbname=tenant_%d sslmode=disable",
tenantID,
)), &gorm.Config{})
if err != nil {
return nil, err
}
m.pools[tenantID] = db
return db, nil
}
مزايا قاعدة البيانات المنفصلة
- العزلة القصوى: فصل كامل للبيانات
- الأمان الأفضل: لا خطر من الوصول إلى بيانات المستأجرين
- التخصيص الكامل: يمكن لكل مستأجر أن يكون له نماذج مختلفة تمامًا
- التوسع المستقل: يمكن توسيع قواعد بيانات المستأجرين بشكل فردي
- الامتثال السهل: يلبي متطلبات العزلة الصارمة
- نسخ احتياطية لكل مستأجر: سهلة، استعادة مستقلة
- لا يوجد مستأجرين مزعجين: أداء المستأجرين لا يؤثر على بعضهم البعض
عيوب قاعدة البيانات المنفصلة
- أعلى تكلفة: تتطلب عدة نسخ من قواعد البيانات موارد أكثر
- تعقيد التشغيل: إدارة العديد من قواعد البيانات (النسخ الاحتياطية، المراقبة، المهاجرات)
- قيود الاتصال: كل نسخة من قاعدة البيانات لديها قيود على عدد الاتصالات
- التحليلات عبر المستأجرين: يتطلب توزيع البيانات أو ETL
- تعقيد المهاجرات: يجب تشغيل المهاجرات عبر جميع قواعد البيانات
- عبء الموارد: تحتاج إلى موارد أكثر من الذاكرة، المعالج، والتخزين
أفضل لقاعدة بيانات منفصلة
- SaaS للشركات مع عملاء عالية القيمة
- متطلبات الامتثال الصارمة (HIPAA، GDPR، SOC 2)
- عندما يحتاج المستأجرين إلى تخصيص كبير
- عدد مستأجرين منخفض إلى معتدل (عشرات إلى مئات منخفضة)
- عندما يكون لدى المستأجرين نماذج بيانات مختلفة تمامًا
اعتبارات الأمان
بلا عرض النمط المختار، الأمان أمر حيوي:
1. أمان مستوى الصف (RLS)
RLS في PostgreSQL تفلتر الاستعلامات تلقائيًا حسب المستأجر، مما يوفر طبقة أمان على مستوى قاعدة البيانات. هذه الميزة قوية بشكل خاص في تطبيقات التنقيط المتعدد. للمزيد من التفاصيل حول RLS في PostgreSQL، سياسات الأمان، والميزات المتقدمة الأخرى لـ PostgreSQL، راجع قائمة PostgreSQL.
-- تفعيل RLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- سياسة لعزل المستأجرين
CREATE POLICY tenant_isolation ON orders
FOR ALL
USING (tenant_id = current_setting('app.current_tenant')::INTEGER);
-- تعيين سياق المستأجر في التطبيق
SET app.current_tenant = '123';
2. الفلترة على مستوى التطبيق
يجب دائمًا ترشيح حسب tenant_id في كود التطبيق. تستخدم الأمثلة أدناه GORM، ولكن ORMs المختلفة لديها طرقها الخاصة لبناء الاستعلامات. للمعلومات حول اختيار ORM المناسب لتطبيقك متعدد المستأجرين، راجع مقارنة ORMs لـ Go.
// ❌ سيء - نقص ترشيح المستأجر
db.Where("email = ?", email).First(&user)
// ✅ جيد - تضمين ترشيح المستأجر دائمًا
db.Where("tenant_id = ? AND email = ?", tenantID, email).First(&user)
// ✅ أفضل - استخدام النطاقات أو الوسطية
db.Scopes(TenantScope(tenantID)).Where("email = ?", email).First(&user)
3. تجميع الاتصالات
استخدم تجميعات الاتصالات التي تدعم سياق المستأجر:
// PgBouncer مع تجميع عبر المعاملات
// أو استخدام توجيه الاتصالات على مستوى التطبيق
4. سجلات التدقيق
تتبع جميع الوصول إلى بيانات المستأجرين:
type AuditLog struct {
ID uint
TenantID uint
UserID uint
Action string
Table string
RecordID uint
Timestamp time.Time
IPAddress string
}
تحسين الأداء
استراتيجية الفهرسة
الفهرسة المناسبة حيوية لأداء قواعد البيانات متعددة المستأجرين. فهم استراتيجيات الفهرسة في SQL، بما في ذلك الفهارس المركبة والجزئية، أمر ضروري. للمراجع الشاملة حول الأوامر SQL بما في ذلك CREATE INDEX وتحسين الاستعلامات، راجع قائمة SQL. للميزات الخاصة بـ PostgreSQL والتحسينات، راجع قائمة PostgreSQL.
-- فهارس مركبة لاستعلامات المستأجرين
CREATE INDEX idx_orders_tenant_created ON orders(tenant_id, created_at DESC);
CREATE INDEX idx_orders_tenant_status ON orders(tenant_id, status);
-- فهارس جزئية لاستعلامات المستأجرين الشائعة
CREATE INDEX idx_orders_active_tenant ON orders(tenant_id, created_at)
WHERE status = 'active';
تحسين الاستعلامات
// استخدام الأوامر المُعدة مسبقًا لاستعلامات المستأجرين
stmt := db.Prepare("SELECT * FROM users WHERE tenant_id = $1 AND email = $2")
// عمليات دفعة لكل مستأجر
db.Where("tenant_id = ?", tenantID).Find(&users)
// استخدام تجميع الاتصالات لكل مستأجر (لنمط قاعدة البيانات المنفصلة)
مراقبة الأداء
أدوات إدارة قواعد البيانات الفعّالة ضرورية لمراقبة تطبيقات التنقيط المتعدد. ستحتاج إلى تتبع أداء الاستعلامات، استخدام الموارد، وصحة قواعد البيانات عبر جميع المستأجرين. للمقارنة بين أدوات إدارة قواعد البيانات التي يمكن أن تساعدك في ذلك، راجع مقارنة DBeaver مقابل Beekeeper. توفر كلتا الأدوات ميزات ممتازة لإدارة ورصد قواعد بيانات PostgreSQL في بيئات التنقيط المتعدد.
راقب مؤشرات المستأجرين:
- أداء الاستعلامات لكل مستأجر
- استخدام الموارد لكل مستأجر
- عدد الاتصالات لكل مستأجر
- حجم قاعدة البيانات لكل مستأجر
استراتيجية الهجرة
نمط النموذج المشترك
عند تنفيذ مهاجرات قاعدة البيانات، يؤثر اختيار ORM على كيفية التعامل مع تغييرات النموذج. تستخدم الأمثلة أدناه ميزة AutoMigrate في GORM، ولكن ORMs المختلفة لديها استراتيجيات مهاجرة مختلفة. للمعلومات التفصيلية حول كيفية التعامل مع المهاجرات وإدارة النماذج في ORMs لـ Go، راجع مقارنة ORMs لـ Go.
// تطبق المهاجرات تلقائيًا على جميع المستأجرين
func Migrate(db *gorm.DB) error {
return db.AutoMigrate(&User{}, &Order{}, &Product{})
}
نمط النموذج المنفصل/قاعدة بيانات منفصلة
// يجب تشغيل المهاجرات لكل مستأجر
func MigrateAllTenants(tenantIDs []uint) error {
for _, tenantID := range tenantIDs {
db := GetTenantDB(tenantID)
if err := db.AutoMigrate(&User{}, &Order{}); err != nil {
return fmt.Errorf("tenant %d: %w", tenantID, err)
}
}
return nil
}
مصفوفة القرار
| العامل | نموذج مشترك | نماذج منفصلة | قاعدة بيانات منفصلة |
|---|---|---|---|
| العزلة | منخفضة | متوسطة | عالية |
| التكاليف | منخفضة | متوسطة | عالية |
| التوسع | مرتفع | متوسط | منخفض إلى متوسط |
| التخصيص | لا شيء | متوسط | مرتفع |
| تعقيد التشغيل | منخفض | متوسط | مرتفع |
| الامتثال | محدود | جيد | ممتاز |
| أفضل عدد مستأجرين | 1000+ | 10-1000 | 1-100 |
النهج الهجين
يمكنك الجمع بين الأنماط لمستويات مختلفة من المستأجرين:
// المستأجرين الصغار: نموذج مشترك
if tenant.Tier == "standard" {
return GetSharedDB(tenant.ID)
}
// المستأجرين الكبار: قاعدة بيانات منفصلة
if tenant.Tier == "enterprise" {
return GetTenantDB(tenant.ID)
}
الممارسات الجيدة
- الفلترة دائمًا حسب المستأجر: لا تثق أبدًا بـ كود التطبيق وحده؛ استخدم RLS عند الإمكان. فهم أساسيات SQL يساعد في ضمان بناء الاستعلامات بشكل صحيح - راجع قائمة SQL للممارسات المثلى للاستعلامات.
- مراقبة استخدام الموارد لكل مستأجر: تحديد وتحجيم المستأجرين المزعجين. استخدم أدوات إدارة قواعد البيانات مثل تلك المقارنة في دليل DBeaver مقابل Beekeeper لتتبع مؤشرات الأداء.
- تطبيق وسطية سياق المستأجر: تركز على استخراج وتحقق المستأجرين. يؤثر اختيار ORM على كيفية تنفيذ هذا - راجع مقارنة ORMs لـ Go للطرق المختلفة.
- استخدام تجميع الاتصالات: إدارة الاتصالات بالقاعدة البيانات بكفاءة. تغطي ميزات تجميع الاتصالات الخاصة بـ PostgreSQL في قائمة PostgreSQL.
- التحضير لنقل المستأجرين: القدرة على نقل المستأجرين بين الأنماط
- تطبيق الحذف الناعم: استخدام deleted_at بدلًا من الحذف الكامل لبيانات المستأجرين
- تسجيل كل شيء: تسجيل جميع الوصول إلى بيانات المستأجرين للامتثال
- اختبار العزلة: فحص أمني دوري لمنع تسرب البيانات بين المستأجرين
الخاتمة
اختيار النمط المناسب لقاعدة البيانات متعددة المستأجرين يعتمد على متطلباتك الخاصة للعزلة، التكلفة، التوسع، وتعقيد التشغيل. يناسب نمط قاعدة بيانات مشتركة، نموذج مشترك معظم تطبيقات SaaS، بينما يلزم نمط قاعدة بيانات منفصلة لكل مستأجر للعملاء الكبار الذين يحتاجون إلى متطلبات الامتثال الصارمة.
ابدأ بالنمط الأبسط الذي يلبي متطلباتك، وخطط للانتقال إلى نمط أكثر عزلة مع تطور متطلباتك. دائمًا اجعل الأمان والعزلة بين البيانات أولوية، بغض النظر عن النمط المختار.
الروابط المفيدة
- مستندات PostgreSQL لـ Row-Level Security
- بنية قاعدة بيانات SaaS متعددة المستأجرين
- تصميم قواعد بيانات متعددة المستأجرين
- مقارنة ORMs لـ Go لـ PostgreSQL: GORM مقابل Ent مقابل Bun مقابل sqlc
- قائمة PostgreSQL: مرجع سريع للمطورين
- DBeaver مقابل Beekeeper - أدوات إدارة قواعد البيانات SQL
- قائمة SQL - الأوامر SQL الأكثر فائدة