
InterSystems IRIS DBMS تدعم هياكل تخزين البيانات الغريبة - على مستوى العالم. في الواقع ، هذه مفاتيح متعددة المستويات مع مزايا إضافية متعددة في شكل معاملات ووظائف سريعة لاجتياز أشجار البيانات والأقفال ولغة ObjectScript الخاصة بها.
المزيد عن globals في سلسلة مقالات "Globals - Swords-Masons for Data Storage":
الأشجار. الجزء 1الأشجار. الجزء 2صفائف متفرق. الجزء 3أصبح من المثير للاهتمام بالنسبة لي كيف يتم تنفيذ المعاملات في العالمية ، ما هي الميزات الموجودة. بعد كل شيء ، هذا هو هيكل مختلف تماما لتخزين البيانات من الجداول المعتادة. مستوى أقل بكثير.
كما تعلم من نظرية قاعدة البيانات العلائقية ، يجب أن يفي تنفيذ المعاملة الجيدة بمتطلبات
ACID :
أ - الذرية (الذرية). يتم تسجيل جميع التغييرات التي أجريت على المعاملة أو لا شيء على الإطلاق.
ج - الاتساق. بعد اكتمال المعاملة ، يجب أن تكون الحالة المنطقية لقاعدة البيانات متسقة داخليًا. في العديد من الطرق ، ينطبق هذا المتطلب على المبرمج ، ولكن في حالة قواعد بيانات SQL ، ينطبق أيضًا على المفاتيح الخارجية.
الأول - عزل (العزلة). يجب ألا تؤثر المعاملات الموازية على بعضها البعض.
د - دائم. بعد اكتمال المعاملة بنجاح ، يجب ألا تؤثر المشكلات في المستويات الأدنى (انقطاع الطاقة ، على سبيل المثال) على البيانات التي تم تغييرها بواسطة المعاملة.
Globals هي هياكل البيانات غير العلائقية. تم إنشاؤها للعمل فائق السرعة على أجهزة محدودة للغاية. دعونا نفهم تنفيذ المعاملات في العالم باستخدام
صورة عامل ميناء IRIS الرسمية .
لدعم المعاملات في IRIS ، يتم استخدام الأوامر التالية:
TSTART و
TCOMMIT و
TROLLBACK .
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. الاتساق
نظرًا لأنه في قواعد البيانات على globals ، يتم عمل المفاتيح أيضًا على globals (أذكر أن Global هي بنية ذات مستوى أدنى لتخزين البيانات من جدول علائقي) ، للوفاء بمتطلبات الاتساق ، يجب عليك تضمين تغيير المفتاح في نفس المعاملة مثل التغيير العالمي.
على سبيل المثال ، لدينا شخص عالمي ^ حيث نقوم بتخزين الشخصيات ونستخدم TIN كمفتاح.
^person(1234567, 'firstname') = 'Sergey' ^person(1234567, 'lastname') = 'Kamenev' ^person(1234567, 'phone') = '+74995555555 ...
من أجل إجراء بحث سريع بالاسم الأخير والاسم الأول ، قمنا بعمل فهرس ^ ^.
^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 هو أعلى مستوى من العزلة. يتميز بحقيقة أن البيانات المستخدمة بأي طريقة في المعاملة (القراءة أو التغيير) تصبح متاحة للمعاملات الأخرى فقط بعد الانتهاء من المعاملة الأولى.
أولاً ، دعونا نتعرف على ما إذا كان هناك عزل للعمليات في معاملة ما عن الخيط الرئيسي. دعونا فتح 2 النوافذ الطرفية.
لا يوجد عزلة. يرى مؤشر ترابط واحد ما يفعله الشخص الثاني الذي فتح المعاملة.
دعونا نرى ما إذا كانت معاملات التدفقات المختلفة ترى ما يحدث داخلها.
نفتح 2 النوافذ الطرفية وفتح المعاملات 2 في نفس الوقت.
المعاملات المتزامنة ترى بيانات بعضهم البعض. لذلك ، حصلنا على أبسط ، ولكن أيضا أسرع مستوى العزلة قراءة غير ملتزم بها.
من حيث المبدأ ، يمكن توقع ذلك بالنسبة للعالم ، حيث كانت السرعة له الأولوية دائمًا.
ولكن ماذا لو كنا بحاجة إلى مستوى أعلى من العزلة في العمليات العالمية؟
هنا تحتاج إلى التفكير في سبب الحاجة إلى مستويات العزل وكيفية عملها.
أعلى مستوى للعزل من SERIALIZE يعني أن نتيجة المعاملات المنفذة في وقت واحد تعادل تنفيذها المتسلسل ، مما يضمن عدم وجود تصادمات.
يمكننا القيام بذلك بمساعدة أقفال مختصة في ObjectScript ، والتي لديها العديد من الطرق المختلفة للتطبيق: يمكنك القيام بتأمين منتظم ، تدريجي ، متعدد باستخدام أمر
LOCK .
مستويات العزل الأدنى هي مفاضلات مصممة لزيادة سرعة قاعدة البيانات.
دعونا نرى كيف يمكننا تحقيق مستويات مختلفة من العزلة باستخدام الأقفال.
يسمح لك هذا المشغل بأخذ الأقفال الحصرية اللازمة لتغيير البيانات فحسب ، ولكن أيضًا ما يسمى الأقفال المشتركة ، والتي يمكن أن تأخذ عدة خيوط في وقت واحد ، عندما تحتاج إلى قراءة البيانات التي لا ينبغي تغييرها بواسطة عمليات أخرى أثناء القراءة.
مزيد من المعلومات حول طريقة القفل ثنائية الطور باللغتين الروسية والإنجليزية:
→
قفل مرحلتين→
قفل مرحلتينتكمن الصعوبة في أن حالة قاعدة البيانات أثناء المعاملة قد تكون غير متسقة ، ومع ذلك ، فإن هذه البيانات غير المتسقة تكون مرئية للعمليات الأخرى. كيفية تجنب هذا؟
باستخدام الأقفال ، سنجعل هذه النوافذ من الرؤية التي سيتم فيها الاتفاق على حالة قاعدة البيانات. وسيتم التحكم في جميع المكالمات إلى مثل هذه النوافذ من رؤية الحالة المتفق عليها من قبل الأقفال.
الأقفال المشتركة لنفس البيانات قابلة لإعادة الاستخدام - يمكن أن تأخذها عدة عمليات. هذه الأقفال تمنع العمليات الأخرى من تغيير البيانات ، أي يتم استخدامها لتشكيل نوافذ الحالة المنسقة لقاعدة البيانات.
يتم استخدام الأقفال الحصرية لتعديل البيانات - عملية واحدة فقط يمكن أن تأخذ مثل هذا القفل. يمكن للحظر الحصري أن يأخذ:
- أي عملية إذا كانت البيانات مجانية
- فقط العملية التي لديها قفل مشترك على هذه البيانات وأول واحد طلب قفل الحصري.

كلما كانت نافذة الرؤية أضيق ، كلما طال انتظار العمليات الأخرى ، ولكن كلما كانت حالة قاعدة البيانات الموجودة فيها أكثر اتساقًا.
READ_COMMITED - جوهر هذا المستوى هو أننا لا نرى سوى البيانات من التدفقات الأخرى التي تم قفلها. إذا لم يتم الالتزام بالبيانات في معاملة أخرى بعد ، فسوف نرى نسختها القديمة.
هذا يسمح لنا بموازنة العمل بدلاً من انتظار إصدار القفل.
بدون حيل خاصة ، لن نتمكن من رؤية الإصدار القديم من البيانات في 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)
ومع ذلك ، إذا افترضنا أن عمليات قاعدة البيانات يتم تنفيذها على الفور تقريبًا (أذكر أن globals هي بنية مستوى أقل بكثير من جدول علائقي) ، تنخفض الحاجة إلى هذا المستوى.
القراءة المتكررة - في مستوى العزل هذا ،
يُفترض أنه يمكن أن يكون هناك قراءات متعددة للبيانات يمكن تعديلها عن طريق المعاملات المتزامنة.
وفقًا لذلك ، سيتعين علينا وضع قفل مشترك عند قراءة البيانات التي نقوم بتغييرها والأقفال الحصرية على البيانات التي نقوم بتغييرها.
لحسن الحظ ، يتيح مشغل LOCK للمشغل أن يسرد بالتفصيل جميع الأقفال الضرورية ، والتي يمكن أن تكون كثيرة جدًا.
LOCK +^person(123, amount)
عمليات أخرى (في هذا الوقت ، تحاول الخيوط المتوازية تغيير ^ الشخص (123 ، المبلغ) ، ولكن لا يمكن)
LOCK +^person(123, amount) ^person(123, amount) LOCK -^person(123, amount) ^person(123, amount) LOCK -^person(123, amount)
عند قفل قوائم مفصولة بفواصل ، يتم أخذها بالتتابع ، وإذا قمت بذلك:
LOCK +(^person(123),^person(242))
ثم يؤخذون كليا في وقت واحد.
SERIALIZE - سيتعين علينا تعيين الأقفال بحيث يتم في نهاية المطاف تنفيذ جميع المعاملات التي تحتوي على بيانات مشتركة بالتتابع. لهذا النهج ، يجب أن تكون معظم الأقفال حصرية ونقلها إلى أصغر المناطق على مستوى العالم من أجل الأداء.
إذا تحدثنا عن عمليات الشطب في الشخص العالمي ^ ، فإن مستوى عزل SERIALIZE هو الوحيد المقبول بالنسبة له ، حيث يجب أن يتم إنفاق الأموال بشكل متسلسل ، وإلا فإنه من الممكن إنفاق نفس المبلغ عدة مرات.
4. المتانة
لقد أجريت اختبارات مع قطع الصلب من خلال حاوية
docker kill my-iris
القاعدة تسامحت لهم بشكل جيد. تم تحديد أي مشاكل.
استنتاج
بالنسبة للعوالم ، لدى InterSystems IRIS دعم للمعاملات. هم حقا الذرية وموثوقة. لضمان اتساق قاعدة البيانات على globals ، تعد جهود المبرمج واستخدام المعاملات ضرورية ، حيث لا توجد إنشاءات مدمجة معقدة مثل المفاتيح الخارجية.
مستوى عزل globals دون استخدام الأقفال غير مقروء ، وعند استخدام الأقفال ، يمكن ضمانه حتى مستوى SERIALIZE.
تعتمد صحة وسرعة المعاملات على globals إلى حد كبير على مهارة المبرمج: الأقفال المشتركة الأكثر استخدامًا عند القراءة ، وارتفاع مستوى العزل ، وتأمين الأقفال الحصرية ، زادت السرعة.