المعاملات في InterSystems IRIS globals

المعاملات في InterSystems IRIS globalsيدعم InterSystems IRIS DBMS هياكل مثيرة للاهتمام لتخزين البيانات - العالمية. في الأساس، هذه مفاتيح متعددة المستويات مع العديد من الأشياء الجيدة الإضافية في شكل معاملات ووظائف سريعة لاجتياز أشجار البيانات والأقفال ولغة ObjectScript الخاصة بها.

اقرأ المزيد عن الكرات العالمية في سلسلة المقالات "الكنزات العالمية هي سيوف كنز لتخزين البيانات":

الأشجار. الجزء 1
الأشجار. الجزء 2
صفائف متفرقة. الجزء 3

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

وكما هو معروف من نظرية قواعد البيانات العلائقية، فإن التنفيذ الجيد للمعاملات يجب أن يلبي المتطلبات حمض:

أ - الذرية (الذرية). يتم تسجيل جميع التغييرات التي تم إجراؤها في المعاملة أو لا شيء على الإطلاق.

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

أنا - عزل. المعاملات الجارية بالتوازي يجب ألا تؤثر على بعضها البعض.

د – متينة . بعد إتمام المعاملة بنجاح، يجب ألا تؤثر المشكلات في المستويات الأدنى (انقطاع الطاقة، على سبيل المثال) على البيانات التي تم تغييرها بواسطة المعاملة.

العوالم هي هياكل بيانات غير علائقية. لقد تم تصميمها لتعمل بسرعة فائقة على أجهزة محدودة للغاية. دعونا نلقي نظرة على تنفيذ المعاملات في الكرات العالمية باستخدام صورة عامل ميناء IRIS الرسمية.

لدعم المعاملات في IRIS، يتم استخدام الأوامر التالية: تستارت, تيكوميت, الارتداد.

1. الذرية

أسهل طريقة للتحقق هي الذرية. نتحقق من وحدة تحكم قاعدة البيانات.

Kill ^a
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TCOMMIT

ثم نستنتج:

Write ^a(1), “ ”, ^a(2), “ ”, ^a(3)

نحن نحصل:

1 2 3

كل شيء على ما يرام. يتم الحفاظ على الذرية: يتم تسجيل جميع التغييرات.

دعونا نعقد المهمة ونقدم خطأ ونرى كيف يتم حفظ المعاملة، جزئيًا أو لا يتم حفظها على الإطلاق.

دعونا نتحقق من الذرية مرة أخرى:

Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3

ثم سنوقف الحاوية بالقوة ونطلقها ونرى.

docker kill my-iris

هذا الأمر يعادل تقريبًا إيقاف التشغيل القسري، حيث يرسل إشارة SIGKILL لإيقاف العملية فورًا.

ربما تم حفظ المعاملة جزئيًا؟

WRITE ^a(1), ^a(2), ^a(3)
^
<UNDEFINED> ^a(1)

- لا، لم ينج.

لنجرب أمر التراجع:

Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TROLLBACK

WRITE ^a(1), ^a(2), ^a(3)
^
<UNDEFINED> ^a(1)

لم ينج شيء أيضًا.

2. الاتساق

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

على سبيل المثال، لدينا ^شخص عالمي، حيث نقوم بتخزين الشخصيات ونستخدم رقم التعريف الضريبي (TIN) كمفتاح.

^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
...

من أجل إجراء بحث سريع بالاسم الأخير والاسم الأول، قمنا بإنشاء مفتاح ^index.

^index(‘Kamenev’, ‘Sergey’, 1234567) = 1

لكي تكون قاعدة البيانات متسقة، يجب علينا إضافة الشخصية على النحو التالي:

TSTART
^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
^index(‘Kamenev’, ‘Sergey’, 1234567) = 1
TCOMMIT

وبناءً على ذلك، عند الحذف، يجب علينا أيضًا استخدام المعاملة:

TSTART
Kill ^person(1234567)
ZKill ^index(‘Kamenev’, ‘Sergey’, 1234567)
TCOMMIT

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

3. العزلة

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

يمكن مقارنة الموقف عندما يعمل العديد من المستخدمين في وقت واحد مع نفس مستودع التعليمات البرمجية ويحاولون إجراء تغييرات في نفس الوقت على العديد من الملفات في وقت واحد.

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

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

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

عند تنفيذ المعاملات بالتوازي، من المهم بالنسبة لنا ألا تتداخل مع بعضها البعض. هذه هي خاصية العزلة.

تحدد SQL 4 مستويات عزل:

  • قراءة غير ملتزم بها
  • اقرأ ملتزم
  • قراءة متكررة
  • SERIALIZABLE

دعونا ننظر إلى كل مستوى على حدة. تكاليف تنفيذ كل مستوى تنمو بشكل كبير تقريبا.

قراءة غير ملتزم بها - وهذا هو أدنى مستوى من العزلة، ولكنه في نفس الوقت الأسرع. يمكن للمعاملات قراءة التغييرات التي أجراها بعضها البعض.

اقرأ ملتزم هو المستوى التالي من العزلة، وهو حل وسط. لا يمكن للمعاملات قراءة تغييرات بعضها البعض قبل الالتزام، ولكن يمكنها قراءة أي تغييرات تم إجراؤها بعد الالتزام.

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

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

SERIALIZABLE - أعلى مستوى من العزل . ويتميز بأن البيانات المستخدمة بأي شكل من الأشكال في معاملة ما (قراءة أو تغيير) لا تصبح متاحة للمعاملات الأخرى إلا بعد إتمام المعاملة الأولى.

أولاً، دعونا نكتشف ما إذا كان هناك عزل للعمليات في المعاملة عن الخيط الرئيسي. دعونا نفتح نافذتين طرفيتين.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

لا يوجد عزلة. يرى أحد الخيوط ما يفعله الشخص الثاني الذي فتح المعاملة.

دعونا نرى ما إذا كانت المعاملات ذات الخيوط المختلفة ترى ما يحدث بداخلها.

دعونا نفتح نافذتين طرفيتين ونفتح معاملتين بالتوازي.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

ترى المعاملات الموازية بيانات بعضها البعض. لذلك، حصلنا على أبسط وأسرع مستوى للعزل، وهو القراءة غير الملتزمة.

من حيث المبدأ، كان من الممكن أن نتوقع هذا بالنسبة للعوالم العالمية، التي كان الأداء يشكل أولوية بالنسبة لها دائما.

ماذا لو كنا بحاجة إلى مستوى أعلى من العزلة في العمليات على الكرات العالمية؟

هنا تحتاج إلى التفكير في سبب الحاجة إلى مستويات العزل على الإطلاق وكيفية عملها.

أعلى مستوى عزل، SERIALIZE، يعني أن نتيجة المعاملات المنفذة بالتوازي تعادل تنفيذها المتسلسل، مما يضمن عدم وجود تصادمات.

يمكننا القيام بذلك باستخدام الأقفال الذكية في ObjectScript، والتي لها العديد من الاستخدامات المختلفة: يمكنك إجراء قفل منتظم وتزايدي ومتعدد باستخدام الأمر LOCK.

تعتبر مستويات العزل المنخفضة عبارة عن مقايضات مصممة لزيادة سرعة قاعدة البيانات.

دعونا نرى كيف يمكننا تحقيق مستويات مختلفة من العزلة باستخدام الأقفال.

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

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

حجب على مرحلتين
قفل على مرحلتين

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

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

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

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

  1. أي عملية إذا كانت البيانات مجانية
  2. فقط العملية التي لديها قفل مشترك على هذه البيانات وكانت أول من طلب قفلًا حصريًا.

المعاملات في InterSystems IRIS globals

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

READ_COMMITTED — جوهر هذا المستوى هو أننا نرى فقط البيانات الملتزم بها من سلاسل رسائل أخرى. إذا لم يتم الالتزام بالبيانات الموجودة في معاملة أخرى بعد، فسنرى نسختها القديمة.

يتيح لنا ذلك موازنة العمل بدلاً من انتظار تحرير القفل.

بدون حيل خاصة، لن نتمكن من رؤية الإصدار القديم من البيانات في IRIS، لذلك سيتعين علينا الاكتفاء بالأقفال.

وبناء على ذلك، سيتعين علينا استخدام الأقفال المشتركة للسماح بقراءة البيانات فقط في لحظات الاتساق.

لنفترض أن لدينا قاعدة مستخدمين ^شخص يقوم بتحويل الأموال لبعضهم البعض.

لحظة الانتقال من شخص 123 إلى شخص 242:

LOCK +^person(123), +^person(242)
Set ^person(123, amount) = ^person(123, amount) - amount
Set ^person(242, amount) = ^person(242, amount) + amount
LOCK -^person(123), -^person(242)

يجب أن تكون لحظة طلب المبلغ المالي من الشخص 123 قبل الخصم مصحوبة بحظر حصري (افتراضيًا):

LOCK +^person(123)
Write ^person(123)

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

LOCK +^person(123)#”S”
Write ^person(123)

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

قراءة متكررة - يسمح مستوى العزل هذا بقراءات متعددة للبيانات التي يمكن تعديلها عن طريق المعاملات المتزامنة.

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

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

LOCK +^person(123, amount)#”S”
чтение ^person(123, amount)

عمليات أخرى (في هذا الوقت تحاول الخيوط المتوازية تغيير ^person(123, المبلغ)، لكن لا يمكنها ذلك)

LOCK +^person(123, amount)
изменение ^person(123, amount)
LOCK -^person(123, amount)

чтение ^person(123, amount)
LOCK -^person(123, amount)#”S”

عند إدراج الأقفال مفصولة بفواصل، يتم أخذها بالتسلسل، ولكن إذا قمت بذلك:

LOCK +(^person(123),^person(242))

ثم يتم أخذها ذريًا مرة واحدة.

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

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

4. المتانة

لقد أجريت اختبارات باستخدام القطع الصلب للحاوية

docker kill my-iris

القاعدة تحملتهم جيدًا. ولم يتم تحديد أي مشاكل.

اختتام

بالنسبة للشركات العالمية، يوفر InterSystems IRIS دعمًا للمعاملات. فهي حقا ذرية وموثوقة. لضمان اتساق قاعدة البيانات القائمة على العوالم، يلزم بذل جهود المبرمج واستخدام المعاملات، نظرًا لأنها لا تحتوي على بنيات مدمجة معقدة مثل المفاتيح الخارجية.

مستوى عزل العناصر العالمية دون استخدام الأقفال هو "قراءة غير ملتزم بها"، وعند استخدام الأقفال يمكن ضمانه حتى مستوى التسلسل.

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

المصدر: www.habr.com

إضافة تعليق