يقرأ التقاط تغيّرات البيانات (change data capture) سجلّ المعاملات الخاصّ بقاعدة البيانات نفسها، ويحوّل كلّ عملية إدراج وتحديث وحذف إلى حدث مرتَّب. فبدلًا من إعادة استقصاء دفتر الأستاذ بحثًا عن صفوف ربما تغيّرت، تشترك في التغييرات وقت التزام المصدر بها. وبالنسبة للذكاء الاصطناعي المالي، يعني هذا أن تبقى السمات وفهارس الاسترجاع على بُعد ثوانٍ من نظام السجلّ المرجعي، بينما لا يكاد النظام المصدري يشعر بوجودك.
البديل المعتاد هو استعلام مجدوَل يسحب كلّ ما عُدِّل منذ التشغيل الأخير. وهو يعمل حتى يتوقف عن العمل. فإعادة الاستقصاء لا تكون أحدث من فترتها الزمنية، وتُغفل أيّ صفّ جرى تحديثه ثم التراجع عنه بين تشغيلين، ولا يمكنها رؤية عملية حذف على الإطلاق ما لم تُقارن لقطات كاملة. والأسوأ أن الاستعلام الذي يعثر على التغييرات الأخيرة يكون في الغالب مسحًا كاملًا لجدول يفتقر إلى الفهرس المناسب، فتُكلِّف الحداثةُ التي تريدها النظامَ المعاملاتي بالضبط الحِمل الذي يعجز عن تحمّله عند إقفال الرُّبع.
لماذا يتفوّق السجلّ على الاستقصاء
يحتوي سجلّ المعاملات أصلًا على ما تحاول إعادة بنائه عبر الاستقصاء: التسلسل الدقيق للتغييرات المُلتزَم بها، ولكلٍّ منها طابع زمني وموضع. وقراءته مباشرةً تمنحك ثلاثة أمور يعجز عنها الاستقصاء.
- السجلّ كامل. فعمليات الحذف والتحديثات الوسيطة تصل كلٌّ منها كحدث مستقلّ. أما الاستقصاء الذي يعتمد على عمود
updated_atفلا يرى أبدًا صفًّا أُدرِج ثم حُذِف بين تشغيلين، وفي المجال المالي قد يكون ذلك الصفّ دفعةً مصحَّحة أو قيدًا معكوسًا تحتاجه في مسار التدقيق. - السجلّ مرتَّب. تمنحك مواضع السجلّ ترتيبًا كليًّا لكلّ جدول ووسيلةً للاستدلال على السببية. فحين تنتقل حالة دفعة من قيد الانتظار ← مُسوَّاة ← مُرتجَعة في ثماني ثوانٍ، تحصل على الانتقالات الثلاثة بالتسلسل، لا على أيّها صادف أن كان الحالة الراهنة لحظة إطلاق الاستقصاء.
- السجلّ لا يكاد يُكلِّف المصدر شيئًا. فقاعدة البيانات تكتبه أصلًا من أجل المتانة والنسخ المتماثل، ويتتبّعه موصِّل CDC كما تفعل نسخة القراءة المتماثلة. أنت لا تُكدِّس حِمل الاستعلامات على القاعدة الأساسية خلال أكثر ساعات الشهر انشغالًا.
نلجأ افتراضيًّا إلى CDC القائم على السجلّ: فكّ الترميز المنطقي (logical decoding) في Postgres، والسجلّ الثنائي (binlog) في MySQL، وميزة CDC في SQL Server، أو تدفّقات التغيير (change streams) التي توحّدها Debezium عبر الأنظمة الثلاثة جميعًا. أما CDC القائم على الاستعلام عبر عمود updated_at فهو خيار احتياطي للمصادر التي لا نستطيع الحصول فيها على فتحة نسخ متماثل، ونضع عليه علامة بأنه يفقد البيانات عند الحذف كي لا يفترض أحد في المراحل اللاحقة أنه كامل.
الحصول على صحّة النقطة الزمنية (point-in-time correctness) من تدفّق
سبب أهمية CDC للنماذج، لا للوحات المعلومات فحسب، هو أن سجلّ التغييرات ركيزة نظيفة لسمات صحيحة عند النقطة الزمنية (point-in-time correct). فكلّ حدث يحمل طابع زمن الالتزام في المصدر. وإذا احتسبت السمات كما كانت في ذلك الطابع الزمني بدلًا من لحظة معالجة خطّك للحدث صدفةً، حصلت على تاريخ سمات يطابق ما كان معلومًا فعلًا في كلّ لحظة.
في هذا التمييز يختبئ التسرّب (leakage). لنفترض أن فئة مخاطر عميل رُقِّيت في الرابع عشر، وأن نموذجًا يتّخذ قرارًا بشأن معاملة مؤرَّخة في العاشر. إذا عكس مخزن السمات الفئة الراهنة، فقد سرّبت خِلسةً أربعة أيام من المعلومات المستقبلية إلى التدريب، وسيبدو تقييمك دون الاتصال (offline) أفضل مما سيكون عليه الإنتاج أبدًا. يتيح لك CDC تجنّب هذا بنظافة، لأن السجلّ يخبرك أن الفئة كما كانت في العاشر كانت الفئة القديمة. فتقرأ السجلّ حتى نقطة قرار التسمية وتتوقّف.
بضعة أمور نصرّ عليها للحفاظ على نزاهة هذا:
- افصل زمن الحدث عن زمن الاستيعاب في كلّ مكان. فزمن الحدث هو التزام المصدر؛ وزمن الاستيعاب هو لحظة رؤية الموصِّل له. تعتمد السمات على زمن الحدث. وتعتمد مراقبة الكُمون على الفجوة بينهما.
- عامِل الأحداث المتأخرة وغير المرتَّبة على أنها أمر طبيعي، لأنها كذلك في أيّ مصدر موزَّع. تحتاج التجميعات النافذيّة (windowed aggregates) إلى مهلة سماح وإلى سياسة محدَّدة لما يحدث حين يصل تغيير بعد إغلاق النافذة.
- احفظ سلسلة الأثر (lineage). فحين تتغيّر قيمة سمة، ينبغي أن تكون قادرًا على تتبّعها رجوعًا إلى حدث التغيير ومعاملة المصدر المحدَّدين اللذين أنتجاها. هذا الأثر هو ما يجعل قرار النموذج قابلًا للتفسير لمراجع أو مدقّق بعد أشهر.
أين ينكسر CDC بهدوء، وكيف نحتوي ذلك
CDC ليس مجانيًّا، وأنماط أعطاله محدَّدة. أبرزها انحراف المخطّط (schema drift). فحين يضيف فريق مصدري عمودًا، أو يعيد تسمية آخر، أو يغيّر نوعًا، فإن الموصِّل الساذج إمّا يُسقط الحقل أو يوقف التدفّق. نحن نضع عقد بيانات (data contract) أمام الموصِّل بحيث يصبح تغيير المخطّط حدثًا متفاوَضًا عليه له إصدار، لا استدعاءً طارئًا في الثالثة فجرًا. فالتغييرات المتوافقة تمرّ؛ والتغييرات الكاسرة تفشل بصخب عند الحدّ بدلًا من أن تُفسد السمات بصمت.
الثاني هو التسليم مرّة واحدة بالضبط (exactly-once)، وهو صعب فعلًا من الطرف إلى الطرف. فمعظم خطوط CDC تسلّم مرّة واحدة على الأقلّ (at-least-once)، أي أن التغيير نفسه قد يصل مرّتين بعد إعادة تشغيل الموصِّل. وهذا لا بأس به في المجال المالي فقط إذا كانت مرحلتك اللاحقة عديمة الأثر التكراري (idempotent). نحن نُفهرِس عمليات الكتابة على المفتاح الأساسي للمصدر إضافةً إلى موضع السجلّ، بحيث يُعيد الحدث المُعاد تشغيله الكتابة فوق البيانات بدلًا من العدّ المزدوج. فحدث تسوية مكرَّر ينبغي أن يترك الرصيد دون تغيير، لا أن يحرّكه مرّتين.
الثالث هو فجوة التسوية (reconciliation gap). فالتدفّقات تُسقط أحداثًا، والموصِّلات تتأخّر، وفتحات النسخ المتماثل تُسقَط تحت ضغط القرص. نحن لا نجعل CDC المسار الوحيد أبدًا. فدفعة يومية تعيد احتساب التجميعات نفسها من لقطة كاملة وتقارنها بما أنتجه التدفّق. وحين يختلفان بما يتجاوز حدّ التسامح، يفوز رقم الدفعة ويُسجَّل الفرق. تلك المهمّة مملّة، وهي بالضبط ما يتيح لك الوثوق بالمسار السريع بقيّة الوقت.
يستحقّ التعامل مع الحذف ذكرًا خاصًّا به. ففي كثير من الأنظمة المالية لا يُحذَف السجلّ فيزيائيًّا أبدًا، بل يُستبدَل بقيدٍ مصحِّح، وعادةً ما يشير الحذف القاسي (hard delete) إلى شيء تريد أن تعرفه. التقِط عمليات الحذف كأحداث من الدرجة الأولى، واحفظ شاهدة الحذف (tombstone)، ودَع المرحلة اللاحقة تقرّر ما إذا كانت ستُزيل أو تضع علامة “مُستبدَل”. فالتخلّص من الحذف لأن مصبّك لا يفهم سوى عمليات الدمج والإدراج (upserts) هو الطريقة التي تجعل معاملةً معكوسة تظلّ حيّةً في سمة كان ينبغي لها أن تنساها.
متى ما أُنجِز هذا بهذه الحواجز الواقية، حوّل CDC طبقة البيانات من شيء تستقصيه وفق جدول إلى شيء يخبرك متى تغيّر. فترى النماذج فوقه العالم نفسه الذي يراه دفتر الأستاذ، متأخّرةً بضع ثوانٍ فحسب، وترى تاريخ ذلك العالم بالترتيب الذي حدث به فعلًا.
الأسئلة الشائعة
هل يحلّ التقاط تغيّرات البيانات (CDC) محلّ التحميلات الدفعية الليلية بالكامل؟
نادرًا، ونحن لا ندفع باتجاه ذلك. يُبقي CDC الجداول التشغيلية وفهارس الاسترجاع محدَّثة على مدار اليوم، بينما تبقى الدفعة الليلية للتسوية هي مصدر الحقيقة الذي يلتقط أيّ شيء أسقطه التدفّق أو أعاد ترتيبه.
هل يمكنني تشغيل CDC دون المساس بإعدادات قاعدة البيانات المصدرية؟
يحتاج CDC القائم على السجلّ (log-based) إلى تفعيل سجلّ الكتابة المسبقة (write-ahead log) أو السجلّ الثنائي (binary log) وإلى فتحة نسخ متماثل (replication slot) أو ما يعادلها، وهذا تغيير يقوم به مسؤول قاعدة البيانات (DBA). أما CDC القائم على الاستعلام (query-based) عبر عمود updated_at فلا يتطلّب أيّ إعداد على الخادم، لكنه يُغفل عمليات الحذف والتحديثات القاسية، لذلك لا نستخدمه إلا حيث يضمن مخطّط البيانات وجود طابع زمني متزايد باطّراد.
كيف تمنعون خطّ معالجة سمات قائمًا على CDC من تسريب معلومات مستقبلية إلى التدريب؟
يحمل كلّ حدث تغيير طابع زمن الالتزام (commit timestamp) في المصدر، وتُحتسب السمات كما كانت في ذلك الوقت، لا كما كانت وقت الاستيعاب. يقرأ التدريب السجلّ حتى نقطة قرار التسمية (label)، لذا فإن أيّ قيمة كُتبت بعد تلك النقطة تكون غير مرئية للنموذج.