مرحبا يا هبر. منذ وقت ليس ببعيد ، بالنسبة لأحد مشاريعي ، كنت بحاجة إلى قاعدة بيانات مدمجة لتخزين عناصر القيمة الأساسية ، وتوفير دعم المعاملات ، واختيارياً ، البيانات المشفرة. بعد بحث قصير ، صادفت مشروع بيركلي DB . بالإضافة إلى الميزات التي أحتاجها ، توفر قاعدة البيانات هذه واجهة متوافقة مع STL والتي تتيح لك العمل مع قاعدة البيانات كما هو الحال مع حاوية STL عادية (شبه عادية). في الواقع ، ستتم مناقشة هذه الواجهة أدناه.
بيركلي ديسيبل
Berkeley DB عبارة عن قاعدة بيانات مفتوحة المصدر مضمنة وقابلة للتطوير وعالية الأداء. إنه متاح مجانًا للاستخدام في مشاريع مفتوحة المصدر ، لكن للمشاريع الخاصة هناك قيود كبيرة. الميزات المدعومة:
- المعاملات
- سجل تجاوز الفشل للتجاوز
- AES تشفير البيانات
- تكرار
- مؤشرات
- أدوات التزامن للتطبيقات ذات مؤشرات الترابط المتعددة
- سياسة الوصول - كاتب واحد ، العديد من القراء
- التخزين المؤقت
وكذلك العديد من الآخرين.
عند تهيئة النظام ، يمكن للمستخدم تحديد النظم الفرعية التي يجب استخدامها. هذا يلغي هدر الموارد على عمليات مثل المعاملات ، وقطع الأشجار ، والأقفال عندما لا تكون هناك حاجة إليها.
يتوفر اختيار بنية التخزين والوصول إلى البيانات:
- بتري - تنفيذ فرز شجرة متوازنة
- تجزئة تنفيذ التجزئة الخطي
- كومة - يستخدم ملف كومة الذاكرة المؤقتة المقسمة إلى صفحات للتخزين. يتم تحديد كل إدخال بواسطة صفحة وإزاحة داخلها. يتم تنظيم التخزين بطريقة لا تتطلب حذف سجل الضغط. هذا يسمح لك باستخدامه مع نقص المساحة المادية.
- قائمة الانتظار - قائمة انتظار تقوم بتخزين سجلات ذات طول ثابت برقم منطقي كمفتاح. إنه مصمم للإدخال السريع في النهاية ، ويدعم عملية خاصة تزيل وتعيد إدخالًا من رأس قائمة الانتظار في مكالمة واحدة.
- Recno - يسمح لك بحفظ سجلات ذات أطوال ثابتة ومتغيرة برقم منطقي كمفتاح. يوفر الوصول إلى عنصر بواسطة الفهرس الخاص به.
لتجنب الغموض ، من الضروري تحديد العديد من المفاهيم التي يتم استخدامها لوصف عمل Berkeley DB .
قاعدة البيانات هي تخزين البيانات ذات القيمة الرئيسية. يمكن أن يكون الجدول التماثلي لقاعدة بيانات Berkeley DB في قواعد بيانات قواعد البيانات الأخرى.
بيئة قاعدة البيانات عبارة عن مجمّع لقاعدة بيانات واحدة أو أكثر. يحدد الإعدادات العامة لجميع قواعد البيانات ، مثل حجم ذاكرة التخزين المؤقت ، ومسارات تخزين الملفات ، واستخدام وتكوين الأنظمة الفرعية للحظر والمعاملات وتسجيل الدخول.
في حالة الاستخدام النموذجي ، يتم إنشاء بيئة وتكوينها ، ولديها قاعدة بيانات واحدة أو أكثر.
STL واجهة
بيركلي DB هي مكتبة مكتوبة باللغة C. يحتوي على مجلدات للغات مثل Perl و Java و PHP وغيرها. الواجهة لـ C ++ عبارة عن مجمّع عبر رمز C مع كائنات ووراثة. من أجل إتاحة الوصول إلى قاعدة البيانات بشكل مشابه للعمليات مع حاويات STL ، هناك واجهة STL كإضافة على C ++ . في شكل رسومي ، تبدو طبقات الواجهة كما يلي:

لذلك ، تسمح لك واجهة STL باسترداد عنصر من قاعدة البيانات حسب المفتاح (من أجل Btree أو Hash ) أو حسب الفهرس (من أجل Recno ) على غرار حاوية std::map
أو std::vector
، ابحث عن عنصر في قاعدة البيانات من خلال std::find_if
القياسية ، التكرار عبر قاعدة البيانات بأكملها من خلال foreach
. جميع فئات ووظائف واجهة Berkeley DB STL موجودة في مساحة اسم dbstl ، على المدى القصير ، يعني dbstl أيضًا واجهة STL .
تركيب
تدعم قاعدة البيانات معظم منصات Linux و Windows و Android و Apple iOS وغيرها.
بالنسبة إلى Ubuntu 18.04 ، قم فقط بتثبيت الحزم:
- libdb5.3-STL-ديف
- libdb5.3 ++ - ديف
للبناء من مصادر Linux ، تحتاج إلى تثبيت autoconf و libtool . يمكن الاطلاع على أحدث شفرة المصدر هنا .
على سبيل المثال ، قمت بتنزيل الأرشيف بالإصدار 18.1.32 - db-18.1.32.zip. تحتاج إلى فك ضغط الأرشيف والانتقال إلى المجلد المصدر:
unzip db-18.1.32.zip cd db-18.1.32
بعد ذلك ، ننتقل إلى دليل build_unix ونقوم بتشغيل التجميع والتثبيت:
cd build_unix ../dist/configure --enable-stl --prefix=/home/user/libraries/berkeley-db make make install
إضافة إلى مشروع cmake
يستخدم مشروع BerkeleyDBSamples لتوضيح الأمثلة مع Berkeley DB .
هيكل المشروع على النحو التالي:
+-- CMakeLists.txt +-- sample-usage | +-- CMakeLists.txt | +-- sample-map-usage.cpp | +-- submodules | +-- cmake | | +-- FindBerkeleyDB
يصف الجذر CMakeLists.txt المعلمات العامة للمشروع. ملفات المصدر عينة قيد الاستخدام عينة . يبحث sample-use / CMakeLists.txt عن المكتبات ، ويحدد تجميع الأمثلة.
في الأمثلة ، يتم استخدام FindBerkeleyDB لتوصيل المكتبة بمشروع cmake . تتم إضافته على أنه وحدة فرعية في بوابة وحدات فرعية / cmake . أثناء التجميع ، قد تحتاج إلى تحديد BerkeleyDB_ROOT_DIR
. على سبيل المثال ، بالنسبة للمكتبة أعلاه المثبتة من المصادر ، يجب عليك تحديد cmake للعلم -DBerkeleyDB_ROOT_DIR=/home/user/libraries/berkeley-db
.
في ملف الجذر CMakeLists.txt ، أضف المسار إلى وحدة FindBerkeleyDB إلى CMAKE_MODULE_PATH :
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/submodules/cmake/FindBerkeleyDB")
بعد ذلك ، يقوم sample-use / CMakeLists.txt بإجراء بحث في المكتبة بالطريقة القياسية:
find_package(BerkeleyDB REQUIRED)
بعد ذلك ، أضف الملف القابل للتنفيذ واربطه بمكتبة Oracle :: BerkeleyDB :
add_executable(sample-map-usage "sample-map-usage.cpp") target_link_libraries(sample-map-usage PRIVATE Oracle::BerkeleyDB ${CMAKE_THREAD_LIBS_INIT} stdc++fs)
مثال عملي
للتدليل على استخدام dbstl ، دعونا نفحص مثالًا بسيطًا من ملف sample-map-use.cpp . يوضح هذا التطبيق العمل مع dbstl::db_map
في برنامج المفرد مترابطة. يشبه الحاوية نفسها std::map
وتخزين البيانات كزوج مفتاح / قيمة. يمكن أن تكون بنية قاعدة البيانات الأساسية بتري أو تجزئة . بخلاف std::map
، بالنسبة dbstl::db_map<std::string, TestElement>
نوع القيمة الفعلي هو dbstl::ElementRef<TestElement>
. يتم إرجاع هذا النوع ، على سبيل المثال ، dbstl::db_map<std::string, TestElement>::operator[]
. يحدد طرق تخزين كائن من نوع TestElement
في قاعدة البيانات. واحدة من هذه الطريقة هي operator=
.
في المثال ، العمل مع قاعدة البيانات كما يلي:
- يستدعي التطبيق أساليب Berkeley DB للوصول إلى البيانات
- هذه الطرق الوصول إلى ذاكرة التخزين المؤقت للقراءة أو الكتابة
- إذا لزم الأمر ، والوصول مباشرة إلى ملف البيانات
بيانيا ، تظهر هذه العملية في الشكل:

لتقليل تعقيد المثال ، لا يستخدم معالجة الاستثناءات. بعض أساليب حاوية dbstl قد رمي استثناءات عند حدوث أخطاء.
تحليل الرمز
للعمل مع Berkeley DB ، تحتاج إلى توصيل ملفي رأس:
#include <db_cxx.h> #include <dbstl_map.h>
الأول يضيف بدائل واجهة C ++ ، والثاني يعرّف الفئات والوظائف للعمل مع قاعدة البيانات ، كما هو الحال مع الحاوية الترابطية ، بالإضافة إلى العديد من طرق المنفعة. تقع واجهة STL في مساحة اسم dbstl .
للتخزين ، يتم استخدام بنية Btree ، ويعمل std::string
كمفتاح ، والقيمة هي بنية المستخدم TestElement
:
struct TestElement{ std::string id; std::string name; };
في الوظيفة main
، قم بتهيئة المكتبة عن طريق استدعاء dbstl::dbstl_startup()
. يجب أن يكون موجودا قبل الاستخدام الأول لأوليات واجهة STL .
بعد ذلك ، نقوم بتهيئة وفتح بيئة قاعدة البيانات في الدليل الذي تم تعيينه بواسطة المتغير ENV_FOLDER
:
auto penv = dbstl::open_env(ENV_FOLDER, 0u, DB_INIT_MPOOL | DB_CREATE);
علامة DB_INIT_MPOOL
مسؤولة عن تهيئة النظام الفرعي للتخزين المؤقت ، DB_CREATE
- عن إنشاء جميع الملفات اللازمة للبيئة. يسجل الفريق أيضًا هذا الكائن في مدير الموارد. وهو مسؤول عن إغلاق جميع الكائنات المسجلة (كائنات قاعدة البيانات والمؤشرات والمعاملات وما إلى ذلك مسجلة أيضًا) ومسح الذاكرة الديناميكية. إذا كان لديك بالفعل كائن بيئة قاعدة بيانات وتحتاج فقط إلى تسجيله مع مدير الموارد ، يمكنك استخدام الدالة dbstl::register_db_env
.
يتم إجراء عملية مماثلة مع قاعدة البيانات :
auto db = dbstl::open_db(penv, "sample-map-usage.db", DB_BTREE, DB_CREATE, 0u);
ستتم كتابة البيانات الموجودة على القرص في ملف sample-map-use.db ، والذي سيتم إنشاؤه في غياب (بفضل علامة DB_CREATE
) في دليل ENV_FOLDER
. يتم استخدام شجرة للتخزين (معلمة DB_BTREE
).
في Berkeley DB ، يتم تخزين المفاتيح والقيم كصفيف من وحدات البايت. لاستخدام نوع مخصص (في حالتنا TestElement
) ، يجب عليك تحديد وظائف من أجل:
- تلقي عدد البايتات لتخزين الكائن ؛
- تنظيم كائن في صفيف وحدات البايت؛
- unmarshaling.
في المثال ، يتم تنفيذ هذه الوظيفة عن طريق الأساليب الثابتة للفئة TestMarshaller
. TestElement
كائنات TestElement
في الذاكرة كما يلي:
- يتم نسخ طول حقل
id
إلى بداية المخزن المؤقت - البايت التالي يتم وضع محتويات حقل
id
- بعد ذلك ، يتم نسخ حجم حقل
name
- ثم يتم وضع المحتوى نفسه من حقل
name

وصفنا وظائف TestMarshaller
:
TestMarshaller::restore
- تعبئة الكائن TestElement
بالبيانات من المخزن المؤقتTestMarshaller::size
- حجم المخزن المؤقت المطلوب لحفظ الكائن المحدد.TestMarshaller::store
- يحفظ الكائن في المخزن المؤقت.
لتسجيل وظائف التنظيم / dbstl::DbstlElemTraits
التنظيم ، استخدم dbstl::DbstlElemTraits
:
dbstl::DbstlElemTraits<TestElement>::instance()->set_size_function(&TestMarshaller::size); dbstl::DbstlElemTraits<TestElement>::instance()->set_copy_function(&TestMarshaller::store); dbstl::DbstlElemTraits<TestElement>::instance()->set_restore_function( &TestMarshaller::restore );
تهيئة الحاوية:
dbstl::db_map<std::string, TestElement> elementsMap(db, penv);
هذا هو كيف يبدو نسخ العناصر من std::map
إلى الحاوية التي تم إنشاؤها:
std::copy( std::cbegin(inputValues), std::cend(inputValues), std::inserter(elementsMap, elementsMap.begin()) );
ولكن بهذه الطريقة يمكنك طباعة محتويات قاعدة البيانات إلى الإخراج القياسي:
std::transform( elementsMap.begin(dbstl::ReadModifyWriteOption::no_read_modify_write(), true), elementsMap.end(), std::ostream_iterator<std::string>(std::cout, "\n"), [](const auto data) -> std::string { return data.first + "=> { id: " + data.second.id + ", name: " + data.second.name + "}"; });
استدعاء أسلوب begin
في المثال أعلاه يبدو غير عادي بعض الشيء: elementsMap.begin(dbstl::ReadModifyWriteOption::no_read_modify_write(), true)
.
يستخدم هذا التصميم للحصول على أداة تكرار للقراءة فقط . لا يحدد cbegin
طريقة cbegin
؛ بدلاً من ذلك ، يتم استخدام المعلمة readonly
(الثانية) في طريقة begin
. يمكنك أيضًا استخدام مرجع ثابت إلى الحاوية للحصول على أداة تكرار للقراءة فقط . يسمح هذا التكرار فقط بعملية القراءة ؛ عند الكتابة ، سوف يلقي استثناءً.
لماذا يتم استخدام أداة التكرار للقراءة فقط في الكود أعلاه؟ أولاً ، إنها تقوم فقط بعملية قراءة من خلال أداة تكرار. ثانياً ، تقول الوثائق أن أداءها أفضل مقارنة بالإصدار العادي.
إضافة زوج جديد / قيمة جديدة ، أو إذا كان المفتاح موجودًا بالفعل ، يكون تحديث القيمة بسيطًا كما في std::map
:
elementsMap["added key 1"] = {"added id 1", "added name 1"};
كما هو مذكور أعلاه ، تقوم التعليمة elementMap elementsMap["added key 1"]
بإرجاع فئة مجمّع مع operator=
إعادة تعريفها ، حيث تقوم المكالمة التالية بحفظ الكائن مباشرة في قاعدة البيانات.
إذا كنت بحاجة إلى إدراج عنصر في حاوية:
auto [iter, res] = elementsMap.insert( std::make_pair(std::string("added key 2"), TestElement{"added id 2", "added name 2"}) );
الاستدعاء إلى elementsMap.insert
بإرجاع std::pair<, >
. إذا تعذر إدراج الكائن ، فستكون علامة النجاح خاطئة . وإلا ، فإن علامة النجاح تحتوي على true ، ويشير التكرار إلى الكائن المدرج.
هناك طريقة أخرى للعثور على القيمة حسب المفتاح وهي استخدام طريقة dbstl::db_map::find
، على غرار std::map::find
:
auto findIter = elementsMap.find("test key 1");
من خلال برنامج التكرار الذي تم الحصول عليه ، يمكنك الوصول إلى المفتاح - findIter->first
، إلى حقول عنصر findIter->second.id
- findIter->second.id
و findIter->second.name
. لاستخراج زوج مفتاح / قيمة ، يتم استخدام مشغل dereference - auto iterPair = *findIter;
.
عندما يتم تطبيق عامل إلغاء التسجيل ( * ) أو الوصول إلى عضو في الفصل ( -> ) على التكرار ، يتم الوصول إلى قاعدة البيانات واستخراج البيانات منه. علاوة على ذلك ، يتم مسح البيانات التي تم استخراجها مسبقًا ، حتى لو تم تعديلها. هذا يعني أنه في المثال أدناه ، سيتم تجاهل التغييرات التي تم إجراؤها على التكرار ، وسيتم عرض القيمة المخزنة في قاعدة البيانات على وحدة التحكم.
findIter->second.id = "skipped id"; findIter->second.name = "skipped name"; std::cout << "Found elem for key " << "test key 1" << ": id: " << findIter->second.id << ", name: " << findIter->second.name << std::endl;
لتجنب ذلك ، تحتاج إلى الحصول على غلاف الكائن المخزن من التكرار عن طريق استدعاء findIter->second
وحفظه في متغير. بعد ذلك ، قم بإجراء جميع التغييرات على هذا المجمع ، واكتب النتيجة إلى قاعدة البيانات عن طريق استدعاء أسلوب المجمّع _DB_STL_StoreElement
:
auto ref = findIter->second; ref.id = "new test id 1"; ref.name = "new test name 1"; ref._DB_STL_StoreElement();
يمكن أن يكون تحديث البيانات أسهل - ما عليك سوى الحصول على المجمع باستخدام findIter->second
التعليمة findIter->second
وتعيين كائن TestElement
المطلوب ، كما في المثال:
if(auto findIter = elementsMap.find("test key 2"); findIter != elementsMap.end()){ findIter->second = {"new test id 2", "new test name 2"}; }
قبل إنهاء البرنامج ، يجب عليك الاتصال dbstl::dbstl_exit();
لإغلاق وحذف جميع الكائنات المسجلة في مدير الموارد.
في الختام
توفر هذه المقالة نظرة عامة مختصرة حول الميزات الرئيسية لحاويات dbstl باستخدام dbstl::db_map
في برنامج بسيط مترابطة. هذه مجرد مقدمة صغيرة ولا تشمل ميزات مثل المعاملة ، والتأمين ، وإدارة الموارد ، ومعالجة الاستثناء ، والتنفيذ متعدد مؤشرات الترابط.
لم أهدف إلى وصف الطرق والمعلمات الخاصة بها بالتفصيل ، لذلك من الأفضل الإشارة إلى الوثائق المقابلة على واجهة C ++ وعلى واجهة STL