بيانات السلاسل الزمنية: لماذا (وكيف) استخدام قاعدة بيانات علائقية بدلاً من NoSQL

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

نتيجةً لذلك ، أصبحت قواعد بيانات السلاسل الزمنية في عالم الموضة (هنا 33 منها). يتخلى معظم هؤلاء عن زخارف قاعدة بيانات علائقية تقليدية ويعتمدون ما يعرف عمومًا باسم نموذج NoSQL. أنماط الاستخدام متشابهة: أظهر استطلاع حديث أن المطورين يفضلون NoSQL على قواعد البيانات العلائقية لبيانات السلاسل الزمنية بأكثر من 2: 1.

تشمل قواعد البيانات العلائقية: MySQL ، خادم MariaDB ، PostgreSQL. تتضمن قواعد بيانات NoSQL: Flex و InfluxDB و MongoDB و Cassandra و Couchbase و Graphite و Prometheus و ClickHouse و OpenTSDB و DalmatinerDB و KairosDB و RiakTS. المصدر: https://www.percona.com/blog/2017/02/10/percona-blog-poll-database-engine-using-store-time-series-data/

عادةً ما يكون سبب اعتماد قواعد بيانات سلاسل زمنية لـ NoSQL مقياسًا. بينما تحتوي قواعد البيانات العلائقية على العديد من الميزات المفيدة التي لا تملكها معظم قواعد بيانات NoSQL (دعم فهرس ثانوي قوي ؛ مسوحات معقدة ؛ لغة استعلام غنية ؛ JOINs ، وما إلى ذلك) ، من الصعب قياسها.

ونظرًا لتراكم بيانات السلاسل الزمنية بسرعة كبيرة ، يعتقد الكثير من المطورين أن قواعد البيانات العلائقية غير ملائمة لها.

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

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

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

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

يناقش هذا المنشور التوسع. (سيتم نشر منشور التدريج في تاريخ لاحق.)

على وجه الخصوص ، يشرح هذا المنشور:

  • لماذا قواعد البيانات العلائقية لا تزيد بشكل جيد عادة
  • كيف لا تعمل أشجار LSM (المستخدمة عادة في قواعد بيانات NoSQL) على حل احتياجات العديد من تطبيقات السلاسل الزمنية بشكل كاف
  • كيف تكون بيانات السلاسل الزمنية فريدة من نوعها ، وكيف يمكن للمرء الاستفادة من هذه الاختلافات للتغلب على مشكلة القياس ، وبعض نتائج الأداء

دوافعنا ذات شقين: لكل من يواجه مشاكل مماثلة ، مشاركة ما تعلمناه ؛ ولأولئك الذين يفكرون في استخدام TimescaleDB للحصول على بيانات السلاسل الزمنية (بما في ذلك المتشككون!) ، لشرح بعض قرارات التصميم الخاصة بنا.

لماذا لا يتم رفع قواعد البيانات بشكل جيد عادة: يعد تبادل / نفاد الذاكرة باهظ التكلفة

تتمثل مشكلة شائعة في زيادة أداء قاعدة البيانات على جهاز واحد في المفاضلة الكبيرة في التكلفة / الأداء بين الذاكرة والقرص. على الرغم من أن الذاكرة أسرع من القرص ، إلا أنها أكثر تكلفة: حوالي 20 ضعفًا من التخزين في الحالة الصلبة مثل Flash ، و 100 x أعلى من الأقراص الصلبة. في النهاية ، لن تتلاءم مجموعة البيانات الخاصة بنا بأكملها في الذاكرة ، ولهذا السبب سنحتاج إلى كتابة بياناتنا وفهارسنا على القرص.

هذه مشكلة قديمة شائعة في قواعد البيانات العلائقية. ضمن معظم قواعد البيانات العلائقية ، يتم تخزين جدول كمجموعة من الصفحات ذات الحجم الثابت للبيانات (على سبيل المثال ، صفحات بحجم 8 كيلوبايت في PostgreSQL) ، يقوم النظام على أساسها بإنشاء هياكل بيانات (مثل الأشجار B) لفهرسة البيانات. باستخدام فهرس ، يمكن أن يجد الاستعلام بسرعة صفًا له معرف محدد (على سبيل المثال ، رقم الحساب المصرفي) دون مسح الجدول بأكمله أو "السير" في الجدول بترتيب معين.

الآن ، إذا كانت مجموعة العمل من البيانات والفهارس صغيرة ، يمكننا الاحتفاظ بها في الذاكرة.

ولكن إذا كانت البيانات كبيرة بما يكفي بحيث لا يمكننا احتواء جميع الصفحات (ذات الحجم الثابت بالمثل) لشجرة B الموجودة لدينا في الذاكرة ، فإن تحديث جزء عشوائي من الشجرة يمكن أن يشتمل على إدخال / إخراج قرص كبير أثناء قراءة الصفحات من القرص في الذاكرة ، قم بتعديل في الذاكرة ، ثم اكتب مرة أخرى على القرص (عند إخلاءها لإفساح المجال لصفحات B-tree الأخرى). وتحتفظ قاعدة بيانات علائقية مثل PostgreSQL بشجرة B (أو بنية بيانات أخرى) لكل فهرس جدول ، حتى يتم العثور على القيم في هذا الفهرس بكفاءة. لذلك ، تتفاقم المشكلة أثناء فهرسة المزيد من الأعمدة.

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

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

ماذا عن محركات الأقراص الصلبة (SSD)؟ بينما تتخلص حلول مثل محركات أقراص NAND Flash من وقت "البحث" الفعلي ، إلا أنه يمكن قراءتها فقط أو كتابتها في التفاصيل على مستوى الصفحة (اليوم ، عادةً 8 ​​كيلو بايت). لذلك ، حتى لتحديث بايت واحد ، يحتاج البرنامج الثابت لـ SSD إلى قراءة صفحة 8 كيلوبايت من القرص إلى ذاكرة التخزين المؤقت المخزنة مؤقتًا ، وتعديل الصفحة ، ثم كتابة صفحة 8KB المحدثة مرة أخرى إلى كتلة قرص جديدة.

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

أدخل الإنتاجية كدالة من حجم الجدول لـ PostgreSQL 9.6.2 ، يعمل مع 10 عاملين على جهاز Azure قياسي DS4 v2 (8 نواة) مع تخزين SSD (LRS) قائم على SSD. يقوم العملاء بإدراج صفوف فردية في قاعدة البيانات (يحتوي كل منها على 12 عمودًا: طابع زمني ، ومعرف أساسي تم اختياره بشكل عشوائي ، و 10 مقاييس رقمية إضافية). يبدأ معدل PostgreSQL بأكثر من 15 ألف تدرج في الثانية ، ولكن بعد ذلك يبدأ في الانخفاض بشكل ملحوظ بعد 50 مترًا من الصفوف ويبدأ في تجربة تباين كبير للغاية (بما في ذلك فترات تدرج فيها 100 ثانية فقط من الإدخالات في الثانية).

أدخل قواعد بيانات NoSQL مع أشجار دمج Log-Structured (ومشاكل جديدة)

منذ حوالي عقد من الزمان ، بدأنا في رؤية عدد من أنظمة التخزين "NoSQL" تتصدى لهذه المشكلة عبر أشجار الدمج المنظم (LSM) ، والتي تقلل من تكلفة عمل عمليات كتابة صغيرة من خلال إجراء عمليات الكتابة فقط على الإلحاق فقط على القرص.

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

في شجرة LSM ، تتم كتابة جميع التحديثات أولاً على جدول مفروزة في الذاكرة ، ثم يتم مسحها على القرص كدفعة ثابتة ، يتم تخزينها على شكل SSTable ، والتي يتم فهرستها غالبًا في الذاكرة.
(المصدر: https://www.igvita.com/2012/02/06/sstable-and-log-structured-storage-leveldb/)

قد تبدو هذه البنية - التي تم اعتمادها بواسطة العديد من قواعد بيانات "NoSQL" مثل LevelDB و Google BigTable و Cassandra و MongoDB (WiredTiger) و InfluxDB - رائعة في البداية. ومع ذلك ، يقدم مقايضات أخرى: متطلبات ذاكرة أعلى وضعف دعم المؤشر الثانوي.

متطلبات الذاكرة العليا: على عكس B-tree ، في شجرة LSM ، لا يوجد ترتيب واحد: لا يوجد فهرس عالمي ليقدم لنا ترتيبًا فرزًا على جميع المفاتيح. وبالتالي ، يصبح البحث عن قيمة المفتاح أكثر تعقيدًا: أولاً ، تحقق من جدول الذاكرة للحصول على أحدث إصدار من المفتاح ؛ خلاف ذلك ، انظر إلى (على الأرجح) جداول على القرص للعثور على أحدث قيمة مرتبطة بهذا المفتاح. لتجنب الإفراط في إدخال / إخراج القرص (وإذا كانت القيم نفسها كبيرة ، مثل محتوى صفحة الويب المخزنة في BigTable من Google) ، فقد يتم الاحتفاظ بفهارس جميع SSTables بالكامل في الذاكرة ، مما يؤدي بدوره إلى زيادة متطلبات الذاكرة.

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

هناك نهج أفضل لهذه المشكلة. لنبدأ بفهم بيانات السلاسل الزمنية بشكل أفضل.

بيانات السلاسل الزمنية مختلفة

دعنا نعود خطوة إلى الوراء وننظر إلى المشكلة الأصلية التي صممت قواعد البيانات العلائقية لحلها. بدءًا من النظام الأساسي لـ IBM في منتصف السبعينيات ، تم استخدام قواعد البيانات الترابطية فيما أصبح يعرف بمعالجة المعاملات عبر الإنترنت (OLTP).

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

تنشأ بيانات السلاسل الزمنية من العديد من الإعدادات المختلفة: الآلات الصناعية ؛ النقل والخدمات اللوجستية؛ DevOps ، مركز البيانات ، ومراقبة الخادم ؛ والتطبيقات المالية.

الآن لننظر في بعض الأمثلة على أعباء عمل السلاسل الزمنية:

  • DevOps / الخادم / مراقبة الحاوية. يقوم النظام عادةً بجمع مقاييس حول خوادم أو حاويات مختلفة: استخدام وحدة المعالجة المركزية والذاكرة المجانية / المستخدمة ، شبكة tx / rx ، قرص IOPS ، وما إلى ذلك. ترتبط كل مجموعة من المقاييس بطابع زمني واسم خادم فريد / معرّف فريد ومجموعة من العلامات التي تصف سمة ما يتم جمعها.
  • إنترنت الأشياء الاستشعار الاستشعار. يجوز لكل جهاز إنترنت الأشياء الإبلاغ عن قراءات أجهزة استشعار متعددة لكل فترة زمنية. على سبيل المثال ، يمكن أن يشمل ذلك لمراقبة الجودة البيئية والهواء: درجة الحرارة والرطوبة والضغط الجوي ومستويات الصوت وقياسات ثاني أكسيد النيتروجين وأول أكسيد الكربون والجسيمات وما إلى ذلك. وترتبط كل مجموعة من القراءات بطابع زمني ومعرف فريد للجهاز. ، وقد تحتوي على بيانات تعريف أخرى.
  • البيانات المالية. قد تتضمن بيانات التجزئة المالية تدفقات مع طابع زمني واسم الضمان وتغير السعر و / أو السعر الحالي. هناك نوع آخر من البيانات المالية هو معاملات الدفع ، والتي تتضمن معرف حساب فريدًا ، الطابع الزمني ، مبلغ المعاملة ، وأي بيانات تعريف أخرى. (لاحظ أن هذه البيانات مختلفة عن مثال OLTP أعلاه: نحن هنا نسجل كل معاملة ، بينما كان نظام OLTP يعكس فقط الحالة الحالية للنظام.)
  • إدارة الأسطول / الأصول. قد تتضمن البيانات معرف المركبة / الأصول ، والطابع الزمني ، وإحداثيات GPS في ذلك الطابع الزمني ، وأي بيانات وصفية.

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

بمعنى آخر ، لهاتين الحملتين خصائص مختلفة جدًا:

يكتب OLTP

  • تحديثات في المقام الأول
  • وزعت عشوائيا (على مجموعة من المفاتيح الأساسية)
  • في كثير من الأحيان المعاملات عبر مفاتيح أساسية متعددة

سلسلة زمنية يكتب

  • إدراج في المقام الأول
  • في المقام الأول إلى الفاصل الزمني الحديث
  • يرتبط بشكل أساسي بكل من الطابع الزمني والمفتاح الأساسي المنفصل (مثل معرّف الخادم ومعرف الجهاز ومعرف الأمان / الحساب ومعرف المركبة / الأصل وما إلى ذلك)

لماذا هذا مهم؟ كما سنرى ، يمكن للمرء الاستفادة من هذه الخصائص لحل مشكلة التوسع في قاعدة البيانات العلائقية.

طريقة جديدة: التكيف الوقت / الفضاء chunking

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

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

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

هناك طريقة أخرى ، والتي نسميها "اختزال الوقت / الفضاء التكيفي". هذا هو ما نستخدمه في TimescaleDB.

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

بدلاً من مجرد الفهرسة بحسب الوقت ، يقوم TimescaleDB بإنشاء جداول مميزة عن طريق تقسيم البيانات وفقًا لبعدين: الفاصل الزمني والمفتاح الأساسي (مثل معرف الخادم / الجهاز / الأصل). نشير إلى هذه الأجزاء كقطع لتمييزها عن الأقسام ، والتي يتم تحديدها عادةً عن طريق تقسيم مساحة المفتاح الأساسية. نظرًا لأن كل قطعة من هذه القطع يتم تخزينها كجدول قاعدة بيانات نفسه ، ومخطط الاستعلام على دراية بنطاقات القطعة (في الوقت ومساحة المفاتيح) ، يمكن لمخطِّط الاستعلام أن يخبر فورًا عن القطعة (الأجزاء) التي تنتمي إليها بيانات العملية. (ينطبق هذا على كل من إدراج الصفوف ، وكذلك على تشذيب مجموعة القطع التي يجب لمسها عند تنفيذ الاستعلامات.)

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

مقاربات لتنفيذ القطع

لكلتا الطريقتين بديهية لتصميم هذا الوقت / الفضاء chunking كل قيود كبيرة:

النهج رقم 1: فواصل زمنية محددة

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

كل قطعة لها مدة زمنية محددة. ومع ذلك ، إذا زاد حجم البيانات في الوقت ، يصبح حجم القطعة في النهاية كبيرًا جدًا بحيث لا يمكن احتواؤه في الذاكرة.

النهج رقم 2: قطع ثابتة الحجم

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

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

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

TimescaleDB يأخذ نهجا ثالثا أن الأزواج نقاط القوة في كلا النهجين.

المنهج رقم 3: فواصل التكيف (تصميمنا الحالي)

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

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

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

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

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

النتيجة: تحسن بنسبة 15x في معدل الإدراج

الحفاظ على القطع بالحجم الصحيح هو كيف نحقق نتائج INSERT التي تفوق PostgreSQL الفانيليا ، والتي أظهرها أجاي بالفعل في منصبه السابق.

إدراج سرعة نقل TimescaleDB مقابل PostgreSQL ، باستخدام نفس عبء العمل كما هو موضح سابقًا. على عكس فانيليا PostgreSQL ، يحافظ TimescaleDB على معدل إدخال ثابت (حوالي 14.4 ألف تدرج في الثانية ، أو 144 كيلو متر / ثانية ، مع تباين منخفض للغاية) ، بغض النظر عن حجم مجموعة البيانات.

يستمر صبيب الإدراج الثابت هذا أيضًا عند كتابة مجموعات كبيرة من الصفوف في عمليات مفردة إلى TimescaleDB (بدلاً من صف تلو الآخر). مثل هذه الإضافات المجمعة هي ممارسة شائعة لقواعد البيانات المستخدمة في بيئات إنتاج أكثر نطاقًا ، على سبيل المثال ، عند استيعاب البيانات من قائمة انتظار موزعة مثل كافكا. في مثل هذه السيناريوهات ، يمكن لخادم Timescale واحد استيعاب 130 ألف صف (أو 1.3 مليون متر) في الثانية ، أي ما يقرب من 15x من Vanilla PostgreSQL بمجرد أن يصل الجدول إلى صفين 100M.

إدراج الإنتاجية من TimescaleDB مقابل PostgreSQL عند تنفيذ INSERTs من دفعات 10000 صف.

ملخص

يمكن أن تكون قاعدة البيانات العلائقية قوية للغاية بالنسبة لبيانات السلاسل الزمنية. ومع ذلك ، فإن تكاليف التبديل داخل / خارج الذاكرة تؤثر بشكل كبير على أدائها. لكن نُهج NoSQL التي تقوم بتطبيق Log Structured Merge Trees قد غيرت المشكلة فقط ، مقدمة متطلبات ذاكرة أعلى وضعف دعم فهرس ثانوي.

من خلال إدراك أن بيانات السلاسل الزمنية مختلفة ، فإننا قادرون على تنظيم البيانات بطريقة جديدة: تكييف الوقت / الفضاء المكيف. هذا يقلل من التبديل إلى القرص عن طريق الحفاظ على مجموعة بيانات العمل صغيرة بما يكفي لتناسب الذاكرة الداخلية ، مع السماح لنا بالحفاظ على دعم قوي للفهرس الأساسي والثانوي (ومجموعة الميزات الكاملة لبرنامج PostgreSQL). ونتيجة لذلك ، نحن قادرون على زيادة حجم PostgreSQL بشكل كبير ، مما يؤدي إلى تحسن بنسبة 15x في معدلات الإدراج.

ولكن ماذا عن مقارنات الأداء بقواعد بيانات NoSQL؟ هذا المنصب قريبا.

في غضون ذلك ، يمكنك تنزيل أحدث إصدار من TimescaleDB ، الذي تم إصداره بموجب ترخيص Apache 2 ، على GitHub.

هل أعجبتك هذه التدوينة؟ هل أنت مهتم بمعرفة المزيد؟

تابعنا هنا على "متوسط" ، وتحقق من GitHub ، وانضم إلى مجتمع Slack ، واشترك في قائمة مراسلات المجتمع أدناه. نحن نوظف أيضًا!