<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>هندسة البرمجيات | أحمد كمال عمارة</title><link>https://akemara.com/ar/tags/%D9%87%D9%86%D8%AF%D8%B3%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/</link><atom:link href="https://akemara.com/ar/tags/%D9%87%D9%86%D8%AF%D8%B3%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/index.xml" rel="self" type="application/rss+xml"/><description>هندسة البرمجيات</description><generator>Akemara Kit (https://akemara.com)</generator><language>ar</language><lastBuildDate>Wed, 08 Oct 2025 00:00:00 +0000</lastBuildDate><image><url>https://akemara.com/media/logo.svg</url><title>هندسة البرمجيات</title><link>https://akemara.com/ar/tags/%D9%87%D9%86%D8%AF%D8%B3%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/</link></image><item><title>وداعًا للإدراج العشوائي: UUIDv7 مقابل ULID مقابل UUIDv4 — المعرّفات الفريدة والأداء وأثرها على قواعد البيانات</title><link>https://akemara.com/ar/blog/uuidv7-ulid-uuidv4/</link><pubDate>Wed, 08 Oct 2025 00:00:00 +0000</pubDate><guid>https://akemara.com/ar/blog/uuidv7-ulid-uuidv4/</guid><description>&lt;h2 id="introduction"&gt;مقدمة&lt;/h2&gt;
&lt;p&gt;المعرّفات الفريدة هي ما يجعل نماذج البيانات تعمل في التطبيقات الحديثة. واختيار الصيغة الصحيحة لها يؤثر على الأداء وقابلية التوسع، سواء كانت مفاتيح أساسية (Primary Keys) في قاعدة بيانات أو معرّفات في نظام موزّع. حظيت &lt;strong&gt;معرّفات UUID (المعرّفات الفريدة عالميًا)&lt;/strong&gt; بشعبية طويلة لأنها فريدة على مستوى العالم دون الحاجة إلى منسّق مركزي. و&lt;strong&gt;الإصدار الرابع ‏(UUIDv4)&lt;/strong&gt;، وهو معرّف من 128 بت يولَّد عشوائيًا، من أكثرها شيوعًا لبساطته وضآلة احتمال تصادمه مع معرّف آخر. لكن للعشوائية الخالصة عيوبًا في بيئات قواعد البيانات، خصوصًا في الفهرسة والأداء. أما المخططات الأحدث مثل &lt;strong&gt;ULID (المعرّف الفريد القابل للفرز معجميًا)&lt;/strong&gt; و&lt;strong&gt;الإصدار السابع ‏(UUIDv7)&lt;/strong&gt; فتُضمّن عنصر الوقت في المعرّف، ما يجعل فرز المعرّفات معجميًا (أي زمنيًا) ممكنًا ويعالج مشاكل UUIDv4. في هذا المقال سنستعرض الفروق بين UUIDv4 وULID وUUIDv7، ونناقش مشاكل UUIDv4 في قواعد البيانات وكيف يعالجها ULID، وكيف يقارن ULID بالمعيار الجديد UUIDv7. وسنتناول أيضًا استخدامات كل منها وأثرها على قواعد البيانات (بتركيز على MySQL/MariaDB وPostgreSQL)، بما يشمل الأداء وسلوك الفرز والإنتروبيا وقضايا عملية أخرى.&lt;/p&gt;
&lt;h2 id="uuidv4-pure-randomness-and-its-challenges"&gt;UUIDv4: عشوائية خالصة وتحدياتها&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;UUIDv4&lt;/strong&gt; معرّف من 128 بت منها 122 بت عشوائية، وتُستخدم البتات المتبقية لتحديد الإصدار والمتغيّر (Variant). يُكتب عادة بـ 32 خانة سداسية عشرية على نمط 8–4–4–4–12، مثل 7f750c37–4d0b-4f0b-b922–37be0b9138a7. من أبرز مزاياه أنه يعتمد على بايتات عشوائية فقط مع خطر تصادم شبه معدوم (يكاد يكون صفرًا لمعظم التطبيقات، بوجود 2¹²² احتمالًا). لذا فهو ممتاز للأنظمة الموزّعة: أي عقدة تستطيع توليد معرّفاتها باستقلال، واحتمال أن تولّد عقدتان المعرّف نفسه ضئيل للغاية. وهو جيد أيضًا عندما تحتاج إلى عدم القابلية للتنبؤ (كرموز الجلسات والمعرّفات الحساسة أمنيًا) لأن العشوائية تمنع تخمين المعرّفات بالتسلسل.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;المشكلة&lt;/strong&gt; تظهر عندما يعمل UUIDv4 كمفتاح أساسي أو فهرس داخل قاعدة البيانات. فبما أن قيمه عشوائية تقريبًا، لا تتبع الإدراجات الجديدة أي ترتيب طبيعي. تستخدم معظم قواعد البيانات العلائقية فهرس B-Tree للمفاتيح الأساسية، وعند إدراج قيم عشوائية قد تحطّ كل قيمة جديدة في موضع عشوائي من الفهرس. يسبب هذا تجزئة الفهرس (Index Fragmentation): تنقسم صفحات البيانات كثيرًا وتبقى نصف فارغة، وتتبعثر السجلات على القرص. استخدام UUID عشوائي كمفتاح أساسي في محرك InnoDB لدى MySQL (ومحرك MariaDB الذي يعمل بالطريقة نفسها) قد يُهوي بالأداء لأن المحرك يُجمّع الجدول فيزيائيًا حسب المفتاح الأساسي. يتوقع InnoDB مفاتيح أساسية مرتّبة ويتحسّن بملء الصفحات إلى نحو 94%. أما مع الإدراج العشوائي فقد لا تمتلئ الصفحات إلا إلى نحو 50% قبل الانقسام. لذلك قد يتضخم جدول بمفاتيح UUID عشوائية إلى ضعف حجم البيانات نفسها تقريبًا مقارنة بمفاتيح تسلسلية، لكثرة الصفحات الممتلئة جزئيًا. أظهر مثال واقعي من Percona أن جدولًا بمليار صف ومفتاح أساسي UUIDv4 من نوع CHAR(36) بلغ حجمه نحو 1 تيرابايت، وبعد إعادة تنظيمه بمفتاح مرتّب تقلص إلى نحو 450 غيغابايت — أي النصف. لقد سببت الإدراجات العشوائية «تجزئة متطرفة»: صفحات فهرس جدول الـ UUID كانت ممتلئة بنحو 50% فقط، مقابل نحو 90% لمفتاح تسلسلي أو UUID مرتّب.&lt;/p&gt;
&lt;p&gt;لعشوائية UUIDv4 آثار كبيرة على الأداء:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;تجزئة الفهرس وانقسام الصفحات:&lt;/strong&gt; عند إضافة عناصر عشوائية، يضطر الـ B-Tree إلى شق الصفحات من منتصفها بدلًا من الإلحاق في النهاية. كثرة الانقسامات تخلّف صفحات نصف فارغة و«فهرسًا متضخمًا قليل الكثافة»، فيتباطأ معدل الإدراج ويُستهلك القرص. ومع البيانات الضخمة سيتحمل MySQL بمفتاح UUID عشوائي عبء صيانة فهرس أكبر يبطئ الكتابة. وكما يوضح أحد الشروح: عندما تتزايد القيم يمكن إلحاق صفحة جديدة في النهاية، أما مع القيم العشوائية فينتج الانقسام صفحتين نصف ممتلئتين تبقيان كذلك طويلًا.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;محلية الـ Cache:&lt;/strong&gt; الوصول إلى السجلات المضافة حديثًا أقل ودية للـ cache لأن سجلات UUIDv4 متناثرة عبر الفهرس. لا يستطيع الـ buffer الاحتفاظ بالإدراجات «الحديثة» متجاورة لأن «الحديث» في كل مكان. في المقابل، مع الإدراج التسلسلي تتركز أحدث البيانات في عدد صغير من الصفحات المتجاورة الساخنة في الذاكرة. مع UUIDv4 تتشتت مجموعة العمل فتكثر إخفاقات الـ cache، وقد تضطر قاعدة البيانات لتحميل صفحات كثيرة متفرقة للتعامل مع بيانات حديثة. وباستخدام مفتاح تسلسلي يقل الوصول للقرص في أنماط الاستخدام المعتادة في MySQL وPostgreSQL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;عمليات القرص (Disk I/O):&lt;/strong&gt; بسبب عشوائية UUIDv4، لا يستطيع محرك التخزين الإلحاق فحسب؛ بل عليه البحث المستمر عن الصفحات وإعادة ترتيبها. فترتفع عمليات القرص في الأحمال كثيفة الإدراج، ويجري InnoDB كتابات وانقسامات كثيرة في منتصف الفهرس. للتطبيقات كثيفة الكتابة، المفتاح التسلسلي أرفق بالقرص بكثير، وتفادي نمط الإدراج العشوائي يمنح غالبًا معدلات إدراج أسرع بكثير. وقد وجد باحثون أن المعرّفات المرتبة زمنيًا مثل UUIDv7 أو ULID «تقلص عبء عمليات القرص للإدراج بشكل كبير» مقارنة بـ UUIDv4.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;تخزين أكبر وعبء فهارس أثقل:&lt;/strong&gt; معرّفات UUID كبيرة (16 بايت) ما يفاقم التجزئة، وتستهلك أكثر (36 بايت وزيادة) عند تخزينها نصًا (سلسلة من 36 حرفًا). عمود INT تلقائي الزيادة في MySQL يستهلك 4 بايتات، بينما UUID الثنائي 16 بايت (أكبر 4 مرات) وUUID النصي 36 بايت (أكبر 9 مرات من INT). وتتضمن كل الفهارس الثانوية قيمة المفتاح الأساسي، فكلما كبر المفتاح كبرت الفهارس الثانوية. جميع متغيّرات UUID (v4 وv7 وULID وغيرها) 128 بت، والأفضل تخزينها بصيغة ثنائية مدمجة من 16 بايت. تستطيع دالة UUID_TO_BIN()‎ في MySQL مع علم التبديل تخزين v1/v2 بترتيب زمني في 16 بايت. لا يوجد ترتيب كامن يمكن استغلاله في v4، لكن binary(16)‎ يبقى أصغر من char(36)‎.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;إجمالًا، &lt;strong&gt;UUIDv4&lt;/strong&gt; ممتاز للتفرد وسهل التوليد، لكنه بلا أي عنصر قابل للفرز، ما يجعله «غير كفء في المواقف التي يكون فيها الفرز حاسمًا للأداء الأمثل، مثل مفاتيح قواعد البيانات». وقد اكتشفت فرق كثيرة أن استخدامه مفتاحًا أساسيًا يبطئ الإدراج ويضخّم الفهرس مع الوقت. تجربة PostgreSQL أفضل قليلًا من MySQL لأن PostgreSQL لا يُجمّع الجدول حسب المفتاح الأساسي افتراضيًا (كومة الجدول منفصلة)، فالمفاتيح العشوائية تؤذي صفحات الفهرس أساسًا لا الترتيب الفيزيائي للصفوف. ومع ذلك ستعاني فهارس B-Tree في PostgreSQL من انقسام الصفحات وامتلائها الجزئي نفسه مع البيانات العشوائية. باختصار: علة UUIDv4 أنه عشوائي بالكامل، فيحرم قاعدة البيانات من المحلية.&lt;/p&gt;
&lt;h2 id="ulid-sortable-identifiers-with-embedded-timestamp"&gt;ULID: معرّفات قابلة للفرز بطابع زمني مدمج&lt;/h2&gt;
&lt;p&gt;ابتكر Alizain Feerasta &lt;strong&gt;الـ ULID (المعرّف الفريد القابل للفرز معجميًا)&lt;/strong&gt; عام 2016 لعلاج مشاكل الـ UUID العشوائي. الـ ULID أيضًا معرّف من 128 بت، لكن بنيته تختلف تمامًا عن UUIDv4: أول &lt;strong&gt;48 بت طابع زمني&lt;/strong&gt; (ميلي ثانية منذ بداية Unix)، وآخر &lt;strong&gt;80 بت عشوائية&lt;/strong&gt;. بهذا التصميم يحتفظ الـ ULID بالعشوائية الضامنة للتفرد العالمي، ويكتسب في الوقت نفسه بعدًا ترتيبيًا: إذا ولّدت ULIDs عبر الزمن، فإن ترتيبها المعجمي (عند مقارنتها كسلاسل أو بايتات) يعكس وقت إنشائها. بعبارة أخرى: تُفرز الـ ULIDs زمنيًا بدقة الميلي ثانية. لا يُعرض الـ ULID القياسي بالست عشري بل بترميز Base32 لكروكفورد، فينتج سلسلة من 26 حرفًا (مثل 01H9AZ98MT1XY7TK3XN9B0AJP3). لا يتضمن هذا الترميز حروفًا ملتبسة (مثل I وL وO وU) ولا يفرّق بين حالة الأحرف، ما يجعل الـ ULIDs أسهل قراءة ونسخًا ووضعًا في الروابط. وسلسلة الـ 26 حرفًا أقصر وأوضح من سلسلة UUID ذات الـ 36 حرفًا (بالشرطات) أو الـ 32 خانة (بدونها).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;كيف يحل ULID مشاكل UUIDv4:&lt;/strong&gt; بدمج الوقت في المعرّف، يضمن الـ ULID أن المعرّفات المولّدة بترتيب زمني ستُفرز بالترتيب الزمني نفسه. ولهذا فوائد فورية لقواعد البيانات:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;نمط إدراج شبه إلحاقي:&lt;/strong&gt; عند استخدام ULIDs كمفاتيح أساسية، تحمل الإدراجات الجديدة عادة طوابع زمنية أكبر (أحدث)، فتُدرج الصفوف الجديدة عادة في «نهاية» الفهرس، وإن جاز أن تختل قليلًا مع فروق الساعات. يصبح سلوك الفهرس أقرب كثيرًا إلى مفتاح تسلسلي أو تلقائي الزيادة، فتقل انقسامات الصفحات والتجزئة بشدة. استخدام مفتاح مرتّب زمنيًا مثل ULID أو UUIDv7 في MySQL/InnoDB «ينتج انقسامات صفحات أقل وتحسنًا عامًا في كفاءة التخزين». وتبقى البيانات متجمعة حسب وقت إنشائها، وهو مفيد لتطبيقات كثيرة كسجلات الأحداث. يغدو الإدراج أكثر قابلية للتنبؤ، ويُخدم غالبًا بإلحاق صفحات جديدة في النهاية بدل شق القديمة.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;محلية Cache أفضل:&lt;/strong&gt; يجعل الـ ULID المعرّفات المضافة حديثًا متقاربة القيم، فتتركز أحدث الصفوف في جزء صغير من الفهرس. تستطيع قاعدة البيانات إبقاء نهاية الفهرس (أحدث الصفحات) في الذاكرة، فترتفع إصابات الـ cache للإدراجات والاستعلامات الحديثة. كان UUIDv4 يوزّع الصفوف الجديدة في كل مكان، أما ULID فيبقيها مرتبة زمنيًا فيتحسن التخزين المؤقت. مثلًا، استعلام عن أحدث N سجل يمسح نطاقًا واحدًا من الفهرس دون قفزات.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;فرز زمني طبيعي:&lt;/strong&gt; يتيح لك الـ ULID الفرز بالمعرّف نفسه للحصول على الترتيب الزمني، فقد تستغني في بعض الحالات عن عمود طابع زمني مخصص للفرز. إذا كان مفتاحك الأساسي ULID مثلًا، فإن ORDER BY id DESC يعيد الأحدث أولًا لأن أكبر ULID يقابل أحدث طابع زمني. يبسّط هذا منطق التطبيق أو الفهرسة، لكن تذكّر أنك إن احتجت معلومات تاريخ/وقت كاملة أو استعلامات بالتاريخ، فالأرجح أنك ستبقي عمود طابع زمني عادي للراحة. الجوهر أن ULID يشفّر الوقت، وهذا نافع جدًا للسجلات وبيانات السلاسل الزمنية حيث يهم الاسترجاع مرتّبًا.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;رتابة داخل الميلي ثانية الواحدة:&lt;/strong&gt; ماذا لو وُلّد أكثر من ULID في الميلي ثانية نفسها؟ ستتطابق بتات الطابع الزمني. تنص مواصفة ULID عندئذٍ على استخدام زيادة رتيبة (Monotonic) في الجزء العشوائي: إذا استُدعي المولّد أكثر من مرة في أقل من ميلي ثانية، يلاحظ ثبات الطابع الزمني فيضيف 1 إلى الجزء العشوائي لكل معرّف جديد. يضمن هذا بقاء ULIDs المشتركة في الطابع الزمني مرتّبة (المولَّد ثانيًا له جزء عشوائي أكبر بواحد، فيصبح أكبر معجميًا). مثلًا، إن خرج اثنان في الميلي ثانية نفسها فقد ترى 01BX5ZZKBKACTAV9WEVGEMMVS0 يليه 01BX5ZZKBKACTAV9WEVGEMMVS1 — تغيّر الحرف الأخير من 0 إلى 1. تمنع هذه الخطوة التكرارات داخل الطابع الواحد وتحفظ الترتيب. وفي الأنظمة الفعلية، احتمال الحاجة إلى أكثر من 2⁸⁰ معرّفًا في ميلي ثانية شبه معدوم (فذلك 1.2e24، أي أضعاف ملايين المعرّفات في الثانية)، فلا قلق من الفيضان.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;التصادم والإنتروبيا:&lt;/strong&gt; لدى الـ ULID عشوائية من 80 بت، أقل قليلًا من 122 بت في UUIDv4 لكنها هائلة. يمنح الجزء العشوائي 1.2×10²⁴ احتمالًا في كل ميلي ثانية حتى مع تكرر الطابع الزمني. يبقى احتمال التصادم شبه صفري لأي استخدام واقعي (للمقارنة: توليد 2⁴⁰ ≈ تريليون ULID يعطي احتمال تصادم نحو 1 من 2⁴⁰، وهو ضئيل جدًا). ووجدت إحدى الدراسات أن للـ ULIDs «احتمالات تصادم مهملة حتى عند معدلات توليد عالية»، بل إنها أقل عرضة للتصادم من UUIDv7 ذي الـ 74 بت العشوائية. باختصار: الـ ULIDs فريدة عالميًا كما UUIDv4 تمامًا.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;سهولة القراءة:&lt;/strong&gt; يقال كثيرًا إن الـ ULIDs أسهل قراءة للبشر. لا تستخدم أبجدية Base32 لكروكفورد حروفًا متشابهة، فسلسلة الـ 26 حرفًا (مثل 01GZ9TGXKJQR6Z1YT1BP7FY4WX) أسلس في التعامل من UUID من 36 حرفًا بست عشري مختلط وشرطات. ولا تفرّق الـ ULIDs بين حالة الأحرف، ما يجنّبك أخطاء التعامل اليدوي (لا حاجة لحفظ الحالة كما في الست عشري). وهي آمنة في الروابط لاقتصارها على حروف وأرقام. تفيد هذه الخصائص إذا كنت ستعرض المعرّفات أو تسجّلها حيث قد يقرؤها أو يكتبها أحد. لكن ثمة مقايضة: يمكنك تخزين الـ ULIDs نصًا (26 حرفًا) أو ثنائيًا (16 بايت). مقارنة السلاسل أبطأ قليلًا من مقارنة البايتات الخام، والصيغة النصية أكبر (26 بايت مقابل 16). وبعض قواعد البيانات، كـ Postgres، لديها نوع UUID لقيم 16 بايت لكن لا نوع أصلي للـ ULID؛ يمكنك تخزينه في CHAR(26)‎ أو ثنائيًا (BYTEA بعد تحويل Base32 إلى بايتات). وتوفر مكتبات ULID كثيرة الصيغتين: الثنائية الخام والسلسلة المرمّزة.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;خلاصة القول: تجعل الـ ULIDs المعرّفات الفريدة عالميًا أسهل استخدامًا وأسرع وصولًا في قواعد البيانات المتعاملة مع بيانات زمنية. وقد أصبحت معيارًا واقعيًا في أنظمة كثيرة تريد فوائد UUID دون ضريبة أداء المفاتيح العشوائية بالكامل. الجانب السلبي أن ULID ليس (بعد) معيارًا رسميًا من IETF أو ISO، ولا تدعمه كل اللغات وقواعد البيانات، فستستخدم غالبًا مكتبة لتوليده. الخبر الجيد أن ثمة تطبيقات بلغات كثيرة — Java وGo وPython وJavaScript — ويمكنك عند الحاجة تنفيذ المواصفة بنفسك بسهولة. وتذكّر أيضًا أن الـ ULIDs، كأي معرّف زمني، تكشف الطابع الزمني: من يرى ULID يستطيع فك أول 10 حروف لمعرفة لحظة إنشائه بدقة. لا يمثل هذا مشكلة في حالات كثيرة (بل قد يفيد)، لكن إن كنت لا تريد إفشاء عمر السجلات أو ترتيبها (لأسباب أمنية أو خصوصية) فقد لا يكون المخطط الزمني خيارك الأفضل. سنتوسع في هذا بقسم المقارنات.&lt;/p&gt;
&lt;h2 id="uuidv7-the-new-time-ordered-uuid-standard"&gt;UUIDv7: معيار UUID الجديد المرتّب زمنيًا&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;UUIDv7&lt;/strong&gt; عضو جديد في عائلة UUID، قُنّن في RFC 9562 (مايو 2024)، الذي حلّ محل RFC 4122، كمتغيّر UUID مرتّب زمنيًا. وقد استلهم كثيرًا من نجاح ULID. يرسّخ UUIDv7 فكرة «طابع زمني + بتات عشوائية» داخل معيار UUID نفسه. فهو مثل الـ ULID: 128 بت يتصدرها طابع زمني Unix من 48 بت (بالميلي ثانية) في البتات الأعلى، والبقية عشوائية مع إمكانية إضافة عداد رتيب لمنع التكرار داخل الميلي ثانية الواحدة. يخصص UUIDv7 للطابع الزمني 48 بت، ولرقم الإصدار 4 بت (وهي 0111 للإصدار السابع)، والباقي (128−48−4 = 76 بت) مزيج من العشوائية وبتات عداد اختيارية. وبعد حجز بتات مؤشر «المتغيّر» في UUID، يتبقى نحو 74 بت إنتروبيا خالصة. المتغيّر هو أهم بتّين في بايت يدل على UUID وفق المعيار، ويُضبطان للإصدار 7 على النمط القياسي 10xx. هكذا تقل عشوائية UUIDv7 قليلًا عن 80 بت لدى ULID، لكن الفضاء يظل هائلًا: نحو 2⁷⁴ معرّفًا فريدًا في كل ميلي ثانية، مثل ULID عمليًا. وإن احتجت أكثر، تنص المواصفة على استخدام عداد وترحيل الطابع الزمني عند اللزوم — لكن واقعيًا لن يُبلغ 2⁷⁴ (1.8e22) في الميلي ثانية أبدًا. والتصادمات شبه منعدمة؛ وجدت إحدى الدراسات أن خطر التصادم مع UUIDv7 حتى عند معدلات عالية جدًا ضئيل للغاية (وأعلى قليلًا فقط من ULID).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;الميزة الرئيسة لـ UUIDv7&lt;/strong&gt; أنه يمنحك UUID موحّدًا للجميع ومرتّبًا زمنيًا: فوائد ULID (تسلسل زمني وأداء أفضل لقاعدة البيانات) لكن بصيغة تتوافق مع أدوات وأنواع UUID القائمة. يُكتب UUIDv7 قياسيًا كسلسلة ست عشرية من 36 حرفًا بشرطات؛ مثلًا قد يكون 0199c376-ce80–7b08–81fd-5524d4a20ed1 معرّف UUIDv7 — الرقم 7 في المجموعة الثالثة يدل على الإصدار، والمقطع الأول 0199c376ce80 طابع زمني (0x0199c376ce80). وبما أن الطابع الزمني في البتات العليا، فإن &lt;strong&gt;فرز UUIDv7 معجميًا (أو مقارنته كبايتات خام)&lt;/strong&gt; يطابق الترتيب الزمني. إن وضعت قيم UUIDv7 في عمود UUID لدى PostgreSQL أو BINARY(16)‎ لدى MySQL، فترتيبها البايتي الطبيعي مرتّب زمنيًا — أي أن الفهرس التصاعدي على العمود سيرتب صفوفك زمنيًا تلقائيًا. وهي الخاصية نفسها لدى ULID، لكن بترميز كتابي مختلف.&lt;/p&gt;
&lt;p&gt;ومن &lt;strong&gt;منظور قواعد البيانات&lt;/strong&gt;، يعمل UUIDv7 كثيرًا مثل ULID:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;إدراجات مرتّبة:&lt;/strong&gt; تحمل قيم UUIDv7 الجديدة عادة مكوّن طابع زمني متزايدًا (ما لم تنحرف الساعة أو تُولَّد القيم خارج الترتيب)، فتتجه الإدراجات غالبًا إلى «نهاية» الفهرس. يمنحك هذا تجزئة منخفضة ومعامل ملء صفحات مرتفعًا، مثل ULID تمامًا. ومن حيث كفاءة استغلال صفحات الفهرس، سيعامل InnoDB في MySQL مفتاح UUIDv7 الأساسي معاملة قريبة جدًا من AUTO_INCREMENT. ولتفادي مشاكل انقسام الصفحات، يوصي فريق PlanetScale بشدة باستخدام «معرّفات UUID زمنية مثل الإصدار 6 أو 7». ويستفيد PostgreSQL كذلك من المحلية الأفضل فيقل احتياجه لإعادة موازنة الفهرس عبر الزمن.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الأداء:&lt;/strong&gt; عمليًا، وجد كثيرون أن UUIDv7 وULID يقدمان أداء إدراج واستعلام أفضل من UUIDv4. تُظهر القياسات غالبًا إدراجات أسرع واستهلاك قرص أدنى. وجدت إحدى الدراسات مثلًا أن استخدام معرّفات مرتّبة زمنيًا (ULID أو v7) يسرّع التوليد ويخفض عبء الشبكة في الأنظمة الموزّعة؛ وفي اختباراتهم ضاعفت التحسينات سرعة توليد المعرّفات تقريبًا وقلّصت تخزين الفهارس وحركة البيانات بشكل ملموس. قد تختلف تجربتك، لكن الصورة العامة واضحة: قاعدة بياناتك «ستشكرك» على استخدام UUID مرتّب.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;التوافقية:&lt;/strong&gt; يتبع UUIDv7 معيار UUID، فيصلح أينما طُلب «UUID». لدى PostgreSQL نوع UUID يخزن UUIDv7 دون أي تعديل (فهو قيمة 16 بايت). ويجري إضافة دعم v7 إلى مكتبات UUID في لغات كثيرة؛ فمنذ 2023–2024 توجد مكتبات في Java وGo وRust وPython وغيرها تولّد UUIDv7. وحتى إن كان الدعم الأصلي ناقصًا، يمكنك توليد UUIDv7 بأخذ خرج مكتبة ULID وصياغته كـ UUID. لا تحتاج لنوع عمود أو ترميز خاص لتخزينه — فهو يبقى UUID. تخزّنه في MySQL كـ BINARY(16)‎ أو عبر UNHEX()‎ على الصيغة النصية، وفي Postgres يمكنك التحويل من سلسلة إلى UUID أو استخدام دوال من الملحقات لتوليد v7، وثمة نقاش لإضافة uuid_generate_v7()‎ إلى ملحق pgcrypto أو ما شابه. وقد نُشرت مواصفة UUIDv7 رسميًا في RFC 9562 (مايو 2024) الذي حلّ محل RFC 4122 — لذا يتسارع وصول الدعم الرسمي عبر المنصات.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الفروق عن ULID:&lt;/strong&gt; يتشابه UUIDv7 والـ ULID كثيرًا، مع فروق دقيقة:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;المعيارية:&lt;/strong&gt; UUIDv7 معيار رسمي (RFC 9562)، بينما الـ ULID ليس مقننًا رسميًا؛ إنه معيار واقعي رسّخه الاستخدام. يعني هذا أن لـ UUIDv7 مواصفة رسمية تلتزم بها كل التطبيقات.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;التمثيل الكتابي:&lt;/strong&gt; يستخدم UUIDv7 صيغة UUID الست عشرية المألوفة بالشرطات، بينما يستخدم الـ ULID نص Base32 افتراضيًا. قد يؤثر الاختيار على سهولة الاستخدام: الـ ULIDs أقصر وأسهل قراءة للبشر، لكن UUIDv7 قد يندمج أفضل مع أدواتك إن كانت تتوقع سلاسل UUID ست عشرية. لا أفضلية تقنية لترميز على آخر سوى الطول وسهولة القراءة (سلسلة Base32 من 26 حرفًا مقابل ست عشرية من 36 حرفًا).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;التخزين في قاعدة البيانات:&lt;/strong&gt; كلاهما 128 بت عند التخزين. نصيًا يستهلك الـ ULID 26 بايتًا وUUIDv7 يستهلك 36، لكن الأفضل تخزين كليهما ثنائيًا في 16 بايت. لا يمكنك وضع سلسلة ULID مباشرة في نوع UUID لدى PostgreSQL لأنها ليست بصيغة UUID (يمكن تحويل Base32 إلى ست عشري)، بينما يستخدم UUIDv7 نوع UUID فورًا. وفي MySQL ستستخدم BINARY(16)‎ للاثنين. لذا قد يكون UUIDv7 أسلس مع أعمدة UUID القائمة.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;العشوائية:&lt;/strong&gt; لدى الـ ULID 80 بتًا عشوائيًا، ولدى UUIDv7 نحو 74 بتًا بحسب إعداد العدادات. عمليًا يمنحك كلاهما تفردًا وافرًا. الجزء العشوائي في ULID ثابت عند 80 بت، بينما يسمح UUIDv7 بتحويل بعض البتات إلى عداد إن احتجت توليد أعداد ضخمة في الميلي ثانية نفسها. فإذا استخدم تطبيقٌ ما عدادًا من 12 بت و62 بتًا عشوائيًا، فنظريًا قد يكون احتمال تصادم ULID الكامل (80 بت) أدنى — لكن 62 بتًا عشوائيًا أكثر من كافية كما أسلفنا. والتطبيقات الصحيحة لـ UUIDv7 تستخدم مولّد أرقام عشوائية آمنًا للجزء العشوائي فتبقى قوية. وجدت إحدى الدراسات أن خطر تصادم ULID أدنى بنسبة 98% من UUIDv7 في مخطط معيّن ببتات عشوائية أقل للإصدار 7 — لكن كليهما صفري عمليًا للأحجام الواقعية.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;كشف المعلومات:&lt;/strong&gt; كلاهما يشفّر الطابع الزمني حتى الميلي ثانية، فكلاهما يكشف لحظة إنشاء المعرّف، وقد يفشي ذلك ساعة النظام التقريبية وترتيب الأحداث. إن احتجت معرّفات لا تكشف ترتيب إنشائها (مثلًا لمنع المستخدمين من استنتاج عدد السجلات بين معرّفين، أو لإخفاء توقيت إجراء ما)، فلن يصلح ULID ولا UUIDv7؛ عد إلى UUIDv4 أو مخطط عشوائي بالكامل. هذه نقطة تصميم مهمة للمعرّفات العامة: المفتاح الأساسي الداخلي قد لا يحتاج سرية، أما معرّف API خارجي فربما.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;خلاصة: UUIDv7 لأغلب التطبيقات هو أفضل ما في العالمين — سرعة ULID وترتيبه، مع توافقية صيغة UUID وسهولتها. وكما قال أحد المصادر: «بأدوات اليوم، UUIDv7 هو الخيار الافتراضي للمفاتيح الأساسية الداخلية». لا تلجأ إلى ULID إلا حين تتطلب قابلية القراءة البشرية أو أسباب قديمة محددة ذلك، ولا إلى UUIDv4 إلا حين يتعذر كشف معلومات الوقت. والآن لنستعرض التوصيات جنبًا إلى جنب.&lt;/p&gt;
&lt;h2 id="use-cases-and-recommendations"&gt;حالات الاستخدام والتوصيات&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;متى تستخدم UUIDv4:&lt;/strong&gt; إن أردت معرّفًا لا يحمل أي معلومات (كطوابع الوقت أو الترتيب) وتريده غير قابل للتنبؤ، يبقى UUIDv4 خيارًا وجيهًا. وهو متوفر في كل مكان بلا مكتبات إضافية أو عناية خاصة. استخدمه لرموز الأمان ومفاتيح الـ API والمعرّفات الخارجية حين لا تريد كشف وقت الإنشاء. إن كنت لا تريد أن يخمّن المستخدمون عدد طلبات نظامك من رقم الطلب، فـ UUIDv4 العشوائي مناسب. وفي حالات نادرة جدًا من التوليد فائق التوزع حيث قد يسبب حتى الطابع الزمني مشاكل، عشوائية v4 مفهومة وبسيطة. فقط اعلم أن ثمة ثمن أداء في قواعد البيانات؛ وللقواعد الداخلية الكبيرة، v4 ليس الخيار الأمثل عادة للأسباب المذكورة. وقد يخفف نمط «مفتاح داخلي تلقائي الزيادة + UUID كمرجع خارجي» بعض المشاكل لكنه يزيد التعقيد.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;متى تستخدم ULID:&lt;/strong&gt; الـ ULID خيار ممتاز للتطبيقات التي تحتاج معرّفات مرتّبة وسهلة القراءة للبشر. إن كنت تسجّل أحداثًا أو لديك نظام تظهر معرّفاته في السجلات أو الروابط، تجعل الـ ULIDs قراءتها أسهل. وهي مفيدة أيضًا في الواجهات أو الأماكن المرئية للمستخدمين، إذ يستطيع المرء تمييز الأحدث بين معرّفين (فهي تُفرز أبجديًا حسب الزمن). الـ ULIDs رائعة لأنظمة تخزين الأحداث (Event Sourcing) والسجلات والمراسلة وكل مكان تريد فيه حفظ ترتيب الحدوث وربما استنتاج وقت الحدث من المعرّف بسرعة. وتُستخدم لبناء مفاتيح قابلة للفرز تقريبًا (k-sortable) في كثير من قواعد NoSQL وأنظمة الرسائل، فيسرّع ذلك استعلامات النطاق الزمني. انتبه إلى أن الـ ULIDs تعتمد على الساعات، فاحرص على مزامنة ساعات أنظمتك إلى حد معقول؛ إن انحرفت ساعة جهاز كثيرًا فستعكس معرّفاته ذلك (ومع أن الطوابع خارج الترتيب ستستقر بالفرز حسب الوقت المطلق، قد ترى بعض الاختلال إذا تأخرت ساعة خادم قليلًا). وإذا كنت تتعامل مع المعرّفات يدويًا أحيانًا — كنسخ معرّف من مكان لآخر — تفيدك الـ ULIDs لخلوها من الشرطات وقصرها فتقل الأخطاء. وإن كانت بيئتك لا تدعم UUIDv7 بسهولة بعد، فالـ ULID بديل مجرّب — فقط استخدم مكتبة جيدة تتقن الزيادة الرتيبة، وفكّر في تخزينها ثنائيًا للكفاءة إن لم تكن بحاجة لقراءتها مباشرة في قاعدة البيانات.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;متى تستخدم UUIDv7:&lt;/strong&gt; الـ UUIDv7 افتراضي ممتاز لمعظم الأنظمة الجديدة، خصوصًا الـ microservices وقواعد البيانات الداخلية. فهو ملتزم بمعيار UUID ويمنحك فوائد ULID نفسها تقريبًا في الفرز والفهرسة. إن كنت في بيئة تتبنى المعايير الجديدة بسرعة أو تريد ضمان جاهزية معرّفاتك للمستقبل، فهو الخيار الأفضل. مثلًا، إن كنت على PostgreSQL وتريد نوع UUID الأصلي لسهولته، ولّد UUIDv7 في تطبيقك وخزّنه مباشرة بلا مشاكل. وهو ممتاز للمفاتيح الأساسية حيث تريد تفادي مشاكل الزيادة التلقائية — كالوصول لحدود التسلسل أو التنافس عليه — مع الاحتفاظ بسرعة الإدراج التسلسلي. ويفيد كذلك في الأنظمة الموزّعة حيث تساعد الطوابع الزمنية داخل المعرّفات على التنقيح وترتيب الأحداث عالميًا. نقطة أخيرة: إن كان الطابع الزمني المدمج يُعد حساسًا (قد يكشف سلوك المستخدمين في بعض الحالات)، فعامل المعرّفات نفسها كبيانات حساسة. أما للاستخدام الداخلي — مفاتيح قواعد البيانات والمراسلة الداخلية — فذلك ليس مشكلة عادة.&lt;/p&gt;</description></item><item><title>دليل احترافي لإدارة اتصالات قواعد البيانات في Golang مع MySQL/MariaDB</title><link>https://akemara.com/ar/blog/golang-database-connection-management/</link><pubDate>Sat, 20 Sep 2025 00:00:00 +0000</pubDate><guid>https://akemara.com/ar/blog/golang-database-connection-management/</guid><description>&lt;h2 id="the-databasesql-abstraction-its-a-pool-not-a-connection"&gt;تجريد database/sql: إنه Pool وليس اتصالًا&lt;/h2&gt;
&lt;p&gt;الفهم العميق لحزمة &lt;code&gt;database/sql&lt;/code&gt; في Go أساس بناء تطبيقات قوية عالية الأداء. وأكثر سوء الفهم شيوعًا وأفدحها عاقبة هو التعامل مع كائن &lt;code&gt;sql.DB&lt;/code&gt; وكأنه يمثل اتصالًا واحدًا بقاعدة البيانات. إنه ليس كذلك؛ بل هو مدير متطور وآمن للتزامن يدير مجموعة (Pool) من الاتصالات الفعلية بقاعدة البيانات. ترسيخ هذا النموذج الذهني الصحيح هو الخطوة الأولى نحو إتقان التعامل مع قواعد البيانات في Go.&lt;/p&gt;
&lt;h2 id="deconstructing-sqldb-the-concurrent-pool-manager"&gt;تفكيك sql.DB: مدير الـ Pool المتزامن&lt;/h2&gt;
&lt;p&gt;كائن &lt;code&gt;sql.DB&lt;/code&gt; الذي تعيده &lt;code&gt;sql.Open&lt;/code&gt; يمثل مقبضًا (Handle) لقاعدة البيانات، وبتحديد أدق: هو يدير مجموعة من الاتصالات النشطة والخاملة. صُمم هذا المقبض صراحة ليكون آمنًا للاستخدام المتزامن من عدة goroutines، وهي ركيزة معمارية تطبيقات Go الحديثة. ويخفي تصميم الحزمة تعقيد إدارة الاتصالات: تُنشأ اتصالات جديدة تلقائيًا عند الحاجة لخدمة عمليات متوازية، وتُعاد إلى الـ pool حين لا تعود مطلوبة.&lt;/p&gt;
&lt;p&gt;يعني هذا التصميم أن نسخة &lt;code&gt;sql.DB&lt;/code&gt; واحدة يجب أن تُنشأ مرة واحدة عند تهيئة التطبيق، ثم تُشارك — كمتغير على مستوى الـ package عادة أو عبر حقن التبعيات — بين كل أجزاء التطبيق التي تحتاج الوصول لقاعدة البيانات. أما النمط المضاد باستدعاء &lt;code&gt;sql.Open&lt;/code&gt; و&lt;code&gt;db.Close&lt;/code&gt; مع كل طلب أو دالة فهو غير كفء ويهدم الغاية من تجميع الاتصالات أصلًا، إذ يدفع في كل عملية كلفة فتح اتصال TCP جديد وإجراء مصافحة قاعدة البيانات.&lt;/p&gt;
&lt;h3 id="code-example-singleton-sqldb-initialization"&gt;مثال كود: تهيئة sql.DB كنسخة وحيدة&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;database/sql&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;github.com/go-sql-driver/mysql&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// db is a package-level variable to hold the database connection pool.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// sql.Open just validates its arguments and prepares the handle.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// It does not create any connections yet.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mysql&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;user:password@tcp(127.0.0.1:3306)/dbname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;could not connect to database: %v&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// It&amp;#39;s a good practice to set connection pool parameters.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// These will be discussed in detail in Section 3.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetConnMaxLifetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetMaxOpenConns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetMaxIdleConns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Ping the database to verify the connection is alive.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;database is not responding: %v&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The application can now use the &amp;#39;db&amp;#39; variable to perform queries.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The db handle should be closed when the application is shutting down.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// defer db.Close() // In a real app, this would be in a shutdown hook.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-lifecycle-of-a-connection-within-the-pool"&gt;دورة حياة الاتصال داخل الـ Pool&lt;/h2&gt;
&lt;p&gt;عندما تنفّذ إحدى الـ goroutines استعلامًا، يقوم مدير الـ pool بالتالي:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;يبحث عن اتصال خامل متاح.&lt;/li&gt;
&lt;li&gt;يسلّمه للـ goroutine إن وجده.&lt;/li&gt;
&lt;li&gt;إن لم يجد اتصالًا خاملًا وكان دون الحد الأقصى، ينشئ اتصالًا جديدًا.&lt;/li&gt;
&lt;li&gt;إن بُلغ الحد الأقصى، ينتظر حتى يتحرر اتصال.&lt;/li&gt;
&lt;li&gt;يعيد الاتصال إلى مجموعة الخمول بعد الاستخدام.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;آلية إعادة تدوير الاتصالات هذه تحسّن الأداء بشكل كبير.&lt;/p&gt;
&lt;h2 id="how-concurrency-works-the-databasesql-promise"&gt;كيف يعمل التزامن: وعد database/sql&lt;/h2&gt;
&lt;p&gt;للتجريد الذي تقدمه &lt;code&gt;database/sql&lt;/code&gt; أثر عميق على التزامن: لا يوجد أي ضمان لالتصاق الاتصال (Connection Affinity). فتنفيذ عبارتين متتاليتين على مقبض &lt;code&gt;sql.DB&lt;/code&gt; نفسه قد ينتهي بتشغيلهما على اتصالين فعليين مختلفين.&lt;/p&gt;
&lt;p&gt;قد يكون هذا السلوك مصدر حيرة للمطورين المعتادين على نموذج الجلسات. من الأخطاء الشائعة مثلًا إصدار أمر خاص بالجلسة مثل LOCK TABLES mytable WRITE يتبعه INSERT INTO mytable…‎. فإذا نُفذت عبارة الـ INSERT على اتصال مختلف عن الاتصال الذي حاز القفل، ستعلق إلى الأبد منتظرة قفلًا لن تحوزه أبدًا.&lt;/p&gt;
&lt;p&gt;هذا ليس عيبًا في التصميم بل خيار مقصود لتعظيم التزامن والإنتاجية. تفترض الحزمة أن معظم عمليات قاعدة البيانات عديمة الحالة ويمكن لأي اتصال متاح تنفيذها. أما تسلسلات العمليات التي تتطلب حالة جلسة متسقة، فتوفر لها الحزمة آلية صريحة: المعاملات (&lt;code&gt;sql.Tx&lt;/code&gt;). فكل العمليات المنفذة على كائن المعاملة مضمونة التنفيذ على الاتصال الفعلي الواحد نفسه.&lt;/p&gt;
&lt;p&gt;يفرض هذا التصميم نقلة في التفكير: على المطورين هجر عقلية «الجلسة الدائمة» أو «اتصالي الخاص»، والتفكير بدلًا من ذلك في عمليات ذرّية عديمة الحالة يخدمها جمع مجهول من العمال. والمرة الوحيدة التي يصح فيها التعويل على حالة اتصال محدد هي داخل الحدود الواضحة لمعاملة أو &lt;code&gt;sql.Conn&lt;/code&gt; مُدار يدويًا. فهم هذا الفارق أساس لتفادي طيف واسع من الأخطاء المنطقية الخفية العسيرة على التنقيح في تطبيقات Go المتزامنة.&lt;/p&gt;
&lt;h2 id="establishing-and-configuring-the-mysqlmariadb-connection"&gt;إنشاء اتصال MySQL/MariaDB وضبطه&lt;/h2&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="قائمة افعل ولا تفعل للاتصالات: استدعِ db.Ping بعد sql.Open، ولا تستدعِ sql.Open لكل استعلام، واستخدم defer db.Close في main، ولا تتجاهل الأخطاء"
srcset="https://akemara.com/en/blog/golang-database-connection-management/images/webp/establishing-and-configuring-the-mysql-mariadb-connection_hu_2de0e58e44cb60e9.webp 320w, https://akemara.com/en/blog/golang-database-connection-management/images/webp/establishing-and-configuring-the-mysql-mariadb-connection_hu_9565dfb5e3ea329f.webp 480w, https://akemara.com/en/blog/golang-database-connection-management/images/webp/establishing-and-configuring-the-mysql-mariadb-connection_hu_652d7e7d462b63c4.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://akemara.com/en/blog/golang-database-connection-management/images/webp/establishing-and-configuring-the-mysql-mariadb-connection_hu_2de0e58e44cb60e9.webp"
width="760"
height="349"
loading="lazy" data-zoomable data-zoom-src="https://akemara.com/en/blog/golang-database-connection-management/images/webp/establishing-and-configuring-the-mysql-mariadb-connection_hu_b65a1e4e8d0a437b.webp" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;يمر وصل تطبيق Go بقاعدة MySQL أو MariaDB بثلاث خطوات أساسية: اختيار driver موثوق، وصياغة سلسلة الاتصال (الـ DSN) صياغة صحيحة، ثم التحقق من الاتصال. وإتقان هذه التفاصيل من البداية أساس لبناء تطبيق مستقر قابل للصيانة.&lt;/p&gt;
&lt;h2 id="choosing-the-right-driver"&gt;اختيار الـ Driver الصحيح&lt;/h2&gt;
&lt;p&gt;حزمة database/sql في Go واجهة فقط؛ فهي تحتاج driver مخصصًا للتخاطب مع قاعدة البيانات. أنتج المجتمع عدة drivers لـ MySQL وMariaDB، لكن github.com/go-sql-driver/mysql يبرز بوصفه المعيار الواقعي. إنه تطبيق Go خالص يلتزم بواجهة&lt;/p&gt;
&lt;p&gt;database/sql/driver، أي أنه بلا تبعيات لمكتبات C، ما يبسّط النشر. وشعبيته وصيانته النشطة وخصائصه الشاملة تجعله الخيار الموصى به لمعظم المشاريع.&lt;/p&gt;
&lt;p&gt;لاستخدام الـ driver يجب استيراده في التطبيق. ويتم الاستيراد لأجل أثره الجانبي — تحديدًا: دالة &lt;code&gt;init()&lt;/code&gt; في الـ driver تسجّله لدى حزمة database/sql تحت الاسم &amp;ldquo;mysql&amp;rdquo;. ولهذا يُستخدم المعرّف الفارغ (&lt;code&gt;_&lt;/code&gt;) في الاستيراد.&lt;/p&gt;
&lt;h2 id="the-data-source-name-dsn-a-deep-dive"&gt;سلسلة الاتصال (DSN): نظرة معمقة&lt;/h2&gt;
&lt;p&gt;الـ DSN سلسلة نصية تحوي كل ما يحتاجه الـ driver للاتصال بقاعدة البيانات. ويستخدم go-sql-driver/mysql صيغة قياسية:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[username[:password]@][protocol[(address)]]/dbname[?param1=value1&amp;amp;...&amp;amp;paramN=valueN]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;وقد يبدو DSN نموذجي لخادم MySQL محلي هكذا:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myuser:mypassword@tcp(127.0.0.1:3306)/mydatabase?parseTime=true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;المكونات الأساسية واضحة، لكن المعاملات الاختيارية في نهاية الـ DSN حاسمة لسلوك التطبيق الصحيح. وأي خطأ في ضبطها قد يقود إلى أخطاء خفية، خصوصًا في ترميز المحارف ومعالجة الطوابع الزمنية.&lt;/p&gt;
&lt;h3 id="important-parameters"&gt;أهم المعاملات:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;parseTime=true&lt;/code&gt; ← يحوّل DATE/DATETIME إلى &lt;code&gt;time.Time&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loc=Local&lt;/code&gt; ← يستخدم المنطقة الزمنية للنظام&lt;/li&gt;
&lt;li&gt;&lt;code&gt;charset=utf8mb4&lt;/code&gt; ← يضمن دعم Unicode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout=5s&lt;/code&gt; و&lt;code&gt;readTimeout=30s&lt;/code&gt; و&lt;code&gt;writeTimeout=30s&lt;/code&gt; ← للمرونة والصمود&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="best-practice-programmatic-dsn-construction"&gt;أفضل ممارسة: بناء الـ DSN برمجيًا&lt;/h2&gt;
&lt;p&gt;بناء سلسلة الـ DSN يدويًا بضم النصوص عرضة للأخطاء وقد يفتح ثغرات أمنية، خصوصًا إذا حوت كلمات المرور أو المعاملات محارف خاصة تتطلب ترميز URL. يوفر go-sql-driver/mysql بديلًا أكثر أمانًا ووضوحًا: البنية &lt;code&gt;mysql.Config&lt;/code&gt; ودالتها &lt;code&gt;FormatDSN()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;يمنحك هذا النهج فحص أسماء الحقول وقت الترجمة ويتكفل بكل الترميز اللازم تلقائيًا، فيصبح الكود أمتن وأسهل صيانة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;مثال كود: بناء DSN عبر mysql.Config&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;database/sql&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;github.com/go-sql-driver/mysql&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;connectToDB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Create a new config struct.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;myuser&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;my$ecr&amp;amp;tP@ssw0rd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Special characters are handled correctly.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Net&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;127.0.0.1:3306&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DBName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mydatabase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AllowNativePasswords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Recommended for modern MySQL versions.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ParseTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Collation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;utf8mb4_unicode_ci&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Loc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Use the system&amp;#39;s local time zone.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ReadTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WriteTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Use the FormatDSN method to get the connection string.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dsn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FormatDSN&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mysql&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="verifying-the-connection-the-importance-of-dbping"&gt;التحقق من الاتصال: أهمية db.Ping()‎&lt;/h2&gt;
&lt;p&gt;من التفاصيل الجوهرية حول &lt;strong&gt;sql.Open&lt;/strong&gt; أنها لا تفتح اتصالًا فوريًا بقاعدة البيانات ولا تتحقق من صحة معاملات الـ DSN (سوى التحليل الأساسي)؛ إنها تجهّز مقبض sql.DB للاستخدام اللاحق فحسب. وتعني هذه التهيئة الكسولة أن أخطاء الإعداد — ككلمة مرور خاطئة أو اسم مضيف غير صحيح أو جدار ناري يحجب المنفذ — لن تُكتشف إلا عند تنفيذ أول استعلام فعلي.&lt;/p&gt;
&lt;p&gt;قد يقود ذلك إلى تطبيق يقلع ويبدو سليمًا ثم يفشل لاحقًا عند أول محاولة وصول لقاعدة البيانات. ولمنع ذلك وضمان إقلاع «يفشل مبكرًا»، من أفضل الممارسات الحاسمة استدعاء &lt;strong&gt;db.Ping()‎&lt;/strong&gt; أو &lt;strong&gt;db.PingContext()‎&lt;/strong&gt; فور sql.Open. تتحقق هذه الدالة صراحة من إمكانية فتح اتصال بقاعدة البيانات ومن استجابة الخادم، فتمنحك تغذية راجعة فورية عن صحة إعدادات الاتصال.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;connectToDB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Failed to configure database connection: %v&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Ping the database to ensure the connection is valid.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Failed to connect to database: %v&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Successfully connected to the database!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="mastering-connection-pool-configuration"&gt;إتقان ضبط Pool الاتصالات&lt;/h2&gt;
&lt;p&gt;الإعدادات الافتراضية لـ pool الاتصالات في database/sql غالبًا لا تكفي أحمال الإنتاج. والضبط السليم أساس الأداء العالي والاستقرار والمرونة. توفر Go أربع دوال رئيسية للتحكم في سلوك الـ pool هي: SetMaxOpenConns وSetMaxIdleConns وSetConnMaxLifetime وSetConnMaxIdleTime. وهذه الإعدادات ليست مستقلة؛ إنها منظومة مترابطة يجب ضبطها ككل بحسب حمل التطبيق والبنية المحيطة.&lt;/p&gt;
&lt;h2 id="setmaxopenconnsn-your-first-line-of-defense"&gt;SetMaxOpenConns(n): خط دفاعك الأول&lt;/h2&gt;
&lt;p&gt;هذا أهم إعداد في إدارة الـ pool. فهو يفرض حدًا صارمًا على إجمالي الاتصالات (المستخدمة والخاملة معًا) التي يفتحها الـ pool نحو قاعدة البيانات. القيمة الافتراضية صفر — أي بلا حدود.&lt;/p&gt;
&lt;p&gt;والاتصالات المفتوحة بلا حد خطرة. فتحت الضغط العالي قد يطلق تطبيق Go آلاف الـ goroutines، كل منها يحاول تنفيذ استعلام. وبلا حد، قد يحاول التطبيق فتح آلاف الاتصالات فيغرق خادم قاعدة البيانات ويتجاوز حد max_connections المضبوط لديه. والنتيجة أخطاء «Error 1040: Too many connections» وأعطال متتالية لا في التطبيق المسبب وحده، بل في كل خدمة أخرى متصلة بقاعدة البيانات نفسها.&lt;/p&gt;
&lt;p&gt;يعمل SetMaxOpenConns آلية ضغط عكسي حاسمة: عند بلوغ الحد، تنتظر أي goroutine جديدة تطلب اتصالًا حتى يعود اتصال إلى الـ pool، فيُكبح وصول التطبيق لقاعدة البيانات عند معدل محتمل. وتُحدد القيمة بناءً على سعة خادم قاعدة البيانات، وعدد نسخ التطبيق المتصلة به، واحتياجات التزامن.&lt;/p&gt;
&lt;h2 id="setmaxidleconnsn-balancing-readiness-and-resources"&gt;SetMaxIdleConns(n): موازنة الجاهزية والموارد&lt;/h2&gt;
&lt;p&gt;تتحكم هذه الدالة في أقصى عدد اتصالات تبقى مفتوحة في مجموعة الخمول انتظارًا للاستخدام. القيمة الافتراضية 2.&lt;/p&gt;
&lt;p&gt;والمقايضة هنا بين الأداء واستهلاك الموارد:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;القيمة الأعلى&lt;/strong&gt; تعني أن الاتصالات متاحة فورًا عند اندفاعة مرور مفاجئة، فلا تنتظر الطلبات فتح اتصالات جديدة — أي كمون أقل.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;القيمة الأدنى&lt;/strong&gt; توفر الموارد على خادم التطبيق (ذاكرة وواصفات ملفات) وخادم قاعدة البيانات (ذاكرة وخانات عمليات).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;عندما يُطلب اتصال والـ pool فيه أقل من MaxIdleConns اتصالًا خاملًا، قد تُفتح اتصالات جديدة حتى مع قرب تحرر اتصالات قائمة. وبالعكس: عندما يعود اتصال والـ pool ممتلئ بعدد MaxIdleConns خاملًا، يُغلق الاتصال العائد ويُهمل. هذا الفتح والإغلاق المستمر يسمى تقلّب الاتصالات (Connection Churn). ولتقليله، من الاستراتيجيات الشائعة الفعالة للخدمات عالية الإنتاجية ضبط SetMaxIdleConns بقيمة SetMaxOpenConns نفسها؛ فبعد إحماء الـ pool يعاد استخدام الاتصالات إلى أقصى حد، ويقل كمون إنشاء الجديد منها.&lt;/p&gt;
&lt;h2 id="setconnmaxlifetimed-defeating-firewalls-and-stale-connections"&gt;SetConnMaxLifetime(d): قهر الجدر النارية والاتصالات الميتة&lt;/h2&gt;
&lt;p&gt;تضبط هذه الدالة أقصى مدة يمكن فيها إعادة استخدام الاتصال منذ إنشائه. بعدها يوسم الاتصال منتهي الصلاحية، وعند طلبه التالي من مجموعة الخمول يُغلق ويُنشأ بديل جديد. ويحدث هذا الإغلاق كسولًا، لا في الخلفية.&lt;/p&gt;
&lt;p&gt;هذا الإعداد حيوي لموثوقية الإنتاج، خصوصًا في بيئات فيها جدر نارية ذات حالة أو بوابات NAT أو موازِنات أحمال. فلهذه الأجهزة الشبكية عادةً مهلات لقطع اتصالات TCP الخاملة. إذا بقي اتصال في pool تطبيق Go خاملًا أطول من تلك المهلة، قد يسقطه الجهاز الشبكي بصمت دون إخطار التطبيق أو قاعدة البيانات. والـ pool، غافلًا عن موت الاتصال، سيسلّمه لاستعلام لاحق فيفشل بخطأ شبكي مثل bad connection أو connection reset by peer.&lt;/p&gt;
&lt;p&gt;بضبط SetConnMaxLifetime على قيمة أقصر من مهلة الخمول الشبكية (5 دقائق مثلًا إذا كانت مهلة الجدار الناري 10 دقائق)، يعيد التطبيق تدوير الاتصالات استباقيًا قبل أن تفسد، فترتفع مرونته أمام سلوك البنية الشبكية ارتفاعًا كبيرًا.&lt;/p&gt;
&lt;h2 id="setconnmaxidletimed-cleaning-up-after-the-party"&gt;SetConnMaxIdleTime(d): التنظيف بعد انتهاء الحفلة&lt;/h2&gt;
&lt;p&gt;قُدمت هذه الدالة في Go 1.15، وتضبط أقصى مدة يبقى فيها الاتصال خاملًا في الـ pool قبل إغلاقه. وهي تكمّل الإعدادات الأخرى بآلية تسمح للـ pool بالانكماش في فترات النشاط المنخفض.&lt;/p&gt;
&lt;p&gt;تخيّل تطبيقًا ضُبط بقيمة SetMaxIdleConns مرتفعة لاستيعاب ذروات المرور. خلال الذروات سيتضخم الـ pool باتصالات خاملة كثيرة كانت في الماضي تبقى مفتوحة حتى تبلغ ConnMaxLifetime. مع SetConnMaxIdleTime يمكن ضبط التطبيق ليغلق الاتصالات الخاملة منذ 5 دقائق مثلًا، فيصبح الـ pool مرنًا يقلّص استهلاكه بسلاسة بعد فترات الحمل العالي — وهو أكفأ للتطبيق وخادم قاعدة البيانات معًا.&lt;/p&gt;
&lt;h2 id="a-holistic-tuning-strategy"&gt;استراتيجية ضبط شمولية&lt;/h2&gt;
&lt;p&gt;لا تُضبط هذه المعاملات الأربعة بمعزل عن بعضها؛ فقيمها مترابطة ويجب اختيارها بفهم شمولي لحمل التطبيق وبيئة تشغيله. ولا يوجد إعداد «أفضل» عالمي.&lt;/p&gt;
&lt;p&gt;تنطلق عملية تحديد القيم الصحيحة من قيود النظام وأهدافه: SetMaxOpenConns سقف صارم تمليه سعة قاعدة البيانات وعدد نسخ التطبيق. وSetMaxIdleConns رافعة أداء تقايض الموارد بكمون أدنى. وSetConnMaxLifetime خاصية موثوقية تمليها البنية الشبكية الخارجية. وأخيرًا SetConnMaxIdleTime يمنح المرونة ليتكيف الـ pool مع تغير الحمل.&lt;/p&gt;
&lt;p&gt;وتقدم النماذج التالية نقاط انطلاق مبررة لأنماط تطبيقات مختلفة، توضح طريقة التفكير خلف كل إعداد.&lt;/p&gt;
&lt;h2 id="high-traffic-api-server"&gt;خادم API عالي الحركة&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SetMaxOpenConns: ‏(حد اتصالات القاعدة ÷ عدد النسخ) × 0.8&lt;/li&gt;
&lt;li&gt;SetMaxIdleConns: مساوٍ لـ MaxOpenConns&lt;/li&gt;
&lt;li&gt;SetConnMaxLifetime: أقل من مهلة الجدار الناري/موازن الأحمال (5 دقائق مثلًا)&lt;/li&gt;
&lt;li&gt;SetConnMaxIdleTime: ‏5 دقائق&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;المنطق: أولوية الكمون المنخفض عبر إبقاء pool كامل من الاتصالات الدافئة وتفادي التقلب. إعادة تدوير استباقية للاتصالات من أجل الموثوقية، وتنظيف الفائض الخامل في فترات الهدوء. ومعامل الـ 80% يوفر هامش أمان.&lt;/p&gt;
&lt;h2 id="batch-processing-worker"&gt;عامل معالجة الدفعات&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SetMaxOpenConns: عدد الأنوية × 2&lt;/li&gt;
&lt;li&gt;SetMaxIdleConns: عدد الأنوية × 2&lt;/li&gt;
&lt;li&gt;SetConnMaxLifetime: أقل من مهلة الجدار الناري/موازن الأحمال (5 دقائق مثلًا)&lt;/li&gt;
&lt;li&gt;SetConnMaxIdleTime: دقيقة واحدة&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;المنطق: حصر تزامن قاعدة البيانات في حدود قدرة المعالجة المتاحة للعامل. والحمل هنا ثابت عادة لا اندفاعي، فيصح أن تساوي مجموعة الخمول الحجم نفسه مع تنظيف أسرع للاتصالات إذا خمل العامل.&lt;/p&gt;
&lt;h2 id="low-traffic-internal-tool"&gt;أداة داخلية منخفضة الحركة&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SetMaxOpenConns: ‏10&lt;/li&gt;
&lt;li&gt;SetMaxIdleConns: ‏2 (الافتراضي)&lt;/li&gt;
&lt;li&gt;SetConnMaxLifetime: ساعة واحدة&lt;/li&gt;
&lt;li&gt;SetConnMaxIdleTime: ‏10 دقائق&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;المنطق: الهدف الأول بصمة موارد منخفضة، وكمون الاتصال أقل أهمية. وعمر الاتصال الأطول مقبول عمومًا في بيئة شبكة داخلية مستقرة.&lt;/p&gt;
&lt;h2 id="executing-queries-the-developers-day-to-day"&gt;تنفيذ الاستعلامات: العمل اليومي للمطور&lt;/h2&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="دورة حياة الاستعلام في أربع خطوات: احصل على اتصال بـ QueryContext، ومرّ على الصفوف بـ Next، ونفّذ defer rows.Close دائمًا، ثم افحص rows.Err"
srcset="https://akemara.com/en/blog/golang-database-connection-management/images/webp/executing-queries-the-developers-day-to-day_hu_ad8187d36be685e6.webp 320w, https://akemara.com/en/blog/golang-database-connection-management/images/webp/executing-queries-the-developers-day-to-day_hu_66f2e23e62ee7cbd.webp 480w, https://akemara.com/en/blog/golang-database-connection-management/images/webp/executing-queries-the-developers-day-to-day_hu_32e09630e741696c.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://akemara.com/en/blog/golang-database-connection-management/images/webp/executing-queries-the-developers-day-to-day_hu_ad8187d36be685e6.webp"
width="760"
height="230"
loading="lazy" data-zoomable data-zoom-src="https://akemara.com/en/blog/golang-database-connection-management/images/webp/executing-queries-the-developers-day-to-day_hu_e2e46f48aa5c615d.webp" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;بعد إنشاء pool الاتصالات وضبطه، يتعامل المطورون مع قاعدة البيانات عبر مجموعة أساسية من دوال تنفيذ الاستعلامات. والالتزام بأفضل الممارسات في هذه المرحلة حاسم لصحة التطبيق وأمانه ولمنع تسريبات الموارد القادرة على شل الخدمة تحت الضغط.&lt;/p&gt;
&lt;h2 id="write-operations-exec-and-execcontext"&gt;عمليات الكتابة: Exec()‎ وExecContext()‎&lt;/h2&gt;
&lt;p&gt;لعبارات SQL التي لا تعيد صفوفًا — مثل INSERT وUPDATE وDELETE — توفر حزمة database/sql الدالتين Exec()‎ وExecContext()‎. ويفضَّل متغيرا الـ Context لأنهما يتيحان إلغاء الطلبات والمهلات. تعيد هاتان الدالتان قيمتين: واجهة sql.Result وخطأ.&lt;/p&gt;
&lt;p&gt;ومن الممارسات الأمنية الحاسمة ألا تُدمج معاملات الاستعلام مباشرة في نص SQL بدوال مثل fmt.Sprintf أبدًا؛ فهذا يفتح ثغرة حقن SQL. استخدم بدلًا من ذلك علامات المعاملات (&lt;code&gt;?&lt;/code&gt; لدى go-sql-driver/mysql) في نص الاستعلام ومرر القيم وسائط إضافية لدالة Exec، وسيتكفل الـ driver بالمعاملة الآمنة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;أمثلة كود: عمليات كتابة آمنة&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// AddUser inserts a new user into the database.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;AddUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;INSERT INTO users (email, name) VALUES (?,?)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastInsertId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// UpdateUserEmail changes the email for a given user ID.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;UpdateUserEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newEmail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UPDATE users SET email =? WHERE id =?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RowsAffected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// DeleteUser removes a user from the database.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DeleteUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;DELETE FROM users WHERE id =?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RowsAffected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="retrieving-results-lastinsertid-and-rowsaffected"&gt;استرجاع النتائج: LastInsertId()‎ وRowsAffected()‎&lt;/h2&gt;
&lt;p&gt;توفر واجهة sql.Result المعادة من Exec دوالًا لمعرفة نتيجة العملية:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LastInsertId()‎: بعد INSERT في جدول بمفتاح أساسي AUTO_INCREMENT، تعيد هذه الدالة معرّف الصف الجديد. وهي أساسية في المسارات التي تحتاج مرجعًا فوريًا للسجل الجديد.&lt;/li&gt;
&lt;li&gt;RowsAffected()‎: لعبارات UPDATE أو DELETE، تعيد عدد الصفوف التي غيّرتها العملية — مفيدة للتحقق من أن العملية أحدثت الأثر المتوقع.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;وأمثلة الكود في القسم السابق تجسّد الاستخدام الاصطلاحي لهاتين الدالتين.&lt;/p&gt;
&lt;h2 id="read-operations-queryrow-vs-query"&gt;عمليات القراءة: QueryRow()‎ مقابل Query()‎&lt;/h2&gt;
&lt;p&gt;توفر الحزمة دالتين رئيسيتين لتنفيذ عبارات SELECT:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;db.QueryRow()‎ (وQueryRowContext) للاستعلام المتوقع أن يعيد &lt;strong&gt;صفًا واحدًا على الأكثر&lt;/strong&gt; — مثالية لجلب سجل بمفتاحه الأساسي الفريد. تعيد كائن ‎&lt;em&gt;sql.Row. ومن خصائصها المهمة أن QueryRow نفسها لا تعيد خطأ؛ فأي خطأ أثناء الاستعلام يؤجَّل حتى استدعاء Scan()‎ على الـ ‎&lt;/em&gt;sql.Row المعاد. وإذا لم يعد الاستعلام صفوفًا، تعيد Scan()‎ الخطأ الخاص sql.ErrNoRows الذي يجب فحصه صراحة.&lt;/li&gt;
&lt;li&gt;db.Query()‎ (وQueryContext) للاستعلام الذي قد يعيد &lt;strong&gt;صفرًا أو أكثر من الصفوف&lt;/strong&gt;. تعيد قيمتين: كائن ‎&lt;em&gt;sql.Rows وخطأ يجب فحصه فورًا، ثم يُستخدم الـ ‎&lt;/em&gt;sql.Rows للمرور على النتائج.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;مثال كود: QueryRow لجلب مستخدم واحد&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetUserByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryRowContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SELECT id, email, name FROM users WHERE id =?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ErrNoRows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// It&amp;#39;s common to return nil, nil to indicate not found.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// A different error occurred.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-golden-rule-defer-rowsclose"&gt;القاعدة الذهبية: defer rows.Close()‎&lt;/h2&gt;
&lt;p&gt;هذه من أهم الممارسات وأكثرها إغفالًا عند العمل مع database/sql. عند استدعاء db.Query()‎ يُسحب اتصال من الـ pool، ويبقى ذلك الاتصال مشغولًا وغير متاح لبقية الـ goroutines حتى يُغلق كائن ‎*sql.Rows المرتبط به.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;عدم إغلاق ‎*sql.Rows يعني تسريب اتصال.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;فالاتصال الفعلي لا يعود إلى الـ pool أبدًا. وتحت الضغط، سيستنفد التطبيق المسرّب كل اتصالات الـ pool المتاحة (حتى MaxOpenConns) سريعًا. وبعد النفاد، ستعلق كل goroutine تحاول الاستعلام إلى ما لا نهاية — أي أن التطبيق يتجمد عمليًا.&lt;/p&gt;
&lt;p&gt;وأمتن الطرق وأكثرها اصطلاحية لضمان إغلاق ‎*sql.Rows دائمًا هي عبارة defer فور استدعاء db.Query()‎. تضمن defer استدعاء rows.Close()‎ عند خروج الدالة، سواء خرجت طبيعيًا أو بخطأ أو panic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;مثال كود: استعلام مع defer rows.Close()‎&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SELECT id, name FROM users&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;عدم إغلاق rows يقود إلى تسريب الاتصالات.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SELECT id, email, name FROM users ORDER BY name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The Golden Rule: Defer Close to prevent connection leaks.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// rows.Close() will be called by the defer statement.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// After the loop, check for errors that may have occurred during iteration.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;صحيح أن المرور على كل الصفوف بـ rows.Next()‎ حتى تعيد false يغلق الـ ‎*sql.Rows ضمنيًا، لكن التعويل على ذلك هش: إذا انقطعت الحلقة مبكرًا بخطأ في Scan، لن تُغلق الصفوف دون defer. لذا يجب اعتبار defer rows.Close()‎ إلزامية.&lt;/p&gt;
&lt;h2 id="advanced-patterns-for-robust-applications"&gt;أنماط متقدمة لتطبيقات متينة&lt;/h2&gt;
&lt;p&gt;بعد عمليات CRUD الأساسية، تحتاج تطبيقات الإنتاج أنماط تفاعل أكثر تطورًا مع قاعدة البيانات لضمان سلامة البيانات والأداء والموثوقية: العمليات الذرّية بالمعاملات، وتحسينات الأداء بالـ prepared statements، واستراتيجيات جاهزية الخدمة لاستقبال الحركة فور الإقلاع.&lt;/p&gt;
&lt;h2 id="atomic-operations-with-transactions-sqltx"&gt;العمليات الذرّية بالمعاملات (sql.Tx)&lt;/h2&gt;
&lt;p&gt;معاملة قاعدة البيانات سلسلة عمليات تُنفذ كوحدة عمل منطقية واحدة. ووعدها الجوهري هو الذرّية: إما أن تنجح كل عملياتها وتُثبَّت في قاعدة البيانات، وإما لا شيء منها وتبقى القاعدة على حالها الأصلية. وهذا أساس سلامة البيانات في العمليات متعددة الخطوات.&lt;/p&gt;
&lt;p&gt;توفر حزمة database/sql في Go دعمًا أصيلًا للمعاملات عبر كائن ‎&lt;em&gt;sql.Tx الذي تحصل عليه من db.Begin()‎ أو db.BeginTx()‎. وكل العمليات على كائن ‎&lt;/em&gt;sql.Tx مضمونة التنفيذ على الاتصال الفعلي الواحد نفسه.&lt;/p&gt;
&lt;p&gt;والنمط الاصطلاحي لمعالجة المعاملات في Go مصمم للمتانة:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ابدأ المعاملة بـ tx, err := db.BeginTx(ctx, &amp;amp;sql.TxOptions{…})‎.&lt;/li&gt;
&lt;li&gt;نفّذ فورًا defer tx.Rollback()‎. هذه شبكة أمان: إن فشلت أي عملية لاحقة وخرجت الدالة مبكرًا، يُستدعى الـ Rollback حتمًا فيُمنع التحديث الجزئي وتُحرر موارد المعاملة.&lt;/li&gt;
&lt;li&gt;نفّذ كل عمليات قاعدة البيانات عبر دوال كائن tx (مثل tx.ExecContext وtx.QueryRowContext)، وافحص الخطأ بعد كل استدعاء وعُد فورًا عند الفشل.&lt;/li&gt;
&lt;li&gt;إذا نجحت كل العمليات، فالخطوة الأخيرة استدعاء tx.Commit()‎ صراحة. إن نجح الـ Commit اكتملت المعاملة، وسينفَّذ الـ Rollback المؤجل لكنه سيكون بلا أثر على معاملة مثبتة وسيعيد sql.ErrTxDone الذي يُتجاهل عادة.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;مثال كود: تحويل مصرفي ذرّي&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// TransferFunds atomically moves an amount from one account to another.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;TransferFunds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fromAccountID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;toAccountID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Defer a rollback in case of error.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. Check if the &amp;#39;from&amp;#39; account has sufficient balance.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryRowContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SELECT balance FROM accounts WHERE id =?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fromAccountID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;insufficient funds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. Debit the &amp;#39;from&amp;#39; account.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UPDATE accounts SET balance = balance -? WHERE id =?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fromAccountID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. Credit the &amp;#39;to&amp;#39; account.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UPDATE accounts SET balance = balance +? WHERE id =?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;toAccountID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 4. If all operations succeeded, commit the transaction.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="performance-and-security-with-prepared-statements-sqlstmt"&gt;الأداء والأمان مع الـ Prepared Statements ‏(sql.Stmt)&lt;/h2&gt;
&lt;p&gt;العبارة المحضّرة (Prepared Statement) استعلام SQL مُجمَّع مسبقًا يمكن تنفيذه مرات عديدة بمعاملات مختلفة. ولها فائدتان أساسيتان:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;الأمان&lt;/strong&gt;: بفصل منطق الاستعلام عن معاملات البيانات، توفر العبارات المحضّرة حماية قوية من هجمات حقن SQL؛ إذ يعامل محرك قاعدة البيانات المعاملات كبيانات فقط لا ككود قابل للتنفيذ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الأداء&lt;/strong&gt;: للاستعلامات المتكررة، تكون العبارات المحضّرة أكفأ: تحلل قاعدة البيانات الاستعلام وتبني خطة تنفيذه مرة واحدة في مرحلة «التحضير»، ثم لا تتطلب التنفيذات اللاحقة سوى إرسال المعاملات، فينخفض عبء التحليل على الخادم.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;في Go تمثَّل العبارة المحضّرة بـ ‎*sql.Stmt وتُنشأ عبر db.Prepare()‎.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;التفاعل مع Pool الاتصالات&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;يرتبط سلوك العبارات المحضّرة ارتباطًا وثيقًا بتجريد الـ pool. فعند تحضير عبارة بـ db.Prepare()‎ تُحضَّر على اتصال محدد من الـ pool، ويتذكر كائن ‎*sql.Stmt المعاد ذلك الاتصال. وعند استدعاء stmt.Exec()‎ تحاول المكتبة استخدام الاتصال الأصلي؛ فإن كان مشغولًا بطلب آخر، تأخذ database/sql اتصالًا متاحًا آخر بشفافية وتعيد تحضير العبارة عليه قبل التنفيذ. تضمن إعادة التحضير التلقائية هذه توافرًا عاليًا، لكنها قد تسبب «تقلّب عبارات» تحت التزامن العالي، حيث تُحضَّر العبارة نفسها مرات كثيرة عبر اتصالات مختلفة.&lt;/p&gt;
&lt;p&gt;في المقابل، العبارة المحضّرة من معاملة (tx.Prepare()‎) مرتبطة حصريًا باتصال تلك المعاملة الواحد ولن يعاد تحضيرها.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;مثال كود: إدراج دفعات عبر عبارة محضّرة&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;BulkAddProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Prepare the statement once.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PrepareContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;INSERT INTO products(name, price) VALUES(?,?)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Execute the statement multiple times in a loop.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// It&amp;#39;s often better to continue and collect errors, &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// but for simplicity, we return on the first error.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="connection-pre-warming-and-health-checks"&gt;إحماء الاتصالات المسبق وفحوص الصحة&lt;/h2&gt;
&lt;p&gt;مفيد في البيئات عالية الحمل لتفادي كمون الإقلاع.&lt;/p&gt;
&lt;p&gt;عند إقلاع تطبيق Go يكون pool الاتصالات «باردًا» — بلا أي اتصال مفتوح. فتعاني أولى الطلبات الواردة كمونًا أعلى إذ تنتظر فتح اتصالات جديدة عند الطلب. وللخدمات التي يجب أن تخدم بكامل سرعتها فور الإقلاع، من الممارسات الجيدة «إحماء» الـ pool مسبقًا: إنشاء عدد من الاتصالات استباقيًا ووضعها في مجموعة الخمول أثناء مرحلة تهيئة التطبيق.&lt;/p&gt;
&lt;p&gt;ومن استراتيجيات الإحماء الشائعة استدعاء db.Ping()‎ بالتوازي عددًا من المرات حتى قيمة MaxIdleConns المضبوطة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;مثال كود: إحماء pool الاتصالات&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;warmUpPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Ping the database to force a connection to be opened.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PingContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;failed to ping database during warmup: %v&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// In main() after setting pool config:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// stats := db.Stats()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// warmUpPool(context.Background(), db, stats.MaxIdleConns)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;إضافة إلى ذلك، من الممارسات القياسية للـ microservices العاملة في بيئات منسقة مثل Kubernetes كشف نقطة فحص صحة (مثل ‎/healthz) يعرف منها المنسق هل نسخة الخدمة سليمة وجاهزة لاستقبال الحركة. وجزء جوهري من هذا الفحص هو التحقق من الاتصال بالتبعيات النهائية — ومنها قاعدة البيانات. وفحص صحة بسيط وفعال هو استدعاء db.PingContext()‎ بمهلة قصيرة.&lt;/p&gt;
&lt;h2 id="troubleshooting-common-connection-issues"&gt;استكشاف مشاكل الاتصال الشائعة&lt;/h2&gt;
&lt;p&gt;حتى مع تطبيق جيد الضبط، قد تظهر مشاكل الاتصال بقاعدة البيانات في الإنتاج. وفهم الأعراض الشائعة وأسبابها وحلولها أساس الحفاظ على خدمة موثوقة.&lt;/p&gt;
&lt;h2 id="the-symptom-error-1040-too-many-connections"&gt;العرض: «Error 1040: Too many connections»&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;السبب&lt;/strong&gt;: يأتي هذا الخطأ من خادم MySQL مباشرة ويعني بلوغ حد max_connections لديه. وفي سياق تطبيقات Go، يكاد سببه يكون دائمًا محاولة أسطول التطبيقات مجتمعًا فتح اتصالات أكثر مما يسمح به الخادم. والمتهم الأول إعداد db.SetMaxOpenConns()‎ غير مضبوط أو مفرط الكرم.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الحل&lt;/strong&gt;: فرض حد معقول على جانب التطبيق. احسب قيمة SetMaxOpenConns آمنة للنسخة الواحدة (مثلًا: (حد اتصالات القاعدة ÷ عدد النسخ) × 0.8 لهامش أمان) وطبّقها باتساق على كل النسخ.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-symptom-application-hangs-or-deadlocks"&gt;العرض: تعليق التطبيق أو الجمود&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;السبب&lt;/strong&gt;: يفقد التطبيق استجابته وتنقضي مهلات الطلبات. يحدث هذا غالبًا عندما تكون كل اتصالات الـ pool مشغولة وتعلق الـ goroutines الجديدة بلا نهاية منتظرة اتصالًا يتحرر. وله سببان رئيسيان:&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;تسريب الاتصالات&lt;/strong&gt;: السبب الأشيع. يُسحب اتصال لخدمة db.Query لكن الـ ‎*sql.Rows الناتج لا يُغلق أبدًا، فلا يعود الاتصال إلى الـ pool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pool أصغر من اللازم&lt;/strong&gt;: قيمة SetMaxOpenConns المضبوطة أدنى من مستوى التزامن المستمر للتطبيق، فيحتدم تنافس مشروع على مورد شحيح.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;الحل&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;دقق بحثًا عن التسريبات&lt;/strong&gt;: راجع الكود بدقة عن أي استدعاء db.Query أو tx.Query لا يليه فورًا defer rows.Close()‎. هذا المشتبه الأول.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;راقب إحصاءات الـ Pool&lt;/strong&gt;: استخدم db.Stats()‎ لكشف مقاييس الـ pool ‏(OpenConnections وInUse وIdle وWaitCount). إذا كانت InUse تساوي MaxOpenConns باستمرار وWaitCount يتصاعد، فالـ pool مشبع — وهذه البيانات تؤكد تسريبًا أو صغر حجم.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;اضبط SetMaxOpenConns&lt;/strong&gt;: إن لم تجد تسريبات وكان لدى خادم قاعدة البيانات سعة فائضة، ارفع الحد بحذر.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="the-symptom-intermittent-bad-connection-or-connection-reset-by-peer-errors"&gt;العرض: أخطاء متقطعة bad connection أو connection reset by peer&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;السبب&lt;/strong&gt;: تظهر هذه الأخطاء عادة بعد فترة خمول، وهي علامة كلاسيكية على أن جهازًا شبكيًا ذا حالة (جدار ناري أو بوابة NAT أو موازن أحمال) بين التطبيق وقاعدة البيانات أنهى اتصال TCP خاملًا بصمت. والـ pool، غافلًا عن هذا الحدث الخارجي، يحاول إعادة استخدام الاتصال الميت فينتج خطأ شبكي.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الحل&lt;/strong&gt;: أعد تدوير الاتصالات استباقيًا قبل أن تنهيها البنية الشبكية. اضبط db.SetConnMaxLifetime()‎ على مدة أقصر بأمان من مهلة خمول الجهاز الشبكي. فإذا كانت مهلة الجدار الناري 10 دقائق، فإن SetConnMaxLifetime(5 * time.Minute)‎ حل متين.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-symptom-lock-tables-or-other-session-specific-commands-misbehave"&gt;العرض: أوامر LOCK TABLES وأوامر الجلسة الأخرى تتصرف بغرابة&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;السبب&lt;/strong&gt;: يعمل المطور بافتراض خاطئ أن الاستدعاءات المتتالية على كائن ‎*sql.DB ستنفَّذ على اتصال قاعدة البيانات نفسه. وكما ناقشنا في القسم الأول، لا يقدم مدير pool الـ database/sql أي ضمان كهذا وقد يستخدم اتصالًا مختلفًا لكل عبارة لتعظيم التزامن. فالأمر الذي يضبط حالة جلسة (كحيازة قفل أو ضبط متغير جلسة) على اتصال لن يؤثر على أمر لاحق يعمل على اتصال آخر.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الحل&lt;/strong&gt;: عندما يتطلب تسلسل عمليات حالة جلسة متسقة، يجب لفه في معاملة. ابدأ معاملة بـ db.BeginTx()‎؛ فكل العمليات على كائن ‎*sql.Tx الناتج مضمونة التنفيذ على الاتصال الفعلي نفسه، فتُصان حالة الجلسة طوال عمر المعاملة.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;الخلاصة&lt;/h2&gt;
&lt;p&gt;إدارة اتصالات قواعد البيانات الفعالة في Go ليست فكرة لاحقة، بل مكوّن جوهري في بناء تطبيقات قابلة للتوسع ومرنة. توفر حزمة database/sql تجريدًا رفيع المستوى قويًا يتكفل — متى فُهم وضُبط صحيحًا — بتعقيدات التزامن وتجميع الموارد بكفاءة.&lt;/p&gt;
&lt;p&gt;ويمكن تكثيف مبادئ الإدارة الاحترافية في ثلاثة محاور:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;تبنَّ النموذج الذهني الصحيح&lt;/strong&gt;: كائن sql.DB مدير pool اتصالات آمن للتزامن، لا اتصال واحد. والعمليات افتراضيًا عديمة الحالة ويمكن تنفيذها على أي اتصال متاح. هذا الفهم أساس تفادي الأخطاء المنطقية المرتبطة بحالة الجلسة وحسن استثمار خصائص التزامن في الحزمة.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ضبط شمولي واستباقي للـ Pool&lt;/strong&gt;: الإعدادات الافتراضية لا تكفي معظم أحمال الإنتاج. المطلوب استراتيجية شمولية مقصودة تعامل معاملات الضبط الأربعة — SetMaxOpenConns وSetMaxIdleConns وSetConnMaxLifetime وSetConnMaxIdleTime — كمنظومة مترابطة، تراعي حمل التطبيق وسعة قاعدة البيانات وسلوك البنية الشبكية المحيطة، لتحقيق توازن الأداء والاستقرار وكفاءة الموارد.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;يقظة في إدارة الموارد&lt;/strong&gt;: مسؤولية المطور الأولى في العمل اليومي هي الإدارة الدقيقة لدورات حياة الاتصالات. وأهم ممارسة على الإطلاق إغلاق ‎*sql.Rows دائمًا بـ defer rows.Close()‎ لمنع تسريبات الاتصالات — السبب الشائع لانهيار التطبيقات تحت الضغط. وبالمثل، الاستخدام الصحيح للمعاملات بنمط defer tx.Rollback()‎ يضمن سلامة البيانات في العمليات الذرّية.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;باستيعاب هذه المبادئ وتطبيق الأنماط والإعدادات المفصلة في هذا الدليل، يستطيع مطورو Go بناء تطبيقات تتعامل مع قواعد MySQL/MariaDB بطريقة ليست صحيحة وآمنة فحسب، بل عالية الأداء وصامدة في أقسى بيئات الإنتاج.&lt;/p&gt;</description></item><item><title>وكلاء الذكاء الاصطناعي في تطوير البرمجيات: الإبحار في سماء البرمجة الحديثة</title><link>https://akemara.com/ar/blog/ai-agents-software-development/</link><pubDate>Tue, 22 Apr 2025 00:00:00 +0000</pubDate><guid>https://akemara.com/ar/blog/ai-agents-software-development/</guid><description>&lt;p&gt;في عالم تطوير البرمجيات دائم التغير، يمثّل ظهور وكلاء الذكاء الاصطناعي (AI Agents) نقلة نوعية حقيقية. فهذه الأنظمة الذكية تعيد تعريف الطريقة التي تُبنى بها التطبيقات وتُختبر وتُنشر. ولتدرك حجم هذا التحول، تأمّل تشبيهًا من عالم الطيران: الانتقال من قواعد الطيران البصري ‏(VFR) إلى قواعد الطيران الآلي ‏(IFR). فكما تتيح قواعد الطيران الآلي للطيارين الإبحار بأمان اعتمادًا على العدادات بدلًا من الاعتماد على الرؤية وحدها، يمكّن وكلاء الذكاء الاصطناعي المطورين من الإبحار في بيئات برمجية معقدة بدقة وكفاءة أعلى.&lt;/p&gt;
&lt;h2 id="understanding-ai-agents-the-new-co-pilots-in-software-development"&gt;فهم وكلاء الذكاء الاصطناعي: مساعدو الطيار الجدد في تطوير البرمجيات&lt;/h2&gt;
&lt;p&gt;وكلاء الذكاء الاصطناعي أنظمة ذكية قادرة على أداء مهام مثل توليد الكود، وتصحيح الأخطاء (Debugging)، وحتى التخطيط المعماري. إنهم يعملون كمساعدي طيار: يقدمون الاقتراحات، ويرصدون المشكلات المحتملة، ويبسّطون عملية التطوير. هذا التحول يتيح للمطورين التركيز أكثر على القرارات الاستراتيجية وتقليل الانشغال بالمهام الروتينية.&lt;/p&gt;
&lt;p&gt;والمهم أن وكلاء الذكاء الاصطناعي ليسوا أداة واحدة متجانسة؛ فهم يتفاوتون في درجة الاستقلالية، من أنظمة الـ autocomplete إلى وكلاء مستقلين تمامًا قادرين على تخطيط مهام برمجية وتنفيذها. ويتضاعف أثرهم عندما يستطيعون التواصل فيما بينهم ومع الأدوات والبيانات الخارجية — وهنا يأتي دور بروتوكولات مثل Agent-to-Agent ‏(A2A) وModel Context Protocol ‏(MCP).&lt;/p&gt;
&lt;h2 id="from-visual-to-instrumental-the-coding-evolution"&gt;من البصري إلى الآلي: تطور البرمجة&lt;/h2&gt;
&lt;p&gt;البرمجة التقليدية أشبه بالطيران وفق قواعد الطيران البصري. يعتمد المطورون على معرفتهم وحدسهم وخبرتهم، متنقلين بين التوثيق (Documentation) ونقاشات Stack Overflow ونماذجهم الذهنية للأنظمة. هذا الأسلوب مرن لكنه محدود؛ فعند الدخول إلى أنظمة معقدة، يصبح غياب الإرشاد الخارجي عنق زجاجة.&lt;/p&gt;
&lt;p&gt;أما البرمجة بمساعدة الذكاء الاصطناعي فهي المكافئ لقواعد الطيران الآلي. يعمل الوكلاء كعدادات ملاحية: يقترحون الكود، ويلتقطون الأخطاء، ويرشّحون المكتبات، بل ويولّدون الهيكل المبدئي (Scaffolding) للمشروع. يصبح المطور قائد الرحلة، مستخدمًا الذكاء الاصطناعي كمنظومة عدادات تساعده على الإبحار والعمل بفعالية أكبر. والنتيجة: تكرار أسرع للدورات، ونطاق تقني أوسع، وإنتاجية أعلى.&lt;/p&gt;
&lt;h2 id="the-productivity-surge-ai-as-a-development-catalyst"&gt;طفرة الإنتاجية: الذكاء الاصطناعي كمحفّز للتطوير&lt;/h2&gt;
&lt;p&gt;وفق دراسات من GitHub وكلية هارفارد للأعمال، يقضي المطورون الذين يستخدمون أدوات ذكاء اصطناعي مثل Copilot وقتًا أطول بكثير في البرمجة الفعلية ووقتًا أقل في الكود المتكرر (Boilerplate) وأعمال التنسيق. يمكن أن ترتفع الإنتاجية بنسبة تصل إلى 45%، ما يتيح للمطورين تخصيص وقت أكبر لحل مشكلات العمل الجوهرية وللابتكار.&lt;/p&gt;
&lt;p&gt;كما يشجع وكلاء الذكاء الاصطناعي على استكشاف أوسع؛ إذ يشعر المطورون بثقة أكبر عند استخدام تقنيات غير مألوفة لهم لأن الذكاء الاصطناعي قادر على إرشادهم. والقدرة على بناء نموذج أولي واختباره وتحسينه فورًا تحفّز الإبداع والتجريب. الذكاء الاصطناعي لا يحل محل المطور — بل يضاعف إمكاناته.&lt;/p&gt;
&lt;h2 id="agent-to-agent-a2a-protocol-facilitating-inter-agent-communication"&gt;بروتوكول Agent-to-Agent ‏(A2A): تمكين التواصل بين الوكلاء&lt;/h2&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="مخطط توضيحي لبروتوكول A2A وهو يربط وكلاء الذكاء الاصطناعي عبر المنصات"
srcset="https://akemara.com/en/blog/ai-agents-software-development/images/webp/agent-to-agent-a2a-protocol-facilitating-inter-agent-communication_hu_319aa913fd1c7a4d.webp 320w, https://akemara.com/en/blog/ai-agents-software-development/images/webp/agent-to-agent-a2a-protocol-facilitating-inter-agent-communication_hu_84085d0cf365555d.webp 480w, https://akemara.com/en/blog/ai-agents-software-development/images/webp/agent-to-agent-a2a-protocol-facilitating-inter-agent-communication_hu_3dfd7fd438346308.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://akemara.com/en/blog/ai-agents-software-development/images/webp/agent-to-agent-a2a-protocol-facilitating-inter-agent-communication_hu_319aa913fd1c7a4d.webp"
width="760"
height="507"
loading="lazy" data-zoomable data-zoom-src="https://akemara.com/en/blog/ai-agents-software-development/images/webp/agent-to-agent-a2a-protocol-facilitating-inter-agent-communication_hu_a65796eec1f2939a.webp" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;بروتوكول Agent-to-Agent ‏(A2A)، الذي تقوده Google بدعم من كبرى شركات التقنية، معيار مفتوح يتيح لوكلاء الذكاء الاصطناعي التعاون عبر المنصات المختلفة. يسمح A2A للوكلاء باكتشاف بعضهم بعضًا، ومشاركة القدرات، وتنفيذ المهام معًا.&lt;/p&gt;
&lt;h3 id="key-features-of-a2a"&gt;أبرز خصائص A2A&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;اكتشاف الوكلاء&lt;/strong&gt;: عبر بطاقات وكلاء (Agent Cards) غنية بالبيانات الوصفية.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;إدارة المهام&lt;/strong&gt;: بدء مسارات العمل وتتبعها وإنهاؤها.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الحماية&lt;/strong&gt;: مصادقة وصلاحيات مدمجة في البروتوكول.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;دعم المهام الطويلة&lt;/strong&gt;: مصمم لمهام تمتد ساعات أو أيامًا.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;مع A2A، يتحول وكلاء الذكاء الاصطناعي إلى شبكة منسّقة بدلًا من أدوات معزولة، ما يفتح الباب أمام حل مشكلات تعاوني وموزّع عبر كامل الـ software stack.&lt;/p&gt;
&lt;h2 id="model-context-protocol-mcp-tool-and-data-access-for-agents"&gt;بروتوكول Model Context Protocol ‏(MCP): وصول الوكلاء إلى الأدوات والبيانات&lt;/h2&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="مخطط توضيحي لبروتوكول MCP وهو يتيح لوكلاء الذكاء الاصطناعي الوصول إلى الأدوات والبيانات الخارجية"
srcset="https://akemara.com/en/blog/ai-agents-software-development/images/webp/model-context-protocol-mcp-tool-and-data-access-for-agents_hu_f338b2ab1964a649.webp 320w, https://akemara.com/en/blog/ai-agents-software-development/images/webp/model-context-protocol-mcp-tool-and-data-access-for-agents_hu_d31b659f70e8a96d.webp 480w, https://akemara.com/en/blog/ai-agents-software-development/images/webp/model-context-protocol-mcp-tool-and-data-access-for-agents_hu_401c087308b44471.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://akemara.com/en/blog/ai-agents-software-development/images/webp/model-context-protocol-mcp-tool-and-data-access-for-agents_hu_f338b2ab1964a649.webp"
width="760"
height="507"
loading="lazy" data-zoomable data-zoom-src="https://akemara.com/en/blog/ai-agents-software-development/images/webp/model-context-protocol-mcp-tool-and-data-access-for-agents_hu_ebd34bc373c43bb7.webp" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;يركّز بروتوكول Model Context Protocol ‏(MCP) من Anthropic على تمكين وكلاء الذكاء الاصطناعي من التفاعل مع البيانات والأدوات الخارجية. فبينما يتولى A2A التواصل بين الوكلاء، يتولى MCP التكامل بين الوكيل والأنظمة.&lt;/p&gt;
&lt;h3 id="key-features-of-mcp"&gt;أبرز خصائص MCP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;واجهات موحّدة&lt;/strong&gt;: لتكاملات قابلة للتوقع والتوسع.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;معمارية Client-Server&lt;/strong&gt;: فصل واضح للأدوار.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الاكتشاف الديناميكي&lt;/strong&gt;: يستطيع الوكلاء استعلام بيئتهم عن القدرات المتاحة.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;فرض الحماية&lt;/strong&gt;: حدود وصول يتحكم فيها المضيف.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;يساعد MCP وكلاء الذكاء الاصطناعي على أن يصبحوا أذكى وأكثر وعيًا بالسياق، وقادرين على الاندماج في مسارات عمل المؤسسات دون توصيلات مخصصة لكل مهمة.&lt;/p&gt;
&lt;h2 id="a-unified-future-combining-a2a-and-mcp"&gt;مستقبل موحّد: الجمع بين A2A وMCP&lt;/h2&gt;
&lt;p&gt;معًا، يقدم A2A وMCP رؤية قوية. تخيّل فريقًا من الوكلاء: أحدهم يتولى بناء هيكل الكود، وآخر يختبر الـ APIs، وثالث يدير النشر. عبر A2A ينسّقون مهامهم فيما بينهم، وعبر MCP يتفاعلون مع أنظمة التحكم في الإصدارات ومزوّدي الخدمات السحابية وأدوات المراقبة (Observability).&lt;/p&gt;
&lt;p&gt;معمارية كهذه تمهّد الطريق لخطوط CI/CD تقودها نماذج الذكاء الاصطناعي، وأنظمة اختبار مستقلة، وأكثر من ذلك. يتوقف وكلاء الذكاء الاصطناعي عن كونهم مساعدين أفرادًا ليصبحوا أعضاء في الفريق — لكل منهم دور محدد ونطاق استقلالية واضح.&lt;/p&gt;
&lt;h2 id="the-human-in-the-loop-why-software-experts-still-matter"&gt;الإنسان في حلقة القرار: لماذا يبقى خبراء البرمجيات ضرورة؟&lt;/h2&gt;
&lt;p&gt;رغم قوة وكلاء الذكاء الاصطناعي، تبقى الخبرة البشرية حاسمة. فالذكاء الاصطناعي ما زال يتعثر أمام الفروق الدقيقة والحالات الحدّية (Edge Cases) وقابلية الصيانة على المدى الطويل. وكما يراقب الطيارون عداداتهم بحثًا عن أي شذوذ، يجب على المطورين مراجعة مخرجات الذكاء الاصطناعي للتأكد من صحتها وأمانها وتوافقها مع احتياجات العمل.&lt;/p&gt;
&lt;p&gt;وعلى المطورين أيضًا تدريب الوكلاء عبر prompts أفضل، وتصحيح أخطائهم، وتوجيههم تدريجيًا نحو مخرجات أجود. والاستعارة الأنسب غالبًا هي التعامل مع الذكاء الاصطناعي كمطور مبتدئ أو متدرب — قادر لكن قليل الخبرة. يبقى الإرشاد والإشراف والتحقق أمورًا لا غنى عنها.&lt;/p&gt;
&lt;h2 id="conclusion-embracing-ai-augmented-development"&gt;الخلاصة: احتضان التطوير المعزز بالذكاء الاصطناعي&lt;/h2&gt;
&lt;p&gt;يفتتح وكلاء الذكاء الاصطناعي عصر الطيران الآلي في تطوير البرمجيات. لم تعد البرمجة مضطرة لأن تكون يدوية بالكامل، تمامًا كما لم يعد الطيران رهينة السماء الصافية. ومع بروتوكولات مثل A2A وMCP، يستطيع الوكلاء التواصل والتنسيق والوصول إلى الأدوات — ليتحولوا إلى مشاركين كاملين في عملية التطوير.&lt;/p&gt;
&lt;p&gt;لكن أفضل النتائج تتحقق عندما تقترن هذه القوة بالذكاء البشري. على خبراء البرمجيات أن يقودوا الدفة: يحددون الأهداف، ويضمنون الجودة، ويتخذون القرارات المصيرية التي لم يصبح الذكاء الاصطناعي جاهزًا لتحمّل مسؤوليتها بعد. في هذه الشراكة، لا يحل الذكاء الاصطناعي محل المطور — بل يرتقي به.&lt;/p&gt;
&lt;p&gt;هذه ليست نهاية هندسة البرمجيات — بل بداية مستقبل أقوى وأكثر تعاونًا. وقد حان وقت الطيران الآلي.&lt;/p&gt;</description></item><item><title>الـ Macroservices (الخدمات المتوسطة): سد الفجوة بين المونوليث والـ Microservices</title><link>https://akemara.com/ar/blog/macroservices-mini-services/</link><pubDate>Wed, 19 Feb 2025 00:00:00 +0000</pubDate><guid>https://akemara.com/ar/blog/macroservices-mini-services/</guid><description>&lt;h2 id="1-introduction"&gt;1. مقدمة&lt;/h2&gt;
&lt;p&gt;في عالم البرمجيات دائم التطور، قد يكون اختيار المعمارية الصحيحة هو الفارق بين نجاح التطبيق وفشله. جرّبت الشركات عبر السنين أنماطًا معمارية متعددة — من &lt;strong&gt;المونوليث (Monolith)&lt;/strong&gt; في البدايات، إلى &lt;strong&gt;المعمارية الموجهة بالخدمات ‏(SOA)&lt;/strong&gt;، وصولًا إلى &lt;strong&gt;الـ Microservices&lt;/strong&gt; — ولكلٍّ منها مزاياه وتحدياته. غير أن مؤسسات كثيرة اكتشفت أن &lt;strong&gt;الـ microservices الكاملة&lt;/strong&gt; قد تكون أكبر من حاجتها فعلًا، خصوصًا للفرق متوسطة الحجم والأنظمة التي لا تعمل على مقياس Netflix أو Amazon. من هنا تأتي &lt;strong&gt;الـ Macroservices (الخدمات المتوسطة)&lt;/strong&gt;: نهج متوازن يمنحك المعيارية دون العبء التشغيلي لإدارة عدد لا يحصى من الخدمات الصغيرة.&lt;/p&gt;
&lt;p&gt;وجدت دراسة أجرتها O’Reilly عام 2021 أن &lt;strong&gt;77% من المشاركين&lt;/strong&gt; كانوا يستخدمون الـ microservices في بيئة الإنتاج أو يخططون لذلك، لكن &lt;strong&gt;63%&lt;/strong&gt; منهم أفادوا بتعقيد وأعباء تشغيلية كبيرة. تستطيع الـ Macroservices جسر هذه الفجوة بتقديم كثير من مزايا المعماريات القائمة على الخدمات مع إبقاء التعقيد تحت السيطرة.&lt;/p&gt;
&lt;h2 id="2-history-of-software-architecture-monolith-soa-and-microservices"&gt;2. تاريخ معمارية البرمجيات: المونوليث ثم SOA ثم Microservices&lt;/h2&gt;
&lt;h2 id="21-monolithic-architecture"&gt;2.1 المعمارية الأحادية (Monolithic)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;التعريف&lt;/strong&gt;: المونوليث تطبيق برمجي واحد مكتفٍ بذاته، تعيش فيه كل الميزات والوحدات والخدمات داخل codebase واحد ووحدة نشر واحدة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;الحقبة&lt;/strong&gt;: كان شائعًا قبل الألفية الثالثة، وما زال مستخدمًا على نطاق واسع في المشاريع الصغيرة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;التحديات&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;قابلية التوسع&lt;/strong&gt;: يجب توسيع التطبيق بأكمله دفعة واحدة، حتى لو كان الحِمل الثقيل على جزء واحد فقط.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;اختناقات النشر&lt;/strong&gt;: تغيير واحد أو خطأ واحد قد يؤخر إطلاق النظام كله.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;تنسيق الفرق&lt;/strong&gt;: مع نمو التطبيق، يعمل مطورون أكثر على الـ codebase نفسه، فتكثر تعارضات الدمج وتتباطأ دورات التطوير.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;شاهد من الواقع&lt;/strong&gt;: في استبيان أجرته Lightbend عام 2019 للمؤسسات الكبرى، ذكر &lt;strong&gt;45%&lt;/strong&gt; منها أن «تعقيد الكود الأحادي» هو السبب الأول لبحثها عن معماريات جديدة.&lt;/p&gt;
&lt;h2 id="22-service-oriented-architecture-soa"&gt;2.2 المعمارية الموجهة بالخدمات (SOA)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;التعريف&lt;/strong&gt;: قدّمت SOA مفهوم &lt;strong&gt;الخدمات&lt;/strong&gt; التي تتواصل عبر الشبكة، غالبًا من خلال ناقل خدمات مؤسسي ‏(ESB).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;الحقبة&lt;/strong&gt;: انتشرت في العقد الأول من الألفية، خصوصًا في المؤسسات الكبيرة التي تحتاج إلى تكامل بين تطبيقات متعددة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;تحسينات على المونوليث&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ترابط أقل&lt;/strong&gt;: وظائف مغلفة في خدمات بواجهات محددة بوضوح.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;قابلية التوسع&lt;/strong&gt;: أمكن توسيع خدمات بعينها، وإن لم يكن بالاستقلالية نفسها التي تتيحها الـ microservices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;التكامل&lt;/strong&gt;: تكامل أسهل مع الأنظمة القديمة عبر الـ ESB.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;التحديات&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;تعقيد الـ ESB&lt;/strong&gt;: قد يتحول الـ ESB إلى عنق زجاجة أو «نقطة فشل وحيدة».&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;بروتوكولات ثقيلة&lt;/strong&gt;: الخدمات القائمة على SOAP كثيرًا ما سببت أعباء شبكية وتنسيقًا معقدًا.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;شاهد من الواقع&lt;/strong&gt;: وفق تقرير Gartner لعام 2010، طبّقت أكثر من &lt;strong&gt;60%&lt;/strong&gt; من شركات Fortune 500 شكلًا من أشكال SOA، لكن نحو &lt;strong&gt;35%&lt;/strong&gt; منها أفادت بـ«عائد استثمار غير مرضٍ» بسبب تعقيدات إدارة الـ ESB.&lt;/p&gt;
&lt;h2 id="23-microservices"&gt;2.3 الـ Microservices&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;التعريف&lt;/strong&gt;: مجموعة من &lt;strong&gt;الخدمات الصغيرة المستقلة&lt;/strong&gt;، كل منها مسؤول عن قدرة عمل واحدة، وتتواصل عبر بروتوكولات خفيفة (REST أو gRPC أو الرسائل).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;الحقبة&lt;/strong&gt;: بلغت ذروة انتشارها في &lt;strong&gt;عقد 2010&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;أبرز المزايا&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;استقلالية النشر&lt;/strong&gt;: يمكن نشر كل خدمة وتوسيعها وتحديثها دون المساس بالبقية.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;عزل الأعطال&lt;/strong&gt;: فشل خدمة واحدة لا يُسقط النظام بأكمله بالضرورة.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;تعدد تقنيات التخزين&lt;/strong&gt;: تستطيع كل خدمة استخدام قاعدة البيانات أو اللغة الأنسب لها.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;التحديات&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;تعقيد تشغيلي&lt;/strong&gt;: تتطلب أدوات DevOps قوية للـ logging والمراقبة والتتبع الموزّع.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;تكاثر الخدمات&lt;/strong&gt;: قد تفرط الفرق في إنشاء خدمات صغيرة، فيتفشى ما يسمى «انتشار الـ microservices».&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;شاهد من الواقع&lt;/strong&gt;: تشغّل Netflix أكثر من &lt;strong&gt;1,000&lt;/strong&gt; خدمة microservice في الإنتاج، وهو ما يثبت قابلية توسع هائلة لكنه يعكس عبئًا تشغيليًا ضخمًا أيضًا. وانتقلت Uber بالمثل إلى الـ microservices لكنها عانت من إدارة مئات الأنظمة المترابطة، ما استلزم أدوات تنسيق متقدمة.&lt;/p&gt;
&lt;h2 id="3-whats-macroservice-mini-service"&gt;3. ما هي الـ Macroservice (الخدمة المتوسطة)؟&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;الـ Macroservices&lt;/strong&gt; (وتسمى أيضًا &lt;strong&gt;Mini-Services&lt;/strong&gt; أو &lt;strong&gt;Microservices Lite&lt;/strong&gt;) هي &lt;strong&gt;خدمات أكبر حجمًا ومتمحورة حول نطاق عمل&lt;/strong&gt; تقف في منتصف الطريق بين المونوليث الواحد وعشرات الـ microservices. تتولى كل macroservice عادة &lt;strong&gt;نطاقًا محدودًا (Bounded Context)&lt;/strong&gt; كاملًا — أي مجموعة وظائف عمل مترابطة — بدلًا من التركيز على وظيفة واحدة صغيرة.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;الخصائص الأساسية&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;التمحور حول النطاق&lt;/strong&gt;: تغطي كل macroservice نطاقًا أوسع (مثل «المدفوعات والفوترة» أو «المنتجات والمخزون»).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;وحدات نشر أقل&lt;/strong&gt;: بدلًا من نشر كل ميزة صغيرة على حدة، لديك خدمات أقل تصونها وتوسّعها.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;استراتيجيات قواعد بيانات مرنة&lt;/strong&gt;: قد تتشارك الـ macroservices قاعدة بيانات أو تحتفظ كل منها بقاعدتها، حسب متطلبات النطاق.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;تواصل أقل كلفة&lt;/strong&gt;: مع خدمات أقل، تقل الاتصالات البينية واستدعاءات الشبكة مقارنة بالـ microservices.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="4-why-do-we-need-macroservices-what-gap-are-they-filling"&gt;4. لماذا نحتاج الـ Macroservices؟ وما الفجوة التي تسدها؟&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;جسر بين المونوليث والـ Microservices&lt;/strong&gt;:
قد يصبح المونوليث عصيًّا على الإدارة، لكن التبني الكامل للـ microservices قد يقود إلى فوضى تشغيلية. تقدم الـ Macroservices &lt;strong&gt;تفكيكًا منظمًا&lt;/strong&gt; دون الذهاب إلى أقصى الطيف.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;خفض العبء التشغيلي&lt;/strong&gt;:
إدارة عدد كبير من الـ microservices تتطلب ممارسات DevOps متقدمة، من تنسيق Kubernetes إلى خطوط CI/CD معقدة. تُبقي الـ Macroservices &lt;strong&gt;عدد الخدمات&lt;/strong&gt; منخفضًا فينخفض العبء.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;مناسبة لحجم الفرق&lt;/strong&gt;:
يمكن أن يمتلك فريق أو فريقان خدمة macroservice واحدة، بما يتناسب مع حجم ومهارات كثير من المؤسسات — على عكس الـ microservices حيث قد تحتاج كل خدمة فريقًا مخصصًا.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;نطاقات قابلة للتوسع&lt;/strong&gt;:
ما زالت الـ Macroservices تتيح &lt;strong&gt;توسعًا انتقائيًا&lt;/strong&gt;. إذا تعرض نطاق ما (مثل «البحث والتوصيات») لحمل ثقيل، يمكنك توسيع تلك الـ macroservice وحدها.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;شاهد من الواقع&lt;/strong&gt;: وجد استبيان InfoQ لعام 2022 أن &lt;strong&gt;48%&lt;/strong&gt; من شركات السوق المتوسطة (50–500 موظف) رأت أن الـ microservices «أعقد من اللازم» لحجمها — وهو ما يكشف عن فجوة تحتاج نهجًا أخف لكنه معياري.&lt;/p&gt;
&lt;h2 id="5-pros-and-cons-of-macroservices"&gt;5. مزايا الـ Macroservices وعيوبها&lt;/h2&gt;
&lt;h2 id="51-pros"&gt;5.1 المزايا&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. معيارية متوازنة&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;تحصل على حدود واضحة بين النطاقات دون تعقيد عشرات أو مئات الخدمات الصغيرة.&lt;/li&gt;
&lt;li&gt;يسهل على الفرق فهم الوظيفة من طرف إلى طرف داخل نطاق واحد.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. تعقيد تشغيلي أقل&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;خدمات أقل تعني pipelines أقل واكتشاف خدمات أبسط.&lt;/li&gt;
&lt;li&gt;الـ logging والمراقبة والتتبع أسهل إدارة مما هي عليه في بيئة microservices كاملة.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. توسع انتقائي&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;يتيح توسيع نطاقات محددة تشهد حملًا أعلى (مثل خدمة «إتمام الشراء» في الجمعة البيضاء).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. تطوير ونشر أسرع&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;التغييرات في macroservice واحدة لا تمس النظام كله، فتتخلص من إطلاقات المونوليث «كل شيء أو لا شيء».&lt;/li&gt;
&lt;li&gt;تستطيع الفرق النشر بوتيرة أعلى دون أعباء الـ microservices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;5. انتقال أسهل من المونوليث&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;تقسيم مونوليث قائم إلى بضع macroservices أبسط من التحول الكامل إلى microservices.&lt;/li&gt;
&lt;li&gt;يقلص مخاطر إعادة الهيكلة مع تحقيق فوائد الفصل.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="52-cons"&gt;5.2 العيوب&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. عزل أعطال أقل دقة&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;انهيار macroservice واحدة قد يؤثر على عدة ميزات داخل نطاقها.&lt;/li&gt;
&lt;li&gt;عزل الأعطال ليس بمتانة ما تقدمه الـ microservices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. احتمال تداخل النطاقات&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;إذا لم تُعرَّف النطاقات جيدًا، قد تعود الـ macroservices لتتصرف كمونوليث.&lt;/li&gt;
&lt;li&gt;قواعد البيانات أو الكود المشترك قد يؤديان إلى مشكلات ترابط.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. توسيع النطاق بأكمله&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;إذا وقع الحمل على جزء واحد من نطاق الـ macroservice، فعليك توسيع الخدمة كلها.&lt;/li&gt;
&lt;li&gt;قد يهدر ذلك الموارد إن لم تُرسم حدود النطاقات بعناية.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. قد تحتاج إلى تقسيم لاحق&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;النطاق المتنامي قد يحتاج إلى التقسيم مجددًا، ما يعني إعادة هيكلة مستقبلية محتملة.&lt;/li&gt;
&lt;li&gt;على الفرق أن تظل يقظة تجاه حدود النطاقات.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="6-comparison-at-a-glance"&gt;6. مقارنة سريعة&lt;/h2&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="جدول مقارنة بين المونوليث والـ macroservices والـ microservices من حيث عدد الخدمات والنشر وقابلية التوسع والتعقيد وعزل الأعطال والاستخدام الأنسب"
srcset="https://akemara.com/en/blog/macroservices-mini-services/images/webp/6-comparison-at-a-glance_hu_8a7a955b15f07d77.webp 320w, https://akemara.com/en/blog/macroservices-mini-services/images/webp/6-comparison-at-a-glance_hu_d033e838338af1f7.webp 480w, https://akemara.com/en/blog/macroservices-mini-services/images/webp/6-comparison-at-a-glance_hu_774b5334ab1908c2.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://akemara.com/en/blog/macroservices-mini-services/images/webp/6-comparison-at-a-glance_hu_8a7a955b15f07d77.webp"
width="760"
height="501"
loading="lazy" data-zoomable data-zoom-src="https://akemara.com/en/blog/macroservices-mini-services/images/webp/6-comparison-at-a-glance_hu_3328682b85596c4a.webp" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="7-real-world-examples"&gt;7. أمثلة من الواقع&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. Shopify&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;البداية&lt;/em&gt;: انطلقت كمونوليث يشغّل منصتها للتجارة الإلكترونية.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;اليوم&lt;/em&gt;: فككت النظام إلى &lt;strong&gt;خدمات أكبر حجمًا&lt;/strong&gt; (مثل واجهة المتجر، وإتمام الشراء، والمخزون، والفوترة) بدلًا من مئات الـ microservices.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;الفائدة&lt;/em&gt;: عبء أقل من الـ microservices مع الحفاظ على توسع جزئي في مواسم الذروة مثل الجمعة البيضاء.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. Airbnb&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;البداية&lt;/em&gt;: تطبيق مونوليث بـ Ruby on Rails.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;التحول&lt;/em&gt;: فككت المونوليث إلى &lt;strong&gt;macroservices&lt;/strong&gt; مثل «الحجوزات» و«البحث» و«المدفوعات».&lt;/li&gt;
&lt;li&gt;&lt;em&gt;السبب&lt;/em&gt;: احتاجت فصل النطاقات لتسريع التطوير، دون إدخال تعقيد الـ microservices بين ليلة وضحاها.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. Uber (في بداياتها)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;البداية&lt;/em&gt;: خلفية مونوليث بـ Node.js.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;التحول&lt;/em&gt;: أدخلت بضع &lt;strong&gt;خدمات متوسطة&lt;/strong&gt; لإدارة الرحلات ومطابقة السائقين والمدفوعات.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;النتيجة&lt;/em&gt;: ساعدها ذلك على التوسع عالميًا قبل أن تتجه لاحقًا إلى microservices أدق تقسيمًا مع تضخم حجمها.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;شاهد من الواقع&lt;/strong&gt;: في مقابلة مع TechCrunch عام 2022، تبيّن أن &lt;strong&gt;30% من الشركات الناشئة متوسطة المرحلة&lt;/strong&gt; التي تبني منصات رأت في «الـ macroservices» خطوة استراتيجية وسيطة قبل التحول الكامل إلى microservices إذا فرض النمو ذلك.&lt;/p&gt;
&lt;h2 id="8-when-to-choose-microservices"&gt;8. متى تختار الـ Microservices؟&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. نطاق هائل&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;لديك &lt;strong&gt;ملايين&lt;/strong&gt; المستخدمين النشطين يوميًا وتحتاج إلى توسيع وظائف بعينها باستقلالية وقوة.&lt;/li&gt;
&lt;li&gt;مثال: تشغّل Netflix أكثر من ألف microservice للبث والتوصيات وملفات المستخدمين وغيرها.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. فرق مستقلة متعددة&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;فرق مختلفة تريد نشر تحديثاتها باستقلال (نموذج «الفرق بحجم بيتزتين»).&lt;/li&gt;
&lt;li&gt;كل فريق يمتلك خدمته كاملة من الكود إلى قاعدة البيانات.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. الحاجة إلى أفضل تقنية لكل مهمة&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;إذا كنت بحاجة إلى تقنيات متعددة (مثل Java للمدفوعات وPython لتعلم الآلة)، تتيح الـ microservices لكل نطاق أفضل حزمة تقنية.&lt;/li&gt;
&lt;li&gt;بينما تميل الـ Macroservices إلى حزم تقنية أقل تقليلًا للتعقيد.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. عزل أعطال عالٍ&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;خطأ في خدمة المدفوعات يجب ألا يمس البث أو البحث.&lt;/li&gt;
&lt;li&gt;معمارية الـ microservices تعزل كل وظيفة عند حدود خدمتها.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="9-when-to-choose-macroservices-mini-services"&gt;9. متى تختار الـ Macroservices (الخدمات المتوسطة)؟&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. المونوليث أصبح أكبر من اللازم&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;لديك تطبيق ضخم بدأ يستعصي على الإدارة.&lt;/li&gt;
&lt;li&gt;عمليات النشر تستغرق ساعات، وميزة لا علاقة لها بالخطأ تنكسر بسببه.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. نطاق متوسط وفريق متوسط&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;أنت لا تعمل على مقياس Netflix لكنك تحتاج إلى خفض التعقيد وتسريع النشر.&lt;/li&gt;
&lt;li&gt;الفريق متوسط الحجم يجد إدارة الـ macroservices أسهل غالبًا من الـ microservices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. الحاجة إلى توسع جزئي&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;تريد توسيع نطاقات محددة فقط (مثل «الطلبات والمدفوعات» في مواسم الذروة) بدلًا من توسيع مونوليث كامل.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. موارد DevOps محدودة&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;تتطلب الـ microservices أدوات متقدمة للنشر والمراقبة والتصحيح.&lt;/li&gt;
&lt;li&gt;تسمح الـ Macroservices بـ&lt;strong&gt;pipelines أقل وعمليات أبسط&lt;/strong&gt;، ما يجعلها عملية أكثر لفرق DevOps الصغيرة والمتوسطة.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;5. مرونة مستقبلية&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;يمكنك دائمًا تقسيم macroservice إلى عدة microservices إذا نما نظامك كثيرًا واحتاج توسعًا أدق.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="10-conclusion"&gt;10. الخلاصة&lt;/h2&gt;
&lt;p&gt;اختيار معمارية البرمجيات قرار &lt;strong&gt;استراتيجي&lt;/strong&gt; يتوقف على حجم فريقك واحتياجات التوسع لديك ونضجك التشغيلي. تسد &lt;strong&gt;الـ Macroservices (الخدمات المتوسطة)&lt;/strong&gt; فجوة جوهرية بين المونوليث الواحد ومنظومة microservices كاملة:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;المونوليث&lt;/strong&gt;: ممتاز للتطبيقات الصغيرة البسيطة، لكنه يتحول إلى عنق زجاجة مع التوسع.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الـ Microservices&lt;/strong&gt;: مثالية للأنظمة الضخمة المعقدة ذات الفرق المتعددة، لكنها قد تجلب أعباء وتعقيدًا كبيرين.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;الـ Macroservices&lt;/strong&gt;: نهج متوازن — يمنحك &lt;strong&gt;المعيارية والتوسع الانتقائي وتقليل مخاطر النشر&lt;/strong&gt; دون التعقيد المتصاعد الذي يصاحب الـ microservices غالبًا.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;في المحصلة، قد تكون &lt;strong&gt;الـ macroservices&lt;/strong&gt; خيارًا ممتازًا للمؤسسات الساعية إلى انتقال &lt;strong&gt;مدروس ومتمحور حول النطاقات&lt;/strong&gt; بعيدًا عن المعماريات الأحادية. فبتعريف النطاقات المحدودة بعناية، وبناء pipelines قوية للـ DevOps، والانتباه الدائم لنمو النطاقات، يمكنك جني ثمار المعمارية الموزعة بصداع أقل.&lt;/p&gt;
&lt;h2 id="further-reading-references"&gt;قراءات ومراجع إضافية&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;كتاب &lt;em&gt;Monolith to Microservices&lt;/em&gt; لـ Sam Newman (لرؤى حول التفكيك التدريجي).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;استبيان O’Reilly للـ Microservices لعام 2021&lt;/strong&gt; (لإحصاءات التبني في الصناعة).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;تقارير Gartner عن SOA&lt;/strong&gt; (لمنظور تاريخي حول النهج الموجه بالخدمات).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;مدونتا Netflix وUber التقنيتان&lt;/strong&gt; (لقصص واقعية عن تعقيد الـ microservices).&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>فهم نطاق المتغيرات في Golang: المتغيرات المحلية والعامة</title><link>https://akemara.com/ar/blog/golang-variable-scope/</link><pubDate>Wed, 12 Feb 2025 00:00:00 +0000</pubDate><guid>https://akemara.com/ar/blog/golang-variable-scope/</guid><description>&lt;h2 id="introduction"&gt;مقدمة&lt;/h2&gt;
&lt;p&gt;في Golang، يحدد نطاق المتغير (Variable Scope) الأماكن التي يمكن الوصول فيها إلى المتغير وتعديله داخل البرنامج. قد يبدو الموضوع للوهلة الأولى من أساسيات المبتدئين — إلى أن تكتشف أن متغيرًا عامًا ظننت أنك حدّثته لم يتغير أصلًا، والسبب كله علامة &lt;code&gt;:=&lt;/code&gt; واحدة! لذا فإن فهم النطاق &lt;strong&gt;المحلي&lt;/strong&gt; والنطاق &lt;strong&gt;العام&lt;/strong&gt; أمر جوهري لكتابة كود سهل الصيانة وخالٍ من الأخطاء. وعندما تعمل عبر أكثر من package، فإن التعامل الصحيح مع المتغيرات العامة يضمن لك كودًا معياريًا قابلًا لإعادة الاستخدام.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;سنغطي في هذا المقال:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;المتغيرات المحلية والعامة في Golang&lt;/li&gt;
&lt;li&gt;تحديث المتغيرات العامة داخل الدوال&lt;/li&gt;
&lt;li&gt;استخدام المتغيرات العامة عبر أكثر من package&lt;/li&gt;
&lt;li&gt;أفضل الممارسات لإدارة المتغيرات العامة&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="local-vs-global-variables-in-golang"&gt;المتغيرات المحلية والعامة في Golang&lt;/h2&gt;
&lt;h2 id="local-variables-functionblock-scope"&gt;المتغيرات المحلية: نطاق الدالة أو الـ Block&lt;/h2&gt;
&lt;p&gt;يكون المتغير &lt;strong&gt;محليًا (Local)&lt;/strong&gt; عندما يُعرَّف &lt;strong&gt;داخل&lt;/strong&gt; دالة (Function) أو حلقة تكرار أو block. ولا يمكن الوصول إليه إلا داخل تلك الدالة أو ذلك الـ block تحديدًا، ولا يمكن استخدامه في أي مكان آخر.&lt;/p&gt;
&lt;h3 id="example-local-variable-scope"&gt;مثال: نطاق المتغير المحلي&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Local variable in main()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Accessible within main()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Local to the if block&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// fmt.Println(y) // ERROR: y is out of scope here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;المتغير &lt;code&gt;x&lt;/code&gt; محلي داخل &lt;code&gt;main()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;المتغير &lt;code&gt;y&lt;/code&gt; محلي داخل block الـ &lt;code&gt;if&lt;/code&gt; ولا يمكن الوصول إليه خارجه.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="global-variables-package-scope"&gt;المتغيرات العامة: نطاق الـ Package&lt;/h2&gt;
&lt;p&gt;المتغير &lt;strong&gt;العام (Global)&lt;/strong&gt; هو المتغير المُعرَّف &lt;strong&gt;خارج أي دالة&lt;/strong&gt;، ويمكن الوصول إليه من أي مكان داخل الـ package.&lt;/p&gt;
&lt;h3 id="example-global-variable"&gt;مثال: متغير عام&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalVar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Global variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Before update:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalVar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;updateGlobalVar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;After update:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalVar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Value changed globally&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;updateGlobalVar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalVar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Modifying global variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;المتغير &lt;code&gt;globalVar&lt;/code&gt; مُعرَّف &lt;strong&gt;خارج&lt;/strong&gt; أي دالة، ما يجعله متاحًا لكل الدوال داخل الـ package.&lt;/li&gt;
&lt;li&gt;الدالة &lt;code&gt;updateGlobalVar()&lt;/code&gt; تعدّل قيمة &lt;code&gt;globalVar&lt;/code&gt;، وينعكس التغيير على مستوى البرنامج كله.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="updating-a-global-variable-inside-a-function"&gt;تحديث متغير عام داخل دالة&lt;/h2&gt;
&lt;p&gt;يمكن تحديث المتغيرات العامة مباشرة داخل الدوال — &lt;strong&gt;إلا إذا حجبها&lt;/strong&gt; متغير محلي يحمل الاسم نفسه (وهذا ما يُعرف بالـ shadowing).&lt;/p&gt;
&lt;h3 id="example-directly-modifying-a-global-variable"&gt;مثال: تعديل متغير عام مباشرة&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Global variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Modifies the global variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Before increment:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;After increment:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Value updated globally&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="common-mistake-variable-shadowing"&gt;الخطأ الشائع: الـ Shadowing&lt;/h2&gt;
&lt;p&gt;من الأخطاء الشائعة &lt;strong&gt;إعادة تعريف&lt;/strong&gt; متغير عام باستخدام &lt;code&gt;:=&lt;/code&gt;، وهو ما يُنشئ متغيرًا محليًا جديدًا بدلًا من تعديل المتغير العام. هذا هو النوع من الأخطاء الذي يبدو وكأنه يعمل: الدالة تطبع القيمة الجديدة، بينما يحتفظ المتغير العام بقيمته القديمة في صمت.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Global&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;changeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Local&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Creates a new local variable, doesn&amp;#39;t modify the global one&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Inside function:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Before:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;changeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;After:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Still &amp;#34;Global&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;الحل: استخدم &lt;code&gt;data = &amp;quot;Modified Global&amp;quot;&lt;/code&gt; بدلًا من &lt;code&gt;:=&lt;/code&gt; لتحديث المتغير العام.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="using-global-variables-across-multiple-packages"&gt;استخدام المتغيرات العامة عبر أكثر من Package&lt;/h2&gt;
&lt;p&gt;المتغيرات العامة المشتركة بين أكثر من package يجب أن تكون &lt;strong&gt;مُصدَّرة (Exported)&lt;/strong&gt; — أي أن يبدأ اسمها بحرف كبير — وأن تُستورد بالشكل الصحيح.&lt;/p&gt;
&lt;h2 id="project-structure-example"&gt;مثال على هيكل المشروع&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;project/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│── main.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│── config/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── config.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-1-define-a-global-variable-in-config-package"&gt;الخطوة 1: تعريف متغير عام داخل package الـ config&lt;/h2&gt;
&lt;p&gt;أنشئ الملف &lt;code&gt;config/config.go&lt;/code&gt; وعرّف فيه متغيرًا مُصدَّرًا:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Exported global variable (uppercase first letter)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;My Awesome App&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;PrintConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;App Name:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-2-access-the-global-variable-in-maingo"&gt;الخطوة 2: الوصول إلى المتغير العام من main.go&lt;/h2&gt;
&lt;p&gt;استورد package الـ &lt;code&gt;config&lt;/code&gt; واستخدم المتغير العام:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;project/config&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Importing the package&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Application:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Accessing global variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PrintConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;المتغير &lt;code&gt;AppName&lt;/code&gt; متاح هنا لأنه مُصدَّر.&lt;/li&gt;
&lt;li&gt;الدالة &lt;code&gt;config.PrintConfig()&lt;/code&gt; تطبع قيمة المتغير.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-3-modifying-global-variable-from-another-package"&gt;الخطوة 3: تعديل المتغير العام من package آخر&lt;/h2&gt;
&lt;p&gt;المتغيرات العامة داخل أي package &lt;strong&gt;يمكن تحديثها&lt;/strong&gt; من packages أخرى ما دامت مُصدَّرة.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;project/config&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Before:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Modifying global variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Updated App Name&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;After:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="preventing-modification-with-getter-functions"&gt;منع التعديل باستخدام دوال الـ Getter&lt;/h2&gt;
&lt;p&gt;إذا أردت &lt;strong&gt;حماية متغير عام&lt;/strong&gt; من التعديل خارج الـ package الخاص به، اجعله &lt;strong&gt;خاصًا (Private)&lt;/strong&gt; ووفّر دالة getter للوصول إليه.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Unexported variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;My Secure App&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Getter function&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetAppName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;الآن لا يمكن تعديل &lt;code&gt;appName&lt;/code&gt; من &lt;code&gt;main.go&lt;/code&gt;، لكن يظل بالإمكان قراءته عبر &lt;code&gt;config.GetAppName()&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="using-synconce-for-initialization"&gt;استخدام sync.Once للتهيئة&lt;/h2&gt;
&lt;p&gt;استخدم &lt;code&gt;sync.Once&lt;/code&gt; لضمان أن تهيئة (Initialization) المتغير العام تحدث &lt;strong&gt;مرة واحدة فقط&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sync&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Once&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;InitConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Initialized App Name&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;project/config&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InitConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Application:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="best-practices-for-global-variables"&gt;أفضل الممارسات للتعامل مع المتغيرات العامة&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;قلّل من المتغيرات العامة&lt;/strong&gt;: الإفراط فيها يجعل صيانة الكود أصعب مع الوقت.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;استخدم دوال getter/setter&lt;/strong&gt;: إذا كان التعديل يجب أن يكون مقيدًا، وفّر دوال بدلًا من كشف المتغير نفسه.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;استخدم &lt;code&gt;sync.Once&lt;/code&gt; للتهيئة&lt;/strong&gt;: تجنّب التهيئة المتكررة (وسباقات التهيئة المتزامنة).&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;تجنّب التبعيات غير الضرورية بين الـ packages&lt;/strong&gt;: ضع المتغيرات العامة في packages مخصصة لتفادي الاستيراد الدائري (Circular Imports).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;الخلاصة&lt;/h2&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="جدول يلخّص أنواع نطاق المتغيرات في Go: المحلي والعام وعبر الـ packages"
srcset="https://akemara.com/en/blog/golang-variable-scope/images/webp/conclusion_hu_5cf3393778e2e516.webp 320w, https://akemara.com/en/blog/golang-variable-scope/images/webp/conclusion_hu_f2bfe715b01c318.webp 480w, https://akemara.com/en/blog/golang-variable-scope/images/webp/conclusion_hu_27d6136380d88ecf.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://akemara.com/en/blog/golang-variable-scope/images/webp/conclusion_hu_5cf3393778e2e516.webp"
width="760"
height="313"
loading="lazy" data-zoomable data-zoom-src="https://akemara.com/en/blog/golang-variable-scope/images/webp/conclusion_hu_d29cfd7fcea5565e.webp" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;نطاق المتغيرات في Golang يتبع قواعد واضحة: المتغير محلي افتراضيًا، ويصبح متاحًا على مستوى الـ package عندما يُعرَّف في المستوى الأعلى، ولا يُرى عبر الـ packages الأخرى إلا إذا كان مُصدَّرًا.&lt;/p&gt;
&lt;p&gt;وبالالتزام بأفضل الممارسات، ستتمكن من كتابة برامج Go &lt;strong&gt;قابلة للتوسع، معيارية، وسهلة الصيانة&lt;/strong&gt;. 🚀&lt;/p&gt;</description></item><item><title>كيف تصبح واحدًا من أفضل 1% من مهندسي البرمجيات</title><link>https://akemara.com/ar/blog/top-1-percent-software-engineer/</link><pubDate>Sat, 30 Mar 2024 00:00:00 +0000</pubDate><guid>https://akemara.com/ar/blog/top-1-percent-software-engineer/</guid><description>&lt;p&gt;ما الذي يفرّق بين المهندس الذي يستشيره الجميع والمهندس الذي يكتفي بإغلاق التذاكر؟ نادرًا ما تكون الإجابة موهبة البرمجة الخام. إنها مجموعة من العادات — وكل واحدة منها يمكن تعلّمها.&lt;/p&gt;
&lt;h2 id="tldr"&gt;الخلاصة السريعة&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;تعامل بارتياح مع الغموض. أفضل المهندسين يحلّون المشكلات دون الحاجة إلى كل التفاصيل مقدمًا.&lt;/li&gt;
&lt;li&gt;بادر بنفسك. حدّد المشكلات الحرجة للفريق أو الشركة وتصدَّ لحلها.&lt;/li&gt;
&lt;li&gt;كن خبيرًا. عمّق معرفتك في مجال محدد حتى تصبح الشخص المرجعي فيه.&lt;/li&gt;
&lt;li&gt;ركّز على العملاء. ابنِ منتجات تحل مشكلات حقيقية لأشخاص حقيقيين.&lt;/li&gt;
&lt;li&gt;ابقَ على البساطة. تجنّب الإفراط في الهندسة (Overengineering) وركّز على حلول تعمل الآن.&lt;/li&gt;
&lt;li&gt;شارك معرفتك. ساعد الآخرين على التعلم والنمو عبر الإرشاد (Mentoring) وردّ الجميل للمجتمع التقني.&lt;/li&gt;
&lt;li&gt;افهم الأعمال. اربط جهودك بالمشاريع التي تصب في صالح أرباح الشركة.&lt;/li&gt;
&lt;li&gt;حسّن ثقافة العمل. ساهم في بيئة عمل إيجابية تجذب المواهب المميزة وتحافظ عليها.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;التميّز في هندسة البرمجيات لا يتعلق بمهارات البرمجة وحدها؛ بل بمحاكاة ممارسات وفلسفات أفضل 1% في الصناعة&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="embrace-ambiguity"&gt;تصالح مع الغموض&lt;/h2&gt;
&lt;p&gt;القدرة على الإبحار وسط الضبابية هي ما يفصل المهندس الجيد عن المهندس العظيم. كثير من المهندسين ينتظرون مهام مفصّلة قبل أن يبدأوا العمل، وهي عادة تخنق الابتكار والاستقلالية. أما أصحاب الأداء الاستثنائي، فهم من يستطيعون أخذ مشكلة عامة الملامح — مثل تحسين موثوقية الاختبارات أو خفض تكاليف البنية التحتية — وابتكار حلول لها دون توجيه خطوة بخطوة. التصالح مع الغموض يعني تحمّل المسؤولية رغم المجاهيل، وهي مهارة تعكس النضج والخبرة. القادة يقدّرون المهندس القادر على تعريف معايير النجاح، وبناء افتراضات منطقية، واستكشاف الحلول بشكل استباقي.&lt;/p&gt;
&lt;h2 id="excel-in-problem-solving"&gt;أتقن حل المشكلات&lt;/h2&gt;
&lt;p&gt;المهندسون الاستثنائيون يحلّون المشكلات بشكل استباقي، ويعرفون كيف يحددون القضايا ذات الأثر الأكبر على الفريق أو الشركة ويرتبونها حسب الأولوية. تتطلب هذه المهارة فهمًا عميقًا للـ codebase، وتواصلًا مفتوحًا مع الزملاء، وتحليلًا دقيقًا للمقاييس (Metrics) لكشف المشكلات الكامنة. وبعد تحديد المشكلة، يأتي التحدي التالي: إقناع أصحاب المصلحة (Stakeholders) بمدى إلحاحها. الإقناع الفعّال يستند عادة إلى بيانات قوية وعرض واضح لأثر المشكلة على المشروع أو العمل. كما أن التعلم المستمر والنظرة المتجددة لا يقدَّران بثمن عند البحث عن مشكلات جديدة والدفع نحو حلها.&lt;/p&gt;
&lt;h2 id="deepen-your-expertise"&gt;عمّق خبرتك&lt;/h2&gt;
&lt;p&gt;أن تصبح خبير مجال (Domain Expert) يعني أن تكون الشخص المرجعي في مناطق تقنية أو منتجية محددة. تُكتسب هذه المكانة بالتعلم الدؤوب، والتصدي للمشكلات الغامضة، والمساهمة في حلول تدفع المشروع إلى الأمام. قضاء الوقت في مجال بعينه يكشف لك تعقيداته وتفاصيله الدقيقة، ويرسّخك كمرجع أساسي فيه. الثقة والاستعداد لمشاركة المعرفة ومساعدة الآخرين صفات تزيد هذه السمعة رسوخًا. الطريق إلى الخبرة يتطلب صبرًا وتفانيًا وانخراطًا فعليًا في التعلم وحل المشكلات.&lt;/p&gt;
&lt;h2 id="focus-on-the-customer"&gt;ركّز على العميل&lt;/h2&gt;
&lt;p&gt;أنجح المهندسين هم من يضعون احتياجات المستخدمين النهائيين وملاحظاتهم في المقام الأول. بناء حلول تعالج مشكلات واقعية يضمن أن يكون عملك ذا صلة وقيمة. هذا النهج المتمحور حول العميل يتجاوز الجوانب التقنية للتطوير ليأخذ في الحسبان الأثر العملي للمنتج. البقاء على اتصال دائم بملاحظات العملاء، والانفتاح على التحسينات التدريجية بناءً على تجاربهم، هو ما يميّز المهندس العظيم عن الجيد.&lt;/p&gt;
&lt;h2 id="simplify-solutions"&gt;بسّط الحلول&lt;/h2&gt;
&lt;p&gt;في مجالٍ التعقيد فيه أمر مفروغ منه، تصبح القدرة على تبسيط الحلول مهارة عالية القيمة. الإفراط في الهندسة يؤدي إلى إهدار الموارد وأنظمة مفرطة التعقيد يصعب صيانتها. أفضل المهندسون يدافعون عن بناء ما هو مطلوب الآن، مع إبقاء العين على قابلية التكيف مستقبلًا. وهم يقدّمون الوظيفة الفعلية وتجربة المستخدم على الاستعراض التقني، فيسلّمون منتجات مفيدة حقًا وسهلة الاستخدام.&lt;/p&gt;
&lt;h2 id="contribute-to-the-community"&gt;ساهم في المجتمع التقني&lt;/h2&gt;
&lt;p&gt;بعيدًا عن الإنجازات الفردية، يدرك أفضل المهندسين قيمة ردّ الجميل للمجتمع. سواء عبر الإرشاد، أو المساهمة في مشاريع الـ open source، أو مشاركة المعرفة في المؤتمرات والمدونات، فهذه المساهمات تُثري المنظومة بأكملها. الانخراط في المجتمع لا يساعد الآخرين على النمو فحسب، بل يصقل مهارات المساهم نفسه ويعزز سمعته.&lt;/p&gt;
&lt;h2 id="understand-business-impact"&gt;افهم الأثر على الأعمال&lt;/h2&gt;
&lt;p&gt;يدرك أفضل المهندسين أهمية مواءمة مشاريعهم مع الأهداف المالية للشركة. فهم يختارون المشاريع القادرة على التأثير الملموس في الأرباح، مدركين أن التقنية وسيلة لغاية — وهي نجاح العمل. الوعي بسياق الأعمال يضمن أن تتجه الجهود التقنية نحو النتائج عالية الأثر، ما يجعل المهندس أصلًا لا تفرّط فيه الشركة.&lt;/p&gt;
&lt;h2 id="cultivate-a-positive-culture"&gt;ازرع ثقافة إيجابية&lt;/h2&gt;
&lt;p&gt;وأخيرًا، يمتد تأثير المهندس العظيم إلى ما هو أبعد من المساهمات التقنية ليشمل الأثر الثقافي. رعاية ثقافة شركة إيجابية وشاملة ومبتكرة تجذب المواهب وتقود إلى النجاح. المشاركة في الهاكاثونات (Hackathons)، والتدوين، والتحدث في المؤتمرات، وتحسين ممارسات التوظيف — كلها طرق يمكن للمهندس أن يساهم بها في بيئة عمل حيوية ومنتجة.&lt;/p&gt;
&lt;p&gt;خلاصة القول: أن تصبح مهندس برمجيات من الطراز الأول يتطلب مزيجًا من التميّز التقني، والحس التجاري، والالتزام تجاه المجتمع الأوسع. بالتركيز على هذه المجالات الثمانية، يستطيع المهندس أن يرتقي بمسيرته المهنية وأن يقدّم مساهمات ذات معنى لفريقه وشركته وللصناعة بأكملها.&lt;/p&gt;</description></item></channel></rss>