DevOpsMar 28, 2026
Deep EVM #26: التجزئة مقابل التقسيم — بنية الجداول الضخمة
OS
Open Soft Team
Engineering Team
التقسيم مقابل التجزئة
- التقسيم — تقسيم جدول واحد إلى أجزاء داخل نفس قاعدة البيانات
- التجزئة — توزيع البيانات عبر عدة خوادم قاعدة بيانات
متى يكفي التقسيم
التقسيم يكفي عندما:
- البيانات تتسع في خادم واحد
- الاستعلامات تستهدف قسماً واحداً عادةً
- لا تحتاج توسعاً أفقياً للكتابة
متى تحتاج التجزئة
التجزئة ضرورية عندما:
- حجم البيانات يتجاوز سعة خادم واحد
- حمل الكتابة يتجاوز قدرة خادم واحد
- تحتاج عزلاً جغرافياً للبيانات
استراتيجيات مفتاح التجزئة
// تجزئة بالتجزئة
fn get_shard(user_id: Uuid, num_shards: usize) -> usize {
let hash = xxhash(&user_id.as_bytes());
hash as usize % num_shards
}
// تجزئة بالنطاق
fn get_shard_by_date(created_at: DateTime, shards: &[ShardConfig]) -> &ShardConfig {
shards.iter().find(|s| s.date_range.contains(&created_at)).unwrap()
}
التجزئة مع Rust
struct ShardedPool {
pools: Vec<PgPool>,
}
impl ShardedPool {
async fn query_user(&self, user_id: Uuid) -> Result<User> {
let shard = get_shard(user_id, self.pools.len());
sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
.bind(user_id)
.fetch_one(&self.pools[shard])
.await
.map_err(Into::into)
}
async fn query_all_shards(&self, query: &str) -> Result<Vec<Row>> {
let futures: Vec<_> = self.pools.iter()
.map(|pool| sqlx::query(query).fetch_all(pool))
.collect();
let results = futures::future::join_all(futures).await;
Ok(results.into_iter().flatten().flatten().collect())
}
}
تحديات التجزئة
- الاستعلامات عبر التجزئات — بطيئة ومعقدة
- المعاملات الموزعة — تحتاج 2PC أو saga
- إعادة التوازن — تغيير عدد التجزئات يتطلب نقل البيانات
- مفتاح التجزئة — اختيار سيء يسبب نقاط ساخنة
المقارنة
| الجانب | التقسيم | التجزئة |
|---|---|---|
| التعقيد | منخفض | مرتفع |
| التوسع | عمودي | أفقي |
| المعاملات | مدعومة | معقدة |
| الصيانة | سهلة | صعبة |
| أقصى حجم | ~1 تيرابايت | غير محدود |
الخلاصة
ابدأ بالتقسيم — إنه أبسط ويكفي لمعظم الحالات. انتقل للتجزئة فقط عندما تصل لحدود خادم واحد. والأهم: اختر مفتاح التجزئة بعناية لأنه صعب التغيير لاحقاً.