كان بحث ‘الأماكن القريبة’ يمسح الكوكب بأكمله: كيف أنقذتنا خوارزمية ‘Geohash’ من جحيم استعلامات المسافة؟

يا جماعة الخير، السلام عليكم ورحمة الله. اسمحولي اليوم أحكيلكم قصة صارت معي قبل كم سنة، قصة علمتني درس ما بنساه في البرمجة وتحسين الأداء.

كنا وقتها شغالين على تطبيق اجتماعي جديد، فكرته بسيطة: تربط الناس ببعضها بناءً على الاهتمامات المشتركة والموقع الجغرافي. ومن أهم الميزات، طبعًا، كانت ميزة “الأشخاص القريبون منك”. في البداية، ومع قاعدة بيانات صغيرة فيها مية ولا ميتين مستخدم للتجربة، كانت الأمور “لوز”، ماشية زي الحلاوة. الكود كان بسيط وواضح: لما المستخدم يفتح الشاشة، بنجيب موقعه، وبنلف على كل المستخدمين في قاعدة البيانات، نحسب المسافة بينه وبين كل واحد فيهم، وبنعرضله اللي ضمن نطاق 5 كيلومتر.

شعرت وقتها بفخر المبرمج اللي “بيخلّص شغله” بسرعة. لكن الفرحة، يا أحبائي، لم تدم طويلاً. في يوم الإطلاق التجريبي، ومع أول دفعة من المستخدمين الحقيقيين (حوالي 10 آلاف مستخدم)، صارت الكارثة. التطبيق صار “يعلّق”، والخوادم بتصيح من الضغط، والمعالج (CPU) واصل 100% ومش راضي ينزل. وصلني اتصال من مدير المشروع، وصوته كان كفيل يوضح حجم المصيبة: “شو يا أبو عمر؟! التطبيق ميت! الناس مش قادرين يفتحوا صفحة ‘القريبون مني’!”.

هنا بدأت رحلتي المحمومة لاكتشاف سبب هذا الجحيم، وكيف أنقذتني خوارزمية بسيطة وعبقرية اسمها “Geohash”.

المشكلة: جحيم استعلامات المسافة

لما رجعت للكود، كان واضح وين المشكلة. المشكلة ما كانت في الكود نفسه، بل في “الفكرة” من وراه. الطريقة اللي كنت بستخدمها، واللي بيسموها “Brute Force” أو القوة الغاشمة، كانت كارثية على نطاق واسع.

تخيل معي السيناريو: عندك 100,000 مستخدم في قاعدة البيانات. كل مرة مستخدم واحد بيطلب يشوف مين جنبه، الخادم المسكين كان مضطر يعمل التالي:

  1. يسحب كل الـ 100,000 مستخدم من قاعدة البيانات.
  2. لكل مستخدم منهم، يقوم بعملية حسابية معقدة (باستخدام معادلة هافرساين – Haversine formula) لحساب المسافة على سطح الكرة الأرضية.
  3. يقارن النتيجة مع المسافة المطلوبة (مثلاً 5 كم).
  4. يجمع النتائج ويرجعها للمستخدم.

هذا يعني 100,000 عملية حسابية لكل طلب واحد! ولو 10 مستخدمين طلبوا بنفس الثانية، فإنت بتحكي عن مليون عملية حسابية! المشكلة الأكبر كانت إن الفهرسة (Indexing) على أعمدة خطوط الطول (longitude) والعرض (latitude) تقريبًا ما إلها أي فايدة في هاي الحالة، لأن الاستعلام مش بسيط (مثل `WHERE city = ‘Amman’`)، بل هو استعلام مكاني معقد على بُعدين. قاعدة البيانات كانت بتعمل “Full Table Scan”، يعني بتمر على كل سجل في الجدول، وهذا هو التعريف الحرفي لـ “جحيم الأداء”.

نصيحة من أبو عمر: القاعدة الأولى في تحسين الأداء هي “لا تقم بعمل لا داعي له”. سؤالي لنفسي كان: هل أنا مضطر أحسب المسافة مع شخص في أمريكا عشان أظهر لناس قريبين مني في عمّان؟ طبعًا لأ. كان لازم ألاقي طريقة أستثني فيها الـ 99.9% من البيانات البعيدة قبل ما أبدأ أي حسابات.

وميض الأمل: فك شيفرة الـ Geohash

بعد ساعات من البحث والقراءة عن “Spatial Indexes” والحلول المعقدة مثل R-trees و Quad-trees (اللي غالبًا بتحتاج قواعد بيانات متخصصة زي PostGIS)، وقعت عيني على مصطلح “Geohash”. الفكرة بدت بسيطة لدرجة شككت فيها في البداية، لكنها كانت عبقرية بشكل لا يصدق.

الفكرة الأساسية: تحويل الخريطة إلى شبكة نصّية

خوارزمية Geohash بتقوم بفعل شيء واحد بسيط وساحر: تحويل إحداثيات (خط طول، خط عرض) إلى سلسلة نصية (string).

الفكرة هي تقسيم العالم كله إلى شبكة من المستطيلات. كل مستطيل في هاي الشبكة له رمز فريد. ولو قسمنا هذا المستطيل لمستطيلات أصغر، بنضيف حرف جديد للرمز. النتيجة؟

  • كلما زاد طول السلسلة النصية للـ Geohash، كلما كانت المنطقة الجغرافية اللي بتمثلها أصغر وأكثر دقة.
  • وهنا السحر: الأماكن المتقاربة جغرافيًا غالبًا ما يكون لها Geohash متشابه في بدايته (prefix).

مثلاً، لو كان الـ Geohash لموقعك هو sv8w1z4، فالمطعم اللي جنبك ممكن يكون الـ Geohash تبعه sv8w1z6. لاحظت التشابه؟ هذا التشابه هو مفتاح الحل كله.

هيك، الخوارزمية حولت مشكلة البحث في فضاء ثنائي الأبعاد (خطوط الطول والعرض) إلى مشكلة بحث نصي بسيطة في بُعد واحد!

كيف أنقذنا الـ Geohash؟ التطبيق العملي

بعد ما فهمت المبدأ، التطبيق كان أسهل مما توقعت. العملية تمت على خطوتين:

الخطوة الأولى: تخزين الـ Geohash

أول شيء عملته هو تعديل جدول المستخدمين في قاعدة البيانات. أضفت عمود جديد اسمه geohash من نوع string.

بعدها، كتبت سكربت بسيط بيمر على كل المستخدمين، يحسب الـ Geohash لموقع كل واحد فيهم (باستخدام مكتبة برمجية جاهزة، ما في داعي تخترع العجلة)، ويخزنه في العمود الجديد. طبعًا، أي مستخدم جديد أو أي مستخدم بيحدّث موقعه، بيتم حساب وتحديث الـ Geohash تبعه تلقائيًا.

أهم خطوة: قمت بإضافة فهرس (Index) على عمود الـ geohash. هذا الفهرس هو اللي رح يعطينا السرعة الخارقة.

الخطوة الثانية: الاستعلام الذكي (The Smart Query)

الآن، بدل الاستعلام القديم البطيء، صار الاستعلام الجديد كالتالي:

  1. لما المستخدم يطلب “الأشخاص القريبون”، بنجيب موقعه الحالي.
  2. بنحسب الـ Geohash لموقعه، ولكن بنأخذ أول 5 أو 6 حروف منه فقط. ليش؟ لأنه كلما قل طول الـ prefix، زادت المساحة الجغرافية اللي بغطيها. 6 حروف بتعطيك مساحة تقريبًا 0.6 كيلومتر مربع، وهذا مناسب كنقطة بداية.
  3. نقوم بتنفيذ استعلام نصي بسيط جدًا في قاعدة البيانات.

-- Let's say the user's geohash prefix (6 chars) is 'sv8w1z'
SELECT user_id, name, latitude, longitude
FROM users
WHERE geohash LIKE 'sv8w1z%';

هذا الاستعلام سريع جدًا جدًا بفضل الفهرس على عمود الـ `geohash`. هو فعليًا بيطلب من قاعدة البيانات: “أعطيني كل المستخدمين اللي الـ geohash تبعهم بيبدأ بـ ‘sv8w1z'”. قاعدة البيانات بتستخدم الفهرس وبترجعلك مجموعة صغيرة جدًا من النتائج في أجزاء من الثانية، بدل ما كانت تمسح الجدول كله.

تحدي الحواف والمناطق المجاورة

بعد أول تجربة ناجحة، لاحظت مشكلة صغيرة. ماذا لو كان المستخدم على حافة منطقة Geohash، وأقرب شخص له موجود في المنطقة المجاورة مباشرة؟ استعلام الـ `LIKE` البسيط ما رح يجيبه.

الحل، لحسن الحظ، بسيط ومدمج في كل مكتبات الـ Geohash. بالإضافة للـ Geohash تبع المستخدم، بنطلب من المكتبة تعطينا الـ Geohashes للمناطق الثمانية المجاورة (شمال، جنوب، شرق، غرب، والاتجاهات البينية).

فبيصير الاستعلام النهائي يجمع كل المستخدمين من منطقة المستخدم والمناطق الثمانية المحيطة به.


-- geohash_prefix = 'sv8w1z'
-- neighbors = ['sv8w1y', 'sv8w3b', 'sv8w38', ...etc]
-- all_prefixes = ['sv8w1z', 'sv8w1y', 'sv8w3b', 'sv8w38', ...]

SELECT user_id, name, latitude, longitude
FROM users
WHERE LEFT(geohash, 6) IN ('sv8w1z', 'sv8w1y', 'sv8w3b', ...);

نصيحة عملية: هذا الاستعلام سيجلب لك مجموعة مرشحين (candidates) أكبر بقليل من حاجتك. بعد أن تحصل على هذه القائمة الصغيرة والسريعة (ربما 30-50 مستخدم بدلاً من 100,000)، يمكنك الآن أن تقوم بتطبيق معادلة هافرساين الدقيقة عليهم لترتيبهم حسب المسافة الفعلية وعرض الأقرب فقط. لقد قمنا بتحويل مشكلة ضخمة إلى مشكلة صغيرة جدًا.

أمثلة كود بسيطة

لتقريب الصورة، هاي أمثلة بسيطة بلغة Python باستخدام مكتبة `pygeohash` لإظهار سهولة الموضوع:


import pygeohash as pgh

# إحداثيات مدينة القدس
lat, lon = 31.7683, 35.2137

# 1. تشفير الإحداثيات إلى Geohash
# precision=7 يعطي دقة حوالي 150 متر
geohash_code = pgh.encode(lat, lon, precision=7)
print(f"Geohash for Jerusalem: {geohash_code}")
# Output: Geohash for Jerusalem: sv8wr2t

# 2. إيجاد المناطق المجاورة
neighbors = pgh.neighbors(geohash_code)
print(f"Neighbors: {neighbors}")
# Output: {'n': 'sv8wr2v', 'ne': 'sv8wr2z', 'e': 'sv8wr2y', ...}

# 3. فك تشفير الـ Geohash لإرجاع الإحداثيات (تقريبية)
decoded_lat, decoded_lon = pgh.decode(geohash_code)
print(f"Decoded Lat/Lon: {decoded_lat}, {decoded_lon}")

الخلاصة: نصيحة من أبو عمر

في ذلك اليوم، تحول تطبيقنا من سلحفاة بالكاد تتحرك إلى غزال يركض بسرعة البرق. انخفض استخدام المعالج من 100% إلى أقل من 5% تحت نفس الضغط. كل هذا بفضل فهم المشكلة الحقيقية واختيار الخوارزمية المناسبة.

خوارزمية Geohash هي مثال رائع على كيف يمكن لحل بسيط وأنيق أن يتفوق على الحلول المعقدة في 90% من الحالات. هي ليست مثالية لكل السيناريوهات المكانية المعقدة، لكن بالنسبة لمشكلة “الأماكن القريبة”، فهي أداة لا تقدر بثمن.

يا جماعة، البرمجة مش بس كتابة كود، هي فن حل المشاكل. لما تواجهك مشكلة أداء، ما تستسلم للطريقة الواضحة. ابحث، اقرأ، جرب. الحل العبقري ممكن يكون أبسط مما بتتخيل، وممكن يكون موجود من سنين زي خوارزمية الـ Geohash. خليكم فضوليين، والله يوفقكم. 😉

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

​معمارية البرمجيات

كانت واجهاتنا شبكة عنكبوت: كيف أنقذ نمط ‘بوابة الواجهة البرمجية’ (API Gateway) مشروعنا من الفوضى؟

واجهات المستخدم تتحدث مع عشرات الخدمات المصغرة؟ فوضى عارمة! في هذه المقالة، أسرد لكم حكايتي مع هذه المشكلة وكيف كان نمط 'بوابة الواجهة البرمجية' (API...

23 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

كان بحثنا يفهم الكلمات لا المعاني: كيف أنقذتنا ‘التضمينات المتجهة’ (Vector Embeddings) من جحيم البحث الحرفي؟

بتذكر مرة كنا بنبني نظام بحث داخلي لشركة، وكان الموظف يسأل "كيف آخذ إجازة مرضية؟" والنظام ما يرجعله إشي، لأن المستند الرسمي عنوانه "سياسة الإجازات...

23 مايو، 2026 قراءة المزيد
تسويق رقمي

كانت حملاتنا تصرخ في الفراغ: كيف أنقذت “تجزئة الجمهور بالذكاء الاصطناعي” ميزانيتنا التسويقية؟

أشارككم قصة حقيقية من قلب الميدان، كيف انتقلنا من إهدار الميزانيات الإعلانية على استهداف خاطئ إلى تحقيق نتائج مذهلة. اكتشفوا معنا كيف يمكن لتجزئة الجمهور...

23 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

كان موقعنا تحفة فنية… لكن للمبصرين فقط: كيف أنقذتنا معايير الوصولية (a11y) من جحيم استبعاد المستخدمين؟

أشارككم قصة حقيقية حول مشروع كاد أن يفشل بسبب إهمالنا لمستخدميه من ذوي الهمم. اكتشفوا كيف حولنا تصميمًا جميلًا لكنه مُقصِر إلى تجربة شاملة للجميع،...

23 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

كانت استعلاماتنا تستغرق دهراً: كيف أنقذتنا ‘فهارس قواعد البيانات’ من جحيم الفحص الكامل للجداول؟

في هذه المقالة، يشارك أبو عمر تجربته مع بطء قواعد البيانات وكيف كانت الفهارس (Indexes) هي طوق النجاة. سنتعلم ما هي الفهارس، كيف تعمل، وكيف...

23 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت تطبيقاتنا تسأل ‘هل من جديد؟’ كل ثانية: كيف أنقذتنا WebSockets من جحيم الاستجداء المستمر للبيانات (Polling)؟

أتذكر جيداً ذلك المشروع الذي كاد أن يحرق خوادمنا، حيث كانت البيانات تتحدث بصوت عالٍ وتطبيقاتنا صماء. في هذه المقالة، أسرد لكم حكايتي مع الـ...

23 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت سيرفراتنا “بتتثاوب” نصف الوقت: كيف أنقذتنا الحوسبة بدون خوادم (Serverless) من فواتير السحابة المؤلمة؟

أشارككم قصة حقيقية من قلب معاناتنا مع فواتير الحوسبة السحابية المرتفعة، وكيف كان الانتقال إلى بنية "Serverless" باستخدام AWS Lambda هو طوق النجاة الذي خفّض...

23 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كان ملفي على GitHub مقبرة رقمية: كيف حولته من مستودع أكواد إلى أداة تجذب عروض العمل؟

أشارككم قصتي مع ملفي المهمل على GitHub وكيف حولته من مجرد مقبرة للمشاريع المنسية إلى واجهة احترافية وسيرة ذاتية تفاعلية جذبت لي عروض عمل مميزة....

23 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا تستغيث: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات المتكررة؟

في ليلة إطلاق عصيبة، كادت قاعدة بياناتنا أن تنهار تحت وطأة الاستعلامات المتكررة. في هذه المقالة، أشارككم قصة حقيقية حول كيف أنقذنا الموقف باستخدام التخزين...

23 مايو، 2026 قراءة المزيد
البودكاست