हाय, हैब्र। इतनी देर पहले, मेरी एक परियोजना के लिए, मुझे एक एम्बेडेड डेटाबेस की आवश्यकता थी, जो कुंजी-मूल्य तत्वों को संग्रहीत करेगा, लेनदेन सहायता प्रदान करेगा, और वैकल्पिक रूप से, एन्क्रिप्टेड डेटा। एक छोटी खोज के बाद, मैं बर्कले डीबी परियोजना के पार आया। मुझे जिन विशेषताओं की आवश्यकता है, इसके अलावा, यह डेटाबेस एक एसटीएल-संगत इंटरफ़ेस प्रदान करता है जो आपको डेटाबेस के साथ नियमित (लगभग सामान्य) एसटीएल कंटेनर के साथ काम करने की अनुमति देता है। दरअसल, इस इंटरफ़ेस के बारे में नीचे चर्चा की जाएगी।
बर्कले डीबी
बर्कले डीबी एक एम्बेडेड, स्केलेबल, उच्च-प्रदर्शन, ओपन सोर्स डेटाबेस है। यह मुक्त स्रोत परियोजनाओं में उपयोग के लिए नि: शुल्क उपलब्ध है, लेकिन स्वामित्व वाले लोगों के लिए महत्वपूर्ण सीमाएं हैं। समर्थित विशेषताएं:
- लेन-देन
- असफलता के लिए असफल-आगे लॉग
- एईएस डेटा एन्क्रिप्शन
- प्रतिकृति
- सूचकांक
- Multithreaded अनुप्रयोगों के लिए तुल्यकालन उपकरण
- पहुँच नीति - एक लेखक, कई पाठक
- कैशिंग
साथ ही कई अन्य।
जब सिस्टम को इनिशियलाइज़ किया जाता है, तो उपयोगकर्ता निर्दिष्ट कर सकता है कि किस सबसिस्टम का उपयोग करना है। यह लेन-देन, लॉगिंग, ताले जैसे ऑपरेशनों पर संसाधनों की बर्बादी को समाप्त करता है जब उन्हें ज़रूरत नहीं होती है।
भंडारण संरचना और डेटा एक्सेस का विकल्प उपलब्ध है:
- बीट्री - सॉर्ट किए गए संतुलित पेड़ का कार्यान्वयन
- हैश - रैखिक हैश कार्यान्वयन
- हीप - भंडारण के लिए तार्किक रूप से पृष्ठांकित एक ढेर फ़ाइल का उपयोग करता है। प्रत्येक प्रविष्टि को एक पृष्ठ और उसके भीतर एक ऑफसेट द्वारा पहचाना जाता है। भंडारण इस तरह से आयोजित किया जाता है कि रिकॉर्ड को हटाने के लिए संघनन की आवश्यकता नहीं होती है। यह आपको भौतिक स्थान की कमी के साथ इसका उपयोग करने की अनुमति देता है।
- कतार - एक कतार जो एक कुंजी के रूप में एक तार्किक संख्या के साथ एक निश्चित लंबाई के रिकॉर्ड को संग्रहीत करती है। यह अंत में त्वरित प्रविष्टि के लिए डिज़ाइन किया गया है, और एक विशेष ऑपरेशन का समर्थन करता है जो एक कॉल में कतार के प्रमुख से एक प्रविष्टि को हटाता है और वापस करता है।
- Recno - आप एक कुंजी के रूप में एक तार्किक संख्या के साथ फिक्स्ड और परिवर्तनीय लंबाई दोनों के रिकॉर्ड को सहेजने की अनुमति देता है। किसी तत्व को उसके सूचकांक तक पहुंच प्रदान करता है।
अस्पष्टता से बचने के लिए, कई अवधारणाओं को परिभाषित करना आवश्यक है जो बर्कले डीबी के काम का वर्णन करने के लिए उपयोग किए जाते हैं।
डेटाबेस एक कुंजी-मूल्य डेटा संग्रहण है। अन्य DBMS में बर्कले DB डेटाबेस का एक एनालॉग एक तालिका हो सकती है।
एक डेटाबेस वातावरण एक या एक से अधिक डेटाबेस के लिए एक आवरण है । सभी डेटाबेस के लिए सामान्य सेटिंग्स को परिभाषित करता है, जैसे कि कैश का आकार, फ़ाइल भंडारण पथ, उपयोग और कॉन्फ़िगरेशन ऑफ ब्लॉकिंग, ट्रांजेक्शन, लॉगिंग सबसिस्टम।
एक विशिष्ट उपयोग के मामले में, एक वातावरण बनाया और कॉन्फ़िगर किया गया है, और इसमें एक या अधिक डेटाबेस हैं ।
एसटीएल इंटरफ़ेस
बर्कले DB एक पुस्तकालय है जो C में लिखा गया है । इसमें पर्ल , जावा , पीएचपी और अन्य जैसी भाषाओं के लिए बाइंडर हैं। C ++ का इंटरफ़ेस C कोड पर ऑब्जेक्ट और इनहेरिटेंस के साथ एक आवरण है। एसटीएल कंटेनरों के साथ संचालन के लिए डेटाबेस को समान रूप से एक्सेस करना संभव बनाने के लिए, सी ++ पर ऐड-ऑन के रूप में एसटीएल इंटरफ़ेस है । चित्रमय रूप में, इंटरफ़ेस परतें इस तरह दिखती हैं:

तो, एसटीएल इंटरफ़ेस आपको कुंजी ( बीटीआरई या हैश ) या इंडेक्स ( रिकेनो के लिए ) से डेटाबेस से एक तत्व को पुनः प्राप्त करने की अनुमति देता है। इसी तरह से std::map
या std::vector
कंटेनर, मानक std::find_if
माध्यम से डेटाबेस में एक तत्व ढूंढें std::find_if
फ़ॉरेस्ट foreach
माध्यम से पूरे डेटाबेस पर पुनरावृति। बर्कले DB STL इंटरफ़ेस के सभी वर्ग और कार्य dbstl नेमस्पेस में हैं, लघु के लिए, dbstl का अर्थ STL इंटरफ़ेस भी होगा।
स्थापना
डेटाबेस अधिकांश लिनक्स प्लेटफार्मों , विंडोज , एंड्रॉइड , ऐप्पल आईओएस , आदि का समर्थन करता है।
उबंटू 18.04 के लिए, बस संकुल स्थापित करें:
- libdb5.3-STL-देव
- libdb5.3 ++ - देव
लिनक्स स्रोतों से निर्माण करने के लिए, आपको ऑटोकॉन्फ़ और लिबटूल स्थापित करना होगा । नवीनतम स्रोत कोड यहां पाया जा सकता है ।
उदाहरण के लिए, मैंने 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 परियोजना में जोड़ना
बर्कलेडीबीएस प्रोजेक्ट्स का उपयोग बर्कले डीबी के साथ उदाहरणों को दर्शाने के लिए किया जाता है।
परियोजना की संरचना इस प्रकार है:
+-- CMakeLists.txt +-- sample-usage | +-- CMakeLists.txt | +-- sample-map-usage.cpp | +-- submodules | +-- cmake | | +-- FindBerkeleyDB
रूट CMakeLists.txt परियोजना के सामान्य मापदंडों का वर्णन करता है। नमूना स्रोत फ़ाइलें नमूना-उपयोग में हैं । नमूना-उपयोग / CMakeLists.txt पुस्तकालयों के लिए खोज, उदाहरणों की विधानसभा को परिभाषित करता है।
उदाहरण में, FindBerkeleyDB का उपयोग लाइब्रेरी को cmake प्रोजेक्ट से कनेक्ट करने के लिए किया जाता है। इसे सबमॉड्यूल्स / सेमीके में गिट सबमॉड्यूल के रूप में जोड़ा जाता है। असेंबली के दौरान, आपको BerkeleyDB_ROOT_DIR
निर्दिष्ट करने की आवश्यकता हो सकती है। उदाहरण के लिए, स्रोतों से स्थापित लाइब्रेरी के लिए, आपको ध्वज cmake -DBerkeleyDB_ROOT_DIR=/home/user/libraries/berkeley-db
बर्कले- -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")
उसके बाद, नमूना-उपयोग / 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 के उपयोग को प्रदर्शित करने के लिए , आइए हम फ़ाइल नमूना-मानचित्र-उपयोग . 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=
।
उदाहरण में, डेटाबेस के साथ काम निम्नानुसार है:
- आवेदन बर्कले DB डेटा का उपयोग करने के लिए तरीके कहते हैं
- ये तरीके पढ़ने या लिखने के लिए कैश तक पहुँचते हैं
- यदि आवश्यक हो, तो एक्सेस सीधे डेटा फ़ाइल में है
चित्रात्मक रूप से, यह प्रक्रिया चित्र में दिखाई गई है:

उदाहरण की जटिलता को कम करने के लिए, यह अपवाद हैंडलिंग का उपयोग नहीं करता है। कुछ dbstl कंटेनर पद्धतियां अपवाद तब ला सकती हैं जब त्रुटियां होती हैं।
कोड पार्सिंग
बर्कले डीबी के साथ काम करने के लिए , आपको दो हेडर फ़ाइलों को जोड़ने की आवश्यकता है:
#include <db_cxx.h> #include <dbstl_map.h>
पहला एक C ++ इंटरफ़ेस प्राइमेटिव्स जोड़ता है , और दूसरा एक डेटाबेस के साथ काम करने के लिए वर्गों और कार्यों को परिभाषित करता है, साथ ही एक सहयोगी कंटेनर के साथ-साथ कई उपयोगिता विधियां भी। STL इंटरफ़ेस dbstl नामस्थान में स्थित है।
भंडारण के लिए, Btree संरचना का उपयोग किया जाता है , std::string
कुंजी के रूप में कार्य करता है, और मान उपयोगकर्ता संरचना है
struct TestElement{ std::string id; std::string name; };
main
फ़ंक्शन में, dbstl::dbstl_startup()
कॉल करके लाइब्रेरी को इनिशियलाइज़ करें। यह एसटीएल इंटरफ़ेस के आदिमों के पहले उपयोग से पहले स्थित होना चाहिए।
उसके बाद, हम उस निर्देशिका में डेटाबेस वातावरण को इनिशियलाइज़ और ओपन करते हैं , जो 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);
डिस्क पर डेटा सैंपल-मैप-यूसेज। डीबी फाइल को लिखा जाएगा, जो कि ENV_FOLDER
डायरेक्टरी में अनुपस्थिति ( DB_CREATE
फ्लैग की DB_CREATE
) बनाया जाएगा। एक पेड़ भंडारण के लिए प्रयोग किया जाता है ( DB_BTREE
पैरामीटर)।
बर्कले डीबी में, कुंजियों और मूल्यों को बाइट्स की एक सरणी के रूप में संग्रहीत किया जाता है। एक कस्टम प्रकार का उपयोग करने के लिए (हमारे मामले में TestElement
), आपको TestElement
कार्य करने होंगे:
- ऑब्जेक्ट को संग्रहीत करने के लिए बाइट्स की संख्या प्राप्त करना;
- किसी वस्तु को बाइट्स के एक सरणी में पिघलाना;
- unmarshaling।
उदाहरण में, यह कार्यक्षमता TestMarshaller
वर्ग के स्थिर तरीकों द्वारा की 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 + "}"; });
उपरोक्त उदाहरण में elementsMap.begin(dbstl::ReadModifyWriteOption::no_read_modify_write(), true)
विधि को कॉल begin
थोड़ा असामान्य लगता है: elementsMap.begin(dbstl::ReadModifyWriteOption::no_read_modify_write(), true)
।
इस डिज़ाइन का उपयोग केवल पढ़ने के लिए पुनरावृत्ति प्राप्त करने के लिए किया जाता है। dbstl cbegin
पद्धति को परिभाषित नहीं करता है, इसके बजाय, cbegin
विधि में cbegin
पैरामीटर (दूसरा) का उपयोग किया जाता है। आप केवल पढ़ने के लिए पुनरावृत्ति प्राप्त करने के लिए कंटेनर के लिए एक निरंतर संदर्भ का उपयोग कर सकते हैं। इस तरह के एक पुनरावृत्ति केवल एक पढ़ने के संचालन की अनुमति देता है; जब लेखन, यह एक अपवाद फेंक देगा।
उपरोक्त कोड में केवल पढ़ने के लिए पुनरावृत्ति का उपयोग क्यों किया जाता है? सबसे पहले, यह सिर्फ एक पुनरावृत्ति के माध्यम से एक रीड ऑपरेशन करता है। दूसरे, प्रलेखन का कहना है कि नियमित संस्करण की तुलना में इसका प्रदर्शन बेहतर है ।
एक नई कुंजी / मूल्य जोड़ी जोड़ना, या, यदि कुंजी पहले से मौजूद है, तो मान को अपडेट करना std::map
में उतना ही सरल है:
elementsMap["added key 1"] = {"added id 1", "added name 1"};
जैसा कि ऊपर उल्लेख किया गया है, elementsMap["added key 1"]
निर्देश operator=
साथ एक रैपर क्लास देता है operator=
फिर से परिभाषित किया जाता है, जिसके बाद की कॉल सीधे डेटाबेस में ऑब्जेक्ट को स्टोर करती है।
यदि आपको एक कंटेनर में एक आइटम सम्मिलित करने की आवश्यकता है:
auto [iter, res] = elementsMap.insert( std::make_pair(std::string("added key 2"), TestElement{"added id 2", "added name 2"}) );
std::pair<, >
रिटर्न के लिए कॉल std::pair<, >
। यदि ऑब्जेक्ट नहीं डाला जा सकता है, तो सफलता ध्वज झूठा होगा। अन्यथा, सफलता ध्वज में सत्य होता है , और पुनरावृत्त सम्मिलित ऑब्जेक्ट को इंगित करता है।
कुंजी द्वारा मान ज्ञात करने का दूसरा तरीका dbstl::db_map::find
विधि का उपयोग करना है, जो std::map::find
समान है std::map::find
::
auto findIter = elementsMap.find("test key 1");
प्राप्त findIter->first
माध्यम से, आप कुंजी - findIter->first
को एक्सेस कर सकते हैं, findIter->first
TestElement
तत्व के क्षेत्रों में - findIter->second.id
और findIter->second.name
। एक कुंजी / मूल्य जोड़ी निकालने के लिए, auto iterPair = *findIter;
ऑपरेटर का उपयोग किया जाता है - auto iterPair = *findIter;
।
जब dereferencing संचालक ( * ) या किसी वर्ग सदस्य ( -> ) तक पहुँच itter पर लागू होता है, तो डेटाबेस तक पहुँचा जाता है और उससे डेटा निकाला जाता है। इसके अलावा, पहले निकाले गए डेटा, भले ही वे संशोधित किए गए हों, मिट जाते हैं। इसका मतलब यह है कि नीचे दिए गए उदाहरण में, इट्रेटर में किए गए परिवर्तन को छोड़ दिया जाएगा, और डेटाबेस में संग्रहीत मूल्य कंसोल पर प्रदर्शित किया जाएगा।
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
से संग्रहीत ऑब्जेक्ट के रैपर को findIter->second
कॉल findIter->second
और इसे एक चर में सहेजना होगा। इसके बाद, इस रैपर पर सभी परिवर्तन करें, और रैपर विधि _DB_STL_StoreElement
कॉल करके डेटाबेस में परिणाम _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
निर्देश के साथ रैपर प्राप्त करें और उदाहरण के रूप में इसे वांछित 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_exit();
संसाधन प्रबंधक में सभी पंजीकृत वस्तुओं को बंद करने और हटाने के लिए।
निष्कर्ष में
यह आलेख dbstl::db_map
का उपयोग करके dbstl::db_map
कंटेनरों की मुख्य विशेषताओं का एक संक्षिप्त अवलोकन प्रदान करता है जो एक साधारण एकल-थ्रेडेड प्रोग्राम में एक dbstl::db_map
रूप में है। यह सिर्फ एक छोटा सा परिचय है और इसमें ट्रांजेक्शनलिटी, लॉकिंग, रिसोर्स मैनेजमेंट, अपवाद हैंडलिंग, और मल्टीथ्रेडिंग निष्पादन जैसी विशेषताएं शामिल नहीं हैं।
मैंने तरीकों और उनके मापदंडों के बारे में विस्तार से वर्णन करने का लक्ष्य नहीं रखा, इसके लिए C ++ इंटरफ़ेस और एसटीएल इंटरफ़ेस पर संबंधित प्रलेखन का उल्लेख करना बेहतर है।