<?xml version="1.0"?>
<rss version="2.0"><channel><title>DevOps: Redis</title><link>https://academy.hsoub.com/devops/servers/databases/redis/?d=4</link><description>DevOps: Redis</description><language>ar</language><item><title>&#x645;&#x627; &#x647;&#x64A; &#x62A;&#x642;&#x646;&#x64A;&#x629; Redis&#x61F;</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-redis%D8%9F-r668/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/redis.png.c5d086903caf8fd3d7735878d4155b30.png" /></p>
<p>
	سنشرح في هذا الفيديو نظام إداة قواعد البيانات غير العلائقية Redis، حيث سنشرح لماذا قد نفكر باستخدام قواعد بيانات Redis والفائدة منها.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="409" id="ips_uid_9744_5" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي تقنية Redis" width="727" data-embed-src="https://www.youtube.com/embed/LuKKiFZy3Jk"></iframe>
</p>

<p>
	يمكنك الاطلاع على تفاصيل إضافية تخص Redis مجانًا في <a href="https://academy.hsoub.com/devops/servers/databases/redis/" rel="">قسمها الخاص على أكاديمية حسوب</a>، وإذا كنت راغبًا في تعلم المزيد باحترافية عن قواعد البيانات بأنواعها وإدارتها ننصحك بالانضمام إلى <a href="https://academy.hsoub.com/learn/computer-science/" rel="">دورة علوم الحاسوب</a> المقدمة من أكاديمية حسوب.
</p>
]]></description><guid isPermaLink="false">668</guid><pubDate>Fri, 26 Aug 2022 15:00:00 +0000</pubDate></item><item><title>&#x641;&#x647;&#x631;&#x633;&#x629; &#x627;&#x644;&#x635;&#x641;&#x62D;&#x627;&#x62A; &#x648;&#x62A;&#x62D;&#x644;&#x64A;&#x644; &#x632;&#x645;&#x646; &#x62A;&#x634;&#x63A;&#x64A;&#x644;&#x647;&#x627; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Redis</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%81%D9%87%D8%B1%D8%B3%D8%A9-%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%88%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%B2%D9%85%D9%86-%D8%AA%D8%B4%D8%BA%D9%8A%D9%84%D9%87%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-r580/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61f7afd3899b7_----.png.d8460c179a73bd4ce15c5241fc03434a.png" /></p>
<p>
	سنُقدِّم في هذه المقالة حل تمرين <a href="https://academy.hsoub.com/devops/servers/databases/redis/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-%D9%84%D8%AD%D9%81%D8%B8-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r579/" rel="">مقالة استخدام قاعدة بيانات Redis لحفظ البيانات</a>. بعد ذلك، سنُحلِّل أداء <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1345/" rel="">خوارزمية</a> فهرسة صفحات الإنترنت، ثم سنبني زاحفَ ويب Web crawler بسيطًا.
</p>

<h2>
	المفهرس المبني على قاعدة بيانات Redis
</h2>

<p>
	سنُخزِّن هيكلي البيانات التاليين في قاعدة بيانات Redis:
</p>

<ul>
	<li>
		سيُقابِل كلَّ كلمة بحثٍ كائنٌ من النوع <code>URLSet</code> هو عبارةٌ عن مجموعة Set في قاعدة بيانات Redis تحتوي على مُحدِّدات الموارد الموحدة URLs التي تحتوي على تلك الكلمة.
	</li>
	<li>
		سيُقابِل كل مُحدّد موارد موحد كائنًا من النوع <code>TermCounter</code> يُمثّل جدول Hash في قاعدة بيانات Redis يربُط كل كلمة بحث بعدد مرات ظهورها.
	</li>
</ul>

<p>
	يُمكِنك مراجعة أنواع البيانات التي ناقشناها في المقالة المشار إليها، كما يُمكِنك قراءة المزيد عن المجموعات والجداول بقاعدة بيانات Redis من <a href="https://redis.io/topics/data-types" rel="external nofollow">توثيقها الرسمي</a>.
</p>

<p>
	يُعرِّف الصنف <code>JedisIndex</code> تابعًا يَستقبِل كلمة بحثٍ ويعيد مفتاح كائن الصنف <code>URLSet</code> المقابل في قاعدة بيانات Redis:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_11" style=""><span class="kwd">private</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> urlSetKey</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> term</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"URLSet:"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> term</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما يُعرِّف الصنفُ المذكور التابع termCounterKey، والذي يستقبل مُحدّد موارد موحدًا ويعيد مفتاح كائن الصنف <code>TermCounter</code> المقابل في قاعدة بيانات Redis:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_13" style=""><span class="kwd">private</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> termCounterKey</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"TermCounter:"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> url</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَستقبِل تابع الفهرسة <code>indexPage</code> محددَ موارد موحدًا وكائنًا من النوع <code>Elements</code> يحتوي على <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">شجرة DOM</a> للفقرات التي نريد فهرستها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_16" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> indexPage</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Elements</span><span class="pln"> paragraphs</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"Indexing "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> url</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// أنشئ كائنًا من النوع‫ TermCounter وأحصِ كلمات الفقرات النصية</span><span class="pln">

    </span><span class="typ">TermCounter</span><span class="pln"> tc </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TermCounter</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
    tc</span><span class="pun">.</span><span class="pln">processElements</span><span class="pun">(</span><span class="pln">paragraphs</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// أضف محتويات الكائن إلى قاعدة بيانات‫ Redis</span><span class="pln">
    pushTermCounterToRedis</span><span class="pun">(</span><span class="pln">tc</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنقوم بالتالي لكي نُفهرِّس الصفحة:
</p>

<ol>
	<li>
		نُنشِئ كائنًا من النوع <code>TermCounter</code> يُمثِل محتويات الصفحة باستخدام شيفرة تمرين المقال المشار إليه بالأعلى.
	</li>
	<li>
		نُضيف محتويات ذلك الكائن في قاعدة بيانات Redis.
	</li>
</ol>

<p>
	تُوضِّح الشيفرة التالية طريقة إضافة كائنات النوع <code>TermCounter</code> إلى قاعدة بيانات Redis:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_19" style=""><span class="kwd">public</span><span class="pln"> </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> pushTermCounterToRedis</span><span class="pun">(</span><span class="typ">TermCounter</span><span class="pln"> tc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Transaction</span><span class="pln"> t </span><span class="pun">=</span><span class="pln"> jedis</span><span class="pun">.</span><span class="pln">multi</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">String</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> tc</span><span class="pun">.</span><span class="pln">getLabel</span><span class="pun">();</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> hashname </span><span class="pun">=</span><span class="pln"> termCounterKey</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// إذا كانت الصفحة مفهرسة مسبقًا، احذف الجدول القديم</span><span class="pln">
    t</span><span class="pun">.</span><span class="pln">del</span><span class="pun">(</span><span class="pln">hashname</span><span class="pun">);</span><span class="pln">


    </span><span class="com">// أضف مدخلًا جديدًا في كائن الصنف‫ TermCounter وعنصرًا جديدًا إلى الفهرس</span><span class="pln">
    </span><span class="com">// لكل كلمة بحث</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">String</span><span class="pln"> term</span><span class="pun">:</span><span class="pln"> tc</span><span class="pun">.</span><span class="pln">keySet</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Integer</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> tc</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="pln">term</span><span class="pun">);</span><span class="pln">
        t</span><span class="pun">.</span><span class="pln">hset</span><span class="pun">(</span><span class="pln">hashname</span><span class="pun">,</span><span class="pln"> term</span><span class="pun">,</span><span class="pln"> count</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
        t</span><span class="pun">.</span><span class="pln">sadd</span><span class="pun">(</span><span class="pln">urlSetKey</span><span class="pun">(</span><span class="pln">term</span><span class="pun">),</span><span class="pln"> url</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> res </span><span class="pun">=</span><span class="pln"> t</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> res</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يَستخدم هذا التابع معاملةً من النوع <code>Transaction</code> لتجميع العمليات، ثم يرسلها جميعًا إلى <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r574/" rel="">الخادم</a> على خطوة واحدة. تُعدّ تلك الطريقة أسرع بكثير من إرسال متتاليةٍ من العمليات الصغيرة.
</p>

<p>
	يمرّ التابع عبر العناصر الموجودة في كائن الصنف <code>TermCounter</code>، ويُنفِّذ التالي من أجل كل عنصرٍ منها:
</p>

<ol>
	<li>
		يبحث عن كائنٍ من النوع <code>TermCounter</code> -أو ينشئه إن لم يجده- في قاعدة بيانات Redis، ثم يضيف حقلًا فيه يُمثِل العنصر الجديد.
	</li>
	<li>
		يبحث عن كائنٍ من النوع <code>URLSet</code> -أو ينشئه إن لم يجده- في قاعدة بيانات Redis، ثم يضيف إليه محدّد الموارد الموحد الحالي.
	</li>
</ol>

<p>
	إذا كنا قد فهرَسنا تلك الصفحة من قبل، علينا أن نحذف كائن الصنف <code>TermCounter</code> القديم الذي يمثلها قبل أن نضيف المحتويات الجديدة.
</p>

<p>
	هذا هو كل ما نحتاج إليه لفهرسة الصفحات الجديدة.
</p>

<p>
	طلبَ الجزءُ الثاني من التمرين كتابةَ التابع <code>getCounts</code> الذي يَستقبِل كلمة بحثٍ ويعيد خريطةً تربط محددات الموارد الموحدة التي ظهرت فيها تلك الكلمة بعدد مرات ظهورها فيها. انظر إلى تنفيذ التابع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_23" style=""><span class="pln">    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> getCounts</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> term</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">map</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HashMap</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Integer</span><span class="pun">&gt;();</span><span class="pln">
        </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> urls </span><span class="pun">=</span><span class="pln"> getURLs</span><span class="pun">(</span><span class="pln">term</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">String</span><span class="pln"> url</span><span class="pun">:</span><span class="pln"> urls</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Integer</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> getCount</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> term</span><span class="pun">);</span><span class="pln">
            </span><span class="typ">map</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> count</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">map</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يَستخدِم هذا التابعُ تابعين مساعدين:
</p>

<ul>
	<li>
		<code>getURLs</code>: يَستقبِل كلمة بحثٍ ويعيد مجموعةً من النوع <code>Set</code> تحتوي على محددات الموارد الموحدة التي ظهرت فيها الكلمة.
	</li>
	<li>
		<code>getCount</code>: يَستقبِل محدد موارد موحدًا URI وكلمة بحث، ويعيد عدد مرات ظهور الكلمة بمحدد الموارد المُمرَّر.
	</li>
</ul>

<p>
	انظر تنفيذات تلك التوابع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_25" style=""><span class="pln">    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> getURLs</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> term</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">set</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> jedis</span><span class="pun">.</span><span class="pln">smembers</span><span class="pun">(</span><span class="pln">urlSetKey</span><span class="pun">(</span><span class="pln">term</span><span class="pun">));</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">set</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Integer</span><span class="pln"> getCount</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> term</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">String</span><span class="pln"> redisKey </span><span class="pun">=</span><span class="pln"> termCounterKey</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
        </span><span class="typ">String</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> jedis</span><span class="pun">.</span><span class="pln">hget</span><span class="pun">(</span><span class="pln">redisKey</span><span class="pun">,</span><span class="pln"> term</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Integer</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	تَعمَل تلك التوابع بكفاءةٍ نتيجةً للطريقة التي صمّمّنا بها المُفهرِس.
</p>

<h2>
	تحليل أداء عملية البحث
</h2>

<p>
	لنفترض أننا فهرسنا عددًا مقداره N من الصفحات، وتوصلنا إلى عددٍ مقداره M من كلمات البحث. كم الوقت الذي سيستغرقه البحث عن كلمةٍ معينةٍ؟ فكر قبل أن تكمل القراءة.
</p>

<p>
	سنُنفِّذ التابع <code>getCounts</code> للبحث عن كلمةٍ، يُنفِّذ ذلك التابع ما يلي:
</p>

<ol>
	<li>
		يُنشِئ خريطةً من النوع <code>HashMap</code>.
	</li>
	<li>
		يُنفِّذ التابع <code>getURLs</code> ليسترجع مجموعة مُحدِّدات الموارد الموحدة.
	</li>
	<li>
		يَستدعِي التابع <code>getCount</code> لكل مُحدِّد موارد، ويضيف مُدْخَلًا إلى الخريطة.
	</li>
</ol>

<p>
	يستغرق التابع <code>getURLs</code> زمنًا يتناسب مع عدد محددات الموارد الموحدة التي تحتوي على كلمة البحث. قد يكون عددًا صغيرًا بالنسبة للكلمات النادرة، ولكنه قد يكون كبيرًا -قد يَصِل إلى N- في حالة الكلمات الشائعة.
</p>

<p>
	سنُنفِّذ داخل الحلقة التابعَ <code>getCount</code> الذي يبحث عن كائنٍ من النوع <code>TermCounter</code> في قاعدة بيانات Redis، ثم يبحث عن كلمةٍ، ويضيف مُدخْلًا إلى خريطةٍ من النوع <code>HashMap</code>. تستغرق جميع تلك العمليات زمنًا ثابتًا، وبالتالي، ينتمي التابع <code>getCounts</code> في المجمل إلى المجموعة O(N)‎ في أسوأ الحالات، ولكن عمليًا، يتناسب زمن تنفيذه مع عدد الصفحات التي تحتوي على تلك الكلمة، وهو عادةً عددٌ أصغر بكثيرٍ من N.
</p>

<p>
	وأما فيما يتعلق بتحليل الخوارزميات، فإن تلك الخوارزمية تتميز بأقصى قدرٍ من الكفاءة، ولكنها مع ذلك بطيئةٌ لأنها ترسل الكثير من العمليات الصغيرة إلى قاعدة بيانات Redis. من الممكن تحسين أدائها باستخدام معاملةٍ من النوع <code>Transaction</code>. يُمكِنك محاولة تنفيذ ذلك أو الاطلاع على الحل في الملف <a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/JedisIndex.java" rel="external nofollow">RedisIndex.java</a> (انتبه إلى أن اسمه في <a href="https://github.com/AllenDowney/ThinkDataStructures" rel="external nofollow">المستودع</a> JedisIndex.java والله أعلم).
</p>

<h2>
	تحليل أداء عملية الفهرسة
</h2>

<p>
	ما الزمن الذي تستغرقه فهرسة صفحةٍ عند استخدام هياكل البيانات التي صمّمْناها؟ فكر قبل أن تكمل القراءة.
</p>

<p>
	لكي نُفهرِّس صفحةً، فإننا نمرّ عبر شجرة DOM، ونعثر على الكائنات التي تنتمي إلى النوع <code>TextNode</code>، ونُقسِّم <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-string-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-class-%D9%88%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-object-%D9%88%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%81%D8%B1%D8%B9%D9%8A%D8%A9-subroutine-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1017/" rel="">السلاسل النصية</a> إلى كلمات بحثٍ. تَستغرِق كل تلك العمليات زمنًا يتناسب مع عدد الكلمات الموجودة في الصفحة.
</p>

<p>
	نزيد العدّاد ضمن خريطة النوع <code>HashMap</code> لكل كلمة بحثٍ ضمن الصفحة، وهو ما يَستغرِق زمنًا ثابتًا، ما يَجعَل الصنف <code>TermCounter</code> يَستغرِق زمنًا يتناسب مع عدد الكلمات الموجودة في الصفحة.
</p>

<p>
	تتطلَّب إضافة كائن الصنف <code>TermCounter</code> إلى قاعدة بيانات Redis حذف كائنٍ آخرَ منها. يستغرِق ذلك زمنًا يتناسب خطّيًّا مع عدد كلمات البحث. بعد ذلك، علينا أن نُنفِّذ التالي من أجل كل كلمة:
</p>

<ol>
	<li>
		نضيف عنصرًا إلى كائنٍ من النوع <code>URLSet</code>.
	</li>
	<li>
		نضيف عنصرًا إلى كائنٍ من النوع <code>TermCounter</code>.
	</li>
</ol>

<p>
	تَستغرِق العمليتان السابقتان زمنًا ثابتًا، وبالتالي، يكون الزمن الكليُّ المطلوبُ لإضافة كائنٍ من النوع <code>TermCounter</code> خطيًا مع عدد كلمات البحث الفريدة.
</p>

<p>
	نستخلص مما سبق أنّ زمن تنفيذ الصنف <code>TermCounter</code> يتناسب مع عدد الكلمات الموجودة في الصفحة، وأن إضافة كائنٍ ينتمي إلى النوع <code>TermCounter</code> إلى قاعدة بيانات Redis تتطلَّب زمنًا يتناسب مع عدد الكلمات الفريدة.
</p>

<p>
	ولمّا كان عدد الكلمات الموجودة في الصفحة يتجاوز عادةً عدد كلمات البحث الفريدة، فإن التعقيد يتناسب طردًا مع عدد الكلمات الموجودة في الصفحة، ومع ذلك، قد تحتوي صفحةٌ ما نظرًيًا على جميع كلمات البحث الموجودة في الفهرس، وعليه، ينتمي الأداء في أسوأ الحالات إلى المجموعة O(M)‎، ولكننا لا نتوقَّع حدوث تلك الحالة عمليًّا.
</p>

<p>
	يتضِّح من التحليل السابق أنه ما يزال من الممكن تحسين أداء الخوارزمية، فمثلًا يُمكِننا أن نتجنَّب فهرسة الكلمات الشائعة جدًا، لأنها أولًا تحتل مساحةً كبيرةً من الذاكرة كما أنها تستغرِق وقتًا طويلًا؛ فهي تَظهرَ تقريبًا في جميعِ كائناتِ النوعين <code>URLSet</code> و <code>TermCounter</code>، كما أنها لا تحمل أهميةً كبيرةً فهي لا تساعد على تحديد الصفحات التي يُحتمَل أن تكون ذاتَ صلةٍ بكلمات البحث.
</p>

<p>
	تتجنَّب غالبية محركات البحث فهرسةَ الكلماتِ الشائعةِ التي يطلق عليها اسم <a href="https://ar.wikipedia.org/wiki/%D8%A7%D8%B3%D8%AA%D8%A8%D8%B9%D8%A7%D8%AF_%D8%A7%D9%84%D9%83%D9%84%D9%85%D8%A7%D8%AA_%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9" rel="external nofollow">الكلمات المهملة stop words</a> ضمن هذا السياق.
</p>

<h2>
	اجتياز بيان graph
</h2>

<p>
	إذا أكملت تمرين مقالة "<a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A3%D8%B3%D9%84%D9%88%D8%A8-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D9%84%D8%B9%D9%85%D9%82-%D8%A3%D9%88%D9%84%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%AA%D9%8A%D9%86-iterables-%D9%88iterators-r1383/" rel="">تنفيذ أسلوب البحث بالعمق أولًا باستخدام الواجهتين Iterables و Iterators</a>"، فلديك بالفعل برنامجٌ يقرأ صفحةً من موقع ويكيبيديا، ويبحث عن أول رابطٍ فيها، ويَستخدِمه لتحميل الصفحة التالية، ثم يكرر العملية. يُعدّ هذا البرنامج بمنزلةِ زاحفٍ من نوع خاص، فعادةً عندما يُذكرُ مصطلح "زاحف إنترنت" Web crawler، فالمقصود برنامج يُنفِّذ ما يلي:
</p>

<ul>
	<li>
		يُحمِّل صفحة بداية معينة، ويُفهرِس محتوياتها.
	</li>
	<li>
		يبحث عن جميع الروابط الموجودة في تلك الصفحة ويضيفها إلى تجميعة collection.
	</li>
	<li>
		يمرّ عبر تلك الروابط، ويُحمِّل كلًّا منها، ويُفهرِسه، ويضيف أثناء ذلك روابطَ جديدة.
	</li>
	<li>
		إذا عَثَر على مُحدّدِ مواردَ مُوحدٍ فهرَسَه من قبل، فإنه يتخطاه.
	</li>
</ul>

<p>
	يُمكِننا أن نتصوّر شبكة الإنترنت كما لو كانت شعبة graph. تُمثِل كل صفحة إنترنت عقدةً node في تلك الشعبة، ويُمثِل كل رابط ضلعًا مُوجّهًا directed edge من عقدةٍ إلى عقدةٍ أخرى. يُمكِنك قراءة المزيد عن <a href="https://wiki.hsoub.com/Algorithms/graphs" rel="external">الشعب</a> إذا لم تكن على معرفةٍ بها.
</p>

<p>
	بناءً على هذا التصور، يستطيع الزاحف أن يبدأ من عقدةٍ معيّنةٍ، ويجتاز الشعبة، وذلك بزيارة العقد التي يُمكِنه الوصول إليها مرةً واحدةً فقط.
</p>

<p>
	تُحدِّد التجميعة التي سنَستخدِمها لتخزين محددات الموارد المحددة نوع الاجتياز الذي يُنفِّذه الزاحف:
</p>

<ul>
	<li>
		إذا كانت التجميعة رتلًا queue، أي تتبع أسلوب "الداخل أولًا، يخرج أولًا" FIFO، فإن الزاحف يُنفِّذ اجتياز السّعة أولًا breadth-first.
	</li>
	<li>
		إذا كانت التجميعة مكدسًا stack، أي تتبع أسلوب "الداخل آخرًا، يخرج أولًا" LIFO، فإن الزاحف يُنفِّذ اجتياز العمق أولًا depth-first.
	</li>
	<li>
		من الممكن تحديد أولويّاتٍ لعناصر التجميعة. على سبيل المثال، قد نرغب في إعطاء أولوية أعلى للصفحات التي لم تُفهرَس منذ فترة طويلة.
	</li>
</ul>

<h2>
	تمرين 12
</h2>

<p>
	والآن، عليك كتابة الزاحف، ستجد ملفات الشيفرة التالية الخاصة بالتمرين في <a href="https://github.com/AllenDowney/ThinkDataStructures" rel="external nofollow">مستودع الكتاب</a>:
</p>

<ul>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/WikiCrawler.java" rel="external nofollow">WikiCrawler.java</a>: يحتوي على شيفرة مبدئيّة للزاحف.
	</li>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/WikiCrawlerTest.java" rel="external nofollow">WikiCrawlerTest.java</a>: يحتوي على اختبارات وحدة للصنف <code>WikiCrawler</code>.
	</li>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/JedisIndex.java" rel="external nofollow">JedisIndex.java</a>: يحتوي على حلِّ تمرينِ <a href="https://academy.hsoub.com/devops/servers/databases/redis/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-%D9%84%D8%AD%D9%81%D8%B8-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r579/" rel="">مقالة استخدام قاعدة بيانات Redis لحفظ البيانات</a>.
	</li>
</ul>

<p>
	ستحتاج أيضًا إلى الأصناف المساعدة التالية التي استخدَمناها في تمارين المقالات السابقة:
</p>

<ul>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/JedisMaker.java" rel="external nofollow">JedisMaker.java</a>
	</li>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/WikiFetcher.java" rel="external nofollow">WikiFetcher.java</a>
	</li>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/TermCounter.java" rel="external nofollow">TermCounter.java</a>
	</li>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/WikiNodeIterable.java" rel="external nofollow">WikiNodeIterable.java</a>
	</li>
</ul>

<p>
	سيتعيّن عليك توفير ملفٍّ يحتوي على بيانات خادم Redis قبل تنفيذ الصنف <code>JedisMaker</code>. إذا أكملت تمرين المقالة مقالة استخدام قاعدة بيانات Redis لحفظ البيانات، فقد جهّزت كل شيء بالفعل، أما إذا لم تكمله، فستجد التعليمات الضرورية لإتمام ذلك في نفس المقالة.
</p>

<p>
	نفِّذ الأمر <code>ant build</code> لتصريف ملفات الشيفرة، ثم نفِّذ الأمر <code>ant JedisMaker</code> لكي تتأكّد من أنه مهياٌ للاتصال مع خادم Redis الخاص بك.
</p>

<p>
	والآن، نفِّذ الأمر <code>ant WikiCrawlerTest</code>. ستجد أن الاختبارات قد فشلت؛ لأن ما يزال عليك إتمام بعض العمل أولًا.
</p>

<p>
	انظر إلى بداية تعريف الصنف <code>WikiCrawler</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_28" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">WikiCrawler</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> final </span><span class="typ">String</span><span class="pln"> source</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">JedisIndex</span><span class="pln"> index</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">Queue</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">queue</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">LinkedList</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;();</span><span class="pln">
    final </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">WikiFetcher</span><span class="pln"> wf </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">WikiFetcher</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">WikiCrawler</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> source</span><span class="pun">,</span><span class="pln"> </span><span class="typ">JedisIndex</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">source </span><span class="pun">=</span><span class="pln"> source</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">index </span><span class="pun">=</span><span class="pln"> index</span><span class="pun">;</span><span class="pln">
        </span><span class="typ">queue</span><span class="pun">.</span><span class="pln">offer</span><span class="pun">(</span><span class="pln">source</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> queueSize</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">queue</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يحتوي هذا الصنف على متغيرات النسخ instance variables التالية:
</p>

<ul>
	<li>
		<code>source</code>: مُحدّد الموارد الموحد الذي ستبدأ منه.
	</li>
	<li>
		<code>index</code>: مفهرِس -من النوع <code>JedisIndex</code>- ينبغي أن تُخزَّن النتائج فيه.
	</li>
	<li>
		<code>queue</code>: عبارة عن قائمة من النوع <code>LinkedList</code>. يُفترَض أن تُخزَّن فيها كلُّ محددات الموارد التي عثرت عليها، ولكن لم تُفهرِسها بعد.
	</li>
	<li>
		<code>wf</code>: عبارة عن كائن من النوع <code>WikiFetcher</code>. عليك أن تَستخدِمه لقراءة صفحات الإنترنت وتحليلها.
	</li>
</ul>

<p>
	عليك الآن أن تكمل التابع <code>crawl</code>. انظر إلى بصمته:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5249_30" style=""><span class="pln">    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> crawl</span><span class="pun">(</span><span class="pln">boolean testing</span><span class="pun">)</span><span class="pln"> throws </span><span class="typ">IOException</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	ينبغي أن تكون قيمة المعامل <code>testing</code> مساويةً للقيمة <code>true</code> إذا كان مستدعيه هو الصنف <code>WikiCrawlerTest</code>، وأن تكون مساويةً للقيمة <code>false</code> في الحالات الأخرى.
</p>

<p>
	عندما تكون قيمة المعامل <code>testing</code> مساويةً للقيمة <code>true</code>، يُفترَض أن يُنفِّذ التابع <code>crawl</code> ما يلي:
</p>

<ul>
	<li>
		يختار مُحدّدَ موارد موحدًا من الرتل وفقًا لأسلوب "الداخل أولًا، يخرج أولًا" ثم يحذفه منه.
	</li>
	<li>
		يقرأ محتويات تلك الصفحة باستخدام التابع <code>WikiFetcher.readWikipedia</code> الذي يعتمد في قراءته للصفحات على نسخٍ مُخزّنةٍ مؤقتًا في المستودع بهدف الاختبار (لكي نتجنَّب أيَّ مشكلات ممكنة في حال تغيّرت النسخُ الموجودةُ في موقع ويكيبيديا).
	</li>
	<li>
		يُفهرِس الصفحة بغض النظر عما إذا كانت قد فُهرِسَت من قبل.
	</li>
	<li>
		ينبغي أن يجد كل الروابط الداخلية الموجودة في الصفحة ويضيفها إلى الرتل بنفس ترتيب ظهورها. يُقصَد بالروابط الداخلية تلك الروابط التي تشير إلى صفحات أخرى ضمن موقع ويكيبيديا.
	</li>
	<li>
		ينبغي أن يعيد مُحدّد الموارد الخاص بالصفحة التي فهرسها.
	</li>
</ul>

<p>
	في المقابل، عندما تكون قيمة المعامل <code>testing</code> مساويةً للقيمة <code>false</code>، يُفترَض له أن يُنفِّذ ما يلي:
</p>

<ul>
	<li>
		يختار مُحدّد موارد موحدًا من الرتل وفقًا لأسلوب "الداخل أولًا، يخرج أولًا" ثم يحذفه منه.
	</li>
	<li>
		إذا كان محدّد الموارد المختار مفهرَسًا بالفعل، لا ينبغي أن يعيد فهرسته، وعليه أن يعيد القيمة <code>null</code>.
	</li>
	<li>
		إذا لم يُفهرَس من قبل، فينبغي أن يقرأ محتويات الصفحة باستخدام التابع. <code>WikiFetcher.fetchWikipedia</code> الذي يعتمد في قراءته للصفحات على شبكة الإنترنت.
	</li>
	<li>
		ينبغي أن يُفهرِس الصفحة، ويضيف أيَّ روابطَ موجودةٍ فيها إلى الرتل، ويعيد مُحدّد الموارد الخاصَّ بالصفحة التي فهرسها.
	</li>
</ul>

<p>
	يُحمِّل الصنف <code>WikiCrawlerTest</code> رتلًا يحتوي على 200 رابط، ثم يَستدعِي التابع <code>crawl</code> ثلاث مرّات، ويفحص في كلِّ استدعاء القيمةَ المعادة والطولَ الجديد للرتل.
</p>

<p>
	إذا أكملت زاحف الإنترنت الخاص بك بشكل صحيح، فينبغي أن تنجح الاختبارات.
</p>

<p>
	ترجمة -بتصرّف- للفصل <a href="https://greenteapress.com/thinkdast/html/thinkdast016.html" rel="external nofollow">Chapter 15: Crawling Wikipedia</a> من كتاب <a href="https://greenteapress.com/thinkdast/html/index.html" rel="external nofollow">Think Data Structures: Algorithms and Information Retrieval in Java</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A-boolean-search-%D9%88%D8%AF%D9%85%D8%AC-%D9%86%D8%AA%D8%A7%D8%A6%D8%AC-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%D9%87%D8%A7-r1455/" rel="">البحث الثنائي Boolean search ودمج نتائج البحث وترتيبها</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/devops/servers/databases/redis/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-%D9%84%D8%AD%D9%81%D8%B8-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r579/" rel="">استخدام قاعدة بيانات Redis لحفظ البيانات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%B2%D9%85%D9%86-%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D9%85%D9%86%D9%81%D8%B0%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1389/" rel="">تحليل زمن تشغيل الخرائط المنفذة باستخدام مصفوفة في جافا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%d8%b4%d8%b1%d8%ad-%d8%a7%d9%84%d9%81%d8%b1%d9%88%d9%82%d8%a7%d8%aa-%d8%a8%d9%8a%d9%86-%d9%82%d9%88%d8%a7%d8%b9%d8%af-%d8%a8%d9%8a%d8%a7%d9%86%d8%a7%d8%aa-sql-%d9%88%d9%86%d8%b8%d9%8a%d8%b1%d8%a7%d8%aa%d9%87%d8%a7-nosql-r71/" rel="">شرح الفروقات بين قواعد بيانات SQL ونظيراتها NoSQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D9%8A%D8%B1%D8%A7%D8%AF-%D9%88%D8%AA%D8%B5%D8%AF%D9%8A%D8%B1-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-%D8%A3%D9%88-mariadb-r361/" rel="">كيفية استيراد وتصدير قواعد بيانات MySQL أو MariaDB</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">580</guid><pubDate>Wed, 02 Mar 2022 17:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Redis &#x644;&#x62D;&#x641;&#x638; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A;</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-%D9%84%D8%AD%D9%81%D8%B8-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r579/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61f7a9bd9afb4_---Redis---.png.c8efc11ce5fec89c2f04cfbc4658f47c.png" /></p>
<p>
	سنُكمِل في التمارين القليلة القادمة بناء محرك البحث الذي تحدثنا عنه في مقالة <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A3%D8%B3%D9%84%D9%88%D8%A8-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D9%84%D8%B9%D9%85%D9%82-%D8%A3%D9%88%D9%84%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B7%D8%B1%D9%8A%D9%82%D8%AA%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%88%D8%AF-%D9%88%D8%A7%D9%84%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1377/" rel="">تنفيذ أسلوب البحث بالعمق أولًا تعاوديًّا وتكراريًّا</a>. يتكوَّن أيّ محرك بحثٍ من الوظائف التالية:
</p>

<ul>
	<li>
		الزحف crawling: ويُنفّذُ من خلال برنامجٍ بإمكانه تحميلُ صفحة إنترنت وتحليلُها واستخراجُ النص وأيِّ روابط إلى صفحاتٍ أخرى.
	</li>
	<li>
		الفهرسة indexing: وتنفّذُ من خلال هيكل بيانات data structure بإمكانه البحث عن كلمة والعثور على الصفحات التي تحتوي على تلك الكلمة.
	</li>
	<li>
		الاسترجاع retrieval: وهي طريقةٌ لتجميع نتائج المُفهرِس واختيار الصفحات الأكثر صلة بكلمات البحث.
	</li>
</ul>

<p>
	إذا كنت قد أتممت تمرين مقالة <a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AE%D8%B1%D9%8A%D8%B7%D8%A9-%D9%88%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%81%D9%87%D8%B1%D8%B3-indexer-r1384/" rel="">استخدام خريطة ومجموعة لبناء مُفهرِّس Indexer</a>، فقد نفَّذت مُفهرسًا بالفعل باستخدام <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-maps-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1430/" rel="">خرائط جافا</a>. سنناقش هذا التمرين هنا وسنُنشِئ نسخةً جديدةً تُخزِّن النتائج في قاعدة بيانات.
</p>

<p>
	وإذا كنت قد أكملت تمرين مقالة <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A3%D8%B3%D9%84%D9%88%D8%A8-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D9%84%D8%B9%D9%85%D9%82-%D8%A3%D9%88%D9%84%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%AA%D9%8A%D9%86-iterables-%D9%88iterators-r1383/" rel="">تنفيذ أسلوب البحث بالعمق أولًا باستخدام الواجهتين Iterables و Iterators</a>، فقد نفَّذت بالفعل زاحفًا يَتبِع أول رابطٍ يعثرُ عليه. سنُنشِئ في التمرين التالي نسخةً أعمّ تُخزِّن كل رابطٍ تجده في رتل queue، وتتبع تلك الروابط بالترتيب.
</p>

<p>
	في النهاية، ستُكلّف بالعمل على برنامج الاسترجاع.
</p>

<p>
	سنُوفِّر شيفرةً مبدئيّةً أقصر في هذه التمارين، وسنعطيك فرصةً أكبر لاتخاذ القرارات المتعلقة بالتصميم. وتجدر الإشارة إلى أن هذه التمارين ذات نهايات مفتوحة، أي سنطرح عليك فقط بعض الأهداف البسيطة التي يتعين عليك الوصول إليها، ولكنك تستطيع بالطبع المُضِي قدمًا إذا أردت المزيد من التحدي.
</p>

<p>
	والآن، سنبدأ بالنسخة الجديدة من المُفهرِس.
</p>

<h2>
	قاعدة بيانات Redis
</h2>

<p>
	تُخزِّن النسخة السابقة من المُفهرِس البيانات في هيكلَيْ بياناتٍ: الأول هو كائنٌ من النوع <code>TermCounter</code> يَربُط كل كلمة بحثٍ بعدد المرات التي ظهرت فيها الكلمة في صفحة إنترنت معينةٍ، والثاني كائنٌ من النوع <code>Index</code> يربُط كلمة البحث بمجموعة الصفحات التي ظهرت فيها.
</p>

<p>
	يُخزَّن هيكلا البيانات في ذاكرة التطبيق، ولذا يتلاشيان بمجرد انتهاء البرنامج. توصف البيانات التي تُخزَّن فقط في ذاكرة التطبيق بأنها "متطايرة volatile"؛ لأنها تزول بمجرد انتهاء البرنامج.
</p>

<p>
	في المقابل، تُوصف البيانات التي تظل موجودةً بعد انتهاء البرنامج الذي أنشأها بأنها "مستمرة persistent". مثال ذلك الملفات المُخزَّنة في نظام الملفات فهي مُستمرة في العموم، وكذلك البيانات المُخزَّنة في قاعدة بيانات أيضًا مستمرة.
</p>

<p>
	يُعدّ تخزين البيانات في ملف واحدة من أبسط طرق حفظ البيانات، فيُمكِننا ببساطة أن نحوِّل هياكل البيانات المُتضمِّنة للبيانات إلى صيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> ثم نكتبها إلى ملف قبل انتهاء البرنامج. عندما نُشغِّل البرنامج مرة أخرى، سنتمكَّن من قراءة الملف وإعادة بناء هياكل البيانات.
</p>

<p>
	ولكن هناك مشكلتان في هذا الحل:
</p>

<ol>
	<li>
		عادةً ما تكون عمليتا قراءة هياكل البيانات الضخمة (مثل مفهرس الويب) وكتابتها عمليتين بطيئتين.
	</li>
	<li>
		قد لا تستوعب مساحة ذاكرة برنامج واحد هيكل البيانات بأكمله.
	</li>
	<li>
		إذا انتهى برنامجٌ معينٌ على نحوٍ غير متوقع (نتيجة لانقطاع الكهرباء مثلًا)، فسنفقد جميع التغييرات التي أجريناها على البيانات منذ آخر مرة فتحنا فيها البرنامج.
	</li>
</ol>

<p>
	وهناك طريقة أخرى لحفظ البيانات وهي <a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r584/" rel="">قواعد البيانات</a>. إذ تُعدّ قواعدُ البيانات البديلَ الأفضلَ، فهي تُوفِّر مساحةَ تخزينٍ مستمرةً، كما أنها قادرةٌ على قراءة جزءٍ من قاعدة البيانات أو كتابته دون الحاجة إلى قراءة قاعدة البيانات أو كتابتها بالكامل.
</p>

<p>
	تتوفَّر الكثير من أنواع نظم إدارة قواعد البيانات DBMS، ويتمتع كلٌّ منها بإمكانيات مختلفة. ويُمكِنك قراءة <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-sqlite-%D9%85%D8%B9-mysql-%D9%85%D8%B9-postgresql-r72/" rel="">مقارنة بين أنظمة إدارة قواعد البيانات العلاقية</a> والاطلاع على سلسلة <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B5%D9%85%D9%8A%D9%85%20%D9%82%D9%88%D8%A7%D8%B9%D8%AF%20%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">تصميم قواعد البيانات</a>.
</p>

<p>
	تُوفِّر قاعدة بيانات Redis -التي سنَستخدِمها في هذا التمرين- هياكل بيانات مستمرة مشابهةً لهياكل البيانات التي تُوفِّرها جافا، فهي تُوفِّر:
</p>

<ul>
	<li>
		قائمة سلاسل نصية مشابهة للنوع <code>List</code>.
	</li>
	<li>
		جداول مشابهة للنوع <code>Map</code>.
	</li>
	<li>
		مجموعات من السلاسل النصية مشابهة للنوع <code>Set</code>.
	</li>
</ul>

<p>
	تُعدّ Redis قاعدة بيانات من نوع زوج مفتاح/قيمة، ويَعني ذلك أن هياكل البيانات (القيم) التي تُخزِّنها تكون مُعرَّفةً بواسطة سلاسل نصية ٍفريدةٍ (مفاتيح). تلعب المفاتيح في قاعدة بيانات Redis نفس الدور الذي تلعبه المراجع references في لغة جافا، أي أنّها تُعرِّف هوية الكائنات. سنرى أمثلةً على ذلك لاحقًا.
</p>

<h2>
	خوادم وعملاء Redis
</h2>

<p>
	عادةً ما تَعمَل قاعدة بيانات Redis كخدمةٍ عن بعد، فكلمة Redis هي في الحقيقة اختصار لعبارة "خادم قاموس عن بعد REmote DIctionary Server"، ولنتمكن من استخدامها، علينا أن نُشغِّل خادم Redis في مكانٍ ما ثم نَتصِل به عبر عميل Redis. من الممكن إعداد ذلك الخادم بأكثرَ من طريقةٍ، كما يُمكِننا الاختيار من بين العديد من برامج العملاء، وسنَستخدِم في هذا التمرين ما يلي:
</p>

<ol>
	<li>
		بدلًا من أن نُثبِّت الخادم ونُشغِّله بأنفسنا، سنَستخدِم خدمةً مثل <a href="http://thinkdast.com/redistogo" rel="external nofollow">RedisToGo</a>. تُشغِّل تلك الخدمة قاعدة بيانات Redis على السحابة cloud، وتُقدِّم خطةً مجانيةً بمواردَ تتناسب مع متطلبات هذا التمرين.
	</li>
	<li>
		بالنسبة للعميل، سنَستخدِم Jedis، وهو عبارةٌ عن مكتبة جافا تحتوي على أصنافٍ وتوابعَ يُمكِنها العمل مع قاعدة بيانات Redis.
	</li>
</ol>

<p>
	انظر إلى التعليمات المُفصَّلة التالية لمساعدتك على البدء:
</p>

<ul>
	<li>
		أنشِئ حسابًا على <a href="http://thinkdast.com/redissign" rel="external nofollow">موقع RedisToGo</a>، واختر الخطة التي تريدها (ربما الخطة المجانية).
	</li>
	<li>
		أنشِئ نسخة آلة افتراضية يعمل عليها خادم Redis. إذا نقرت على تبويب "Instances"، ستجد أن النسخة الجديدة مُعرَّفة باسم استضافة ورقم منفذ. كان اسم النسخة الخاصة بنا مثلًا هو dory-10534.
	</li>
	<li>
		انقر على اسم النسخة لكي تفتح صفحة الإعدادات، وسجِّل اسم مُحدّد الموارد الموحد URL الموجود أعلى الصفحة. سيكون مشابهًا لما يلي:
	</li>
</ul>

<pre class="ipsCode">redis://redistogo:1234567feedfacebeefa1e1234567@dory.redistogo.com:10534    
</pre>

<p>
	يحتوي محدد الموارد الموحد السابق ذكره على اسم الاستضافة الخاص بالخادم <code>dory.redistogo.com</code>، ورقم المنفذ 10534، وكلمة المرور التي سنحتاج إليها للاتصال بالخادم، وهي السلسلة النصية الطويلة المُكوَّنة من أحرف وأعدادٍ في المنتصف. ستحتاج تلك المعلومات في الخطوة التالية.
</p>

<h2>
	إنشاء مفهرس يعتمد على Redis
</h2>

<p>
	ستجد الملفات التالية الخاصة بالتمرين في <a href="https://github.com/AllenDowney/ThinkDataStructures" rel="external nofollow">مستودع الكتاب</a>:
</p>

<ul>
	<li>
		<code>JedisMaker.java</code>: يحتوي على بعض الأمثلة على الاتصال بخادم Redis وتشغيل بعض توابع Jedis.
	</li>
	<li>
		<code>JedisIndex.java</code>: يحتوي على شيفرةٍ مبدئيةٍ لهذا التمرين.
	</li>
	<li>
		<code>JedisIndexTest.java</code>: يحتوي على اختبارات للصنف <code>JedisIndex</code>.
	</li>
	<li>
		<code>WikiFetcher.java</code>: يحتوي على شيفرةٍ تقرأ صفحات إنترنت وتُحلِّلها باستخدام مكتبة jsoup. كتبنا تلك الشيفرة في تمارين المقالات المشار إليها بالأعلى.
	</li>
</ul>

<p>
	ستجد أيضًا الملفات التالية التي كتبناها في نفس تلك التمارين:
</p>

<ul>
	<li>
		<code>Index.java</code>: يُنفِّذ مفهرِسًا باستخدام هياكل بيانات تُوفِّرها جافا.
	</li>
	<li>
		<code>TermCounter.java</code>: يُمثِل خريطةً تربُط كلمات البحث بعدد مرات حدوثها.
	</li>
	<li>
		<code>WikiNodeIterable.java</code>: يمرّ عبر عقد <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">شجرة DOM</a> الناتجة من مكتبة jsoup.
	</li>
</ul>

<p>
	إذا تمكَّنت من كتابة نسخك الخاصة من تلك الملفات، يُمكِنك استخدامها لهذا التمرين. إذا لم تكن قد أكملت تلك التمارين أو أكملتها ولكنك غير متأكّد مما إذا كانت تَعمَل على النحو الصحيح، يُمكِنك أن تَنسَخ الحلول من مجلد solutions.
</p>

<p>
	والآن، ستكون خطوتك الأولى هي استخدام عميل Jedis للاتصال بخادم Redis الخاص بك. يُوضِّح الصنف <code>RedisMaker.java</code> طريقة القيام بذلك: عليه أولًا أن يقرأ معلومات الخادم من ملفٍّ، ثم يتصل به، ويُسجِل دخوله باستخدام كلمة المرور، وأخيرًا، يُعيد كائنًا من النوع <code>Jedis</code> الذي يُمكِن استخدامه لتنفيذ عمليات Redis.
</p>

<p>
	ستجد الصنف <code>JedisMaker</code> مُعرَّفًا في الملف <code>JedisMaker.java</code>. يَعمَل ذلك الصنف كصنف مساعد حيث يحتوي على التابع الساكن <code>make</code> الذي يُنشِئ كائنًا من النوع <code>Jedis</code>. يُمكِنك أن تَستخدِم ذلك الكائن بعد التصديق عليه للاتصال بقاعدة بيانات Redis الخاصة بك.
</p>

<p>
	يقرأ الصنف <code>JedisMaker</code> بيانات خادم Redis من ملفٍّ اسمه <code>redis_url.txt</code> موجودٍ في المجلد <code>src/resources</code>:
</p>

<ul>
	<li>
		استخدم مُحرّرَ نصوصٍ لإنشاء وتعديل الملف <code>ThinkDataStructures/code/src/resources/redis_url.txt</code>.
	</li>
	<li>
		ضع فيه مُحدّد موارد الخادم الخاص بك. إذا كنت تَستخدِم خدمة RedisToGo، سيكون محدد الموارد مشابهًا لما يلي:
	</li>
</ul>

<pre class="ipsCode">redis://redistogo:1234567feedfacebeefa1e1234567@dory.redistogo.com:10534
</pre>

<p>
	لا تضع هذا الملف في مجلدٍ عامٍّ لأنه يحتوي على كلمة مرور خادم Redis. يُمكِنك تجنُّب وقوع ذلك عن طريق الخطأ باستخدام الملف ‎.gitignore الموجود في <a href="https://github.com/AllenDowney/ThinkDataStructures" rel="external nofollow">مستودع الكتاب</a> لمنع وضع الملف فيه.
</p>

<p>
	نفِّذ الأمر <code>ant build</code> لتصريف ملفات الشيفرة والأمر <code>ant JedisMaker</code> لتشغيل المثال التوضيحيّ بالتابع <code>main</code>:
</p>

<pre class="ipsCode">    public static void main(String[] args) {

        Jedis jedis = make();

        // String
        jedis.set("mykey", "myvalue");
        String value = jedis.get("mykey");
        System.out.println("Got value: " + value);

        // Set
        jedis.sadd("myset", "element1", "element2", "element3");
        System.out.println("element2 is member: " + 
                           jedis.sismember("myset", "element2"));

        // List
        jedis.rpush("mylist", "element1", "element2", "element3");
        System.out.println("element at index 1: " + 
                           jedis.lindex("mylist", 1));

        // Hash
        jedis.hset("myhash", "word1", Integer.toString(2));
        jedis.hincrBy("myhash", "word2", 1);
        System.out.println("frequency of word1: " + 
                           jedis.hget("myhash", "word1"));
        System.out.println("frequency of word1: " + 
                            jedis.hget("myhash", "word2"));

        jedis.close();
    }
</pre>

<p>
	يَعرِض المثال أنواع البيانات والتوابع التي ستحتاج إليها غالبًا في هذا التمرين. ينبغي أن تحصل على الخرج التالي عند تشغيله:
</p>

<pre class="ipsCode">Got value: myvalue
element2 is member: true
element at index 1: element2
frequency of word1: 2
frequency of word2: 1
</pre>

<p>
	سنشرح طريقة عمل تلك الشيفرة في القسم التالي.
</p>

<h2>
	أنواع البيانات في قاعدة بيانات Redis
</h2>

<p>
	تَعمَل Redis كخريطةٍ تربط مفاتيحَ (سلاسل نصيّة) بقيم. قد تنتمي تلك القيم إلى مجموعة أنواع مختلفة من البيانات. يُعدّ النوع <em>string</em> واحدًا من أبسط الأنواع التي تُوفِّرها قاعدة بيانات Redis. لاحظ أننا سنكتب أنواع بيانات Redis بخطوط مائلة لنُميزها عن أنواع جافا.
</p>

<p>
	سنَستخدِم التابع <code>jedis.set</code> لإضافة سلسلةٍ نصيّةٍ من النوع <em>string</em> إلى قاعدة البيانات. قد تجد ذلك مألوفًا، فهو يشبه التابع <code>Map.put</code>، حيث تُمثِل المعاملات المُمرَّرة المفتاح الجديد وقيمته المقابلة. في المقابل، يُستخدَم التابع <code>jedis.get</code> للبحث عن مفتاحٍ معينٍ والعثور على قيمته. انظر الشيفرة إلى التالية:
</p>

<pre class="ipsCode">        jedis.set("mykey", "myvalue");
        String value = jedis.get("mykey");
</pre>

<p>
	كان المفتاح هو <code>"mykey"</code> والقيمة هي <code>"myvalue"</code> في هذا المثال.
</p>

<p>
	تُوفِّر Redis هيكل البيانات <em>set</em> الذي يَعمَل بشكلٍ مشابهٍ للنوع <code>Set&lt;String&gt;‎</code> في جافا. إذا أردت أن تضيف عنصرًا جديدًا إلى مجموعةٍ من النوع <em>set</em>، اختر مفتاحًا يُحدّد هوية تلك المجموعة، ثم اِستخدِم التابع <code>jedis.sadd</code> كما يلي:
</p>

<pre class="ipsCode">        jedis.sadd("myset", "element1", "element2", "element3");
        boolean flag = jedis.sismember("myset", "element2");
</pre>

<p>
	لاحِظ أنه ليس من الضروري إنشاء المجموعة بخطوةٍ منفصلةٍ، حيث تُنشؤها Redis إن لم تكن موجودةً. تُنشِئ Redis في هذا المثال مجموعةً من النوع <em>set</em> اسمها <code>myset</code> تحتوي على ثلاثة عناصر.
</p>

<p>
	يفحص التابع <code>jedis.sismember</code> ما إذا كان عنصر معين موجودًا في مجموعة من النوع <em>set</em>. تَستغرِق عمليتا إضافة العناصر وفحص عضويّتها زمنًا ثابتًا.
</p>

<p>
	تُوفِّر Redis أيضًا هيكل البيانات <em>list</em> الذي يشبه النوع <code>List&lt;String&gt;‎</code> في جافا. يضيف التابع <code>jedis.rpush</code> العناصر إلى النهاية اليمنى من القائمة، كما يلي:
</p>

<pre class="ipsCode">        jedis.rpush("mylist", "element1", "element2", "element3");
        String element = jedis.lindex("mylist", 1);
</pre>

<p>
	مثلما سبق، لا يُعدّ إنشاء هيكل البيانات قبل إضافة العناصر إليه أمرًا ضروريًا. يُنشِئ هذا المثال قائمةً من النوع <em>list</em> اسمها <code>mylist</code> تحتوي على ثلاثة عناصر.
</p>

<p>
	يَستقبِل التابع <code>jedis.lindex</code> فهرسًا هو عبارةٌ عن عددٍ صحيحٍ، ويعيد عنصرَ القائمةِ المشارَ إليه. تَستغرِق عمليتا إضافة العناصر واسترجاعها زمنًا ثابتًا.
</p>

<p>
	أخيرًا، تُوفِّر Redis الهيكل <em>hash</em> الذي يشبه النوع <code>Map&lt;String, String&gt;‎</code> في جافا. يضيف التابع <code>jedis.hset</code> مُدخَلًا جديدًا إلى الجدول على النحو التالي:
</p>

<pre class="ipsCode">        jedis.hset("myhash", "word1", Integer.toString(2));
        String value = jedis.hget("myhash", "word1");
</pre>

<p>
	يُنشِئ هذا المثال جدولًا جديدًا اسمه <code>myhash</code> يحتوي على مدخلٍ واحدٍ يربُط المفتاح <code>word1</code> بالقيمة <code>"2"</code>.
</p>

<p>
	تنتمي المفاتيح والقيم إلى النوع <em>string</em>، ولذلك، إذا أردنا أن نُخزِّن عددًا صحيحًا من النوع <code>Integer</code>، علينا أن نُحوِّله أولًا إلى النوع <code>String</code> قبل أن نَستدعِي التابع <code>hset</code>. وبالمثل، عندما نبحث عن قيمة باستخدام التابع <code>hget</code>، ستكون النتيجة من النوع <code>String</code>، ولذلك، قد نضطرّ إلى تحويلها مرةً أخرى إلى النوع <code>Integer</code>.
</p>

<p>
	قد يكون العمل مع النوع <em>hash</em> في قاعدة بيانات Redis مربكًا نوعًا ما؛ لأننا نَستخدِم مفتاحين، الأول لتحديد الجدول الذي نريده، والثاني لتحديد القيمة التي نريدها من الجدول. في سياق قاعدة بيانات Redis، يُطلَق على المفتاح الثاني اسم "الحقل field"، أي يشير مفتاح مثل <code>myhash</code> إلى جدولٍ معينٍ من النوع <em>hash</em> بينما يشير حقلٌ مثل <code>word1</code> إلى قيمةٍ ضمن ذلك الجدول.
</p>

<p>
	عادةً ما تكون القيم المُخزَّنة في جداول من النوع <em>hash</em> أعدادًا صحيحة، ولذلك، يوفِّر نظام إدارة قاعدة بيانات Redis بعض التوابع الخاصة التي تعامل القيم وكأنها أعداد مثل التابع <code>hincrby</code>. انظر إلى المثال التالي:
</p>

<pre class="ipsCode">        jedis.hincrBy("myhash", "word2", 1);
</pre>

<p>
	يسترجع هذا التابع المفتاح <code>myhash</code>، ويحصل على القيمة الحالية المرتبطة بالحقل <code>word2</code> (أو على الصفر إذا لم يكن الحقل موجودًا)، ثم يزيدها بمقدار 1، ويكتبها مرةً أخرى في الجدول.
</p>

<p>
	تستغرق عمليات إضافة المُدْخَلات إلى جدول من النوع <em>hash</em> واسترجاعها وزيادتها زمنًا ثابتًا.
</p>

<h2>
	تمرين 11
</h2>

<p>
	بوصولك إلى هنا، أصبح لديك كل المعلومات الضرورية لإنشاء مُفهرسٍ قادرٍ على تخزين النتائج في قاعدة بيانات Redis.
</p>

<p>
	والآن، نفِّذ الأمر <code>ant JedisIndexTest</code>. ستفشل بعض الاختبارات لأنه ما يزال أمامنا بعض العمل.
</p>

<p>
	يختبر الصنف <code>JedisIndexTest</code> التوابع التالية:
</p>

<ul>
	<li>
		<code>JedisIndex</code>: يستقبل هذا الباني كائنًا من النوع <code>Jedis</code> كمعامل.
	</li>
	<li>
		<code>indexPage</code>: يضيف صفحة إنترنت إلى المفهرس. يَستقبِل سلسلةً نصيّةً من النوع <code>String</code> تُمثِل مُحدّد موارد URL بالإضافة إلى كائن من النوع <code>Elements</code> يحتوي على عناصر الصفحة المطلوب فهرستها.
	</li>
	<li>
		<code>getCounts</code>: يستقبل كلمةَ بحثٍ ويعيد خريطةً من النوع <code>Map&lt;String, Integer&gt;‎</code> تربُط كل محدد مواردَ يحتوي على تلك الكلمة بعدد مرات ظهورها في تلك الصفحة.
	</li>
</ul>

<p>
	يُوضِح المثال التالي طريقة استخدامِ تلك التوابع:
</p>

<pre class="ipsCode">        WikiFetcher wf = new WikiFetcher();
        String url1 = 
            "http://en.wikipedia.org/wiki/Java_(programming_language)";
        Elements paragraphs = wf.readWikipedia(url1);

        Jedis jedis = JedisMaker.make();
        JedisIndex index = new JedisIndex(jedis);
        index.indexPage(url1, paragraphs);
        Map&lt;String, Integer&gt; map = index.getCounts("the");
</pre>

<p>
	إذا بحثنا عن <code>url1</code> في الخريطة الناتجة <code>map</code>، ينبغي أن نحصل على 339، وهو عدد مرات ظهور كلمة "the" في مقالة ويكيبيديا عن <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D9%84%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-java-r599/" rel="">لغة جافا</a> (نسخة المقالة التي خزّناها).
</p>

<p>
	إذا حاولنا فهرسة الصفحة مرةً أخرى، ستحُلّ النتائج الجديدة محل النتائج القديمة.
</p>

<p>
	إذا أردت تحويل هياكل البيانات من جافا إلى Redis، فتذكّر أن كل كائنٍ مُخزّنٍ في قاعدة بيانات Redis مُعرَّفٌ بواسطة مفتاحٍ فريدٍ من النوع <em>string</em>. إذا كان لديك نوعان من الكائنات في نفس قاعدة البيانات، فقد ترغب في إضافة كلمةٍ إلى بداية المفاتيح لتمييزها عن بعضها. على سبيل المثال، لدينا النوعان التاليان من الكائنات:
</p>

<ul>
	<li>
		يُمثِل النوع <code>URLSet</code> مجموعةً من النوع <em>set</em> في قاعدة بيانات Redis. تحتوي تلك المجموعة على محددات الموارد الموحدة URL التي تحتوي على كلمةٍ بحثٍ معينة. يبدأ المفتاح الخاص بكل قيمةٍ من النوع <code>URLSet بكلمة</code>":<code>URLSet</code>"، وبالتالي، لكي نحصل على محددات الموارد الموحدة التي تحتوي على كلمة "the"، علينا أن نسترجع المجموعة التي مفتاحها هو"<code>URLSet</code>:<code>the</code>".
	</li>
	<li>
		يُمثِل النوع <code>TermCounter</code> جدولًا من النوع <em>hash</em> في قاعدة بيانات Redis. يربُط هذا الجدول كل كلمة بحث ظهرت في صفحة معيّنة بعدد مرات ظهورها. يبدأ المفتاح الخاصُّ بكل قيمة من النوع <code>TermCounter</code> بكلمة <code>"TermCounter:‎"</code> وينتهي بمحدد الموارد الموحّدِ الخاص بالصفحة التي نبحث فيها.
	</li>
	<li>
		يحتوي التنفيذ الخاص بنا على قيمةٍ من النوع <code>URLSet</code> لكل كلمة بحثٍ، وعلى قيمةٍ من النوع <code>TermCounter</code> لكل صفحة مُفهرسَة. وفَّرنا أيضًا التابعين المساعدين <code>urlSetKey</code> و <code>termCounterKey</code> لإنشاء تلك المفاتيح.
	</li>
</ul>

<h2>
	المزيد من الاقتراحات
</h2>

<p>
	أصبح لديك الآن كل المعلومات الضرورية لحل التمرين، لذا يُمكِنك أن تبدأ الآن إذا أردت، ولكن ما يزال هناك بعض الاقتراحات القليلة التي ننصحك بقراءتها:
</p>

<ul>
	<li>
		سنوفِّر لك مساعدةً أقلّ في هذا التمرين، ونترك لك حريّة أكبر في اتخاذ بعض القرارات المتعلقة بالتصميم. سيكون عليك تحديدًا أن تُفكِّر بالطريقة التي ستُقسِّم بها المشكلة إلى أجزاءَ صغيرةٍ يُمكِن اختبار كلٍّ منها على حدة. بعد ذلك، ستُجمِّع تلك الأجزاء إلى حلٍّ كاملٍ. إذا حاولت كتابة الحل بالكامل على خطوةٍ واحدةٍ بدون اختبار الأجزاء الأصغر، فقد تستغرِق وقتًا طويلًا جدًا لتنقيح الأخطاء.
	</li>
	<li>
		تُمثِّل الاستمرارية واحدةً من تحديات العمل مع البيانات المستمرة، لأن الهياكل المُخزَّنة في قواعد البيانات قد تتغير في كل مرةٍ تُشغِّل فيها البرنامج. فإذا تسبَّبت بخطأٍ في قاعدة البيانات، سيكون عليك إصلاحه أو البدء من جديد. ولكي نُبقِي الأمور تحت السيطرة، وفّرنا لك التوابع <code>deleteURLSets</code> و <code>deleteTermCounters</code> و <code>deleteAllKeys</code> التي تستطيع أن تَستخدِمها لتنظيف قاعدة البيانات والبدء من جديد. يُمكِنك أيضًا استخدام التابع <code>printIndex</code> لطباعة محتويات المُفهرِس.
	</li>
	<li>
		في كلّ مرةٍ تستدعي فيها أيًّا من توابع <code>Jedis</code>، فإنه يُرسِل رسالةً إلى الخادم الذي يُنفِّذ بدوره الأمر المطلوب، ثم يردّ برسالة. إذا نفَّذت الكثير من العمليات الصغيرة، فستحتاج إلى وقت طويل لمعالجتها، ولهذا، من الأفضل تجميع متتالية من العمليات ضمن معاملة واحدة من النوع <code>Transaction</code> لتحسين الأداء.
	</li>
</ul>

<p>
	على سبيل المثال، انظر إلى تلك النسخة البسيطة من التابع <code>deleteAllKeys</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1983_14" style=""><span class="pln">    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> deleteAllKeys</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> keys </span><span class="pun">=</span><span class="pln"> jedis</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="str">"*"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">String</span><span class="pln"> key</span><span class="pun">:</span><span class="pln"> keys</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            jedis</span><span class="pun">.</span><span class="pln">del</span><span class="pun">(</span><span class="pln">key</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	في كل مرةٍ تَستدعِي خلالها التابع <code>del</code>، يضطرّ العميل إلى إجراء اتصالٍ مع الخادم وانتظار الرد. إذا كان المُفهرِس يحتوي على بضع صفحاتٍ، فقد يَستغرِق تنفيذ ذلك التابع وقتًا طويلًا. بدلًا من ذلك، يُمكِنك أن تُسرِّع تلك العملية باستخدام كائنٍ من النوع <code>Transaction</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1983_18" style=""><span class="pln">    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> deleteAllKeys</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> keys </span><span class="pun">=</span><span class="pln"> jedis</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="str">"*"</span><span class="pun">);</span><span class="pln">
        </span><span class="typ">Transaction</span><span class="pln"> t </span><span class="pun">=</span><span class="pln"> jedis</span><span class="pun">.</span><span class="pln">multi</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">String</span><span class="pln"> key</span><span class="pun">:</span><span class="pln"> keys</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            t</span><span class="pun">.</span><span class="pln">del</span><span class="pun">(</span><span class="pln">key</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        t</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يعيد التابع <code>jedis.multi</code> كائنًا من النوع <code>Transaction</code>. يُوفِّر هذا الكائن جميع التوابع المتاحة في كائنات النوع <code>Jedis</code>. عندما تستدعي أيًّا من تلك التوابع بكائنٍ من النوع <code>Transaction</code>، فإن العميل لا يُنفِّذها تلقائيًا، ولا يتصل مع الخادم، وإنما يُخزِّن تلك العمليات إلى أن تَستدعِي التابع <code>exec</code>، وعندها، يُرسِل جميعَ العملياتِ المُخزَّنة إلى الخادم في نفس الوقت، وهو ما يكون أسرع عادةً.
</p>

<h2>
	تلميحات بسيطة بشأن التصميم
</h2>

<p>
	الآن وقد أصبح لديك جميع المعلومات المطلوبة، يمكنك البدء في حل التمرين. إذا لم يكن لديك فكرةٌ عن طريقة البدء، يُمكِنك العودة لقراءة المزيد من التلميحات البسيطة.
</p>

<p>
	لا تتابع القراءة قبل أن تُشغِّل شيفرة الاختبار، وتُجرِّب بعض أوامر Redis البسيطة، وتكتب بعض التوابع الموجودة في الملف <code>JedisIndex.java</code>.
</p>

<p>
	إذا لم تتمكّن من متابعة الحل فعلًا، إليك بعض التوابع التي قد ترغب في العمل عليها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1983_21" style=""><span class="pln">    </span><span class="com">/**
     * أضف محدد موارد موحدًا إلى المجموعة الخاصة بكلمة
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> add</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> term</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TermCounter</span><span class="pln"> tc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

    </span><span class="com">/**
     * ابحث عن كلمة وأعد مجموعة مُحدِّدات الموارد الموحدة التي تحتوي عليها
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Set</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> getURLs</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> term</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

    </span><span class="com">/**
     * أعد عدد مرات ظهور كلمة معينة بمحدد موارد موحد
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Integer</span><span class="pln"> getCount</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> term</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

    </span><span class="com">/**
     * ‫أضف محتويات كائن من النوع `TermCounter` إلى قاعدة بيانات Redis
     */</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> pushTermCounterToRedis</span><span class="pun">(</span><span class="typ">TermCounter</span><span class="pln"> tc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	تلك هي التوابع التي استخدمناها في الحل، ولكنها ليست بالتأكيد الطريقة الوحيدة لتقسيم المشكلة. لذلك، استعن بتلك الاقتراحات فقط إذا وجدتها مفيدة، وتجاهلها تمامًا إذا لم تجدها كذلك.
</p>

<p>
	اكتب بعض الاختبارات لكل تابعٍ منها أولًا، فبمعرفة الطريقة التي ينبغي بها أن تختبر تابعًا معينًا، عادةً ما تتكوَّن لديك فكرةٌ عن طريقة كتابته.
</p>

<p>
	ترجمة -بتصرّف- للفصل <a href="https://greenteapress.com/thinkdast/html/thinkdast015.html" rel="external nofollow">Chapter 14: Persistence</a> من كتاب <a href="https://greenteapress.com/thinkdast/html/index.html" rel="external nofollow">Think Data Structures: Algorithms and Information Retrieval in Java</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/devops/servers/databases/redis/%D9%81%D9%87%D8%B1%D8%B3%D8%A9-%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%88%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%B2%D9%85%D9%86-%D8%AA%D8%B4%D8%BA%D9%8A%D9%84%D9%87%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-r580/" rel="">فهرسة الصفحات وتحليل زمن تشغيلها باستخدام قاعدة بيانات Redis</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/java/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D9%86%D8%A9-balanced-trees-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-r1454/" rel="">استخدام أشجار البحث الثنائية والأشجار المتزنة balanced trees لتنفيذ الخرائط</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%84%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%AA%D8%AE%D8%B2%D9%8A%D9%86%D9%87%D8%A7-%D9%88%D8%A7%D8%AE%D8%AA%D9%84%D8%A7%D9%81%D9%87-%D8%B9%D9%86-%D9%86%D8%B8%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r509/" rel="">تحليل نظام الملفات لإدارة البيانات وتخزينها واختلافه عن نظام قاعدة البيانات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%d8%b4%d8%b1%d8%ad-%d8%a7%d9%84%d9%81%d8%b1%d9%88%d9%82%d8%a7%d8%aa-%d8%a8%d9%8a%d9%86-%d9%82%d9%88%d8%a7%d8%b9%d8%af-%d8%a8%d9%8a%d8%a7%d9%86%d8%a7%d8%aa-sql-%d9%88%d9%86%d8%b8%d9%8a%d8%b1%d8%a7%d8%aa%d9%87%d8%a7-nosql-r71/" rel="">شرح الفروقات بين قواعد بيانات SQL ونظيراتها NoSQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D9%8A%D8%B1%D8%A7%D8%AF-%D9%88%D8%AA%D8%B5%D8%AF%D9%8A%D8%B1-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-%D8%A3%D9%88-mariadb-r361/" rel="">كيفية استيراد وتصدير قواعد بيانات MySQL أو MariaDB</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">579</guid><pubDate>Mon, 28 Feb 2022 16:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x625;&#x64A;&#x62C;&#x627;&#x62F; &#x633;&#x62C;&#x644;&#x627;&#x62A; Redis &#x639;&#x644;&#x649; &#x646;&#x638;&#x627;&#x645; &#x623;&#x648;&#x628;&#x646;&#x62A;&#x648;</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%8A%D8%AC%D8%A7%D8%AF-%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-redis-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r258/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_05/redis-logs-ubuntu.png.3a36c580740f44c497349228c1f4a9ef.png" /></p>

<h1 dir="rtl">
	<a name="_GoBack" rel="external"></a>
</h1>

<p dir="rtl">
	إنّ السجلّات logs أمر ضروري لحل مشاكل تثبيت Redis، وقد تتساءل عن مكان تواجد هذه السجلّات أو عن المكان الذي يقوم Redis بتخزين السجلّات فيه على نظام Ubuntu.
</p>

<p dir="rtl" style="text-align: center;">
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="16642" data-unique="n4dguwqdf" src="https://academy.hsoub.com/uploads/monthly_2016_05/redis-logs-ubuntu.png.9f0b967f1a1482a65c8cfc6e7ac3588e.png" alt="redis-logs-ubuntu.png"></p>

<p dir="rtl">
	عند تثبيت Redis بالطريقة الافتراضية باستخدام <span style="font-family:courier new,courier,monospace;">apt-get</span> على Ubuntu 14.04 فسيقوم Redis بإنشاء سجلّاته في المسار <span style="font-family:courier new,courier,monospace;">var/log/redis/redis-server.log/</span>.
</p>

<p dir="rtl">
	لاستعراض آخر 10 أسطر في سجلّات redis، نقوم بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_7">
<span class="pln">$ sudo tail /var/log/redis/redis-server.log</span></pre>

<p dir="rtl">
	لاحظ في الأمر السابق استخدامنا لـ <span style="font-family:courier new,courier,monospace;">sudo</span> نظرًا لأننا نحتاج لصلاحيات أعلى لنتمكن من قراءة سجلّات redis.
</p>

<p dir="rtl">
	أما في حال تثبيت Redis من ملفّات الشيفرة المصدرية على Ubuntu 14.04، فسيقوم بتخزين السجلّات في المسار <span style="font-family:courier new,courier,monospace;">var/log/redis_6379.log/</span>.
</p>

<p dir="rtl">
	ولاستعراض آخر 10 أسطر نستخدم الأمر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_9">
<span class="pln">$ sudo tail /var/log/redis_6379.log</span></pre>

<h2 dir="rtl">
	التحقق من السجلات المؤرشفة
</h2>

<p dir="rtl">
	يقوم Redis بأرشفة ملفّات السجلّات القديمة، ولاستعراض قائمة الملفّات المتوفرة ننفذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_12">
<span class="pln">$ ls /var/log/redis</span></pre>

<p dir="rtl">
	فسيظهر الخرج بما يشبه التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_14">
<span class="pln">redis-server.log redis-server.log.1.gz
</span></pre>

<p dir="rtl">
	ولاستعراض آخر 10 أسطر من سجل مؤرشف نحتاج أولًا أن نقوم بفك ضغطه، ونستخدم <span style="font-family:courier new,courier,monospace;">gunzip</span> لفك ضغط الملفات من نوع gz، كما في الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_16">
<span class="pln">$ sudo gunzip /var/log/redis/redis-server.log.1.gz</span></pre>

<p dir="rtl">
	<strong>انتبه</strong>: يقوم <span style="font-family:courier new,courier,monospace;">gunzip</span> بفك ضغط ملفات gz وحذف الملف الأساسي المضغوط، لذا إن كنت تريد المحافظة على الأرشيف المضغوط توفيرًا للمساحة واستعراض محتويات الملف المضغوط بشكل مؤقت فقم بنسخه إلى المسار المؤقت <span style="font-family:courier new,courier,monospace;">tmp/</span> وقم بفك ضغطه هناك.
</p>

<p dir="rtl">
	ومن ثم ننفذ الأمر tail مع الملف الذي نحصل عليه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_18">
<span class="pln">$ sudo tail /var/log/redis/redis-server.log.1</span></pre>

<h2 dir="rtl">
	البحث عن ملفات السجلات باستخدام أمر find
</h2>

<p dir="rtl">
	إن لم تكن ملفّات السجلّات في المسارات المذكورة السابقة، فمن الممكن تنفيذ عملية بحث واسعة باستخدام الأمر <span style="font-family:courier new,courier,monospace;">find</span> في المسار <span style="font-family:courier new,courier,monospace;">var/logs/ </span>على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_20">
<span class="pln">$ find /var/log/* -name *redis*</span></pre>

<p dir="rtl">
	أو البحث في كامل النظام.
</p>

<p dir="rtl">
	قد تأخذ عملية البحث بهذا الشكل بعض الوقت إن كنت تملك الكثير من الملفّات، وقد تظهر لك بعض التحذيرات حول عدم كفاية الصلاحيات للبحث في بعض المسارات، وهذا أمر طبيعي، بالرغم من أنّنا نتجنب أسوأ مسارين للبحث فيهما وهما <span style="font-family:courier new,courier,monospace;">proc/ </span>و <span style="font-family:courier new,courier,monospace;">sys/ </span>باستعمال المعامل<span style="font-family:courier new,courier,monospace;"> prune-</span>. سيظهر تنفيذ الأمر التالي جميع الملفّات التي تحوي على كلمة redis في اسمه، بما في ذلك ملفات التثبيت:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_22">
<span class="pln">$ find / -path /sys -prune -o -path /proc -prune -o -name *redis*</span></pre>

<h2 dir="rtl">
	تحديد مسار تخزين السجلات في ملف إعدادات Redis
</h2>

<p dir="rtl">
	نستطيع تغيير مسار تخزين ملفات السجلّات وذلك بتغيير المسار في ملف الإعدادات <span style="font-family:courier new,courier,monospace;">redis.conf</span>، ويتواجد الملف غالبًا في المسار <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span>
</p>

<p dir="rtl">
	نقوم بفتح الملف بمحرر نصوص مثل <span style="font-family:courier new,courier,monospace;">nano</span> أو أي محرر آخر تفضّله:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_24">
<span class="pln">$ sudo nano /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	ونبحث عن السطر الذي يبدأ بـ <span style="font-family:courier new,courier,monospace;">logfile</span> ونقوم بتغيير المسار الذي يظهر فيه إلى المسار الجديد، كما بالإمكان تغيير اسم ملف السجل الأساسي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9091_26">
<span class="pln">logfile /var/log/redis/redis-server.log</span></pre>

<h2 dir="rtl">
	التحقق من سجلات systemd باستخدام journalctl في Ubuntu 15.04 والإصدارات الأحدث منه
</h2>

<p dir="rtl">
	قد نحتاج أن نتحقق من سجلّات Redis التي يقوم بالتقاطها سجل النظام <span style="font-family:courier new,courier,monospace;">systemd</span> (يستخدم نظام Ubuntu 15.04 والأحدث منه سجل النظام <span style="font-family:courier new,courier,monospace;">systemd</span>، بينما يستخدم Ubuntu 14.04 سجّلات <span style="font-family:courier new,courier,monospace;">Upstart</span> بشكل افتراضي).
</p>

<h2 dir="rtl">
	الخلاصة
</h2>

<p dir="rtl">
	لتتعلّم المزيد حول إعدادات Redis، يرجى قراءة هذا المقال حول <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81-%D8%AA%D8%B6%D8%A8%D8%B7-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-redis-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r241/" rel="">إعداد عنقود </a><a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81-%D8%AA%D8%B6%D8%A8%D8%B7-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-redis-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r241/" rel="">Redis cluster</a>.
</p>

<p dir="rtl">
	ترجمة -وبتصرّف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-find-redis-logs-on-ubuntu" rel="external nofollow">How To Find Redis Logs on Ubuntu</a> لصاحبته Sharon Campbell.
</p>
]]></description><guid isPermaLink="false">258</guid><pubDate>Sat, 21 May 2016 19:33:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x62D;&#x645;&#x627;&#x64A;&#x629; &#x62E;&#x627;&#x62F;&#x648;&#x645; Redis &#x639;&#x644;&#x649; &#x623;&#x648;&#x628;&#x646;&#x62A;&#x648; 14.04</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-redis-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r245/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_03/secure-redis-server-ubuntu.png.934bade62df474b3a33afbe01703909c.png" /></p>

<p dir="rtl">
	سنتطرق في هذا الدّرس إلى كيفية تطبيق حماية أساسيّة لخادوم Redis.
</p>

<p dir="rtl" style="text-align: center;">
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="14755" data-unique="958n73ezq" src="https://academy.hsoub.com/uploads/monthly_2016_03/secure-redis-server-ubuntu.png.8c0952582c94334b1854ab0486fc367f.png" alt="secure-redis-server-ubuntu.png"></p>

<p dir="rtl">
	على الرغم من هذا، عليك تذكر بأن Redis قد صُمّم أساسا للاستخدام من طرف مستخدمين موثوقين على بيئة موثوقة، بدون أي مميزات أمنيّة خاصة به. لفهم هذه النّقطة أكثر إليك اقتباسا <a href="http://redis.io/topics/security" rel="external nofollow">من الموقع الرسمي لـ</a><a href="http://redis.io/topics/security" rel="external nofollow">redis</a>:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			"Redis مُصمّم للوصول إليه من عملاء موثوقين داخل بيئة موثوقة. ما يعني أن توفير الوصول إلى خادوم Redis الخاص بك مباشرة على الإنترنت، أو من بيئة يُمكن لمستخدمين غير موثوقين أن يصلوا مباشرة إلى منفذ TCP الخاص بـ Redis أو إلى مقبس UNIX ليست فكرة جيّدة."
		</p>
	</div>
</blockquote>

<p dir="rtl">
	بشكل عام، Redis ليس مُحسّنا لحماية قصوى بل لأداء عالي وبساطة فائقة.
</p>

<p dir="rtl">
	الأداء والبساطة بلا حماية بمثابة وصفة لكارثة. حتى مميّزات الحماية القليلة لدى Redis ليست ممتازة، وتشمل كلمة مرور بسيطة غير مُشفّرة، إعادة تسميّة الأوامر وتعطيلها. وتفتقر إلى نظام حقيقي للتحكم بالولوج.
</p>

<p dir="rtl">
	على الرغم من هذا، فإن ضبط هذه الخصائص الأمنيّة يبقى خطوة كبيرة إلى الأمام وأفضل من ترك قاعدة بياناتك بدون حماية.
</p>

<p dir="rtl">
	ستقرأ في هذا الدّرس كيفيّة ضبط الخصائص الأمنيّة القليلة التي يمتلكها Redis، وبعض الخصائص الأمنية الأخرى للنظام ما سيزيد من نسبة الحماية لخادوم Redis مُستقل على Ubuntu 14.04.
</p>

<p dir="rtl">
	ننوّه إلى أن هذا الدرس لا يتحدث عن حالات وجود كل من خادوم Redis وتطبيقات العملاء على استضافات مختلفة أو مراكز بيانات مختلفة. أو الحالات التي يجب على الاتصال بـ Redis أن يقطع شبكات غير آمنة أو غير موثوقة تتطلب نوعا مختلفا من الإعداد، مثل ضبط <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></abbr></abbr> proxy أو <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr></abbr> بين خواديم Redis ، إضافة إلى الخطوات المذكورة هنا.
</p>

<h2 dir="rtl">
	المتطلبات
</h2>

<p dir="rtl">
	ستحتاج في هذا الدّرس:
</p>

<ul dir="rtl">
<li>
		خادوم Ubuntu مع مستخدم sudo مُضَاف، انظر <a href="http://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7%D8%A6%D9%8A-%D9%84%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r4/" rel="">درس الإعداد الابتدائي لخادوم أوبنتو </a><a href="http://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7%D8%A6%D9%8A-%D9%84%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r4/" rel="">14.04 </a>
	</li>
	<li>
		جدار ناري iptables معدّ باستخدام الدرس <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D9%81%D8%B0-%D9%86%D9%85%D9%88%D8%B0%D8%AC%D8%A7-%D9%84%D9%84%D8%AC%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D9%86%D8%A7%D8%B1%D9%8A-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-iptables-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r168/" rel="">كيف تنفذ نموذجا للجدار الناري باستعمال Iptables</a> إلى غاية خطوة "تحديث nameservers" (إذا لم تقم بإعداد جزء الخادوم nameserver فالـAPT لن يعمل).
	</li>
	<li>
		Redis مُثبت وقيد التنفيذ باستعمال الخطوات في الموضحة في الدرس <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r46/" rel="">كيفية تنصيب واستخدام Redis على أوبنتو</a>.
	</li>
</ul>
<h2 dir="rtl">
	الخطوة الأولى، التأكد من أن Redis قيد التشغيل
</h2>

<p dir="rtl">
	أولاً ادخل إلى الخادوم باستعمال <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></abbr>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_10">
<span class="pln"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></abbr> username@server-ip-address</span></pre>

<ul dir="rtl">
<li>
		<strong>username</strong>: اسم المستخدم
	</li>
	<li>
		<strong>server-ip-address</strong>: عنوان IP الخاص بالخادوم
	</li>
</ul>
<p dir="rtl">
	للتأكد من أنّ Redis قيد التنفيذ، استعمل سطر الأوامر الخاص ب Redis .يُستعمل الأمر <span style="font-family:courier new,courier,monospace;">redis-cli</span> للوصول إلى سطر أوامر Redis.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_12">
<span class="pln">redis-cli</span></pre>

<p>
	إذا سبق وأن أعددت كلمة مرور لـ Redis، فيجب عليك الاستيثاق بالأمر <span style="font-family:courier new,courier,monospace;">auth</span> بعد الاتّصال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_14">
<span class="pln">auth كلمة_المرور</span></pre>

<p dir="rtl">
	المخرج سيكون كلمة OK
</p>

<p dir="rtl">
	اختبر خادوم قاعدة البيانات:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_16">
<span class="pln">ping</span></pre>

<p>
	الرّد:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_18">
<span class="pln">PONG</span></pre>

<p dir="rtl">
	اُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_21">
<span class="pln">quit</span></pre>

<h2 dir="rtl">
	الخطوة الثانية، حماية الخادوم بـاستخدام Iptables
</h2>

<p dir="rtl">
	إذا اتّبعت المُتطلّبات من أجل ضبط Iptables، فلك كامل الحريّة في تخطّي هذه الخطوة، أو يُمكنك القيام بذلك الآن.
</p>

<p dir="rtl">
	Redis مجرّد تطبيق يُنفّذ على خادومك، ولأنه لا يملك أي خصائص أمنيّة خاصة، فإنّ أول خطوة لحمايته حقيقة تكمن في حماية الخادوم الذي يُشغل منه.
</p>

<p dir="rtl">
	في حالة خادوم موجّه للعامة كخادومك Ubuntu 14.04، فإن ضبط جدار ناريّ كما هو مبيّن في <a href="https://www.digitalocean.com/community/tutorials/how-to-implement-a-basic-firewall-template-with-iptables-on-ubuntu-14-04" rel="external nofollow">درس </a><a href="https://www.digitalocean.com/community/tutorials/how-to-implement-a-basic-firewall-template-with-iptables-on-ubuntu-14-04" rel="external nofollow">Iptables</a> يُعتبر الخطوة الأولى لذلك. اتّبع ذلك الرّابط واضبط جدارا ناريّا الآن.
</p>

<p dir="rtl">
	إذا طبّقت أحكام الجدار النّاري باستعمال ذلك الدّرس، فلن تحتاج لإضافة قاعدة أخرى لـ Redis،
</p>

<p dir="rtl">
	افتراضيّاً، إن أي مرور traffic يُمنَع إلّا عند السّماح له صراحةً. وبما أن خادوم Redis افتراضيّا يستمع على واجهة الاسترجاع (loopback (127.0.0.1 أو localhost، فلا يجب القلق حول المرور عبر المنفذ الافتراضي.
</p>

<p dir="rtl">
	إذا كنت تحتاج للسّماح لعنوان IP للوصول خصّيصا لـ Redis، يُمكنك التحقق من العنوان الذي يستمع منه Redis، وعلى أي منفذ باستخدام أمر grep لمُخرجات الأمر netstat. العمود الرّابع 127.0.0.1:6379 يطلعنا على عنوان IP والمنفذ المرتبطين بـ Redis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_109">
<span class="pln">sudo netstat -plunt | grep -i redis</span></pre>

<p dir="rtl">
	المخرجات:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_111">
<span class="pln">tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 8562/redis-server 1</span></pre>

<p dir="rtl">
	تأكّد من أن هذا العنوان مسموح له في الجدار النّاري. للمزيد من المعلومات عن كيفيّة إضافة الأحكام، اطّلع رجاء على <a href="http://academy.hsoub.com/devops/linux/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-iptables-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D9%88%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AC%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D9%86%D8%A7%D8%B1%D9%8A-r119/" rel="">درس أساسيّات </a><a href="http://academy.hsoub.com/devops/linux/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-iptables-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D9%88%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AC%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D9%86%D8%A7%D8%B1%D9%8A-r119/" rel="">Iptables.</a>
</p>

<h2 dir="rtl">
	الخطوة الثالثة، الربط إلى المضيف المحلي localhost
</h2>

<p dir="rtl">
	افتراضيًّا، يُمكن الوصول إلى خادوم Redis من المضيف المحليّ، على أي حال، إذا اتّبعت درس ضبط Redis على خادوم متبوع/سيّد فقد حدّثت ملفّ الإعدادات للسّماح للاتّصالات من أي مكان. وهذا ليس آمنا كالربط إلى المُضيف المحليّ.
</p>

<p dir="rtl">
	افتح ملفّ إعدادات Redis للتّعديل:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_113">
<span class="pln">sudo nano /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	ابحث عن هذا السّطر وتأكّد من أنه ليس على شكل تعليق (احذف الرّمز # إذا كان موجودا):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_115">
<span class="pln">bind 127.0.0.1</span></pre>

<p dir="rtl">
	سنستمرّ في استعمال هذا الملفّ، لذلك أبقه مفتوحا الآن.
</p>

<h2 dir="rtl">
	الخطوة الرابعة، ضبط كلمة مرور لـ Redis
</h2>

<p dir="rtl">
	إذا ثبتت Redis باتّباع درس <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81-%D8%AA%D8%B6%D8%A8%D8%B7-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-redis-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r241/" rel="">كيف تضبط خواديم Redis متعددة على أوبنتو 14.04</a> فيجب عليك أن تكون قد ضبطت كلمة مرور مُسبقا، يُمكنك أن تضع كلمة مرور أكثر أمانا باتّباع هذا القسم الآن، وهذه الخطوات تعرض كيفية وضع كلمة مرور لخادوم قاعدة البيانات.
</p>

<p dir="rtl">
	ضبط كلمة مرور لـ Redis يفعّل إحدى ميزتي الحماية المُضمّنة في Redis، وهو الأمر <span style="font-family:courier new,courier,monospace;">auth</span>، الذي يطلُب من العملاء الاستيثاق للوصول إلى قاعدة البيانات. تُضبَطُ كلمة المرور مُباشرة في ملف الإعدادات<span style="font-family:courier new,courier,monospace;"> etc/redis/redis.conf/</span> الذي يجب أن تكون قد فتحته منذ الخطوة الماضيّة.
</p>

<p dir="rtl">
	اذهب عن قسم <span style="font-family:courier new,courier,monospace;">SECURITY</span> وابحث عن سطر مُعلّق بالرمز # كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_74">
<span class="pln"># requirepass foobared</span></pre>

<p dir="rtl">
	أزل التّعليق عن السّطر بحذف الرمز #، وغيّر <span style="font-family:courier new,courier,monospace;">foobared</span> إلى كلمة مرور قويّة وطويلة.
</p>

<p dir="rtl">
	عوضاً عن الإتيان بكلمة مرور من عندك، يمكنك استعمال أداة مثل <span style="font-family:courier new,courier,monospace;">apg</span> و <span style="font-family:courier new,courier,monospace;">pwgen</span> لتوليد واحدة. إذا لم تكن ترغب بتثبيت أداة فقط لتوليد كلمة مرور، يُمكنك استخدام السطر التّالي. لتوليد كلمة مرور مُختلفة عن هذا المثال غير القيمة بداخل علامتي التنصيص:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_76">
<span class="pln">echo "hsoub-academy" | sha256sum</span></pre>

<p dir="rtl">
	المُخرج سيبدو كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_78">
<span class="pln">e118c2b8fa805fbd0a6af458ab7df9fea14881b87c2e793b4a2192cda30bf8d5</span></pre>

<p dir="rtl">
	لكنّ كلمة المرور المولّدة لن تكون منطوقة، وهي قويّة وطويلة وهو تماما المطلوب لحماية Redis. بعد نسخ ولصق مُخرج هذا الأمر كقيمة جديدة لـ <span style="font-family:courier new,courier,monospace;">requirepass</span>، يجب أن تكون كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_80">
<span class="pln">requirepass 960c3dac4fa81b4204779fd16ad7c954f95942876b9c4fb1a255667a9dbe389d</span></pre>

<p dir="rtl">
	إذا كنت تُفضّلُ كلمة مرور أقصر، استخدم مُخرج الأمر التالي عوضا عنها. غيّر القيمة بين علامتي التنصيص لكي لا تولّد نفس كلمة المرور:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_82">
<span class="pln">echo "hsoub-academy" | sha1sum</span></pre>

<p dir="rtl">
	المخرج سيكون أقصر هذه المرّة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_84">
<span class="pln">773a12cfe8616ff740258f8843c567f32227ac0f</span></pre>

<p dir="rtl">
	بعد ضبط كلمة المرور، احفظ الملفّ وأعد تشغيل Redis :
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_86">
<span class="pln">sudo service redis-server restart</span></pre>

<p dir="rtl">
	لاختبار عمل كلمة المرور، حاول الوصول إلى سطر أوامر Redis :
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_88">
<span class="pln">redis-cli</span></pre>

<p dir="rtl">
	السطر التالي يعرض سلسلة من الأوامر المُستعملة لاختبار ما إذا كانت كلمة المرور تعمل أو لا، الأمر الأول يُحاول إعطاء قيمة لمفتاح قبل القيام بالاستيثاق:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_90">
<span class="pln">set key1 10</span></pre>

<p dir="rtl">
	الأمر لن يعمل، لذلك فإنك سترى خطأ في المخرجات:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_92">
<span class="pln">(error) NOAUTH Authentication required.</span></pre>

<p dir="rtl">
	الأمر الثاني يقوم بالاستيثاق بكلمة المرور التي وضعناها في ملفّ إعدادات Redis.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_94">
<span class="pln">auth your_redis_password</span></pre>

<p dir="rtl">
	سيُوافق Redis في الحال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_97">
<span class="pln">OK</span></pre>

<p dir="rtl">
	بعد هذا، تنجح إعادة تنفيذ الأمر السّابق.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_99">
<span class="pln">set key1 10</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_101">
<span class="pln">OK</span></pre>

<p dir="rtl">
	الأمر<span style="font-family:courier new,courier,monospace;"> get key1</span> يطلب من Redis قيمة المفتاح الجديد:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_103">
<span class="pln">get key1</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_105">
<span class="pln">“10”</span></pre>

<p dir="rtl">
	الأمر الأخير يقوم بإنهاء سطر الأوامر<span style="font-family:courier new,courier,monospace;"> redis-cli</span>. ويمكنك أيضاً استخدام <span style="font-family:courier new,courier,monospace;">exit</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_107">
<span class="pln">quit</span></pre>

<p dir="rtl">
	تالياّ، سننظر إلى إعادة تسميّة أوامر Redis.
</p>

<h2 dir="rtl">
	الخطوة الخامسة، إعادة تسمية الأوامر الخطرة
</h2>

<p dir="rtl">
	الخاصيّة الأمنيّة الأخرى المبنيّة داخل Redis تُخوّل لك إعادة تسميّة أو تعطيل أوامر معيّنة والتّي تُعتبر أوامر خطيرة.
</p>

<p dir="rtl">
	عند تشغيل مستخدمين غير مُخولين لأوامر معينّة فقد تُستخدم هذه الأوامر لإعادة ضبط، أو تدمير أو حذف بياناتك. ومثل كلمة مرور الاستيثاق،فإن إعادة تسمية الأوامر أو تعطيلها تتم في نفس قسم <span style="font-family:courier new,courier,monospace;">SECURITY</span> من الملفّ <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span>.
</p>

<p dir="rtl">
	بعض الأوامر المعروف أنها خطيرة هي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_44">
<span class="pln">FLUSHDB, FLUSHALL, KEYS, PEXPIRE, DEL, CONFIG, SHUTDOWN, BGREWRITEAOF, BGSAVE, SAVE, SPOP, SREM, RENAME و DEBUG</span></pre>

<p dir="rtl">
	وهذه القائمة ليست شاملة، لكنّ إعادة تسميّتها أو تعطيلها كلّها يُعتبر نقطة بداية جيّدة.
</p>

<p dir="rtl">
	تعطيل أو إعادة تسميّة أمر يعتمد على مدى استعمالك للأمر، فمثلاً إذا كنت تعلم أنّك لن تستعمل أبدا أمرا يُمكن أن يُستَغلّ سلبيّاً، فمن المستحسن أن تُعطّله، عدا ذلك فمن الأفضل إعادة التّسميّة.
</p>

<p dir="rtl">
	لتشغيل أو تعطيل أمر ما، افتح ملفّ الإعدادات للتحرير مرة أخرى:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_46">
<span class="pln">sudo nano /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	هذه بعض الأمثلة فقط. ويجب عليك أن تعيد تسميّة أو تعطّل الأوامر بما يُناسبك. يُمكنك التحقق من الأوامر بنفسك وتحديد إمكانيّة سوء استعملها على <a href="http://redis.io/commands" rel="external nofollow">redis.io/commands</a>.
</p>

<p dir="rtl">
	لتعطيل أو إيقاف أمر ما، فقط أعد تسميّتها إلى قيمة فارغة كما هو مبيّن أسفله:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_48">
<span class="pln"># It is also possible to completely kill a command by renaming it into

# an empty string:

#

rename-command FLUSHDB ""

rename-command FLUSHALL ""

rename-command DEBUG ""</span></pre>

<p dir="rtl">
	وعند إعادة تسميّة أمر ما، عيّن اسما آخر كقيمة، كما في الأمثلة أسفله. الأوامر المعاد تسميّتها يجب أن تكون صعبة التخمين، وفي نفس الوقت سهلة التذكر بالنسبة لك.ولا تجعلها صعبة عليك.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_50">
<span class="pln">rename-command CONFIG ""

rename-command SHUTDOWN SHUTDOWN_MENOT

rename-command CONFIG ASC12_CONFIG</span></pre>

<p dir="rtl">
	بعد إعادة التّسميّة، طبّق التعديلات بإعادة تشغيل Redis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_52">
<span class="pln">sudo service redis-server restart</span></pre>

<p dir="rtl">
	لاختبار الأمر الجديد، ادخل إلى سطر أوامر Redis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_54">
<span class="pln">redis-cli</span></pre>

<p dir="rtl">
	بعد ذلك، على افتراض بأنّك قمت بإعادة تسميّة الأمر <span style="font-family:courier new,courier,monospace;">CONFIG</span> إلى <span style="font-family:courier new,courier,monospace;">ASC12_CONFIG</span>، المُخرج التّالي يوضح كيفيّة التأكد من أنّ الأمر الجديد يعمل.
</p>

<p dir="rtl">
	بعد الاستيثاق:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_56">
<span class="pln">auth your_redis_password</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_58">
<span class="pln">OK</span></pre>

<p dir="rtl">
	يجب أن تكون مُحاولة استخدام الأمر <span style="font-family:courier new,courier,monospace;">config</span> باسمه الأصلي فاشلة، لأنّنا بالطبع قمنا بإعادة تسميّتها:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_60">
<span class="pln">config get requirepass</span></pre>

<p dir="rtl">
	المُخرج":
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_62">
<span class="pln">(error) ERR unknown command 'config'</span></pre>

<p dir="rtl">
	مناداة الأمر باسمه الجديد سيعمل (وهو غير حسّاس لحالة الأحرف):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_64">
<span class="pln">asc12_config get requirepass</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_66">
<span class="pln">1) "requirepass"
2) "your_redis_password"</span></pre>

<p dir="rtl">
	أخيرا، يمكنك الخروج من<span style="font-family:courier new,courier,monospace;"> redis-cli</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_68">
<span class="pln">exit</span></pre>

<p dir="rtl">
	<strong>ملاحظة</strong>: إذا كنت تستخدم مسبقا سطر الأوامر Redis ثم أعدت تشغيل Redis، سيتوجب عليك إعادة الاستيثاق. إن لم تقوم بذلك، ستحصل على هذا الخطأ عند كتابة أمر ما:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_70">
<span class="pln">NOAUTH Authentication required.</span></pre>

<p dir="rtl">
	على الرغم من إعادة تسميّتك للأوامر، فهناك جملة تحذيريّة في آخر قسم <span style="font-family:courier new,courier,monospace;">SECURITY</span> على ملفّ <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span> تقول:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			يرجى ملاحظة أن تغيير أسماء الأوامر الداخلة في ملفّ AOF أو المُحالة إلى التّوابع (Slaves) قد تُسبّب المشاكل.
		</p>
	</div>
</blockquote>

<p dir="rtl">
	هذا يعني أنّه إذا لم يكن الأمر المعاد تسميّته داخل ملفّ AOF، أو إذا كان موجودا لكنّ ملفّ AOF لم يُحَلْ إلى التّوابع، فلا يجب أن يكون هناك أي مُشكلة.
</p>

<p dir="rtl">
	لذلك تذكّر عند محاولة تغيير أسماء الأوامر، أفضل وقت لإعادة تسميّة أمر هو عند عدم استخدام AOF، أو مباشرة بعد التثبيت أي قبل نشر التطبيق المُستعمِل لـ Redis.
</p>

<p dir="rtl">
	عندما تستخدم AOF وعند التّعامل مع نظام تابع-متبوع، ضع في بالك هذه الإجابة من صفحة المسائل في المشروع على Github. الاقتباس التّالي هو جواب على سؤال الكاتب:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			تُسجّل الأوامر إلى الـ AOF وتنسخ إلى التّابع بطريقة الإرسال ذاتها، لذلك إذا حاولت رد AOF على نموذج لا يمتلك نفس أسماء الأوامر، فقد تواجه تناقضات لأن الأمر لا يُمكن تنفيذه (الأمر نفسه يقع بالنسبة للتّوابع).
		</p>
	</div>
</blockquote>

<p dir="rtl">
	لذلك فإن أفضل طريقة للتعامل مع إعادة التسمية في حالات كهذه هي أن تتأكّد من أنّ إعادة تسميّة الأوامر مطبّقة في جميع الخوادم في أنظمة تابع-متبوع.
</p>

<h2 dir="rtl">
	الخطوة السادسة، ضبط مجلد البيانات وصلاحيات المستخدم للملفات
</h2>

<p dir="rtl">
	في هذه الخطوة، سوف نعرض بعض تعديلات المالك والصلاحيّات التّي يُمكنك القيّام لها لزيّادة حماية Redis. يتمثّل هذا في التأكد من أنّ المُستخدم الذي يحتاج إلى الوصول إلى Redis فقط يملك صلاحيّات قراءة البيانات. هذا المُستخدم افتراضيّا هو المُستخدم redis.
</p>

<p dir="rtl">
	يُمكنك التحقق من هذا باستخدام أداة <span style="font-family:courier new,courier,monospace;">grep</span> مع الأمر <span style="font-family:courier new,courier,monospace;">ls</span> لرؤية معلومات عن المُجلّد الأب لمجلّد بيانات Redis. الأمر والمُخرجات أسفله.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_42">
<span class="pln">ls -l /var/lib | grep redis</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_40">
<span class="pln">drwxr-xr-x 2 redis   redis   4096 Aug  6 09:32 redis</span></pre>

<p dir="rtl">
	يُمكنك ملاحظة بأن مجلّد بيانات Redis مملوك من طرف المُستخدم Redis، مع وصول ثانوي للمجموعة redis. وهذا أمر جيّد.
</p>

<p dir="rtl">
	الأمر السيئ هو صلاحيّات الوصول إلى المُجلّد، أي 755. للتأكد من أن المُستخدم redis هو فقط من يمتلك حقّ الوصول إلى المجلد ومُحتوياته، غيّر الصلاحية إلى 700:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_38">
<span class="pln">sudo chmod 700 /var/lib/redis</span></pre>

<p dir="rtl">
	الصلاحيّة الأخرى التّي عليك تغييرها هي الخاصّة بملفّ إعدادات Redis. يمتلك افتراضيّا صلاحيّة 644 ومملوك للمُستخدم الجذر root، مع مالك ثانوي يتمثّل في مجموعة الجذر root group:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_36">
<span class="pln">ls -l /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_34">
<span class="pln">-rw-r--r-- 1 root root 30176 Jan 14 2014 /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	هذه الصّلاحيّة (644) تعني مقروء من الكل، والتي تعتبر فكرة سيّئة لأن ذلك الملف يحتوي على كلمة المرور غير المُشفرة التّي وضعناها في الخطوة الرّابعة.
</p>

<p dir="rtl">
	نحتاج إلى تغيير المالك والصّلاحيّات. يجبُ أن تكون مملوكة للمُستخدم redis مع مجموعة الجذر كمالك ثانوي، لفعل ذلك، شغّل الأمر التّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_32">
<span class="pln">sudo chown redis:root /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	بعد ذلك غيّر المالك لكي يتمكّن مالك الملفّ فقط من قراءته أو/و الكتابة عليه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_29">
<span class="pln">sudo chmod 600 /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	يُمكنك التحقّق من المالك الجديد والصّلاحيّات باستخدام الأمر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_27">
<span class="pln">ls -l /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	المُخرج:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_25">
<span class="pln">total 40

-rw------- 1 redis root 29716 Sep 22 18:32 /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	في الأخير، أعد تشغيل Redis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8021_23">
<span class="pln">sudo service redis-server restart</span></pre>

<h2 dir="rtl">
	خاتمة
</h2>

<p dir="rtl">
	تذكّر بأنه عند دخول أي شخص إلى خادومك، فمن السّهل التحايل على خصائص Redis الأمنيّة التي قمنا بضبطها. لذلك فإنّ أكثر خاصيّة أمنيّة هي التي تجعل من تجاوز هذا الحاجز صعبا قدر المُستطاع. أي خاصيّة الجدار النّاري.
</p>

<p dir="rtl">
	للتّقدّم بحماية خادومك إلى المستوى التّالي، فعليك ضبط نظام كشف تطفّل مثل OSSEC.
</p>

<p dir="rtl">
	إذا كنت تريد حماية تواصل Redis عبر شبكات غير آمنة فعليك توظيف <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></abbr></abbr> proxy، كما هو منصوح به من مطوّري Redis على دليل حمايةRedis الرسمي. وضبط <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></abbr></abbr> proxy لحماية تواصل Redis فهو موضوع آخر.
</p>

<p dir="rtl">
	لم نتطرّق إلى قائمة شاملة من أوامر Redis بقسم إعادة التّسميّة، لكنّك تستطيع إلقاء نظرة على مجموعة الأوامر بنفسك وتحديد إمكانيّة سوء استعملها على <a href="http://redis.io/commands" rel="external nofollow">redis.io/commands</a>.
</p>

<p dir="rtl">
	ترجمة -بتصرّف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-your-redis-installation-on-ubuntu-14-04" rel="external nofollow">How To Secure Your Redis Installation on Ubuntu 14.04</a> لصاحبه finid.
</p>
]]></description><guid isPermaLink="false">245</guid><pubDate>Tue, 15 Mar 2016 21:31:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641; &#x62A;&#x636;&#x628;&#x637; &#x62E;&#x648;&#x627;&#x62F;&#x64A;&#x645; Redis &#x645;&#x62A;&#x639;&#x62F;&#x62F;&#x629; &#x639;&#x644;&#x649; &#x623;&#x648;&#x628;&#x646;&#x62A;&#x648; 14.04</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81-%D8%AA%D8%B6%D8%A8%D8%B7-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-redis-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r241/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_03/redis-cluster-ubuntu.png.ce456d44ba113ed1adb6cc720892cfb8.png" /></p>

<p dir="rtl">
	Redis عبارة عن نظام تخزين مؤقّت cache بنمط مفتاح-قيمة key-value مفتوح المصدر.
</p>

<p dir="rtl" style="text-align: center;">
	<img alt="redis-cluster-ubuntu.png.e4489cfb89b643d" class="ipsImage ipsImage_thumbnailed" data-fileid="13715" data-unique="u3ibcgsco" src="https://academy.hsoub.com/uploads/monthly_2016_03/redis-cluster-ubuntu.png.e4489cfb89b643d920eb7d9cde809f6b.png"></p>

<p dir="rtl">
	إن كنت ترغب في استخدام Redis على بيئة الإنتاج الخاصة بك، فإن نسخ بياناتك على خادومين على الأقل يُعتبر من بين أفضل الممارسات (best practices)، تتيح الوفرة الاسترداد في حالة فشل البيئة، ما يُعتبر مهمّا عند نمو قاعدة مستخدمي تطبيقك.
</p>

<p dir="rtl">
	بنهاية هذا الدرس، سنضبط خادومي Redis كالتالي:
</p>

<ul dir="rtl"><li>
		خادوم Redis للمتبوع/السيد (master server)
	</li>
	<li>
		خادوم Redis للتّابع (slave server)
	</li>
</ul><p dir="rtl">
	سنشرح كذلك كيفيّة الانتقال إلى خادوم التّابع وضبطه كسيّدِِ مؤقّت.
</p>

<p dir="rtl">
	لك كامل الحريّة في ضبط أكثر من خادوم واحد خاص بالتابع.
</p>

<p dir="rtl">
	هذا الدرس يركز على ضبط عنقود تابع ومتبوع خاص بـredis، لتعلم المزيد عن Redis بشكل عام واستخدامه الأساسي كقاعدة بيانات، <a href="http://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-ubuntu-r46/">ألق نظرة على</a><a href="http://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-ubuntu-r46/"> هذا الدرس</a>.
</p>

<h2 dir="rtl">
	المتطلبات
</h2>

<p dir="rtl">
	في حين سيعمل هذا على إصدارات أقدم وتوزيعات لينكس أخرى، لكن ننصح باستعمال نظام أوبنتو 14.04.
</p>

<p dir="rtl">
	سنعتمد على:
</p>

<ul dir="rtl"><li>
		نظام أوبنتو Ubuntu 14.04 LTS.
	</li>
	<li>
		خادومين، بأي حجم تريد، واحد متبوع وآخر أو أكثر للتّوابع.
	</li>
	<li>
		ادخل إلى خواديمك عبر <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr> مع مستخدم غير جدري مع صلاحيّات sudo كما هو مبيّن في <a href="http://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7%D8%A6%D9%8A-%D9%84%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r4/">الإعداد الابتدائي لخادوم أوبنتو </a><a href="http://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A7%D8%A8%D8%AA%D8%AF%D8%A7%D8%A6%D9%8A-%D9%84%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1404-r4/">14.04</a> .
	</li>
</ul><h2 dir="rtl">
	الخطوة الأولى: تثبيت Redis
</h2>

<p dir="rtl">
	سنبدأ مع الخادوم الذي سيستضيف المتبوع/السّيّد، وأول خطوة هي تثبيت Redis. أولا نحتاج إلى إضافة مستودع Redis الخاص بـ Chris Lea (كما جرت عليه العادة، توخ الحذر عند إضافة مستودعات طرف ثالث، نستخدم هذا المستودع لأن صاحبه حسنُ السمعة):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_7">
<span class="pln">sudo add-apt-repository ppa:chris-lea/redis-server</span></pre>

<p dir="rtl">
	اضغط ENTER لقبول المستودع.
</p>

<p dir="rtl">
	شغل الأمر التالي لتحديث الحزم:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_9">
<span class="pln">sudo apt-get update</span></pre>

<p dir="rtl">
	ثبت خادوم Redis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_11">
<span class="pln">sudo apt-get update</span></pre>

<p dir="rtl">
	تأكد أنّ Redis مُشغل وقيد التنفيذ:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_13">
<span class="pln">redis-benchmark -q -n 1000 -c 10 -P 5</span></pre>

<p dir="rtl">
	الأمر أعلاه يخبر بأننا نريد تشغيل الأمر <span style="font-family:courier new,courier,monospace;">redis-benchmark</span> في وضع صامت، مع 1000 طلبات إجماليّة، 10 اتصالات موازية، و 5 طلبات أنابيب. للمزيد من المعلومات عن هذا الأمر، شغّل الأمر<span style="font-family:courier new,courier,monospace;"> redis-benchmark –help</span> على الطرفيّة لتظهر لك معلومات وأمثلة حول الأمر.
</p>

<p dir="rtl">
	بعد انتهاء العمليّة، يجب أن ترى مُخرجات تبدو كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_15">
<span class="pln">PING_INLINE: 166666.67 requests per second
PING_BULK: 249999.98 requests per second
SET: 249999.98 requests per second
GET: 499999.97 requests per second
INCR: 333333.34 requests per second
LPUSH: 499999.97 requests per second
LPOP: 499999.97 requests per second
SADD: 499999.97 requests per second
SPOP: 499999.97 requests per second
LPUSH (needed to benchmark LRANGE): 499999.97 requests per second
LRANGE_100 (first 100 elements): 111111.12 requests per second
LRANGE_300 (first 300 elements): 27777.78 requests per second
LRANGE_500 (first 450 elements): 8333.33 requests per second
LRANGE_600 (first 600 elements): 6369.43 requests per second
MSET (10 keys): 142857.14 requests per second</span></pre>

<h2 dir="rtl">
	الخطوة الثانية: ضبط Redis المتبوع
</h2>

<p dir="rtl">
	الآن بما أن Redis قيد التنفيذ على العنقود المتكون من الخادومين، يجب علينا تعديل ملفات الإعدادات. كما سنرى، هناك اختلافات طفيفة بين ضبط الخادوم المتبوع والتابع.
</p>

<p dir="rtl">
	لنبدأ أولا مع المتبوع master.
</p>

<p dir="rtl">
	افتح الملف <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span> باستخدام محرر النصوص المُفضّل لديك:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_17">
<span class="pln">sudo nano /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	وعدّل الأسطر التّالية:
</p>

<p dir="rtl">
	ضع قيمة معقولة لمؤقّت الإبقاء على قيد الحياة KeepAlive للـTCP:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_19">
<span class="pln">tcp-keepalive 60</span></pre>

<p dir="rtl">
	اجعل الخادوم متاحا للجميع على الويب بجعل هذا السّطر كتعليق (أو بحذفه):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_21">
<span class="pln">#bind 127.0.0.1</span></pre>

<p dir="rtl">
	بحكم طبيعة Redis وسرعاتها العاليّة قد ينفذ مهاجم هجوم القوة الغاشمة Brute force على كلمة المرور. لذلك ننصح إزالة تعليق سطر <span style="font-family:courier new,courier,monospace;">requirepass</span> وإضافة كلمة مرور معقّدة (أو من الأفضل أن تكون جملة مرور معقدة):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_23">
<span class="pln">requirepass your_redis_master_password</span></pre>

<p>
	على حسب سيناريو استخدامك، قد ترغب في تعديل السّطر التالي أو لا. لغرض هذا الدرس ، نفترض أن حذف أي مفتاح key deletion ممنوع. قم بإزالة التعليق عن هذا السّطر واضبطه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_25">
<span class="pln">maxmemory-policy noeviction</span></pre>

<p dir="rtl">
	أخيرا، سنقوم بالتعديلات التاليّة، وهي مطلوبة للنسخ الاحتياطي للبيانات. أزل التعليق/أو اضبط هذه الأسطر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_29">
<span class="pln">appendonly yes
appendfilename redis-staging-ao.aof</span></pre>

<p dir="rtl">
	احفظ التغييرات.
</p>

<p dir="rtl">
	أعد تشغيل خدمة Redis لإعادة تحميل تعديلات الضبط التي قمنا بها.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_31">
<span class="pln">sudo service redis-server restart</span></pre>

<p dir="rtl">
	إذا أردت، يُمكنك إضافة بعض المحتوى إلى قاعدة بيانات المتبوع من خلال اتباع قسم <a href="http://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-ubuntu-r46/">أوامر </a><a href="http://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-ubuntu-r46/">Redis </a><a href="http://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-ubuntu-r46/">في هذا الدرس</a>. حتى نتمكن من معرفة كيفية نسخه إلى الخادوم التابع.
</p>

<p dir="rtl">
	الآن وبعد أن جهزنا خادوم المتبوع أو السيّد، لننتقل إلى خادوم التابع.
</p>

<h2 dir="rtl">
	الخطوة الثالثة: إعداد Redis التابع
</h2>

<p dir="rtl">
	نحتاج للقيام ببعض التعديلات التي ستسمح لخادوم التابع بالاتّصال مع المتبوع:
</p>

<p dir="rtl">
	افتح الملف<span style="font-family:courier new,courier,monospace;"> etc/redis/redis.conf/</span> بمحررك المُفضل:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_33">
<span class="pln">sudo nano /etc/redis/redis.conf</span></pre>

<p dir="rtl">
	عدّل الأسطر التالية، بعض الإعدادات ستكون تماما مثل الإعدادات الخاصة بالمتبوع.
</p>

<p dir="rtl">
	اجعل الخادوم متاحا للجميع على الويب بتحويل هذا السطر إلى تعليق (عبر إضافة # إلى بدايته):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_35">
<span class="pln">#bind 127.0.0.1</span></pre>

<p dir="rtl">
	يحتاج التابع كذلك إلى كلمة مرور لنستطيع إعطاء الأوامر له (مثل INFO). أزل التعليق عن هذا السطر وضع كلمة سر للخادوم:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_37">
<span class="pln">requirepass your_redis_slave_password</span></pre>

<p>
	أزل التعليق عن هذا السطر وضع عنوان IP الخاص بالمتبوع، متبوعا برقم المنفذ المضبوط على الخادوم. افتراضيّا يكون الرقم 6379 هو رقم المنفذ:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_39">
<span class="pln">SLAVEOF your_redis_master_ip 6379</span></pre>

<p dir="rtl">
	تأكد من تغيير your_redis_master_ip إلى عنوان IP الخاص بالمتبوع.
</p>

<p dir="rtl">
	احفظ الآن هذه التغييرات، واخرج من الملف. ومن ثم، أعد تشغيل الخدمة كما فعلنا مع خادوم المتبوع:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_41">
<span class="pln">sudo service redis-server restart</span></pre>

<p dir="rtl">
	هذا الأمر سيعيد ضبط Redis وسيحمل الملفات التي عدّلناها.
</p>

<p dir="rtl">
	اتصل بـredis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_43">
<span class="pln">redis-cli -h 127.0.0.1 -p 6379</span></pre>

<p dir="rtl">
	استوثق بكلمة المرور الخاصة بالتابع:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_45">
<span class="pln">AUTH your_redis_slave_password</span></pre>

<p dir="rtl">
	لقد شغّلنا الآن عنقود Redis يعمل كتابع ومتبوع، مع ضبط كل من الخادومين بشكل صحيح.
</p>

<h2 dir="rtl">
	الخطوة الرابعة: تحقق من نسخ التابع-المتبوع
</h2>

<p dir="rtl">
	سيسمح لنا اختبار هذا التّنصيب فهم كيفية تصرف خوادم Redis بشكل أفضل، عندما نرغب في بداية برمجة الحماية من الفشل. ما سنفعله الآن هو التحقق من أن الإعدادات تعمل بشكل صحيح، وأن المتبوع يتواصل مع التابع بشكل صحيح.
</p>

<p dir="rtl">
	أولا، نتصل بـredis عبر الطرفيّة، على خادوم المتبوع:
</p>

<p dir="rtl">
	اتّصل أولا بالنموذج المحليّ، مع استعمال المنفذ الافتراضي 6379. إذا كنت قد غيّرت المنفذ، فغير الأمر بما يناسب:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_47">
<span class="pln">redis-cli -h 127.0.0.1 -p 6379</span></pre>

<p dir="rtl">
	الآن، استوثق مع Redis باستعمال كلمة المرور التي وضعتها عند ضبط المتبوع:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_49">
<span class="pln">AUTH your_redis_master_password</span></pre>

<p dir="rtl">
	يجب عليك أن تحصل على OK كمُخرج. الآن، كل ما عليك فعله هو تنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_51">
<span class="pln">INFO</span></pre>

<p dir="rtl">
	سترى كلّ ما تحتاج معرفته عن خادوم المتبوع. ما يهمنا هو القسم <span style="font-family:courier new,courier,monospace;">Replication# </span>والذي يجب أن تبدو كالمخرجات التاليّة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_53">
<span class="pln">. . .

# Replication
role:master
connected_slaves:1
slave0:ip=111.111.111.222,port=6379,state=online,offset=407,lag=1
master_repl_offset:407
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:406

. . .</span></pre>

<p dir="rtl">
	لاحظ سطر<span style="font-family:courier new,courier,monospace;"> connected_slaves:1</span>، الذي يخبرنا بأن نموذج التابع يتواصل مع الخادوم المتبوع. كما يُمكنك ملاحظة أنّنا حصلنا على عنوان IP الخاص بالتابع، وكذلك المنفذ، الحالة، ومعلومات أخرى.
</p>

<p dir="rtl">
	الآن لنلق نظرة على قسم <span style="font-family:courier new,courier,monospace;">Replication#</span> في الخادوم الخاص بالتابع، العمليّة نفسها التي قمنا بها مع المتبوع، ادخل إلى Redis، طبق الأمر <span style="font-family:courier new,courier,monospace;">INFO</span>، وانظر إلى المُخرجات:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_55">
<span class="pln">. . .

# Replication
role:slave
master_host:111.111.111.111
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:1401
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

. . .</span></pre>

<p dir="rtl">
	يُمكننا ملاحظة أن هذا الخادوم يقوم بدور التابع، وأنه يتواصل مع خادوم Redis المتبوع، ولا يملك أي توابع خاصة به.
</p>

<h2 dir="rtl">
	الخطوة الخامسة: التبديل إلى التابع
</h2>

<p dir="rtl">
	بناء هذه الهندسة يعني أننا نريد التعامل مع الأخطاء والفشل بطريقة يُمكننا التأكد بها من سلامة البيانات وفي أقل وقت توقف ممكن لتطبيقنا. يُمكن لأي تابع أن يرتقي ليصبح متبوعا. أولا، لنختبر التبديل بشكل يدوي.
</p>

<p dir="rtl">
	على خادوم التابع، يجب أن نتصل مع نموذج Redis:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_57">
<span class="pln">redis-cli -h 127.0.0.1 -p 6379</span></pre>

<p dir="rtl">
	الآن قم بالاستيثاق مع Redis باستخدام كلمة المرور التي استخدمتها عند ضبط التابع.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_59">
<span class="pln">AUTH your_redis_slave_password</span></pre>

<p dir="rtl">
	قم بإنهاء دور التابع:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_61">
<span class="pln">SLAVEOF NO ONE</span></pre>

<p dir="rtl">
	يجب الحصول على OK كمُخرج. الآن، نفذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_63">
<span class="pln">INFO</span></pre>

<p dir="rtl">
	قسم <span style="font-family:courier new,courier,monospace;">Replication#</span> في الرد يجب أن يبدو كالمخرجات التاليّة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_622_65">
<span class="pln">. . .

# Replication
role:master
connected_slaves:0
master_repl_offset:1737
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

. . .</span></pre>

<p dir="rtl">
	كما هو متوقع، فقد تحول التابع إلى متبوع، وهو الآن جاهز لقبول اتّصالات من خوادم أخرى (إذا كانت موجودة). يُمكننا استعماله كمخزن نسخ احتيّاطي مؤقّت أثناء إصلاح الأخطاء في المتبوع الأصلي.
</p>

<p dir="rtl">
	إذا كنت تمتلك عدة توابع مُعتمدة على المتبوع الرئيسي، فسيتوجب عليك توجيههم إلى المتبوع الجديد.
</p>

<p dir="rtl">
	يُمكن القيّام بذلك بسهولة، بالخطوات التاليّة التي يجب تطبيقها في حال كُشِف عن أيّ أخطاء.
</p>

<ul dir="rtl"><li>
		من التطبيق، أرسل جميع الطلبات لـredis إلى خادوم تابع.
	</li>
	<li>
		على هذا التابع، نفّذ الأمر<span style="font-family:courier new,courier,monospace;"> SLAVEOF NO ONE</span>. منذ النسخة 1.0.0 من Redis هذا الأمر يُخبر التابع بالتوقف عن نسخ البيانات، والبداية في التصرف كخادوم متبوع.
	</li>
	<li>
		على جميع التابعين المتبقّين (إذا كانوا موجودين)، تشغيل الأمر <span style="font-family:courier new,courier,monospace;">SLAVEOF hostname port </span>سيوجههم للتوقف عن النسخ من المتبوع القديم، وتجاهل البيانات، والبداية في النسخ من المتبوع الجديد. تأكد من تغيير <span style="font-family:courier new,courier,monospace;">hostname</span> و <span style="font-family:courier new,courier,monospace;">port</span> بالمعلومات المناسبة من المتبوع الجديد.
	</li>
	<li>
		بعد حل المشكلة، قد يرجع المتبوع القديم كمتبوع رئيسي، إذا كانت الإعدادات الخاصّة بك تتطلّب ذلك.
	</li>
</ul><p dir="rtl">
	هناك العديد من الطرق الممكنة لتنفيذ الخطوات أعلاه. ومع ذلك، الأمر يعود لك لتنفيذ أي حل تراه ملائما لبيئتك، وتأكد من اختبارها قبل أن يحدث أي فشل حقيقي.
</p>

<h2 dir="rtl">
	الخطوة السادسة: إعادة الاتصال بالمتبوع الأصلي
</h2>

<p dir="rtl">
	لنرجع الاتصال بالمتبوع الأصلي. على الخادوم التابع ادخل إلى Redis وشغل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2767_9">
<span class="pln">SLAVEOF your_redis_master_ip 6379</span></pre>

<p dir="rtl">
	تأكد من تغيي<span style="font-family:courier new,courier,monospace;">ر your_redis_master_ip</span> إلى عنوان IP الخاص بالمتبوع
</p>

<p dir="rtl">
	إذا قمت بتشغيل الأمر <span style="font-family:courier new,courier,monospace;">INFO</span> مجددا، يجب أن تلاحظ أنّنا عدنا إلى الإعدادات الأصليّة.
</p>

<h2 dir="rtl">
	في الختام
</h2>

<p dir="rtl">
	لقد قمنا بإعداد بيئة مكونة من خادومين بنجاح، واحد يتصرف كمتبوع Redis والآخر ينسخ البيانات كتابع. بهذه الطريقة، إذا كان خادوم المتبوع يعاني من أي أخطاء أو فقد بياناتنا علي، فقد تعلمنا كيفية تبديل تابع ليصبح متبوعا كاحتياط إلى حين إصلاح المشكلة.
</p>

<p dir="rtl">
	الخطوات التالية تتعلق ببرمجة إجراء الحماية من الخطأ التلقائي، أو ضمان اتصالات آمنة بين جميع خوادمك باستعمال حلول <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr> مثل <a href="http://academy.hsoub.com/devops/servers/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D9%84%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-openvpn-%D8%B9%D9%84%D9%89-ubuntu-r57/">OpenVPN</a> أو Tinc، اختبار الإجراءات والسكربتات للتحقق من صحة إعداداتك.
</p>

<p dir="rtl">
	إضافة إلى ذلك، يجب عليك اتخاذ احتياطات عند نشر مثل هذه لإعدادات على بيئات الإنتاج. يجب أن تدرس توثيق Redis ويجب أن تفهم جيدا أي من نماذج الأمان هي الملائمة لتطبيقك. نستعمل عادة Redis كمَخزن للجلسة، وما يحتويه من معلومات قد يكون ذا قيمة لمهاجم ما. الممارسة الشائعة هي أن تمتلك هذه الخواديم إمكانية وصول فقط عبر شبكة خاصة private network.
</p>

<p dir="rtl">
	هذه نقطة بداية بسيطة عن كيفية بناء مخزن بيانات خاص بك؛ وليس درسا شاملا عن ضبط Redis لاستخدام هندسة تابع-متبوع. إذا كان هناك أي شيء كنت ترغب في تغطيّته في هذا الدرس، اترك تعليقات أسفله، ولمزيد من المعلومات عن هذا الموضوع، فإن <a href="http://academy.hsoub.com/questions/devops-6/">قسم أسئلة </a><a href="http://academy.hsoub.com/questions/devops-6/">DevOps </a><a href="http://academy.hsoub.com/questions/devops-6/">بالأكاديمية</a> تعتبر أماكن جيّدة لتبدأ منها.
</p>

<p dir="rtl">
	ترجمة -وبتصرّف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-configure-a-redis-cluster-on-ubuntu-14-04" rel="external nofollow">How To Configure a Redis Cluster on Ubuntu 14.04</a> لصاحبه Florin Dobre.
</p>
]]></description><guid isPermaLink="false">241</guid><pubDate>Sun, 06 Mar 2016 21:44:49 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x644;&#x646;&#x633;&#x62E; &#x627;&#x644;&#x627;&#x62D;&#x62A;&#x64A;&#x627;&#x637;&#x64A; &#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Redis &#x639;&#x644;&#x649; Ubuntu</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-redis-%D8%B9%D9%84%D9%89-ubuntu-r125/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_10/redis-backup.png.32babebef29c8f957f668a739f7a8431.png" /></p>

<p dir="rtl">إنّ <strong>Redis</strong> هي عبارة عن ذاكرة مؤقتة cache بنمط مفتاح-قيمة key-value ومَخزَن في الذاكرة (أي قاعدة بيانات) والتي يمكن نقلها وحفظها بشكل دائم على القرص، سنشرح في هذا الدّرس كيفيّة النّسخ الاحتياطي لقاعدة بيانات Redis على خادوم Ubuntu.</p><p dir="rtl" style="text-align: center;"><a href="https://academy.hsoub.com/uploads/monthly_2015_10/redis-backup.png.c4125e56b595e3afc12a06e9c5bf9ef4.png" class="ipsAttachLink ipsAttachLink_image"><img data-fileid="6386" src="https://academy.hsoub.com/uploads/monthly_2015_10/redis-backup.thumb.png.5448c4627b248cb5242b6c7aa59b399c.png" class="ipsImage ipsImage_thumbnailed" alt="redis-backup.thumb.png.5448c4627b248cb52"></a></p><p dir="rtl">يتم حفظ بيانات Redis بشكل افتراضي إلى القرص في ملف <span style="font-family:courier new,courier,monospace;">rdb.</span>، والذي هو لقطة snapshot لنقطة في الوقت المناسب لمجموعة بيانات Redis لدينا، يتم عمل اللقطة في فواصل مُحدّدة، ويكون هذا رائعًا من أجل النسخ الاحتياطيّة لدينا.</p><h2 dir="rtl">المتطلبات الأساسية</h2><p dir="rtl">سنحتاج لإكمال الخطوات في هذا الدّرس إلى:</p><ul dir="rtl"><li>خادوم Ubuntu.</li><li><a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-ubuntu-r46/">تنصيب redis</a>، تستطيع اتباع الإعداد الرئيسي master في هذا الدّرس لإعداد Redis (بالرغم من أنّه سيعمل بشكل جيّد مع عنقود تابع ومتبوع master-slave cluster).</li><li>التحقق من أنّ خادوم Redis قيد التشغيل لدينا.</li><li>إن تمّ تعيين كلمة سر لخادوم Redis -والذي ننصح به بشدّة- يكون ذلك مفيدًا، توجد كلمة السّر في ملف الإعدادات <span style="font-family:courier new,courier,monospace;">/etc/redis/redis.conf</span></li></ul><h2 dir="rtl">الخطوة الأولى – إيجاد دليل بيانات Redis</h2><p dir="rtl">تقوم Redis بتخزين بياناتها إلى دليل على خادومنا، وهو ما نريده من أجل النّسخ الاحتياطي، نحتاج في البداية لمعرفة مكان هذا الدّليل.</p><p dir="rtl">يوجد دليل قاعدة بيانات Redis في Ubuntu وتوزيعات لينِكس الأخرى في المسار <span style="font-family:courier new,courier,monospace;">var/lib/redis/</span>، ولكن إن كُنّا ندير خادوم تم تغيير مكان بيانات Redis فيه، نستطيع تحديد مكانه بكتابة ما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo locate *rdb</pre><p dir="rtl">وبإمكاننا بشكل بديل إيجاده عن طريق المُحث prompt الذي يُدعى <span style="font-family:courier new,courier,monospace;">redis-cli</span>، ولفعل هذا نكتب ما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">redis-cli</pre><p dir="rtl">إن لم يكن خادوم Redis قيد التشغيل ستكون الاستجابة التي سنتلقاها هي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected&gt;</pre><p>وفي هذه الحالة نقوم بتشغيل Redis وإعادة الاتصال باستخدام الأوامر التالية:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo service redis-server start</pre><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">redis-cli</pre><p dir="rtl">سيتغير مُحث الـ Shell إلى:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt;</pre><p dir="rtl">في الوقت الذي نكون متصلين فيه إلى Redis سيقوم الأمران التاليان بالاستيثاق authenticate معه والحصول على دليل البيانات:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; auth insert-redis-password-here
127.0.0.1:6379&gt;
127.0.0.1:6379&gt; config get dir</pre><p dir="rtl">يجب أن يكون خَرْج الأمر الأخير هو دليل بيانات Redis لدينا:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">1) "dir"
2) "/var/lib/redis"</pre><p>نلاحظ دليل Redis لدينا فإن كان مختلفًا عن الدّليل الظاهر هنا فيجب أن نحرص على استعماله خلال هذا الدّرس.</p><p dir="rtl">نستطيع الآن الخروج من واجهة سطر الأوامر لقاعدة البيانات:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; exit</pre><p dir="rtl">نتحقّق من أنّ هذا هو الدّليل الصحيح:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">ls /var/lib/redis</pre><p dir="rtl">ينبغي أن نرى ملف<span style="font-family:courier new,courier,monospace;"> dump.rdb</span> وهو عبارة عن بيانات Redis، إن تمّ تمكين <span style="font-family:courier new,courier,monospace;">appendonly</span> فسنرى ملف يُدعى <span style="font-family:courier new,courier,monospace;">appendonly.aof</span> أو حتى أي ملف <span style="font-family:courier new,courier,monospace;">aof.</span> آخر، والذي يحتوي على سجل لجميع عمليّات الكتابة التي تم تلقّيها بواسطة الخادوم.</p><p dir="rtl">قم بقراءة هذا المنشور حول استمرار Redis persistence من أجل النقاش حول الاختلافات بين هذين الملفين.</p><p dir="rtl">بشكل مُبسَّط فإنّ الملف <span style="font-family:courier new,courier,monospace;">rdb.</span> هو اللقطة الحاليّة، والملف <span style="font-family:courier new,courier,monospace;">aof.</span> يقوم بحفظ تاريخ Redis، وكلاهما يستحقّان أخذ نسخة احتياطيّة عنهما.</p><p dir="rtl">سنبدأ فقط بالملف <span style="font-family: 'courier new', courier, monospace; line-height: 24.8889px;">rdb.</span> وننتهي بنسخة احتياطيّة تلقائيّة لكلا الملفين.</p><h2 dir="rtl">الخطوة الثانية (اختيارية) – إضافة عينة sample بيانات</h2><p dir="rtl">سنقوم في هذا القسم بإنشاء عيّنة بيانات لتخزينها في قاعدة بيانات Redis، إن كنتَ تملك مٌسبقًا بيانات على خادومك تستطيع النسخ الاحتياطي للمحتوى الموجود حاليًّا.</p><p dir="rtl">نقوم بتسجيل الدخول من خلال واجهة سطر الأوامر لقاعدة البيانات:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">redis-cli</pre><p dir="rtl">نقوم بالاستيثاق بكتابة ما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; auth insert-redis-password-here</pre><p dir="rtl">فلنقم بإضافة عيّنة بيانات، يجب أن نتلقّى استجابة OK بعد كل خطوة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; SET shapes:triangles "3 sides"
127.0.0.1:6379&gt;
127.0.0.1:6379&gt; SET shapes:squares "4 sides"</pre><p dir="rtl">نتأكّد من أنّه تمّت إضافة البيانات:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; GET shapes:triangles
127.0.0.1:6379&gt;
127.0.0.1:6379&gt; GET shapes:squares</pre><p dir="rtl">وهذا هو الخَرْج output الذي يجب أن نحصل عليه:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">"3 sides"
"4 sides"</pre><p dir="rtl">ولفرض هذه التغييرات على الملف <span style="font-family:courier new,courier,monospace;">var/lib/redis/dump.rdb/</span> نقوم بحفظهم:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; save</pre><p dir="rtl">نستطيع الآن الخروج:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; exit</pre><p dir="rtl">بإمكاننا الآن إن كُنّا نرغب التّحقّق من محتويات الملف <span style="font-family:courier new,courier,monospace;">dump</span>، يجب أن يحتوي على بياناتنا ولو أنّها في شكل مناسب للآلة أكثر:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo cat /var/lib/redis/dump.rdb
</pre><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">REDIS0006?shapes:squares4 sidesshapes:triangles3 sides??o????C</pre><h2 dir="rtl">الخطوة الثالثة – النسخ الاحتياطي لبيانات Redis</h2><p dir="rtl">حان الوقت الآن لعمل نسخة احتياطيّة بعد أن أصبحنا نعلم مكان تواجد بيانات Redis لدينا، نجد هذا الاقتباس من موقع Redis الرّسمي:</p><blockquote class="ipsQuote" data-cite="اقتباس" data-ipsquote=""><p dir="rtl">إنّ Redis ودود جدًّا من ناحية النّسخ الاحتياطي للبيانات، حيث يُمكننا نسخ ملفّات RDB أثناء تشغيل قاعدة البيانات: لا يتم تعديل ملفّات RDB أبدًا بعد إنتاجها، وتستخدم أثناء إنتاجها اسمًا مُؤقّتًا ثمّ تتم تسميتها باسمها النهائي تلقائيًّا باستخدام (rename(2 فقط عندما تكتمل اللقطة الجديدة.</p></blockquote><p dir="rtl">إذًا نستطيع النسخ الاحتياطي أو نسخ ملف قاعدة البيانات أثناء تشغيل خادوم Redis، وعلى افتراض أنّنا نريد النسخ الاحتياطي لها إلى دليل داخل الدّليل الرئيسي لدينا فإنّ القيام بالنسخ الاحتياطي بسيط بكتابة ما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo cp /var/lib/redis/dump.rdb /home/sammy/redis-backup-001</pre><p dir="rtl">يحفظ Redis المحتوى هنا بشكل دوري، ممّا يعني أنّ الأمر السابق غير مضمون بالنسبة لنا وأنّنا لا نملك أحدث نسخة احتياطيّة حتى الدقيقة الأخيرة إن كان كل ما نقوم به هو تشغيل هذا الأمر، نحتاج لحفظ البيانات الخاصّة بنا أولًا.</p><p dir="rtl">ومع ذلك إن كان من المقبول فقدان كمية صغيرة من البيانات فإنّ النّسخ الاحتياطي لهذا الملف فقط سيعمل بشكل جيّد.</p><h3 dir="rtl">حفظ حالة قاعدة البيانات</h3><p dir="rtl">للحصول على نسخة أحدث بكثير من بيانات Redis فمن الأفضل النفاذ إلى<span style="font-family:courier new,courier,monospace;"> redis-cli</span>، سطر أوامر Redis.</p><p dir="rtl">نقوم بالاستيثاق كما شرحنا في الخطوة الأولى.</p><p dir="rtl">ثمّ نكتب الأمر حفظ save كما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">127.0.0.1:6379&gt; save</pre><p dir="rtl">ينبغي أن يكون الخَرْج مُشابِهًا لهذا:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">OK
(1.08s)</pre><p dir="rtl">نخرج من قاعدة البيانات.</p><p dir="rtl">نستطيع الآن تشغيل الأمر<span style="font-family:courier new,courier,monospace;"> cp</span> السّابق واثقين من أنّ النسّخة الاحتياطيّة هي أحدث نسخة حتى الآن بشكل كامل.</p><p dir="rtl">بينما يُزوِّدنا الأمر <span style="font-family:courier new,courier,monospace;">cp</span> بنسخة احتياطيّة مرّة واحدة من قاعدة البيانات، فإنّ الحل الأنسب هو إعداد وظيفة <strong>cron</strong> تقوم بأتمتة هذه العمليّة، واستخدام أداة تستطيع تنفيذ تحديثات تزايديّة incremental updates وتستعيد البيانات إن احتجنا لذلك.</p><h2 dir="rtl">الخطوة الرابعة – إعداد التحديثات التلقائية باستخدام rdiff-backup وCron</h2><p dir="rtl">سنقوم في هذا القسم بإعداد نسخ احتياطي تلقائي يشمل كامل دليل بيانات Redis لدينا، بما في ذلك كلا ملفي البيانات.</p><p dir="rtl">توجد العديد من أدوات النسخ الاحتياطي التلقائي المتاحة، سنستخدم أداة جديدة وسهلة الاستخدام تُدعى <span style="font-family:courier new,courier,monospace;">rdiff-backup</span>.</p><p dir="rtl">إنّ <span style="font-family:courier new,courier,monospace;">rdiff-backup </span>هي أداة نسخ احتياطي تعمل عبر سطر الأوامر، من المحتمل أنّ <span style="font-family:courier new,courier,monospace;">rdiff-backup</span> غير مُثبَّتة على خادومك، لذا يجب عليك تثبيتها أولًا:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo apt-get install -y rdiff-backup</pre><p dir="rtl">نستطيع الآن بعد تثبيتها أن نختبرها بالنسخ الاحتياطي لبيانات Redis لدينا إلى مجلّد في دليلنا الرئيسي، سنفترض في هذا المثال أنّ الدليل الرئيسي لدينا هو <span style="font-family:courier new,courier,monospace;">home/sammy/</span>.</p><p dir="rtl">نلاحظ أنّ الدليل الهدف سيتم إنشاؤه من قبل الـ script إن لم يكن موجودًا، أي بمعنى آخر لا نحتاج إنشاءه بأنفسنا.</p><p dir="rtl">ومع وجود <span style="font-family:courier new,courier,monospace;">preserve-numerical-ids--</span> ستكون الملكيّات نفسها للمجلدات المصدر والوجهة.</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo rdiff-backup --preserve-numerical-ids /var/lib/redis /home/sammy/redis</pre><p dir="rtl">وكما وجدنا مع الأمر <span style="font-family:courier new,courier,monospace;">cp</span> سابقًا فإنّ هذه نسخة احتياطيّة لمرّة واحدة، ولكنّ الذي تغيّر هو أنّنا نقوم الآن بالنسخ الاحتياطي لكامل الدليل <span style="font-family:courier new,courier,monospace;">var/lib/redis/</span> وباستخدام <span style="font-family:courier new,courier,monospace;">rdiff-backup</span>.</p><p dir="rtl">سنقوم الآن بأتمتة النّسخ الاحتياطي باستخدام <strong>cron</strong> بحيث تحدث النسخة الاحتياطيّة في وقت مُحدّد، ولإنجاز هذا نفتح <span style="font-family:courier new,courier,monospace;">crontab</span> النّظام:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo crontab -e</pre><p dir="rtl">(إن لم تستخدم <span style="font-family:courier new,courier,monospace;">crontab</span> على هذا الخادوم من قبل قم باختيار مُحرّر النّصوص المفضل لديك عند سؤالك عنه.)</p><p dir="rtl">نقوم بإضافة المُدخَل entry التالي في نهاية الملف:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">0 0 * * * rdiff-backup --preserve-numerical-ids --no-file-statistics /var/lib/redis /home/sammy/redis</pre><p dir="rtl">سيقوم مُدخَل Cron هذا بعمل نسخة احتياطيّة لـ Redis يوميًّا عند منتصف الليل، سيعطّل التحوّل <span style="font-family:courier new,courier,monospace;">no-file-statistics--</span> الكتابة إلى الملف <span style="font-family:courier new,courier,monospace;">file_statistics </span>في الدّليل <span style="font-family:courier new,courier,monospace;">rdiff-backup-data</span>، والذي يجعل من <span style="font-family:courier new,courier,monospace;">rdiff-backup </span>يعمل بشكل أسرع أكثر ويستخدم مساحة قرص أقل بقليل.</p><p dir="rtl">ونستطيع بشكل بديل استخدام هذا المُدخَل للقيام بنسخ احتياطي يومي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">@daily rdiff-backup --preserve-numerical-ids --no-file-statistics /var/lib/redis /home/sammy/redis</pre><p dir="rtl">وكما هو مذكور سيتم عمل نسخة احتياطيّة مرّة في اليوم، لذا بإمكانك العودة غدًا من أجل اختباره لآخر مرّة، أو تستطيع بشكل مؤقّت زيادة وتيرة النّسخ الاحتياطي لكي تتأكّد من أنّه يعمل.</p><p dir="rtl">ولأنّ الملفّات مملوكة من قبل مستخدم النّظام redis نستطيع التحقّق من أنّها في مكانها باستخدام هذا الأمر.(تأكّد من الانتظار إلى أن يتم تحفيز بدء النسخ الاحتياطي):</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">ls -l /home/sammy/redis</pre><p dir="rtl">ينبغي أن يبدو الخَرْج مُشابِهًا لما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">total 20

-rw-rw---- 1 redis redis    70 Sep 14 13:13 dump.rdb
drwx------ 3 root  root  12288 Sep 14 13:49 rdiff-backup-data
-rw-r----- 1 redis redis   119 Sep 14 13:09 redis-staging-ao.aof</pre><p dir="rtl">نمتلك الآن نُسَخ احتياطيّة يوميّة لبيانات Redis لدينا مُخزَّنة في الدّليل الرئيسي على نفس الخادوم.</p><h2 dir="rtl">الخاتمة</h2><p dir="rtl">إنّ النّسخ الاحتياطي لبيانات Redis بالطرق المذكورة في هذا الدّرس جيّد عندما لا نمانع من النّسخ الاحتياطي للبيانات إلى دليل على نفس الخادوم.</p><p dir="rtl">يبقى النهج الأكثر أمانًا بالطّبع هو النّسخ الاحتياطي إلى جهاز آخر، تستطيع استكشاف المزيد من خيارات النسخ الاحتياطي بقراءة هذا الدّرس حول النُسَخ الاحتياطيّة: <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A9-%D9%81%D8%B9%D8%A7%D9%84%D8%A9-%D9%84%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D9%84%D9%84%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A-vps-r108/">كيفية اختيار استراتيجية فعالة للنسخ الاحتياطي للخادوم الخاص الافتراضي VPS</a>.</p><p dir="rtl">نستطيع استخدام العديد من طرق النّسخ الاحتياطي هذه مع نفس الملفّات في الدّليل <span style="font-family:courier new,courier,monospace;">var/lib/redi/</span>.</p><p dir="rtl">ترجمة -وبتصرّف- لـ <a rel="external nofollow" href="https://www.digitalocean.com/community/tutorials/how-to-back-up-your-redis-data-on-ubuntu-14-04">How To Back Up Your Redis Data on Ubuntu 14.04</a> لصاحبه finid.</p>
]]></description><guid isPermaLink="false">125</guid><pubDate>Wed, 28 Oct 2015 20:40:38 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x625;&#x639;&#x62F;&#x627;&#x62F; &#x62E;&#x627;&#x62F;&#x648;&#x645; Redis &#x643;&#x645;&#x62F;&#x627;&#x648;&#x644; &#x644;&#x644;&#x62C;&#x644;&#x633;&#x629; Session Handler &#x645;&#x646; &#x623;&#x62C;&#x644; PHP &#x639;&#x644;&#x649; Ubuntu</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-redis-%D9%83%D9%85%D8%AF%D8%A7%D9%88%D9%84-%D9%84%D9%84%D8%AC%D9%84%D8%B3%D8%A9-session-handler-%D9%85%D9%86-%D8%A3%D8%AC%D9%84-php-%D8%B9%D9%84%D9%89-ubuntu-r123/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_10/redis-session-handler-php.png.05388b14bf73cd714c83a88ee75ed1c2.png" /></p>

<p dir="rtl">إنّ Redis هو عبارة عن نظام تخزين وذاكرة مؤقتة cache بنمط مفتاح-قيمة key-value مفتوح المصدر، ويتم الإشارة له أيضًا كخادوم بُنية معطيات data structure بسبب دعمه المتقدّم للعديد من أنواع البيانات كالتلبيدات Hashes، اللوائح lists، المجموعات sets والخرائط الثنائيّة Bitmaps من بين الخواديم الأخرى، يدعم أيضًا الحشد clustering والذي يجعل منه مُستخدَمًا عادةً في البيئات ذات التوافر العالي والقابلة للتطوير.</p><p dir="rtl" style="text-align: center;"><a href="https://academy.hsoub.com/uploads/monthly_2015_10/redis-session-handler-php.png.2376974a1d3d5e2f0be93f7816c43a7d.png" class="ipsAttachLink ipsAttachLink_image"><img data-fileid="6203" src="https://academy.hsoub.com/uploads/monthly_2015_10/redis-session-handler-php.thumb.png.84ff2ab32cbb17c85cb4a40759025a67.png" class="ipsImage ipsImage_thumbnailed" alt="redis-session-handler-php.thumb.png.84ff"></a></p><p dir="rtl">سنشاهد في هذا الدّرس كيفيّة تثبيت وإعداد خادوم Redis خارجي لكي يتم استخدامه كمُداوِل للجلسة Session Handler من أجل تطبيق PHP يعمل على Ubuntu.</p><p dir="rtl">إنّ مُداوِل الجلسة مسؤول عن تخزين واسترجاع البيانات المحفوظة ضمن الجلسات، حيث تستخدم PHP افتراضيًّا الملفّات من أجل هذا، يُمكن استخدام مُداوِل الجلسة الخارجي من أجل إنشاء بيئات PHP قابلة للتطوير خلف مُوازِن الحِمل load balancer، حيث ستتصل جميع عُقَد nodes التّطبيقات إلى خادوم مركزي لتتشارك معلومات الجلسة.</p><h2 dir="rtl">المتطلبات الأساسية</h2><p dir="rtl">سنعمل في هذا الدّرس على خادومين مُنفصلين، ومن الهام من أجل الأداء والأمان أن تتوضّع كلا Droplets الخاصّة بهما في نفس مركز البيانات مع تمكين ربط الشبكات الخاصّة Private networking، وهذا ما سنحتاجه:</p><ul dir="rtl"><li>خادوم ويب PHP يقوم بتشغيل LAMP أو LEMP على Ubuntu – سنقوم بالإشارة لهذا الخادوم بـ web.</li><li>خادوم Ubuntu آخر حيث سيتم تثبيت Redis – سنقوم بالإشارة لهذا الخادوم بـ redis.</li></ul><p dir="rtl">سنحتاج نفاذ مناسب عبر SSH إلى كلا الخادومين كمستخدم اعتيادي مع صلاحيّات sudo.</p><p dir="rtl">نستطيع أيضًا من أجل خادوم Redis استخدام تطبيق Redis بنقرة واحدة والانتقال للخطوة الثانية.</p><h2 dir="rtl">الخطوة الأولى – تثبيت خادوم Redis</h2><p dir="rtl">إنّ أول شيء نحتاجه هو الحصول على خادوم Redis يعمل على redis Droplet الخاصّة بنا.</p><p dir="rtl">سنستخدم مُدير حِزَم Ubuntu الاعتيادي مع مستودع PPA موثوق يتم تزويدنا به بواسطة Chris Lea، وهو ضروري لكي نتأكّد أنّنا نحصل على آخر إصدار مُستَقر من Redis.</p><p dir="rtl">كنصيحة أمان عامّة ينبغي أن نستخدم PPAs من مصادر موثوقة فقط.</p><p dir="rtl">نُضيف في البداية مستودع PPA بتنفيذ الأمر التالي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo add-apt-repository ppa:chris-lea/redis-server</pre><p dir="rtl">نضغط <span style="font-family:courier new,courier,monospace;">Enter</span> للتأكيد.</p><p dir="rtl">نحتاج الآن لتحديث الذاكرة المؤقتة cache لمدير الحِزَم:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo apt-get update</pre><p dir="rtl">فلنقم أخيرًا بتثبيت Redis بتنفيذ الأمر التالي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo apt-get update</pre><p dir="rtl">ينبغي الآن أن يكون تم تثبيت Redis على خادومنا، ولاختبار التثبيت نُجرِّب هذا الأمر:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">redis-cli ping</pre><p dir="rtl">سيقوم هذا الأمر بالاتصال إلى نموذج instance من Redis يعمل على localhost على المنفذ 6379، ويجب أن نتلقّى PONG كاستجابة.</p><h2 dir="rtl">الخطوة الثانية – ضبط Redis لكي يقبل اتصالات خارجية</h2><p dir="rtl">يسمح Redis بالاتصال فقط إلى <span style="font-family:courier new,courier,monospace;">localhost</span> والذي يعني بشكل أساسي أنّنا نملك النفاذ إليه فقط من داخل الخادوم حيث تمّ تثبيت Redis، نحتاج إلى تغيير هذه الإعدادات للسماح بالاتصالات التي تأتي من خواديم أخرى على نفس الشبكة الخاصّة مثل الخادوم redis.</p><p dir="rtl">إنّ أول شيء يجب علينا فعله هو معرفة عنوان IP الجهاز Redis على الشبكة الخاصّة، ينبغي تنفيذ الخطوات التالية على الخادوم redis.</p><p dir="rtl">نقوم بتنفيذ الأمر <span style="font-family:courier new,courier,monospace;">ifconfig</span> للحصول على معلومات حول واجهات شبكتنا:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo ifconfig</pre><p dir="rtl">ينبغي أن نتلقّى خَرْجًا Output مُشابِهًا لما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">eth0     Link encap:Ethernet HWaddr 04:01:63:7e:a4:01
         inet addr:188.166.77.33 Bcast:188.166.127.255 Mask:255.255.192.0
         inet6 addr: fe80::601:63ff:fe7e:a401/64 Scope:Link
         UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
         RX packets:3497 errors:0 dropped:0 overruns:0 frame:0
         TX packets:3554 errors:0 dropped:0 overruns:0 carrier:0
         collisions:0 txqueuelen:1000
         RX bytes:4895060 (4.8 MB) TX bytes:619070 (619.0 KB)

eth1     Link encap:Ethernet HWaddr 04:01:63:7e:a4:02
         inet addr:10.133.14.9 Bcast:10.133.255.255 Mask:255.255.0.0
         inet6 addr: fe80::601:63ff:fe7e:a402/64 Scope:Link
         UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
         RX packets:8 errors:0 dropped:0 overruns:0 frame:0
         TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
         collisions:0 txqueuelen:1000
         RX bytes:648 (648.0 B) TX bytes:578 (578.0 B)</pre><p dir="rtl">فلنبحث عن<span style="font-family:courier new,courier,monospace;"> inet_addr</span> المُخصَّص للواجهة eth1، في هذه الحالة نجده <strong>10.133.14.9</strong>، وهذا هو عنوان IP الذي سنستخدمه لاحقًا للاتصال إلى الخادوم redis من الخادوم web.</p><p dir="rtl">نفتح الملف <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span> باستخدام مُحرِّر سطر الأوامر command line editor المُفضَّل لدينا ونبحث عن السّطر الذي يحتوي على التّعريف bind، يجب أن نُضيف عنوان IP للشبكة الخاصّة لدينا إلى هذا السّطر كما يلي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo vim /etc/redis/redis.conf</pre><p><strong>etc/redis/redis.conf/</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">bind localhost 10.133.14.9</pre><p dir="rtl">إن وجدنا <strong>127.0.0.1</strong> بدلًا من <span style="font-family:courier new,courier,monospace;">localhost</span> فليس هناك مشكلة، فقط نضيف عنوان IP الخاص لدينا بعد ما هو موجود.</p><p dir="rtl">نحتاج الآن لإعادة تشغيل الخدمة Redis لتطبيق التغييرات:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo service redis-server restart</pre><p dir="rtl">إن قمنا بتثبيت Redis باستخدام تطبيق النقرة الواحدة فسيكون اسم الخدمة <strong>redis</strong> بدلًا من <strong>redis-server</strong>. ولإعادة تشغيلها نكتب الأمر:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo service redis restart</pre><p dir="rtl">بعد هذه التغييرات سيكون أي خادوم داخل نفس الشّبكة الخاصّة قادرًا على الاتصال إلى نموذج Redis.</p><h2 dir="rtl">الخطوة الثالثة – تعيين كلمة سر لخادوم Redis</h2><p dir="rtl">لإضافة طبقة أمان إضافيّة إلى تثبيت Redis لدينا فنحن نُشجِّع على تعيين كلمة سر للنفاذ إلى بيانات الخادوم، سنقوم بتحرير نفس ملف الإعدادات الذي قمنا بتحريره في الخطوة السّابقة <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo vim /etc/redis/redis.conf</pre><p dir="rtl">نقوم الآن بإلغاء التّعليق uncomment عن السّطر الذي يحتوي على requirepass ونضع كلمة سر قويّة:</p><p dir="rtl"><strong>etc/redis/redis.conf/</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">requirepass yourverycomplexpasswordhere</pre><p>نعيد تشغيل خدمة Redis لكي يتم تطبيق التغييرات:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo service redis-server restart
</pre><h2 dir="rtl">الخطوة الرابعة – اختبار الاتصال إلى Redis والاستيثاق Authentication</h2><p dir="rtl">لكي نتأكّد من أنّ كافّة التغييرات تعمل كما هو متوقّع نقوم بالاتصال إلى خدمة Redis من داخل الجهاز redis:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">redis-cli -h 10.133.14.9</pre><p><strong style="line-height: 22.4px;">المخرجة:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">10.133.14.9:6379&gt;</pre><p dir="rtl">بالرغم من أنّه ليس من الإجباري هنا تحديد المُعامِل host (بما أنّنا نتصل من localhost)، فقد قمنا بذلك للتأكّد من أنّ خدمة Redis تقبل الاتصالات الموجّهة إلى واجهة الشّبكة الخاصّة.</p><p dir="rtl">إن كُنّا قد عرّفنا كلمة سر وحاولنا الآن النفاذ إلى البيانات ينبغي أن نتلقّى خطأ AUTH:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">10.133.14.9:6379&gt; keys *</pre><p><strong style="line-height: 22.4px;">المخرجة:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">(error) NOAUTH Authentication required.</pre><p dir="rtl">نحتاج من أجل الاستيثاق أن نقوم فقط بتنفيذ الأمر<span style="font-family:courier new,courier,monospace;"> AUTH</span> مع تزويده بنفس كلمة السّر التي عرّفناها في الملف <span style="font-family:courier new,courier,monospace;">etc/redis/redis.conf/</span>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">10.133.14.9:6379&gt; AUTH yourverycomplexpasswordhere</pre><p dir="rtl">ينبغي أن نتلقّى استجابة<strong> OK</strong>، والآن إن قمنا بتنفيذ الأمر:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">10.133.14.9:6379&gt; keys *</pre><p dir="rtl">يجب أن يكون الخَرْج مُشابِهًا لما يلي:</p><p><strong style="line-height: 22.4px;">المخرجة:</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">(empty list or set)</pre><p dir="rtl">يعني هذا الخَرْج فقط أنّ خادوم Redis لدينا فارغ، وهو ما كُنّا نتوقّعه تمامًا، حيث أنّ الخادوم web ليس مُعدًّا بعد لاستخدام خادوم Redis هذا كمداوِل للجلسة.</p><p dir="rtl">فلنحافظ على جلسة SSH هذه مفتوحة ومتّصلة إلى<span style="font-family:courier new,courier,monospace;"> redis-cli</span> بينما نقوم بتنفيذ الخطوات التالية، سنعود إلى المُحِث <span style="font-family:courier new,courier,monospace;">prompt redis-cli</span> لكي نتحقّق من أنّه يتم تخزين بيانات الجلسة بشكل مناسب، وذلك بعد أن نقوم بالتغييرات الضروريّة للخادوم web.</p><h2 dir="rtl">الخطوة الخامسة – تثبيت امتداد Redis Extension على الخادوم web</h2><p dir="rtl">يجب تنفيذ الخطوات التالية على الخادوم web، نحتاج إلى تثبيت الامتداد PHP Redis وإلّا لن تكون PHP قادرة على الاتصال إلى الخادوم Redis.</p><p dir="rtl">في البداية نقوم بتحديث الذاكرة المؤقتة لمدير الحِزَم بتنفيذ الأمر التالي:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo apt-get update</pre><p dir="rtl">نقوم بعدها بتثبيت الحِزمَة <strong>php5-redis</strong>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo apt-get install php5-redis</pre><p dir="rtl">ينبغي الآن أن يكون الخادوم web قادرًا على الاتصال إلى Redis.</p><h2 dir="rtl">الخطوة السادسة – تعيين Redis كمداول الجلسة الافتراضي على الخادوم web</h2><p dir="rtl">نحتاج الآن لتحرير الملف <span style="font-family:courier new,courier,monospace;">php.ini</span> على الخادوم web لتغيير مُداوِل الجلسة الافتراضي لـ PHP، يعتمد مسار الملف على استخدامنا LAMP أو LEMP، ففي LAMP على Ubuntu يكون المسار عادةً في <span style="font-family:courier new,courier,monospace;">etc/php5/apache2/php.ini/</span>، وفي LEMP على Ubuntu يكون المسار عادةً في <span style="font-family:courier new,courier,monospace;">etc/php5/fpm/php.ini/</span>.</p><p dir="rtl">إن لم نكن متأكّدين من مسار الملف<span style="font-family:courier new,courier,monospace;"> php.ini</span> نستطيع إيجاده بطريقة سهلة باستخدام الدالّة <span style="font-family:courier new,courier,monospace;">()phpinfo</span>، فقط نقوم بوضع الشيفرة code التالية في ملف نسمّيه <span style="font-family:courier new,courier,monospace;">info.php</span> داخل دليل الويب الجذري root:</p><pre data-pbcklang="php" data-pbcktabsize="4" class="php ipsCode prettyprint">&lt;?php
phpinfo();</pre><p dir="rtl">عند الوصول للـ script من متصفحنا ننظر إلى السطر الذي يحتوي على ملف الإعدادات الذي تمّ تحميله، ينبغي أن نجد المسار الصحيح للملف<span style="font-family:courier new,courier,monospace;"> php.ini</span> قد تمّ تحميله.</p><p dir="rtl">يجب ألّا ننسى إزالة الملف <span style="font-family:courier new,courier,monospace;">info.php </span>بعد الانتهاء من هذا، لأنّه يحتوي على معلومات حسّاسة حول البيئة لدينا.</p><p dir="rtl">نفتح الملف <span style="font-family:courier new,courier,monospace;">php.ini</span> ونبحث عن السّطر الذي يحتوي على<span style="font-family:courier new,courier,monospace;"> session.save_handler</span>، إنّ القيمة الافتراضيّة له هي files، ينبغي أن نقوم بتغييرها إلى redis.</p><p dir="rtl">على بيئة <strong>LAMP</strong>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo vim /etc/php5/apache2/php.ini</pre><p dir="rtl">على بيئة <strong>LEMP</strong>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo vim /etc/php5/fpm/php.ini</pre><p dir="rtl"><strong>etc/php5/fpm/php.ini/</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">session.save_handler = redis</pre><p dir="rtl">ينبغي أن نجد الآن السّطر الذي يحتوي على <span style="font-family:courier new,courier,monospace;">session.save_path</span>، نقوم بإلغاء التعليق عنه وتغيير قيمته بحيث تحتوي مقطع string اتصال Redis، يجب أن يتّبع المحتوى النَّسَق التالي كله في سطر واحد:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">tcp://IPADDRESS:PORT?auth=REDISPASSWORD</pre><p dir="rtl"><strong>etc/php5/fpm/php.ini/</strong></p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">session.save_path = "tcp://10.133.14.9:6379?auth=yourverycomplexpasswordhere"</pre><p dir="rtl">نحتاج فقط إلى تزويد المُعامِل <span style="font-family:courier new,courier,monospace;">auth</span> إن كُنّنا قد أعددنا كلمة سر عند ضبط Redis.</p><p dir="rtl">نحفظ الملف ونعيد تشغيل خدمة php.</p><p dir="rtl">على بيئة<strong> LAMP</strong>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo service apache2 restart</pre><p dir="rtl">على بيئة <strong>LEMP</strong>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo service php5-fpm restart
</pre><h2 dir="rtl">الخطوة السابعة – اختبار مداولة Redis للجلسة</h2><p dir="rtl">لكي نتأكّد من أنّ الجلسات لدينا يتم الآن مُداوَلتها من قبل Redis نحتاج PHP script أو تطبيق يقوم بتخزين المعلومات في الجلسات، سنستخدم Script بسيط يقوم بتنفيذ عدّاد Counter، حيث تتم زيادة الرقم كلّما أعدنا تحميل الصّفحة.</p><p dir="rtl">ننشئ ملف يُدعى <span style="font-family:courier new,courier,monospace;">test.php</span> على الخادوم web ونضعه داخل المجلّد الجذر للمستند document root folder:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">sudo vim /usr/share/nginx/html/test.php</pre><p dir="rtl">لا يجب أن ننسى تغيير <span style="font-family:courier new,courier,monospace;">usr/share/nginx/html/</span> بحيث يكون مسار جذر المستند لدينا.</p><p><strong>usr/share/nginx/html/test.php/</strong></p><pre data-pbcklang="php" data-pbcktabsize="4" class="php ipsCode prettyprint">&lt;?php

//simple counter to test sessions. should increment on each page reload.
session_start();
$count = isset($_SESSION['count']) ? $_SESSION['count'] : 1;

echo $count;

$_SESSION['count'] = ++$count;</pre><p>نتوجّه في متصفحنا إلى <a rel="external nofollow" href="http://web/test.php">http://web/test.php</a> لكي نصل إلى الـ script، ينبغي أن يقوم بزيادة الرقم كلما قمنا بإعادة تحميل الصّفحة.</p><p dir="rtl">يجب أن نمتلك الآن معلومات الجلسة مُخزّنة على خادوم Redis، وللتحقّق من ذلك نعود إلى جلسة SSH على الجهاز redis، حيث قُمنا مُسبَقًا بالاتصال إلى خدمة Redis باستخدام<span style="font-family:courier new,courier,monospace;"> redis-cli</span>.</p><p dir="rtl">نقوم بجلب المحتوى مرّة أخرى باستخدام <strong>* keys</strong>:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">10.133.14.9:6379&gt; keys *</pre><p dir="rtl">ينبغي أن نحصل على خَرْج مُشابِه لهذا:</p><pre data-pbcklang="html" data-pbcktabsize="4" class="html ipsCode prettyprint">1) "PHPREDIS_SESSION:j9rsgtde6st2rqb6lu5u6f4h83"</pre><p dir="rtl">وهذا يُظهِر أنّه يتم تخزين معلومات الجلسة على خادوم Redis، ونستطيع الاتصال من خواديم ويب إضافيّة إلى خادوم Redis بطريقة مماثلة.</p><h2 dir="rtl">الخاتمة</h2><p dir="rtl">إنّ Redis خدمة تخزين من نمط مفتاح-قيمة سريعة وقويّة نستطيع أيضًا استخدامها كمُداوِل للجلسة في PHP، مما يُمكّننا من الحصول على بيئات PHP قابلة للتطوير عن طريق تزويدها لنا بنظام مُوزَّع لتخزين الجلسة، وللمزيد من المعلومات حول تقييس scaling تطبيقات PHP تستطيع التحقّق من هذا الدّرس: تقييس تطبيقات PHP أفقيًّا.</p><p dir="rtl">ترجمة -وبتصرّف- للمقال <a rel="external nofollow" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-redis-server-as-a-session-handler-for-php-on-ubuntu-14-04">How to Set Up a Redis Server as a Session Handler for PHP on Ubuntu 14.04</a> لصاحبته Erika Heidi.</p>
]]></description><guid isPermaLink="false">123</guid><pubDate>Mon, 19 Oct 2015 19:12:15 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x62A;&#x646;&#x635;&#x64A;&#x628; &#x648;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Redis &#x639;&#x644;&#x649; &#x623;&#x648;&#x628;&#x646;&#x62A;&#x648;</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r46/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_05/redis.png.c6718009a8c1061c011f6a749cccd172.png" /></p>

<div id="preview-contents">
	<p class="preview-content" id="wmd-preview">
		تمّ تطوير <code>Redis</code> في عام 2009، وهو مُخزّن بيانات بنمط <strong>المفتاح والقيمة</strong> (key value)، وهو مفتوح المصدر، ويُقدّم مرونة في الاستخدام، وكما في جميع نمط قواعد البيانات من نوع <strong>NoSQL</strong> الّتي اتبعها <code>Redis</code>، أمثال <strong>Cassandra</strong>, <strong>CouchDB</strong>, <strong>MongoDB</strong>، فإن <code>Redis</code> يَسمح للمُستخدِم بتخزين كميات ضخمة من البيانات بدون التقيد المفروض بقواعد البيانات العلائقيّة/الارتباطيّة (relational database)، بالإضافة إلى أنّه قد تمّ مُشابهته ومقارنته مع <strong><a href="http://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%88%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%B0%D9%91%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D9%8F%D8%AE%D8%A8%D9%91%D8%A6%D8%A9-memcache-%D8%B9%D9%84%D9%89-ubuntu-r47/">memcache</a></strong>، فيُمكن استخدامه بعناصره الأساسية كنظام تخبئة.
	</p>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-31">
		<h2 id="إعداد-بيئة-ومتطلبات-redis">
			إعداد بيئة ومتطلبات Redis
		</h2>

		<p>
			قبل الشروع في تنصيب <code>redis</code>، يجب توفّر بعض المُتطلّبات والّتي من شأنها أنّ تجعل من عمليّة التنصيب أكثر سهولة.
		</p>

		<p>
			سيتمّ في البداية تحديث جميع حزم <span style="font-family:courier new,courier,monospace;"><strong>apt-get</strong></span>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
sudo apt-get update</pre>

		<p>
			سيتمّ بعد ذلك تحميل مُترجم (compiler) باستخدام الحزمة<span style="font-family:courier new,courier,monospace;"> <strong>build-essential</strong></span>، والّتي من شأنها المساعدة في تنصيب <code>Redis</code> من المصدر:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
sudo apt-get install build-essential</pre>

		<p>
			سيتمّ في الخطوة الأخيرة تحميل الأداة <strong>tcl</strong> الّتي يَعتمد عليها <code>Redis</code>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
sudo apt-get install tcl8.5</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-32">
		<h2 id="تنصيب-redis">
			تنصيب Redis
		</h2>

		<p>
			بعد أنّ تمّ تنصيب المُتطلّبات الأساسيّة، فمن المُمكن الآن الشروع وتنصيب <code>redis</code>، ويُمكن تحديد الإصدار المطلوب أو تحميل الإصدار الأخير والذي سيحمل دائمًا الاسم <span style="font-family:courier new,courier,monospace;"><strong>redis-stable</strong></span>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
wget http://download.redis.io/redis-stable.tar.gz</pre>

		<p>
			يجب بعد ذلك فك ضغط الملفّ والانتقال إليه:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
tar xvzf redis-stable.tar.gz
cd redis-stable</pre>

		<p>
			يجب الآن المُتابعة مع الأمر <span style="font-family:courier new,courier,monospace;"><strong>make</strong></span>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
make</pre>

		<p>
			ولتنصيب <code>Redis</code> على كامل النّظام، فيُمكن إما نسخ ملفاته من المصدر:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
sudo cp src/redis-server /usr/local/bin/
sudo cp src/redis-cli /usr/local/bin/</pre>

		<p>
			أو تنفيذ الأمر التّالي:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
sudo make install</pre>

		<p>
			بعد انتهاء عمليّة التنصيب، من المُستحسن تشغيل <code>Redis</code> كحارس (daemon) في خلفيّة النّظام، ولعمل ذلك يأتي <code>Redis</code> بملفّ برمجي (سكريبت) لهذه المُهمّة.
		</p>

		<p>
			يجب الانتقال إلى المسار <span style="font-family:courier new,courier,monospace;"><strong>utils</strong></span> للوصول إلى هذا الملفّ:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
cd utils</pre>

		<p>
			ومن ثم تشغيل الملفّ الخاص بتوزيعات <strong>Ubuntu/Debian</strong>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
sudo ./install_server.sh</pre>

		<p>
			سيَعرض السكريبت بعض الأسئلة لإتمام عمليّة التهيئة، ولكن يُمكن الاعتماد على الإعداد الافتراضي والاكتفاء بالضغط على <strong>Enter</strong>، وبعد انتهاء عملية التهيئة سيكون خادم <code>Redis</code> يعمل في الخلفيّة (background).
		</p>

		<p>
			يُمكن تنفيذ الأمر التّالي للوصول إلى قاعدة البيانات <code>Redis</code>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
redis-cli</pre>

		<p>
			يُمكن اختبار <code>Redis</code> كالتّالي:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
$ redis-cli
redis 127.0.0.1:6379&gt; ping
PONG
redis 127.0.0.1:6379&gt; set mykey somevalue
OK
redis 127.0.0.1:6379&gt; get mykey
"somevalue"</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-33">
		<h2 id="أوامر-redis">
			أوامر Redis
		</h2>

		<p>
			يتمّ إضافة بعض المُعطيات إلى سلسلة رموز (string)، والّتي تُعتبر من أبسط أنواع البيانات (datatype)، باستخدام الأمر التّالي:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; SET users:SomeOne "job: SomeJob, email:example@example.com, age: some number "
OK</pre>

		<p>
			تمّ في الأمر السابق استخدام الأمر <strong>SET</strong> متبوعًا بالمُفتاح <strong>users:SomeOne</strong>، ومن ثمّ القيمة (value)، وهي سلسلة الرموز (string) نفسها.
		</p>

		<p>
			لا تُؤثر النقطتان (:) الموجودة في المُفتاح على الأمر السابق، ولكن من المُمكن الاستفادة منهما في وصف أوضح للمُفتاح المُراد تعيينه.
		</p>

		<p>
			يُمكن استعراض تفاصيل سلسلة الرموز السابقة باستخدام الأمر <span style="font-family:courier new,courier,monospace;"><strong>GET</strong></span>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
    GET users:SomeOne 
"job: SomeJob, email:example@example.com, age: some number "</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-34">
		<h3 id="المجالات-ranges">
			المجالات (Ranges)
		</h3>

		<p>
			يتمّ تحديد <strong>مجال</strong> باستخدام مُعاملين والذين سيُمثلان العنصر الأوّل والعنصر الأخير، فالعنصر الأوّل سيُعتبر <strong>0</strong>، وإن كان المُعامل الأخير <strong>-1</strong>، فإن جميع العناصر حتّى نهاية القائمة سيتمّ شملها، فعلى سبيل المثال، إن كانت قائمة تحتوي على ألوان قوس قزح مُرتبة بالترتيب <strong>ROYGBV</strong>، فيُمكن استعراضها كما في التّالي:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; LRANGE ROYGBV 0 3
1) "red"
2) "orange"
3) "yellow"
4) "green"
&gt; LRANGE ROYGBV 0 -1
1) "red"
2) "orange"
3) "yellow"
4) "green"
5) "blue"
6) "violet"
&gt; LRANGE ROYGBV 3 -1
1) "green"
2) "blue"
3) "violet"</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-35">
		<h3 id="انتهاء-الصلاحية-expiration">
			انتهاء الصلاحية (Expiration)
		</h3>

		<p>
			لا يُعتبر <code>Redis</code> ذو فائدة في تخزين المعلومات فقط، بل يُمكن استخدامه أيضًا في إنهاء صلاحيتها.
		</p>

		<p>
			يُعيّن الوقت اللازم على المُفتاح أنّ يتواجد خلاله إما بالثواني أو باستخدام طابع الوقت الخاصّ <strong>يونكس</strong> (Unix Time stamp)، ويتحكم الأمر <strong>EXPIRE</strong> بالمُدّة الّتي يتوجب على المُفتاح التواجد فيها، ويَعرض الأمر <strong>TTL</strong> الوقت المُتبقي حتّى انتهاء الصلاحيّة.
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; SET classified:information "Secret Stuff"
OK
&gt; EXPIRE classified:information 45
(integer) 1
&gt; TTL classified:information
(integer) 31</pre>

		<p>
			وفي مُحاولة لاستعادة المعلومات بعد انقضاء مدّة الصلاحيّة، ستكون النتيجة هي <strong>nil</strong>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; GET classified:information
(nil)</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-36">
		<h3 id="التزايد-أو-العلاوة-incrementing">
			التزايد أو العلاوة (Incrementing)
		</h3>

		<p>
			يَملك <code>Redis</code> القدرة على زيادة سلاسل الرموز (strings) في قاعدة بياناته، مع الانتباه أنّه في حال وجود عمليّة جارية في زيادة قيمةٍ ما، فلا يستطيع أمر آخر التدخل والتعديل، هذا الأمر من شأنه أن يجعل من قاعدة البيانات ثابتة وغير شائبة.
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; SET population 6
OK
&gt; INCRBY population 10
(integer) 16
&gt; INCR population
(integer) 17</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-37">
		<h3 id="المداولات-transactions">
			المداولات (Transactions)
		</h3>

		<p>
			يملك <code>Redis</code> القدرة على إنجاز المُداولات (transactions)، والّتي يجب أنّ تخضع إلى:
		</p>

		<ul><li>
				تنفيذ الأوامر بالترتيب، ولن يتمّ مقاطعتها خلال العمليّة من قبل طلبات أُخرى.
			</li>
			<li>
				على المُداولات أنّ تُعالج كلها دفعةً واحدة.
			</li>
		</ul><p>
			تبدأ المُداولات بالأمر <strong>MULTI</strong> وبعد ذلك لتنفيذها يتمّ استخدام الأمر <strong>EXEC</strong>.
		</p>

		<p>
			سيتمّ الخروج من المُداولة، عند أي مُقاطعة للعمليّة، وسيواجه <code>Redis</code> خطًا سيوقفه من إعادة التشغيل حتّى تنفيذ الأمر <code>edis-check-aof</code> ليتمّ التراجع عن المُداولة الجزئية الّتي تمّت بالفعل قبل حدوث الخطأ وحذفها.
		</p>

		<p>
			سيَتمكّن الخادم بعد ذلك من إعادة التشغيل.
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; MULTI
OK
&gt; SET population 6
QUEUED
&gt; INCRBY population 10
QUEUED
&gt; INCR population
QUEUED
&gt; EXEC
1) OK
2) (integer) 16
3) (integer) 17</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-38">
		<h2 id="أنواع-البيانات-في-redis">
			أنواع البيانات في Redis
		</h2>

		<p>
			يَملك <code>Redis</code> خمسة أنواع من البيانات:
		</p>

		<ul><li>
				Strings
			</li>
			<li>
				Sets
			</li>
			<li>
				Sorted Sets
			</li>
			<li>
				Lists
			</li>
			<li>
				Hashes
			</li>
		</ul></div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-39">
		<h3 id="سلاسل-الرموز-strings">
			سلاسل الرموز (Strings)
		</h3>

		<p>
			تُعتبر سلاسل الرموز جوهر وأساس الأنواع في <code>Redis</code>
		</p>

		<p>
			الأوامر التّالية هي الأوامر الشائعة مع سلاسل الرموز:
		</p>

		<ul><li>
				<strong>SET</strong>: يُعيّن قيمة إلى مُفتاح.
			</li>
			<li>
				<strong>GET</strong>: يجلب قيمة من مُفتاح.
			</li>
			<li>
				<strong>DEL</strong>: حذف مُفتاح وقيمته.
			</li>
			<li>
				<strong>INCR</strong>: زيادة قيمة مُفتاح.
			</li>
			<li>
				<strong>INCRBY</strong>: زيادة قيمة مُفتاح بقيم مُحدّدة.
			</li>
			<li>
				<strong>EXPIRE</strong>: الوقت اللازم على المُفتاح التواجد به، ويُعيّن بالثواني.
			</li>
		</ul><p>
			تُستخدم سلاسل الرموز في تخزين الكائنات (objects):
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; SET newkey "the redis string begins"
OK
&gt; GET newkey
"the redis string begins"</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-40">
		<h3 id="المجموعات-sets">
			المجموعات (Sets)
		</h3>

		<p>
			يُمكن الجمع بين سلاسل الرموز (strings)، ولذلك يُمكن استخدام مجموعات <code>Redis</code>، والّتي يُمكن القول عنها أنها مجموعة من سلاسل الرموز غير المُرتبة.
		</p>

		<p>
			بعض الأوامر الشائعة مع المجموعات:
		</p>

		<ul><li>
				<strong>SADD</strong>: إضافة عنصر أو عناصر إلى مجموعة.
			</li>
			<li>
				<strong>SMEMBERS</strong>: جلب جميع العناصر المُعيّنة.
			</li>
			<li>
				<strong>SINTER</strong>: إيجاد تقاطع أكثر من مجموعة.
			</li>
			<li>
				<strong>SISMEMBER</strong>: التأكّد من وجود قيمة في مجموعة.
			</li>
			<li>
				<strong>SRANDMEMBER</strong>: جلب عنصر عشوائي.
			</li>
		</ul><p>
			يُستفاد من المجموعات في <code>Redis</code> في العديد من الحالات، ولأن كل عنصر من مجموعة هو فريد، فإن إضافة عنصر إلى المجموعة لا يتطلّب عمليّة “افحص قبل الإضافة”، بدلًا من ذلك، فإن المجموعة ستتأكّد فيما إذا كان العنصر مكرّرًا أم لا، وذلك عند أي تنفيذ للأمر <strong>SADD</strong>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; SADD colors red
(integer) 1
redis 127.0.0.1:6379&gt; SADD colors orange
(integer) 1
redis 127.0.0.1:6379&gt; SADD colors yellow
(integer) 1
redis 127.0.0.1:6379&gt; SADD colors orange
(integer) 0
redis 127.0.0.1:6379&gt; SMEMBERS colors
1) "red"
2) "yellow"
3) "orange"</pre>

		<p>
			يُستفاد من المجموعات بشكل خاصّ في فحص وضبط عناوين <strong>IPs</strong> في حال أنها فريدة لزوار صفحة ما، أو مثلًا في استخلاص عناصر بشكل عشوائي بالأمر <strong>SRANDMEMBER</strong>.
		</p>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-41">
		<h3 id="المجموعات-المرتبة-sorted-sets">
			المجموعات المرتبة (Sorted Sets)
		</h3>

		<p>
			إن المجموعات المُرتبة هي اسمٌ على مُسمّى، فهي مجموعة من سلاسل الرموز (strings) مُرتبطة مع رقم مع ترتيب، وهو بشكلٍ افتراضيّ من الأصغر إلى الأكبر.
		</p>

		<p>
			يَعمل هذا النوع من البيانات بشكل مُلائم جدًا مع المجالات (ranges)، وبسبب التّرتيب الخاصّ به، فإن إضافة وحذف وتحديث القيم تتمّ بسرعة ملحوظة.
		</p>

		<p>
			بعض الأوامر الشائعة مع المجموعات المُرتّبة:
		</p>

		<ul><li>
				<strong>ZADD</strong>: إضافة عناصر إلى مجموعة مُرتّبة.
			</li>
			<li>
				<strong>ZRANGE</strong>: عرض عناصر مجموعة مُرتّبة.
			</li>
			<li>
				<strong>ZREVRANGE</strong>: عرض عناصر مجموعة مُرتّبة بشكل عكسي.
			</li>
			<li>
				<strong>ZREM</strong>: إزالة عناصر من مجموعة مُرتّبة.
			</li>
		</ul><p>
			يُمكن إنشاء مجموعة مُرتّبة كما في المثال التّالي، وهو لمساحة بعض البلدان، فعلى الرغم من تعيينها بشكل عشوائي، فإن استعراضها يَكون على التّرتيب:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; ZADD countries 9 Tuvalu
(integer) 1
&gt; ZADD countries 62 Liechtenstein
(integer) 1
&gt; ZADD countries .7 Monaco
(integer) 1
&gt; ZADD countries .2 VaticanCity
(integer) 1
&gt; ZADD countries 107 Seychelles
(integer) 1
redis 127.0.0.1:6379&gt; ZRANGE countries 0 -1
1) "VaticanCity"
2) "Monaco"
3) "Tuvalu"
4) "Liechtenstein"
5) "Seychelles"</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-42">
		<h3 id="القوائم-lists">
			القوائم (Lists)
		</h3>

		<p>
			تُعتبر القوائم في <code>Redis</code> مجموعة من القيم المُرتّبة، وبالتّالي فهي عكس <strong>المجموعات</strong> (Sets)، والّتي هي غير مُرتّبة، فيُمكن إضافة عناصر إلى بداية ونهاية قائمة، حتّى بوجود ملايين العناصر داخل القائمة، وبسرعة كبيرة جدًا.
		</p>

		<p>
			الأوامر الأكثر شيوعًا مع القوائم:
		</p>

		<ul><li>
				<strong>LPUSH</strong>: إضافة قيمة في بداية قائمة.
			</li>
			<li>
				<strong>RPUSH</strong>: إضافة قيمة في نهاية قائمة.
			</li>
			<li>
				<strong>LPOP</strong>: جلب وإزالة العنصر الأوّل في قائمة.
			</li>
			<li>
				<strong>LREM</strong>: إزالة العناصر من قائمة.
			</li>
			<li>
				<strong>LRANGE</strong>: جلب مجال من العناصر من قائمة.
			</li>
			<li>
				<strong>LTRIM</strong>: تعديل قائمة بترك عناصر مُحدّدة من مجال.
			</li>
		</ul><p>
			أمثلة:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; RPUSH lunch.provider alice
(integer) 1
&gt; RPUSH lunch.provider bob
(integer) 2
&gt; RPUSH lunch.provider carol
(integer) 3
&gt; RPUSH lunch.provider don
(integer) 4
&gt; RPUSH lunch.provider emily
(integer) 5</pre>

		<p>
			عند الرغبة بالدفع (push) إلى واجهة صف الانتظار (queue)، فمن المُمكن استخدام الأمر <strong>LPUSH</strong>:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
LPUSH lunch.provider zoe
(integer) 6</pre>

		<p>
			يُمكن استخدام الأمر <strong>LRANGE</strong> لاستعراض عناصر القائمة جميعًا:
		</p>

		<pre>
<code> LRANGE lunch.provider 0 -1
1) "zoe"
2) "alice"
3) "bob"
4) "carol"
5) "don"
6) "emily"
</code></pre>

		<p>
			تُستخدم القوائم في مُعظم الأحيان لإنشاء خط زمني للأحداث، أو في الحفاظ على مجموعة مُحدودة من العناصر
		</p>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-43">
		<h3 id="المبعثرات-hashes">
			المبعثرات (Hashes)
		</h3>

		<p>
			تُعتبر المبَعثرات في <code>Redis</code> من الأدوات المُفيدة في تمثيل الكائنات (objects) ذو العديد من الحقول (fields)، فهي مُعدّة لتخزين عدد هائل من الحقول في حجم صغير، وتستطيع البَعثرة تخزين أكثر من أربعة مليارات من أزواج قيم الحقول (field-value).
		</p>

		<p>
			بعض أوامر البعثرة الأكثر شيوعًا في <code>Redis</code>:
		</p>

		<ul><li>
				<strong>HMSET</strong>: تعيين قيم بعثرة مُتعدّدة.
			</li>
			<li>
				<strong>HSET</strong>: تعيين حقل البعثرة بقيمة سلسلة رموز (string).
			</li>
			<li>
				<strong>HGET</strong>: جلب قيمة حقل البعثرة.
			</li>
			<li>
				<strong>HMGET</strong>: جلب جميع القيم من حقول البعثرة المعطاة.
			</li>
			<li>
				<strong>HGETALL</strong>: جلب جميع القيم لبعثرة ما.
			</li>
		</ul><p>
			تُستخدم البَعثرة في وصف حساب المُستخدِم:
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; HMSET user:1 username someuser password 4bAc0s email someuser@example.com
OK
&gt; HGETALL user:1
1) "username"
2) "jsmith"
3) "password"
4) "4bAc0s"
5) "email"
6) "jsmith@gmail.com"</pre>

		<p>
			يتمّ استخدام الأمر <strong>HMGET</strong> للبحث عن معلومة مُحدّدة، وعرض القيم للحقول المطلوبة فقط.
		</p>

		<pre class="ipsCode prettyprint" data-pbcklang="" data-pbcktabsize="">
&gt; HMGET user:1 username email
1) "someuser"
someuser@example.com</pre>
	</div>

	<div class="wmd-preview-section preview-content" id="wmd-preview-section-44">
		<h2 id="الختام">
			الختام
		</h2>

		<p>
			انتشر Redis بسرعة كبيرة، واكتسب شهرةً واسعة بين المواقع أمثال: Github, Flickr, Disqus، وغيرها من المواقع، وذلك لتوافقه مع مُعظم لغات البرمجة، فهو بالفعل تقنية مُفيدة يُنصح بالاعتماد عليها وتجربتها.
		</p>

		<p>
			ترجمة -وبتصرّف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-redis" rel="external nofollow">How To Install and Use Redis</a> لصاحبته <a href="https://www.digitalocean.com/community/users/etel" rel="external nofollow">Etel Sverdlov</a>.
		</p>
	</div>
</div>
]]></description><guid isPermaLink="false">46</guid><pubDate>Fri, 08 May 2015 21:21:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x625;&#x639;&#x62F;&#x627;&#x62F; &#x645;&#x64A;&#x632;&#x629; &#x627;&#x644;&#x62A;&#x62E;&#x632;&#x64A;&#x646; &#x627;&#x644;&#x645;&#x624;&#x642;&#x62A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Redis &#x644;&#x62A;&#x633;&#x631;&#x64A;&#x639; &#x648;&#x648;&#x631;&#x62F;&#x628;&#x631;&#x64A;&#x633; &#x639;&#x644;&#x649; Ubuntu 14.04</title><link>https://academy.hsoub.com/devops/servers/databases/redis/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D9%85%D9%8A%D8%B2%D8%A9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-redis-%D9%84%D8%AA%D8%B3%D8%B1%D9%8A%D8%B9-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-%D8%B9%D9%84%D9%89-ubuntu-1404-r14/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2015_03/wp-redis_480x300.png.645731aa71574d5b425ad8d4e1298199.png" /></p>
<h2 dir="rtl">مقدّمة</h2><p dir="rtl">Redis هو عبارة عن مَخزَن قيم مفاتيح key value store مفتوح المصدر يمكنه العمل كمخزن لتخزين البيانات في الذاكرة in-memory store أو كمخزن تخزين بيانات مؤقت. Redis هو خادوم بنية بيانات data structure server يُمكن استخدامه إمّا كخادوم قاعدة بيانات لوحده أو مرتبطًا مع قاعدة بيانات أخرى مثل MySQL لتسريع بعض الأشياء، وهو ما سنشرحه في هذا الدليل.</p><p dir="rtl">في هذا الدليل، سيتم إعداد Redis كذاكرة تخزين مؤقت cache لووردبريس لتخفيف الحمل الزائد عن عمليات الاستعلام query التي تتم على قاعدة البيانات المُستخدمة لعرض صفحة ووردبريس. النتيجة ستكون توفير موقع ووردبريس أسرع بكثير من ذي قبل ويستخدم موارد أقل للتعامل مع قاعدة البيانات بالإضافة إلى توفير ذاكرة تخزين مؤقت مضبوطة ومستمرة. سيتم استخدام توزيعة Ubuntu 14.04 في هذا الدليل.</p><p dir="rtl">صحيحٌ أن الوضع يختلف من موقعٍ إلى آخر، ولكن أدناه ستجد مثالًا على قياس أداء الصفحة الرئيسية لعملية تثبيت ووردبريس افتراضية مع وبدون Redis, كما تم إعداده باستخدام هذا الدليل. تم استخدام أدوات مطوري كروم للقيام بعملية الاختبار مع تعطيل عملية التخزين المؤقت الخاصة بالمتصفح لتجنب التأثير على النتائج.</p><ul dir="rtl"><li>صفحة ووردبريس الرئيسية الافتراضية من دون Redis: وقت تحميل الصفحة: 804 ميلي ثانية.</li><li>صفحة ووردبريس الرئيسية الافتراضية مع Redis:وقت تحميل الصفحة: 449 ميلي ثانية.</li></ul><p dir="rtl">تحذير: عملية التطبيق هذه لـRedis للقيام بعملية التخزين المؤقت لذاكرة ووردبريس يعتمد على سكربت script طرفٍ ثالث تم تطويره عبر طرفٍ خارجي. إذا كنتَ تريد القيام بعملية تطبيق Redis لووردبريس بنفسك فعليك أن تقوم بعمل بعض العمل الإضافي بناءً على المفاهيم التي سيتم تقديمها هنا.</p><h2 dir="rtl">Redis أو Memcached</h2><p dir="rtl">يُعتبر Memcached أيضًا خيارًا مشهورًا لعمل ذاكرة تخزينٍ مؤقت، لكن في هذه الحالة، Redis يقوم بكل ما يستطيع Memcached القيام به بالإضافة إلى تشكيلة أوسع من المميزات. <a rel="external nofollow" href="http://stackoverflow.com/questions/10558465/memcache-vs-redis">هذه الصفحة على </a><a rel="external nofollow" href="http://stackoverflow.com/questions/10558465/memcache-vs-redis">Stack Overflow</a> بها بعض المعلومات العامة عن المقارنة بينهما.</p><h2 dir="rtl">كيف يعمل التخزين المؤقت caching؟</h2><p dir="rtl">عندما يتم تحميل صفحة ووردبريس لأول مرّة، يتم إجراء عملية استعلام على قاعدة البيانات المثبتة على الخادوم. Redis يتذكر أو بالأحرى يخزن مؤقتًا عملية الاستعلام هذه. لذا عندما يحاول مستخدمٌ آخر القيام بتحميل صفحة الووردبريس هذه فالنتيجة سيتم توفيرها من طرف Redis والذاكرة دون الحاجة إلى القيام بعملية استعلام جديدة من قاعدة البيانات.</p><p dir="rtl">عملية تطبيق Redis المُستخدمة في هذا الدليل ستجعله يعمل ككائن تخزينٍ مؤقت مستمر (persistent object cache) لووردبريس (دون انتهاء). يعمل كائن التخزين المؤقت عبر تخزين عمليات استعلام SQL التي يحتاجها ووردبريس لتحميل الصفحات مؤقتًا في الذاكرة.</p><p dir="rtl">عندما يتم تحميل أيّ صفحة، يتم توفير النتائج الناتجة عن عمليات الاستعلام في SQL من الذاكرة باستخدام Redis, لذا فإنه لا توجد هناك حاجة لعمل عملية استعلام جديدة من قاعدة البيانات من جديد. وهو ما يعطي سرعةً أكبر في تحميل الصفحات بالإضافة إلى حملٍ أقل من طرف الخادوم على موارد قاعدة البيانات. إذا كان هناك استعلام غير متوفر في Redis، فإنّ قاعدة البيانات تقوم بتوفير نتيجة عملية الاستعلام تلك ويقوم Redis بإضافة تلك النتيجة إلى ذاكرته المؤقتة.</p><p dir="rtl">إذا تمّ تحديث أيّ قيمة في قاعدة البيانات (مثل إنشاء موضوع أو صفحة جديدة على ووردبريس) فإنّ القيمة المُخزّنة الموازية لتلك القيمة في Redis يتم إبطالها تجنبًا لعرض بيانات قديمة.</p><p dir="rtl">إذا واجهتَ مشاكل مع التخزين المؤقت، فإنه بإمكانك إلغاء صلاحية ذاكرة التخزين المؤقت الخاصة بـRedis باستخدام أمر flushall عبر سطر الأوامر الخاص بـRedis:</p><pre class="php ipsCode prettyprint">redis-cli</pre><p dir="rtl">بمجرد أن ترى سطر الأوامر، طبّق:</p><pre class="php ipsCode prettyprint">flushall</pre><p dir="rtl">مرجع إضافي: <a rel="external nofollow" href="http://codex.wordpress.org/Class_Reference/WP_Object_Cache">التوثيق الخاص بكائن التخزين المؤقت لووردبريس</a>.</p><h2 dir="rtl">المتطلبات</h2><p dir="rtl">قبل البدء بهذا الدليل، ستحتاج إلى إعداد مستخدمٍ بصلاحيات الجذر (sudo) بالإضافة إلى تثبيت ووردبريس.</p><ul><li><p dir="rtl">خادوم Ubuntu 14.04 (من المستحسن أن تكون بذاكرة عشوائية 1 جيجابايت أو أعلى).</p></li><li><p dir="rtl">إضافة  مستخدم بصلاحيات الجذر (sudo).</p></li><li><p dir="rtl">تثبيت ووردبريس. </p></li></ul><h2 dir="rtl">الخطوة الأولى – تثبيت Redis</h2><p dir="rtl">بهدف استخدام Redis مع ووردبريس، فإننا بحاجة إلى تثبيت حزمتين: <span style="font-family:'courier new', courier, monospace;">redis-server</span> و <span style="font-family:'courier new', courier, monospace;">php5-redis</span>. حزمة <span style="font-family:'courier new', courier, monospace;">redis-server</span> توفرّ تطبيق Redis نفسه، بينما حزمة <span style="font-family:'courier new', courier, monospace;">php5-redis</span> تقوم بتوفير امتداد PHP للتطبيقات المكتوبة بـPHP مثل ووردبريس للتعامل مع Redis.</p><p dir="rtl">لتثبيت هذه البرمجيات:</p><pre class="php ipsCode prettyprint">sudo apt-get install redis-server php5-redis</pre><h2 dir="rtl">الخطوة الثانية – إعداد Redis كذاكرة تخزين مؤقت</h2><p dir="rtl">يمكن لـRedis أن يعمل إمّا كمخزن قاعدة بيانات NoSQL أو كذاكرة تخزينٍ مؤقت. في هذا الدليل وهذه الحالة بالضبط، سيتم ضبط Redis كذاكرة تخزينٍ مؤقت. الإعدادات التالية ستكون مطلوبة بهدفِ فعلِ ذلك.</p><p dir="rtl">حرر الملف <span style="font-family:'courier new', courier, monospace;">/etc/redis/redis.conf</span> عن طريق الأمر:</p><pre class="php ipsCode prettyprint">sudo nano /etc/redis/redis/conf</pre><p dir="rtl">وقم بإضافة السطور التالية لنهاية الملف:</p><pre class="php ipsCode prettyprint">maxmemory 256mb
maxmemory-policy allkeys-lru
</pre><p><span style="line-height:1.6;">ثم احفظ الملف.</span></p><h2 dir="rtl">الخطوة الثالثة – حمّل سكربت ذاكرة التخزين المؤقت الخلفي لـRedis</h2><p dir="rtl">تم تطوير سكربت الـPHP هذا لووردبريس بواسطة <a rel="external nofollow" href="https://github.com/ericmann/Redis-Object-Cache/raw/master/object-cache.php">Eric Mann</a>. وهو عبارة عن سندٍ خلفي backend لكائن ذاكرةِ التخزين المؤقت من Redis لووردبريس.</p><p dir="rtl">عليك تحميل سكربت <span style="font-family:'courier new', courier, monospace;">object-cache.php</span>. عملية التحميل هذه ستتم من خواديم DigitalOcean، ولكن هذا السكربت هو سكربت تم تطويره بواسطة طرفٍ ثالث. يجب عليك قراءة التعليقات داخل السكربت لمعرفة كيفية عمله.</p><p dir="rtl">لتحميل سكربت الـPHP:</p><pre class="php ipsCode prettyprint">wget https://assets.digitalocean.com/articles/wordpress_redis/object-cache.php</pre><p dir="rtl">قم بنقل الملف إلى مسار<span style="font-family:'courier new', courier, monospace;"> /wp-content</span> داخل مجلد ووردبريس الخاص بك:</p><pre class="php ipsCode prettyprint">sudo mv object-cache.php /var/www/html/wp-content/</pre><p dir="rtl">قد يكون مسار المجلد (باللون الأحمر) مختلفًا اعتمادًا على طريقة تثبيتك لووردبريس.</p><h2 dir="rtl">الخطوة الرابعة – فعّل إعدادات التخزين المؤقت في ملف <span style="font-family:'courier new', courier, monospace;">wp-config.php</span></h2><p dir="rtl">الآن، قم بتحرير ملف <span style="font-family:'courier new', courier, monospace;">wp-config.php</span> لإضافة ملحِ مفتاحِ ذاكرةِ تخزينٍ مؤقت (cache key salt) مع اسم موقعك (أو أيّ سلسلة string تريدها):</p><pre class="php ipsCode prettyprint">nano /var/www/html/wp-config.php</pre><p dir="rtl">ثم أضف السطر الآتي إلى نهاية قسم <span style="font-family:'courier new', courier, monospace;">* Authentication Unique Keys and Salts</span>:</p><pre class="php ipsCode prettyprint">define('WP_CACHE_KEY_SALT', 'example.com');</pre><p dir="rtl">يمكنك استخدام اسم النطاق الخاص بك domain أو أي سلسلة نصية كملح salt.</p><p dir="rtl">ملاحظة: للمستخدمين الذين يستضيفون أكثر من موقع ووردبريس واحد، يمكن لكلِ موقعٍ أن يتشارك باستخدام تثبيت Redis واحد طالما أن كلّ موقعٍ منفصل يمتلك ملحَ مفتاح ذاكرة التخزين المؤقت الخاص به.</p><p dir="rtl">أيضًا، قم بإضافة السطر التالي بعد سطر <span style="font-family:'courier new', courier, monospace;">WP_CACHE_KEY_SALT</span> لإنشاء ذاكرة تخزينٍ مؤقت مستمرة مع مُلحق كائن ذاكرة التخزين المؤقت لـRedis:</p><pre class="php ipsCode prettyprint">define('WP_CACHE', true);</pre><p dir="rtl">في النهاية، يجب أن يكون ملفّك هكذا:</p><pre class="php ipsCode prettyprint"> * Authentication Unique Keys and Salts.

. . .

define('NONCE_SALT',       'put your unique phrase here');

define('WP_CACHE_KEY_SALT', 'example.com');
define('WP_CACHE', true);
</pre><p><span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:1.6;">احفظ الملف وأغلقه.</span></p><h2 dir="rtl">الخطوة الخامسة – قم بإعادة تشغيل Redis و Apache</h2><p dir="rtl">أخيرًا، قم بإعادة تشغيل redis-service و apache2. لإعادة تشغيل Redis:</p><pre class="php ipsCode prettyprint">sudo service redis-server restart</pre><p dir="rtl">لإعادة تشغيل Apache:</p><pre class="php ipsCode prettyprint">sudo service apache2 restart</pre><p dir="rtl">أيضًا قم بإعادة تشغيل php5-fpm إذا كنتَ تستخدمها; هذا ليس جزءًا أساسيًا من عملية التثبيت، وعلى كل حال، لفعل ذلك طبّق:</p><pre class="php ipsCode prettyprint">sudo service php5-fpm restart</pre><p dir="rtl">هذا كل شيء ! أصبح موقع ووردبريس الخاص بك مضبوطًا ليستخدم تخزين Redis المؤقت. يجب أن تلاحظ تحسنًا إذا قمتَ بالتحقق من سرعة تحميل صفحات موقعك واستهلاك الموارد الآن.</p><h2 dir="rtl">مراقبة Redis باستخدام redis-cli</h2><p dir="rtl">لمراقبة Redis، استخدم أمر redis-cli كالتالي:</p><pre class="php ipsCode prettyprint">redis-cli monitor</pre><p dir="rtl">عندما تقوم بتشغيل هذا الأمر، ستشاهد خرجًا بالوقت الحقيقي لعمليات تخديم الاستعلامات المُخزّنة مؤقتًا التي يديرها Redis. إذا لم ترى أيّ شيء، فقم بزيارة موقعك وقم بإعادة تحميل أيّ صفحة.</p><p dir="rtl">بالأسفل مثال على خرجٍ من موقع ووردبريس مضبوط باستخدام هذا الدليل عبر Redis:</p><pre class="php ipsCode prettyprint">OK
1412273195.815838 "monitor"
1412273198.428472 "EXISTS" "example.comwp_:default:is_blog_installed"
1412273198.428650 "GET" "example.comwp_:default:is_blog_installed"
1412273198.432252 "EXISTS" "example.comwp_:options:notoptions"
1412273198.432443 "GET" "example.comwp_:options:notoptions"
1412273198.432626 "EXISTS" "example.comwp_:options:alloptions"
1412273198.432799 "GET" "example.comwp_:options:alloptions"
1412273198.433572 "EXISTS" "example.comwp_site-options:0:notoptions"
1412273198.433729 "EXISTS" "example.comwp_:options:notoptions"
1412273198.433876 "GET" "example.comwp_:options:notoptions"
1412273198.434018 "EXISTS" "example.comwp_:options:alloptions"
1412273198.434161 "GET" "example.comwp_:options:alloptions"
1412273198.434745 "EXISTS" "example.comwp_:options:notoptions"
1412273198.434921 "GET" "example.comwp_:options:notoptions"
1412273198.435058 "EXISTS" "example.comwp_:options:alloptions"
1412273198.435193 "GET" "example.comwp_:options:alloptions"
1412273198.435737 "EXISTS" "example.comwp_:options:notoptions"
1412273198.435885 "GET" "example.comwp_:options:notoptions"
1412273198.436022 "EXISTS" "example.comwp_:options:alloptions"
1412273198.436157 "GET" "example.comwp_:options:alloptions"
1412273198.438298 "EXISTS" "example.comwp_:options:notoptions"
1412273198.438418 "GET" "example.comwp_:options:notoptions"
1412273198.438598 "EXISTS" "example.comwp_:options:alloptions"
1412273198.438700 "GET" "example.comwp_:options:alloptions"
1412273198.439449 "EXISTS" "example.comwp_:options:notoptions"
1412273198.439560 "GET" "example.comwp_:options:notoptions"
1412273198.439746 "EXISTS" "example.comwp_:options:alloptions"
1412273198.439844 "GET" "example.comwp_:options:alloptions"
1412273198.440764 "EXISTS" "example.comwp_:options:notoptions"
1412273198.440868 "GET" "example.comwp_:options:notoptions"
1412273198.441035 "EXISTS" "example.comwp_:options:alloptions"
1412273198.441149 "GET" "example.comwp_:options:alloptions"
1412273198.441813 "EXISTS" "example.comwp_:options:notoptions"
1412273198.441913 "GET" "example.comwp_:options:notoptions"
1412273198.442023 "EXISTS" "example.comwp_:options:alloptions"
1412273198.442121 "GET" "example.comwp_:options:alloptions"
1412273198.442652 "EXISTS" "example.comwp_:options:notoptions"
1412273198.442773 "GET" "example.comwp_:options:notoptions"
1412273198.442874 "EXISTS" "example.comwp_:options:alloptions"
1412273198.442974 "GET" "example.comwp_:options:alloptions"
</pre><p dir="rtl"><span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;line-height:1.6;">اضغط CTRL-C لإيقاف الخرج.</span></p><p dir="rtl">هذا الأمر مفيد لرؤية الاستعلامات التي يعالجها Redis بالضبط.</p><h2 dir="rtl">الخاتمة</h2><p dir="rtl">بعد اتّباع هذا الدليل، سيصبح ووردبريس مضبوطًا لاستخدام Redis كذاكرة تخزينٍ مؤقت على Ubuntu 14.04.</p><p dir="rtl"> </p><p dir="rtl">ترجمة -وبتصرّف- للمقال: <a rel="external nofollow" href="https://www.digitalocean.com/community/tutorials/how-to-configure-redis-caching-to-speed-up-wordpress-on-ubuntu-14-04">How To Configure Redis Caching to Speed Up WordPress on Ubuntu 14.04</a></p><p dir="rtl"> </p>
]]></description><guid isPermaLink="false">14</guid><pubDate>Thu, 12 Mar 2015 14:08:00 +0000</pubDate></item></channel></rss>
