أفضل ممارسات Redis ، الجزء الأول

في سلسلة من المقالات العديدة ، سأقدم ترجميمي المُعدّل لقسم أفضل ممارسات Redis من موقع Redis Labs الرسمي.

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

قمنا بتقسيم دليل أفضل الممارسات هذا إلى فصول وفصول فرعية حسب الحاجة (ملاحظة المترجم: بعض الفصول الفرعية قصيرة ، لذا سأجمعها في فصل واحد):

  • في الفصل "أنماط الفهرسة" ، سننظر في طرق لتجاوز الوصول المعتاد إلى قيمة المفتاح مع Redis. يتضمن طرقًا لاستخدام الأنماط الرئيسية بذكاء باستخدام أنواع بيانات Redis المختلفة ليس فقط للمساعدة في العثور على البيانات ، ولكن أيضًا لتقليل تعقيد الوصول ؛
  • يركز فصل أنماط التفاعل على أنماط Redis التي تنقل البيانات عبر البنية التحتية. في هذه الحالة ، لا يعمل Redis كمستودع ، بل كدليل للبيانات ؛
  • يصف الفصل "أنماط تخزين البيانات" أساليب حفظ تمثيلات معقدة للبيانات في Redis. سنقوم بحساب النصوص المعقدة للوثائق التي يمكن تعميمها بطرق بسيطة ومعقدة ؛
  • تم توضيح الأنماط المتعلقة بالبيانات المخزنة مؤقتًا في الفصل "أنماط السلاسل الزمنية" ؛
  • وغالبا ما يستخدم الحد الأقصى للسرعة في Redis. في الفصل "الأنماط الأساسية للحد من السرعة" سننتقل إلى أساسيات حالات الاستخدام الخاصة به ؛
  • لطالما شوهد عامل التصفية Bloom في Redis ، وفي الفصل "Patterns with a Bloom Filter" ، ننظر إلى هياكل البيانات الاحتمالية وكيف تختلف عن نظائرها غير المحتملة ؛
  • العداد استقبال عميق بشكل مدهش. في فصل منفصل ، نستكشف كيفية حساب النشاط والعناصر الفريدة بطرق حسابية فعالة ؛
  • أخيرًا ، سنتحدث عن كيفية الاستفادة من Lua للحصول على Redis للقيام بالمزيد بأقل.

هذا الدليل غير متسق ، لذلك يمكنك البدء به مع أي فصل. يمكنك أيضًا استخدام التنقل في بداية كل منشور للعثور على شيء مناسب.

أنماط الفهرسة


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

تختلف الفهرسة في Redis تمامًا عما يتم تقديمه في قواعد البيانات الأخرى ، لذلك ستحدد سيناريوهات الاستخدام والبيانات الخاصة بك أفضل استراتيجية للفهرسة. في هذا الفصل ، سوف نلقي نظرة على بعض استراتيجيات استرداد البيانات العامة إلى جانب استرداد المفتاح / القيمة البسيط:

  • مجموعات مرتبة كمؤشرات ؛
  • مؤشرات معجمية
  • المؤشرات الجغرافية المكانية
  • تحديد الموقع الجغرافي IP
  • البحث عن النص الكامل ؛
  • فهارس مقسمة.

مجموعات مرتبة كمؤشرات


المجموعات المصنفة (ZSETs) هي نوع بيانات قياسي في Redis يمثل العديد من الكائنات الفريدة (لا يتم حفظ التكرارات) ، حيث يتم تعيين كل كائن إلى رقم (يسمى "العد") ، والذي يعمل كآلية فرز طبيعية. وعلى الرغم من أنه لا يمكن تكرار الكائنات ، فإن أي عدد قليل من الكائنات يمكن أن يكون له نفس العدد. مع تعقيد الوقت المنخفض نسبياً لإضافة وحذف والحصول على مجموعة من القيم (حسب الرتبة أو العدد) ، فإن المجموعات المصنفة مناسبة تمامًا لكونها فهارس. على سبيل المثال ، خذ دول العالم ، مرتبة حسب السكان:

> ZADD countries-by-pop 1409517397 china > ZADD countries-by-pop 146573899 russia > ZADD countries-by-pop 81456724 germany > ZADD countries-by-pop 333016381 usa > ZADD countries-by-pop 1 mars > ZADD countries-by-pop 37290812 afghanistan > ZADD countries-by-pop 1388350202 india 

سيكون الحصول على أفضل 5 دول بسيطًا:

 > ZRANGE countries-by-pop 0 4 1) "mars" 2) "afghanistan" 3) "germany" 4) "russia" 5) "india" 

والحصول على الدول التي يتراوح عدد سكانها ما بين 10،000،000 و 1،000،000،000:

 > ZRANGEBYSCORE countries-by-pop 10000000 1000000000 1) "afghanistan" 2) "germany" 3) "russia" 

يمكنك إنشاء فهارس متعددة لإظهار طرق مختلفة لفرز البيانات. في مثالنا ، يمكننا استخدام نفس الكائنات ، لكن بدلاً من عدد الأشخاص ، نأخذ الكثافة السكانية والحجم الجغرافي وعدد مستخدمي الإنترنت ، إلخ. سيؤدي ذلك إلى إنشاء فهارس عالية الأداء لمختلف الجوانب. بالإضافة إلى ذلك ، من خلال تقسيم اسم كائن مع بيانات حوله مخزنة إما في Redis (في Hash ، على سبيل المثال) أو في مخزن بيانات آخر ، يمكن أن تحصل العملية الثانوية على معلومات إضافية حول كل عنصر عند الضرورة.

معجم الفهارس


تحتوي المجموعات المصنفة (ZSET) مع الترتيب حسب العدد على خاصية واحدة مثيرة للاهتمام يمكن استخدامها لإنشاء آلية للفرز الأبجدي الخام. الخاصية هي أنه يمكن إرجاع الكائنات التي لها نفس النتيجة بترتيب معجم وقيم حدودية. خذ البيانات التالية:

 > ZADD animal-list 0 bison 0 boa 0 dog 0 emu 0 falcon 0 alligator 0 chipmunk 

سيضيف هذا الأمر العديد من الحيوانات إلى مفتاح قائمة الحيوانات. لكل كائن درجة 0. بعد تنفيذ أمر ZRANGE باستخدام الوسيطتين 0 و -1 ، نرى ترتيبًا غريبًا:

 > ZRANGE animal-list 0 -1 1) "alligator" 2) "bison" 3) "boa" 4) "chipmunk" 5) "dog" 6) "emu" 7) "falcon" 

على الرغم من أن العناصر لم تتم إضافتها أبجديًا ، فقد تم إرجاعها مرتبة أبجديًا. هذا الترتيب هو نتيجة مقارنة السلسلة الثنائية ، البايت بت. هذا يعني أنه سيتم إرجاع أحرف ASCII بالترتيب الأبجدي. هذا يشير إلى ما يلي:

  • لن يتم التعرف على الأحرف في الأحرف الصغيرة والعلوية على أنها هي نفسها ؛
  • لن يتم فرز أحرف متعددة البايتات كما هو متوقع.

يوفر Redis أيضًا بعض الميزات المتقدمة لزيادة تضييق نطاق عمليات البحث المعجمية. على سبيل المثال ، نريد إرجاع الحيوانات التي تبدأ ب b وتنتهي بـ e . يمكننا استخدام الأمر التالي:

 > ZRANGEBYLEX animal-list [b (f 1) "bison" 2) "boa" 3) "chipmunk" 4) "dog" 5) "emu" 

الحجة (f يمكن أن تكون مربكة بعض الشيء. هذه نقطة مهمة ، لأن Redis ليس لديه أدنى فكرة عن الفهم الحرفي لأحرف الأبجدية. هذا يعني أنه يجب علينا أن نأخذ في الاعتبار أن كل شيء يبدأ بـ e سيبقى دائمًا قبل كل شيء يبدأ بـ f ، بصرف النظر عن الحروف اللاحقة ملاحظة أخرى هي أن القوس المربّع يشير إلى البحث مع التضمين ، وأن القوس المستدير يشير إلى البحث دون إدراج. وفي حالتنا ، إذا استفسرنا عن b ، فسيتم تضمين ذلك في القائمة ، بينما لن تظهر f في التحديد. جميع العناصر حتى النهاية ، واستخدام ترميز رمز الماضي (255 أو عشرية 0xFF):

 > ZRANGEBYLEX animal-list [c "[\xff" 1) "chipmunk" 2) "dog" 3) "emu" 4) "falcon" 

يمكن أيضًا تقييد هذا الأمر ، وبالتالي ضمان ترقيم الصفحات:

 > ZRANGEBYLEX animal-list [b (f LIMIT 0 2 1) "bison" 2) "boa" > ZRANGEBYLEX animal-list [b (f LIMIT 2 2 1) "chipmunk" 2) "dog" 

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

الفهارس الجغرافية المكانية


لدى Redis العديد من فرق الفهرسة الجغرافية المكانية (فرق GEO) ، ولكن على عكس الفرق الأخرى ، ليس لهذه الفرق أنواع بيانات خاصة بها. هذه الأوامر تكمل بالفعل نوع مجموعة فرزها. يتم تحقيق ذلك عن طريق ترميز خطوط الطول والعرض في درجة المجموعة المصنفة باستخدام خوارزمية geohash.
من السهل إضافة عناصر إلى فهرس جغرافي. لنفترض أنك تتعقب مجموعة من السيارات التي تسير على الطريق. نحن نسمي هذه المجموعة من السيارات ببساطة "سيارات". دعنا نقول أنه يمكن تعريف جهازك الخاص ككائن "my-car" (نستخدم المصطلح "object" لأن الفهرس الجغرافي هو ببساطة شكل من أشكال المجموعة). لإضافة جهاز إلى المجموعة ، يمكننا تنفيذ الأمر:

 > GEOADD cars -115.17087 36.12306 my-car 

الوسيطة الأولى هي المجموعة التي نضيف إليها ، والثاني هو خط الطول ، والثالث هو خط الطول ، والرابع هو اسم الكائن.

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

 > GEOADD cars -115.17172 36.12196 my-car 

أضف سيارة ثانية إلى "السيارات". هذه المرة فولوديا يقودها:

 > GEOADD cars -115.171971 36.120609 volodias-car 

بالنظر إلى الإحداثيات ، يمكنك القول أن هذه السيارات قريبة جدًا من بعضها البعض ، ولكن كم؟ يمكنك تعريف هذا باستخدام الأمر GEODIST:

 > GEODIST cars my-car volodias-car "151.9653" 

وهذا يعني أن سيارتين على بعد حوالي 151 متر. يمكنك أيضًا حساب الوحدات الأخرى:

 > GEODIST cars my-car robins-car ft "498.5737" 

هذا عاد نفس المسافة في الخطوات. يمكنك أيضًا استخدام الأميال (مل) أو الكيلومترات (كم).

الآن دعونا نرى من هو في دائرة نصف قطرها من نقطة معينة:

 > GEORADIUS cars -115.17258 36.11996 100 m 1) "volodias-car" 

عاد هذا الجميع داخل دائرة نصف قطرها 100 متر حول النقطة المحددة. يمكنك أيضًا أن تطلب من كل شخص في دائرة نصف قطرها أي كائن من المجموعة:

 > GEORADIUSBYMEMBER cars volodias-car 152 m 1) "volodias-car" 2) "my-car" 

يمكننا أيضًا تمكين المسافة عن طريق إضافة وسيطة WITHDIST الاختيارية (يعمل هذا مع GEORADIUS أو GEORADIUSBYMEMBER):

 > GEORADIUSBYMEMBER cars volodias-car 152 m WITHDIST 1) 1) "volodias-car" 2) "0.0000" 2) 1) "my-car" 2) "151.9653" 

وسيطة اختيارية أخرى لـ GEORADIUS و GEORADIUSBYMEMBER هي WITHCOORD ، والتي تُرجع إحداثيات كل كائن. يمكن استخدام WITHDIST و WITHCOORD معًا أو بشكل منفصل:

 > GEORADIUSBYMEMBER cars volodias-car 152 m WITHDIST WITHCOORD 1) 1) "volodias-car" 2) "0.0000" 3) 1) "-115.17197102308273315" 2) "36.12060917648089031" 2) 1) "my-car" 2) "151.9653" 3) 1) "-115.17171889543533325" 2) "36.12196018285882104" 

بما أن الفهارس الجغرافية المكانية هي مجرد بديل للمجموعات المصنفة ، يمكن استخدام بعض مشغلي الأخيرة. إذا كنا نريد إزالة "سيارتي" من مجموعة "السيارات" ، فيمكننا استخدام أمر المجموعة المصنفة من ZREM:

 > ZREM cars my-car 

يوفر Redis مجموعة غنية من الأدوات للعمل مع الجغرافيا المكانية ، وفي هذا القسم درسنا فقط الأدوات الأساسية.

تحديد الموقع الجغرافي IP


يمكن أن يكون العثور على الموقع الفعلي للخدمة المتصلة مفيدًا جدًا. عادة ما تكون جداول تحديد الموقع الجغرافي لـ IP كبيرة جدًا ويصعب إدارتها بفعالية. يمكننا استخدام مجموعات مرتبة لتنفيذ خدمات تحديد الموقع الجغرافي IP سريعة وفعالة.

تتم الإشارة إلى IPv4 غالبًا بترميز عشري (74.125.43.99 ، على سبيل المثال). ومع ذلك ، ترى خدمات الشبكة نفس العنوان كرقم 32 بت ، حيث يمثل كل بايت واحدًا من أربعة أرقام في شكل عشري. المثال أعلاه سيكون 0x4A7D2B63 بالسداسي عشري أو 1249717091 بالتدوين العشري.

تتوفر مجموعات بيانات تحديد الموقع الجغرافي IP على نطاق واسع وعادة ما تأخذ شكل جدول بسيط مع ثلاثة أعمدة (البداية والنهاية والموقع). البداية والنهاية هي التمثيل العشري لـ IPv4. في Redis ، يمكننا تكييف المجموعات المصنفة لهذا التنسيق ، لأنه لا توجد "فتحات" في نطاقات IP ، وبالتالي ، يمكننا أن نفترض بأمان أن نهاية نطاق ما هي بداية نطاق آخر.

على سبيل المثال البسيط ، أضف نطاقات قليلة إلى المجموعات المصنفة:

 > ZADD ip-loc 1249716479 us:1 > ZADD ip-loc 1249716735 taiwan:1 > ZADD ip-loc 1249717759 us:2 > ZADD ip-loc 1249718015 finland:1 

الوسيطة الأولى هي مفتاح مجموعتنا ، والثاني هو التمثيل العشري لنهاية نطاق IP ، والأخير هو الكائن نفسه. لاحظ أن كائن مجموعة له رقم بعد النقطتين. هذا فقط لتسهيل مثال. تحتوي جداول IP الحقيقية على معرّفات فريدة لكل نطاق (والمزيد من المعلومات الإضافية من مجرد اسم البلد).

للاستعلام عن الجدول الخاص بعنوان IP محدد ، يمكننا استخدام الأمر ZRANGEBYSCORE مع بعض الوسائط الإضافية. خذ عنوان IP وقم بتحويله إلى رقم عشري. يمكن القيام بذلك باستخدام لغة البرمجة الخاصة بك. أولاً ، استخدم العنوان من المثال الأصلي 74.125.43.99 (1249717091). إذا أخذنا هذا الرقم كنقطة مرجعية ولم نحدد الحد الأقصى ، ثم قصر النتيجة على الكائن الأول فقط ، فسنجد موقعه الجغرافي:

 > ZRANGEBYSCORE ip-loc 1249717091 +inf LIMIT 0 1 1) "us:2" 

الوسيطة الأولى هي مفتاح مجموعتنا المصنفة ، والثانية هي التمثيل العشري لعنوان IP ، والثالثة (+ inf) تطلب من Redis أن يطلبها بدون حد أعلى ، وتشير الوسيطات الثلاث الأخيرة ببساطة إلى أننا نريد الحصول على النتيجة الأولى فقط.

بحث النص الكامل


قبل ظهور الوحدات النمطية ، تم تنفيذ البحث عن النص الكامل باستخدام أوامر Redis الأصلية. وحدة RedisSearch أكثر إنتاجية بكثير من هذا النمط ، ومع ذلك ، في بعض البيئات ، لا يتوفر. بالإضافة إلى ذلك ، يعد هذا النمط مثيراً للاهتمام ويمكن تعميمه على أعباء العمل الأخرى التي لن يكون RedisSearch مثاليًا فيها.
لنفترض أن لدينا عدة مستندات نصية تحتاج إلى البحث. قد لا يكون هذا هو حالة الاستخدام الواضحة لـ Redis ، حيث إنها توفر وصولًا رئيسيًا ، لكن من ناحية أخرى ، يمكن استخدام Redis كمحرك بحث كامل النص بالكامل.

أولاً ، لنأخذ بعض النصوص في المستندات:

"Redis سريع جداً"
"الفهود سريعة"
"الفهود لها بقع"

نقسمهم إلى مجموعات من الكلمات ، مفصولة بمساحة للبساطة:

 > SADD ex1 redis is very fast > SADD ex2 cheetahs are very fast > SADD ex3 cheetahs have spots 

لاحظ أننا نضع كل سطر في مجموعته الخاصة. قد يبدو أننا نضيف السطر بالكامل - SADD متغير ويأخذ عدة عناصر دفعة واحدة كحجج. قمنا أيضًا بتحويل جميع الكلمات إلى أحرف صغيرة.
ثم نحتاج إلى عكس هذا الفهرس وإظهار الكلمة التي تحتويها الوثيقة. للقيام بذلك ، سنضع مجموعة لكل كلمة ونضع اسم المستند ككائن:

 > SADD redis ex1 > SADD is ex1 > SADD very ex1 ex2 > SADD fast ex1 ex2 > SADD cheetahs ex2 ex3 > SADD have ex3 > SADD spots ex3 

من أجل الوضوح ، قمنا بتقسيم هذا إلى أوامر مختلفة ، ولكن يتم تنفيذ جميع الأوامر عادةً بشكل تلقائي في كتلة MULTI / EXEC.

للاستعلام عن فهرس النص الكامل الصغير الخاص بنا ، نستخدم الأمر SINTER (تقاطع المجموعات). ابحث عن مستندات ذات "جدًّا" و "سريع":

 > SINTER very fast 1) "ex2" 2) "ex1" 

في حالة عدم وجود مستندات مطابقة للطلب ، سنحصل على نتيجة فارغة:

 > SINTER cheetahs redis (empty list or set) 

من أجل التناسق ، من الأفضل استخدام SUNION بدلاً من SINTER:

 > SUNION cheetahs redis 1) "ex2" 2) "ex1" 3) "ex3" 

إزالة كائن من الفهرس أكثر تعقيدًا قليلاً. أولاً ، احصل على الكلمات المفهرسة من المستند ، ثم احذف معرف المستند من كل كلمة:

 > SMEMBERS ex3 1) "spots" 2) "have" 3) "cheetahs" > SREM have ex3 > SREM cheetahs ex3 > SREM spots ex3 

ليس لدى Redis مشغل منفصل لتنفيذ كل هذه الخطوات باستخدام أمر واحد ، لذا يجب عليك أولاً الاستعلام عن الأمر SMEMBERS ، ثم حذف كل كائن بالتسلسل باستخدام SREM.

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

فهارس مقسمة


هناك مثيل واحد (أو حادة) من Redis يكون قابلاً للتطبيق للغاية ، ولكن هناك ظروف قد تحتاج فيها إلى فهرس موزع عبر مثيلات متعددة. على سبيل المثال ، لزيادة الإنتاجية عن طريق موازنة الفهارس التي تكون أكبر من المساحة الحرة لمثيل. قل أنك تريد إجراء عملية على مفاتيح متعددة. طريقة فعالة لفصل (قسم) هذه المفاتيح هي ضمان توزيع موحد للمفاتيح عبر كل قسم ، وتنفيذ أي عمليات في كل قسم بالتوازي ، ثم دمج النتائج في النهاية.

لتحقيق توزيع موحد للمفاتيح ، سنستخدم خوارزمية تجزئة غير مشفرة. أي وظيفة تجزئة سريعة ستفعل ، لكننا نستخدم CRC-32 الشهيرة كمثال. في معظم الحالات ، تُرجع هذه الخوارزميات النتيجة السداسية عشرية (بالنسبة إلى "my-cool-document" ستعود CRC-32 إلى F9FDB2C9). يكون التمثيل السداسي عشر أبسط بالنسبة للجهاز ، لكنه مجرد تمثيل آخر للأعداد الصحيحة العشرية ، مما يعني أنه يمكنك إجراء عمليات حسابية على هذه القيم.
بعد ذلك ، تحتاج إلى تحديد عدد الأقسام - يجب أن يكون هذا x2 على الأقل من عدد النسخ. هذا يسهم كذلك في التوسع.

دعنا نقول لدينا 3 نسخ و 6 أقسام. يمكن حساب القسم الذي يتم إرسال المستند إليه من خلال العملية التالية:

التجزئة $ \ _function (المعرف \ _الوثيقة) \ رقم التعديل \ _ الأجزاء $



 CRC32(“my-cool-document”) = F9FDB2C9 (16)  4194153161 (10) 4194153161 mod 6 = 5 

في Redis Enterprise ، يمكنك التحكم في القسم الذي ينتمي إليه المفتاح ، إما باستخدام التعبيرات العادية المحددة مسبقًا ، أو عن طريق لف جزء من المفتاح بأقواس مجعد. على سبيل المثال ، يمكننا تعيين مفتاح المستند كما يلي:

idx: my-cool-document {5}

ثم لدينا مستند آخر يصدر قسمًا برقم 3 ، وبالتالي ، سيبدو المفتاح كما يلي:

idx: my-other-document {3}

إذا كان لديك مفاتيح إضافية إضافية ستحتاج إلى العمل معها وترتبط بهذا المستند ، فيجب أن تكون في القسم نفسه حتى تتمكن من إجراء العمليات باستخدام كلا المفتاحين في نفس الوقت دون مواجهة مجموعة من الأخطاء. للقيام بذلك ، تحتاج إلى إضافة رقم القسم نفسه إلى المفتاح مثل المستند.

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

حول هذا الموضوع ، المادة الأولى تنتهي. التالي سيكون ترجمة للعناوين الفرعية "أنماط التفاعل" و "أنماط تخزين البيانات".

Source: https://habr.com/ru/post/ar485672/


All Articles