{"id":"l-aws-3-3","courseId":"aws-cloud-foundations","lang":"ar","kind":"lesson","summary":"اختر بين Amazon RDS وAmazon DynamoDB انطلاقًا من أنماط الوصول في حمل العمل، واضبط كلًّا منهما مع النسخ الاحتياطية والتشفير والوصول الخاص منذ البداية.","html":"<p>كان لخادم Lantern القديم قاعدة بيانات PostgreSQL واحدة لكل شيء: الفعاليات والأماكن والمنظّمون وكل تأكيد حضور. وخلال ذروة المهرجان، اصطفّت عمليات كتابة تأكيدات الحضور خلف استعلام تقرير بطيء، فتجمّد الموقع كله. على AWS يمكنك أن تمنح كل نوع من البيانات قاعدة البيانات التي تناسبه. الاثنتان اللتان ستتعرّف إليهما أولًا هما <strong>Amazon RDS</strong> و<strong>Amazon DynamoDB</strong>، وتختلفان في معظم الجوانب التي تعنيك فعلًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Amazon RDS: قاعدة بيانات SQL الخاصة بك، تُشغَّل نيابة عنك</h2>\n<p>يشغّل <strong>Amazon Relational Database Service</strong> محرّكًا علائقيًا على بنية تحتية تديرها AWS. ومن المحرّكات المتاحة PostgreSQL وMySQL وMariaDB وOracle وSQL Server وDb2، إضافة إلى <strong>Amazon Aurora</strong>، المحرّك الخاص بـ AWS والمتوافق مع MySQL وPostgreSQL.</p>\n<p>تتولّى AWS الأعمال الروتينية التي كان فريق Lantern ينجزها في الثانية فجرًا: تجهيز الموارد، وتحديث نظام التشغيل والمحرّك في نافذة صيانة تختارها، و<strong>النسخ الاحتياطية التلقائية</strong> مع الاستعادة إلى نقطة زمنية، ونشر <strong>Multi-AZ</strong> مع standby متزامن في منطقة توافر أخرى يتولّى العمل تلقائيًا. ويبقى لك المخطّط، والـ indexes، والاستعلامات، ومستخدمو قاعدة البيانات، وحجم الـ instance، ومن يستطيع الاتصال.</p>\n<p>إليك كيف ينشئ Lantern قاعدة بيانات الفعاليات، مع توضيح الخيارات الآمنة:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">aws</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> rds</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> create-db-instance</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --db-instance-identifier</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> lantern-events</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --engine</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> postgres</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --db-instance-class</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> db.t4g.micro</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --allocated-storage</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 20</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --storage-encrypted</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --master-username</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> lantern_admin</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --manage-master-user-password</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --backup-retention-period</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 7</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --multi-az</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --no-publicly-accessible</span></span></code></pre></div>\n<p>يخزّن <code dir=\"ltr\">--manage-master-user-password</code> كلمة مرور المدير في AWS Secrets Manager بدلًا من سجل أوامر الـ shell. ويُبقي <code dir=\"ltr\">--no-publicly-accessible</code> قاعدة البيانات بعيدة عن الإنترنت؛ والدرس القادم يضعها في شبكة فرعية خاصة. ولأن تطبيق Lantern الحالي يعتمد على SQL، فالترحيل مجرد <code dir=\"ltr\">pg_dump</code> ثم استعادة، مع بقاء الاستعلامات كما هي:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"sql\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">SQL</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">SELECT</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> v</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, date_trunc(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'month'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">starts_at</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> month</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">count</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">AS</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> events</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">FROM</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> events e</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">JOIN</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> venues v </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ON</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> v</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">id</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">venue_id</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">GROUP BY</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> v</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">month</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ORDER BY</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> month</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> DESC</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, events </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">DESC</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre></div>\n<p><strong>كيف يحتسب RDS:</strong> لكل <strong>ساعة تشغيل للـ instance</strong> (مع Savings Plans أو reserved instances للاستخدام الثابت)، إضافة إلى التخزين لكل غيغابايت شهريًا، والنسخ الاحتياطية التي تتجاوز حصة مجانية، ونقل البيانات. ونشر Multi-AZ يضاعف تقريبًا تكلفة الـ instance لأن هناك اثنتين منها. والأهم أن الـ instance تُحتسب <strong>طوال الشهر مشغولة كانت أم خاملة</strong>. ويستطيع Aurora Serverless v2 أن يوسّع السعة مع الحمل وأن يتوقّف مؤقتًا عند الخمول، وهذا يناسب قواعد البيانات ذات الحمل المتقطّع أو قواعد بيانات التطوير.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Amazon DynamoDB: صمّم المفاتيح، واحصل على السرعة بأي حجم</h2>\n<p><strong>DynamoDB</strong> قاعدة بيانات serverless من نوع مفتاح-قيمة ومستندات. لا توجد instance تحدّد حجمها: تنشئ <strong>جدولًا</strong>، وتختار <strong>مفتاحًا أساسيًا (primary key)</strong>، وتقرأ <strong>العناصر (items)</strong> وتكتبها (وهي مستندات شبيهة بـ JSON) بزمن استجابة ثابت بأجزاء قليلة من الألف من الثانية، سواء كانت عشرة طلبات يوميًا أو عشرات الآلاف في الثانية.</p>\n<p>العقبة أنك تصمّم المفاتيح حول الأسئلة التي ستطرحها. بالنسبة إلى تأكيدات الحضور، أسئلة Lantern هي «من سيحضر الفعالية X؟» و«هل أكّد هذا الشخص حضوره إلى X من قبل؟». وهذا يعطي <strong>مفتاح تقسيم (partition key)</strong> هو <code dir=\"ltr\">eventId</code> و<strong>مفتاح ترتيب (sort key)</strong> هو <code dir=\"ltr\">email</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">aws</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dynamodb</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> create-table</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --table-name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> lantern-rsvps</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --attribute-definitions</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> AttributeName=eventId,AttributeType=S</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">                          AttributeName=email,AttributeType=S</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --key-schema</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> AttributeName=eventId,KeyType=HASH</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">               AttributeName=email,KeyType=RANGE</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --billing-mode</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> PAY_PER_REQUEST</span></span></code></pre></div>\n<p>عندئذٍ يصبح عدّ تأكيدات الحضور لفعالية واحدة <strong>Query</strong> رخيصًا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">aws</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dynamodb</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> query</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --table-name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> lantern-rsvps</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --key-condition-expression</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"eventId = :e\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --expression-attribute-values</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> '{\":e\": {\"S\": \"evt-2026-street-food\"}}'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  --select</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> COUNT</span></span></code></pre></div>\n<p><strong>كيف يحتسب DynamoDB:</strong> في نمط <strong>on-demand</strong> (<code dir=\"ltr\">PAY_PER_REQUEST</code>)، لكل طلب قراءة وكتابة إضافة إلى التخزين لكل غيغابايت شهريًا، ولا شيء مقابل وقت الخمول. أما نمط <strong>provisioned</strong>، مع التوسّع التلقائي، فقد يكون أرخص للزيارات الثابتة المتوقّعة. ابدأ بـ on-demand؛ ولا تنتقل إلا حين تُظهر الفاتورة حملًا ثابتًا. والاستعادة إلى نقطة زمنية (point-in-time recovery) إعداد تفعّله لكل جدول، ويجب أن تفعّله.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>المسح الكامل للعثور على بضعة عناصر</span></p>\n<p>يقرأ <code dir=\"ltr\">Scan</code> مع ترشيح كل عنصر في الجدول ثم يتخلّص من معظمها، وتدفع ثمن كل عنصر مقروء. يعمل في الاختبار مع 50 تأكيد حضور، ثم يصبح بطيئًا ومكلفًا مع 500,000. إذا احتاج مسار ساخن (hot path) إلى سؤال لا تستطيع مفاتيحك الإجابة عنه بـ <code dir=\"ltr\">Query</code> أو <code dir=\"ltr\">GetItem</code>، فأضف له global secondary index بدلًا من المسح الكامل.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">الاختيار</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>السؤال</th>\n<th>RDS</th>\n<th>DynamoDB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>هل الأسئلة متنوّعة أو تُطرح عند الطلب؟</td>\n<td>نعم: SQL يتعامل مع الجديد منها</td>\n<td>لا: المفاتيح تجيب عن المعروف منها</td>\n</tr>\n<tr>\n<td>هل تحتاج إلى JOIN وقيود بين عدة جداول؟</td>\n<td>نعم</td>\n<td>نادرًا؛ تلغي التطبيع (denormalise)</td>\n</tr>\n<tr>\n<td>زيارات متقطّعة مع أوقات خمول؟</td>\n<td>تدفع ثمن ساعات الخمول</td>\n<td>تدفع لكل طلب</td>\n</tr>\n<tr>\n<td>كود SQL موجود تريد الاحتفاظ به؟</td>\n<td>احتفظ به</td>\n<td>أعد كتابة طبقة الوصول إلى البيانات</td>\n</tr>\n<tr>\n<td>العمليات التي تبقى عليك</td>\n<td>تحديد الحجم، والاتصالات، والترقيات</td>\n<td>تصميم المفاتيح</td>\n</tr>\n</tbody>\n</table></div><p>ينتهي Lantern بـ <strong>الاثنتين</strong>: الفعاليات والأماكن والمنظّمون في RDS for PostgreSQL، حيث تعمل التقارير وصفحات الإدارة الحالية دون تغيير؛ وتأكيدات الحضور في DynamoDB، حيث تكلّف ذروة الكتابة في فعالية منتشرة بضعة سنتات من الطلبات بدلًا من تجميد قاعدة البيانات المشتركة.</p>\n<aside class=\"callout callout-why\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">لماذا يهمّ هذا</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>الأمان المهمة نفسها في الاثنتين</span></p>\n<p>شفّر البيانات المخزّنة (مفعّل افتراضيًا في DynamoDB، وخيار تضبطه في RDS)، وأبقِ قاعدة البيانات بعيدة عن متناول الإنترنت، وامنح الـ role الخاص بالتطبيق الإجراءات التي يحتاجها فقط، مثل <code dir=\"ltr\">dynamodb:PutItem</code> و<code dir=\"ltr\">dynamodb:Query</code> على ARN جدول واحد.</p>\n</aside>\n<p>قاعدة البيانات خاصة، لكنها محجوبة عن <em>ماذا</em> بالضبط؟ يبني الدرس التالي الشبكة التي تعيش فيها قطع Lantern.</p>\n","toc":[{"id":"sec-1","text":"Amazon RDS: قاعدة بيانات SQL الخاصة بك، تُشغَّل نيابة عنك","level":2},{"id":"sec-2","text":"Amazon DynamoDB: صمّم المفاتيح، واحصل على السرعة بأي حجم","level":2},{"id":"sec-3","text":"الاختيار","level":2}],"takeaways":["يشغّل Amazon RDS محرّكًا علائقيًا مثل PostgreSQL نيابة عنك، ويتولّى التحديثات والنسخ الاحتياطية والتحويل التلقائي في نمط Multi-AZ، بينما تملك أنت المخطّط والاستعلامات والاتصالات.","Amazon DynamoDB قاعدة بيانات serverless من نوع مفتاح-قيمة ومستندات، تصمّم فيها المفاتيح حول الأسئلة التي ستطرحها.","يحتسب RDS في الغالب لكل ساعة تشغيل للـ instance، مشغولة كانت أم خاملة؛ أما DynamoDB بنمط on-demand فيحتسب لكل طلب ولكل غيغابايت مخزّن.","استخدم Query من DynamoDB على المفاتيح في المسارات الساخنة، لأن Scan يقرأ الجدول كله ويحتسبه.","كثير من الأنظمة الحقيقية تستخدم الاثنين، البيانات العلائقية في RDS، وعمليات البحث البسيطة عالية الحجم في DynamoDB."],"further":[{"title":"What is Amazon RDS?","url":"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html"},{"title":"What is Amazon DynamoDB?","url":"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html"},{"title":"DynamoDB throughput capacity (on-demand and provisioned)","url":"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/capacity-mode.html"},{"title":"Configuring and managing a Multi-AZ deployment for Amazon RDS","url":"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html"}],"quiz":[{"q":"<p>يريد أمين صندوق Lantern تقارير مخصّصة عند الطلب مثل «الفعاليات لكل مكان في كل شهر، مع أسماء المنظّمين»، والأسئلة تتغيّر كل ربع سنة. أيّ قاعدة بيانات تناسبه؟</p>","options":[{"text":"DynamoDB، لأنها serverless وتتوسّع تلقائيًا.","why":"التوسّع ليس المشكلة هنا. تجيب DynamoDB عن الأسئلة التي صمّمت المفاتيح لها؛ وكل JOIN جديد عند الطلب يعني indexes أو تصديرات جديدة في كل مرة."},{"text":"RDS for PostgreSQL، لأن عمليات JOIN والتجميع في SQL تجيب عن الأسئلة الجديدة دون إعادة تصميم البيانات.","why":"صحيح. تتألّق قواعد البيانات العلائقية حين تكون الأسئلة متنوّعة وتتغيّر مع الوقت."},{"text":"S3 Standard، لأن التقارير ملفات.","why":"يمكن أن تعيش مخرجات التقرير في S3، لكن S3 ليست قاعدة بيانات ولا تستطيع ربط الصفوف أو تجميعها بنفسها."}],"answer":1},{"q":"<p>مفتاح التقسيم (partition key) في جدول تأكيدات الحضور هو <code dir=\"ltr\">eventId</code> ومفتاح الترتيب (sort key) هو <code dir=\"ltr\">email</code>. أيّ استدعاء يعيد كل تأكيدات الحضور لفعالية واحدة بكفاءة؟</p>","options":[{"text":"Scan مع ترشيح على <code dir=\"ltr\">eventId</code>.","why":"يقرأ Scan كل عنصر في الجدول ثم يطبّق الترشيح بعد ذلك، فتدفع ثمن قراءة الجدول كله."},{"text":"GetItem مع <code dir=\"ltr\">eventId</code> فقط.","why":"يحتاج GetItem إلى المفتاح الأساسي كاملًا، أي مفتاح التقسيم ومفتاح الترتيب، ويعيد عنصرًا واحدًا."},{"text":"Query مع شرط المفتاح <code dir=\"ltr\">eventId = :e</code>.","why":"صحيح. لا يقرأ Query إلا العناصر تحت مفتاح التقسيم ذاك، فتنمو التكلفة مع تأكيدات الحضور لتلك الفعالية، لا مع حجم الجدول."}],"answer":2},{"q":"<p>أيّ إعداد في RDS يحمي قاعدة بيانات الفعاليات في Lantern من خسارة منطقة توافر واحدة؟</p>","options":[{"text":"نشر بنمط Multi-AZ، يحتفظ بنسخة احتياطية جاهزة (standby) في منطقة توافر أخرى ويتحوّل إليها تلقائيًا.","why":"صحيح. ينسخ RDS البيانات إلى الـ standby بشكل متزامن، ويتحوّل إليه حين تواجه منطقة توافر النسخة الرئيسية مشكلة."},{"text":"نسخ احتياطية تلقائية بفترة احتفاظ مدتها سبعة أيام.","why":"تتيح لك النسخ الاحتياطية الاستعادة إلى نقطة زمنية، لكن الاستعادة تنشئ instance جديدة وتستغرق وقتًا. إنها ليست تحويلًا تلقائيًا."},{"text":"فئة instance أكبر.","why":"المزيد من المعالج والذاكرة يساعد مع الحمل، لكن instance أكبر في منطقة توافر واحدة تسقط مع تلك المنطقة."},{"text":"تشفير التخزين.","why":"التشفير يحمي البيانات المخزّنة من القراءة. ولا يفعل شيئًا للتوافرية."}],"answer":0}],"exercise":{"kind":"choice","multiple":true,"options":["rsvps","reports","ticket-lookup","relational-admin","rate-limit"],"correct":["rsvps","ticket-lookup","rate-limit"],"title":"أيّ البيانات مكانها DynamoDB؟","prompt":"<p>يقسّم Lantern قاعدة بيانات PostgreSQL الوحيدة لديه. قرّر لكل ميزة هل Amazon DynamoDB <strong>مناسبة لها</strong>. اختر كل ميزة يجب أن تستخدم DynamoDB؛ أما البقية فتبقى في Amazon RDS for PostgreSQL.</p>\n","hints":["تكون DynamoDB في أقوى حالاتها حين تستطيع أن تحدّد مسبقًا المفتاح الذي سيستخدمه كل طلب بالضبط.","الأسئلة المتنوّعة والمتغيّرة وعمليات JOIN عبر عدة جداول تشير إلى SQL.","الميزات ذات البيانات قصيرة العمر تستطيع استخدام Time to Live (TTL) في DynamoDB لحذف العناصر تلقائيًا دون تكلفة إضافية."],"explanation":"<p>تأكيدات الحضور، والبحث عن التذاكر، وعدّادات تحديد المعدّل، لكلٍّ منها نمط وصول واحد معروف، بمفتاح هو معرّف ما، وتشهد اندفاعات مفاجئة من الزيارات، وهنا يتألّق تسعير DynamoDB لكل طلب وزمن استجابتها الثابت. كما أن عدّادات تحديد المعدّل تنتهي صلاحيتها، ويحذفها TTL نيابة عنك (عادةً خلال يوم أو يومين من انتهائها، لذا يظل الكود يقارن وقت الانتهاء بنفسه). أما تقارير أمين الصندوق فتطرح أسئلة جديدة كل ربع سنة مع عمليات JOIN وتجميع، وتعتمد صفحات الإدارة على مفاتيح خارجية (foreign keys) بين المنظّمين والأماكن والفعاليات؛ وكلاهما عمل علائقي يؤدّيه كود SQL الحالي جيدًا في RDS.</p>\n","checkLabels":{},"optionText":{"rsvps":{"text":"تأكيدات الحضور: كتابة واحد لكل شخص في كل فعالية، وسردها حسب الفعالية، والتحقّق من التكرار.","why":"مناسبة. كل طلب يستخدم <code dir=\"ltr\">eventId</code>، إضافة إلى <code dir=\"ltr\">email</code> للتحقّق من التكرار، وتقفز عمليات الكتابة حين تنتشر فعالية ما."},"reports":{"text":"تقارير ربع سنوية يعيد أمين الصندوق تصميمها كل مرة، وتربط الفعاليات والأماكن والمنظّمين.","why":"غير مناسبة. الأسئلة المخصّصة عند الطلب مع عمليات JOIN هي بالضبط ما يتعامل معه SQL ولا تتعامل معه مفاتيح DynamoDB."},"ticket-lookup":{"text":"التحقّق من تذكرة بمعرّفها عند بوابة المهرجان، آلاف المرات في الدقيقة.","why":"مناسبة. <code dir=\"ltr\">GetItem</code> بمفتاح واحد بحجم كبير وزمن استجابة منخفض هو نقطة القوة الأساسية في DynamoDB."},"relational-admin":{"text":"صفحات الإدارة، التي تعدّل المنظّمين والأماكن والفعاليات مع مفاتيح خارجية بينها.","why":"غير مناسبة. العلاقات والقيود بين عدة جداول عمل علائقي، وكود SQL الحالي يستمر في العمل في RDS."},"rate-limit":{"text":"عدّادات لكل عنوان IP تحدّ من الرسائل المزعجة في تأكيدات الحضور، وتنتهي صلاحية كلٍّ منها بعد عشر دقائق.","why":"مناسبة. مفتاح واحد لكل عنوان IP، وعمليات كتابة صغيرة متكرّرة جدًا، ويزيل TTL العدّادات المنتهية تلقائيًا."}}},"words":709,"readMinutes":4}