Erlang 21 - 7. Tables and Databases

7 टेबल्स और डेटाबेस




erlang

7 टेबल्स और डेटाबेस

7.1 Ets, Dets, और Mnesia

Ets का उपयोग करने वाला प्रत्येक उदाहरण Mnesia में एक संबंधित उदाहरण है। सामान्य तौर पर, सभी Ets उदाहरण Dets तालिकाओं पर भी लागू होते हैं।

संचालन का चयन / मिलान करें

Ets और Mnesia तालिकाओं पर चयन / मैच संचालन बहुत महंगा ऑपरेशन बन सकता है। उन्हें आमतौर पर पूर्ण तालिका को स्कैन करने की आवश्यकता होती है। चयन / मिलान कार्यों की आवश्यकता को कम करने के लिए डेटा की संरचना करने का प्रयास करें। हालाँकि, यदि आपको एक चयन / मैच संचालन की आवश्यकता है, तो यह अभी भी tab2list का उपयोग करने से अधिक कुशल है। इसके और कैसे चयन / मिलान से बचने के उदाहरण निम्नलिखित वर्गों में दिए गए हैं। कार्य ets:select/2 और mnesia:select/3 ets:match/2 से अधिक पसंद किए जाने हैं ets:match/2 , ets:match_object/2 , और mnesia:match_object/3

कुछ परिस्थितियों में, चयन / मैच संचालन को पूर्ण तालिका को स्कैन करने की आवश्यकता नहीं होती है। उदाहरण के लिए, यदि ordered_set गई टेबल को खोजते समय कुंजी का हिस्सा बंध जाता है, या यदि वह मानेसिया टेबल है और उस क्षेत्र पर एक सेकेंडरी इंडेक्स होता है जिसे चुना / मिलान किया जाता है। यदि कुंजी पूरी तरह से बंधी हुई है, तो चयन / मिलान करने का कोई मतलब नहीं है, जब तक कि आपके पास एक बैग टेबल नहीं है और केवल विशिष्ट कुंजी वाले तत्वों के सबसेट में रुचि रखते हैं।

चयन / मिलान ऑपरेशन में उपयोग किए जाने के लिए रिकॉर्ड बनाते समय, आप चाहते हैं कि अधिकांश फ़ील्ड "_" मान लें। सबसे आसान और सबसे तेज़ तरीका निम्नानुसार है:

#person{age = 42, _ = '_'}. 

एक तत्व हटाना

यदि तालिका में तत्व मौजूद नहीं था, तो delete ऑपरेशन सफल माना जाता है। इसलिए यह जांचने का प्रयास किया जाता है कि विलोपन के अनावश्यक होने से पहले तत्व Ets / Mnesia तालिका में मौजूद है। Ets तालिकाओं के लिए एक उदाहरण इस प्रकार है:

करना

...
ets:delete(Tab, Key),
...

ऐसा न करें

...
case ets:lookup(Tab, Key) of
    [] ->
        ok;
    [_|_] ->
        ets:delete(Tab, Key)
end,
...

डेटा लाया जा रहा है

आपके पास पहले से मौजूद डेटा न लाएं।

विचार करें कि आपके पास एक मॉड्यूल है जो सार डेटा प्रकार Person संभालता है। आप इंटरफ़ेस फ़ंक्शन print_person/1 निर्यात करते हैं, जो आंतरिक फ़ंक्शन print_name/1 , print_age/1 और print_occupation/1

ध्यान दें

यदि फ़ंक्शन print_name/1 , और इतने पर, इंटरफ़ेस फ़ंक्शन था, तो स्थिति अलग-अलग होती, क्योंकि आप इंटरफ़ेस के उपयोगकर्ता को आंतरिक डेटा प्रतिनिधित्व के बारे में नहीं जानना चाहते।

करना

%%% Interface function
print_person(PersonId) ->
    %% Look up the person in the named table person,
    case ets:lookup(person, PersonId) of
        [Person] ->
            print_name(Person),
            print_age(Person),
            print_occupation(Person);
        [] ->
            io:format("No person with ID = ~p~n", [PersonID])
    end.

%%% Internal functions
print_name(Person) -> 
    io:format("No person ~p~n", [Person#person.name]).
                      
print_age(Person) -> 
    io:format("No person ~p~n", [Person#person.age]).

print_occupation(Person) -> 
    io:format("No person ~p~n", [Person#person.occupation]).

ऐसा न करें

%%% Interface function
print_person(PersonId) ->
    %% Look up the person in the named table person,
    case ets:lookup(person, PersonId) of
        [Person] ->
            print_name(PersonID),
            print_age(PersonID),
            print_occupation(PersonID);
        [] ->
            io:format("No person with ID = ~p~n", [PersonID])
    end.

%%% Internal functionss
print_name(PersonID) -> 
    [Person] = ets:lookup(person, PersonId),
    io:format("No person ~p~n", [Person#person.name]).

print_age(PersonID) -> 
    [Person] = ets:lookup(person, PersonId),
    io:format("No person ~p~n", [Person#person.age]).

print_occupation(PersonID) -> 
    [Person] = ets:lookup(person, PersonId),
    io:format("No person ~p~n", [Person#person.occupation]).

गैर-स्थायी डेटाबेस संग्रहण

गैर-लगातार डेटाबेस संग्रहण के लिए, मानेसिया local_content तालिकाओं पर ईट्स टेबल को प्राथमिकता local_content । यहां तक ​​कि मानेसिया dirty_write ऑपरेशन भी ईट्स लिखते हैं। Mnesia की जाँच करनी चाहिए कि क्या तालिका को दोहराया गया है या सूचकांक है, इसमें प्रत्येक dirty_write लिए कम से कम एक Ets लुकअप शामिल है। इस प्रकार, एट्स लिखते हैं हमेशा मासेनिया लिखते हैं की तुलना में तेज है।

tab2list

Ets तालिका मानकर कुंजी का उपयोग करता है और इसमें निम्न शामिल हैं:

[#person{idno = 1, name = "Adam",  age = 31, occupation = "mailman"},
 #person{idno = 2, name = "Bryan", age = 31, occupation = "cashier"},
 #person{idno = 3, name = "Bryan", age = 35, occupation = "banker"},
 #person{idno = 4, name = "Carl",  age = 25, occupation = "mailman"}]

यदि आपको Ets तालिका में संग्रहीत सभी डेटा वापस करना चाहिए , तो आप ets:tab2list/1 उपयोग कर सकते हैं। हालांकि, आमतौर पर आप केवल उस जानकारी के एक सबसेट में रुचि रखते हैं जिस स्थिति में ets:tab2list/1 महंगा होता है। यदि आप केवल प्रत्येक रिकॉर्ड से एक क्षेत्र निकालना चाहते हैं, उदाहरण के लिए, प्रत्येक व्यक्ति की आयु, तब:

करना

...
ets:select(Tab,[{ #person{idno='_', 
                          name='_', 
                          age='$1', 
                          occupation = '_'},
                [],
                ['$1']}]),
...

ऐसा न करें

...
TabList = ets:tab2list(Tab),
lists:map(fun(X) -> X#person.age end, TabList),
...

यदि आप केवल "ब्रायन" नाम के सभी व्यक्तियों की आयु में रुचि रखते हैं, तो:

करना

...
ets:select(Tab,[{ #person{idno='_', 
                          name="Bryan", 
                          age='$1', 
                          occupation = '_'},
                [],
                ['$1']}]),
...

ऐसा न करें

...
TabList = ets:tab2list(Tab),
lists:foldl(fun(X, Acc) -> case X#person.name of
                                "Bryan" ->
                                    [X#person.age|Acc];
                                 _ ->
                                     Acc
                           end
             end, [], TabList),
...

वास्तव में नहीं है

...
TabList = ets:tab2list(Tab),
BryanList = lists:filter(fun(X) -> X#person.name == "Bryan" end,
                         TabList),
lists:map(fun(X) -> X#person.age end, BryanList),
...

यदि आपको "ब्रायन" नाम के व्यक्तियों के बारे में Ets तालिका में संग्रहीत सभी जानकारी चाहिए, तो:

करना

...
ets:select(Tab, [{#person{idno='_', 
                          name="Bryan", 
                          age='_', 
                          occupation = '_'}, [], ['$_']}]),
...

ऐसा न करें

...
TabList = ets:tab2list(Tab),
lists:filter(fun(X) -> X#person.name == "Bryan" end, TabList),
...

आर्डर किया गया_सेट टेबल

यदि तालिका में डेटा को एक्सेस किया जाना है, ताकि तालिका में कुंजियों का क्रम महत्वपूर्ण हो, तो तालिका प्रकार के ordered_set का उपयोग अधिक सामान्य set तालिका प्रकार के बजाय किया जा सकता है। एक ordered_set को हमेशा कुंजी फ़ील्ड के संबंध में ordered_set शब्द क्रम में ट्रेस किया जाता है ताकि कुंजी मानों द्वारा दिए गए कार्यों जैसे कि select , match_object , और foldl से रिटर्न वैल्यू का ऑर्डर किया जाए। first और next ऑपरेशन के साथ एक ordered_set का पता ordered_set भी आदेशित कुंजियों को वापस करता है।

ध्यान दें

एक ordered_set केवल इस बात की गारंटी देता है कि वस्तुओं को महत्वपूर्ण क्रम में संसाधित किया गया है। ets:select/2 जैसे कार्यों से परिणाम ets:select/2 कुंजी में परिणाम में शामिल नहीं होने पर भी कुंजी क्रम में ets:select/2 दिखाई देते हैं।

7.2 Ets- विशिष्ट

ईट्स टेबल की कुंजी का उपयोग करना

एक Ets तालिका एक एकल-कुंजी तालिका (या तो हैश तालिका या कुंजी द्वारा आदेशित वृक्ष) है और इसे एक के रूप में उपयोग किया जाना है। दूसरे शब्दों में, जब भी संभव हो चीजों को देखने के लिए कुंजी का उपयोग करें। set Ets तालिका में एक ज्ञात कुंजी द्वारा एक खोज निरंतर है और एक ordered_set Ets तालिका के लिए यह O (logN) है। एक कुंजी लुकअप हमेशा एक कॉल के लिए बेहतर होता है जहां पूरी तालिका को स्कैन करना पड़ता है। पिछले उदाहरणों में, फ़ील्ड idno तालिका की कुंजी है और सभी लुकअप जहां केवल नाम एक ज्ञात परिणाम के लिए (संभवतः बड़े) तालिका के पूर्ण स्कैन में परिणाम ज्ञात होता है।

एक सरल समाधान यह होगा कि name फ़ील्ड का उपयोग idno फ़ील्ड के बजाय कुंजी के रूप में किया idno , लेकिन इससे समस्याएँ उत्पन्न होंगी यदि नाम अद्वितीय नहीं हैं। एक अधिक सामान्य समाधान डेटा के रूप में कुंजी और idno रूप में name साथ एक दूसरी तालिका बनाने के लिए होगा, अर्थात name फ़ील्ड के संबंध में तालिका को अनुक्रमणित करने के लिए। स्पष्ट रूप से, दूसरी तालिका को मास्टर तालिका के अनुरूप रखना होगा। Mnesia आपके लिए यह कर सकता है, लेकिन Mnesia का उपयोग करने में शामिल ओवरहेड की तुलना में एक घर का काढ़ा सूचकांक तालिका बहुत कुशल हो सकती है।

पिछले उदाहरणों में तालिका के लिए एक इंडेक्स टेबल में एक बैग होना चाहिए (जैसे कि चाबियां एक से अधिक बार दिखाई देंगी) और इसमें निम्नलिखित चीजें हो सकती हैं:

[#index_entry{name="Adam", idno=1},
 #index_entry{name="Bryan", idno=2},
 #index_entry{name="Bryan", idno=3},
 #index_entry{name="Carl", idno=4}]

इस सूचकांक तालिका को देखते हुए, "ब्रायन" नाम के सभी व्यक्तियों के लिए age क्षेत्रों की एक खोज इस प्रकार की जा सकती है:

...
MatchingIDs = ets:lookup(IndexTable,"Bryan"),
lists:map(fun(#index_entry{idno = ID}) ->
                 [#person{age = Age}] = ets:lookup(PersonTable, ID),
                 Age
          end,
          MatchingIDs),
...

ध्यान दें कि यह कोड कभी भी ets:match/2 का उपयोग नहीं करता है, बल्कि ets:lookup/2 कॉल का उपयोग करता है। lists:map/2 कॉल का उपयोग केवल तालिका में नाम "ब्रायन" से मेल खाने वाले idno को पार करने के लिए किया जाता है; इस प्रकार मास्टर टेबल में लुकअप की संख्या कम से कम हो जाती है।

इंडेक्स टेबल रखने से टेबल में रिकॉर्ड डालने पर कुछ ओवरहेड का परिचय होता है। इसलिए तालिका से प्राप्त किए गए ऑपरेशनों की संख्या की तुलना तालिका में वस्तुओं को सम्मिलित करने वाले ऑपरेशनों की संख्या से की जानी चाहिए। हालाँकि, ध्यान दें कि लाभ महत्वपूर्ण है जब कुंजी का उपयोग तत्वों को देखने के लिए किया जा सकता है।

7.3 मनेसिया-विशिष्ट

द्वितीयक सूचकांक

यदि आप अक्सर किसी ऐसे फ़ील्ड पर लुकअप करते हैं जो तालिका की कुंजी नहीं है, तो आप "mnesia: select / match_object" का उपयोग करके प्रदर्शन खो देते हैं क्योंकि यह फ़ंक्शन पूरी तालिका का पता लगाता है। आप इसके बजाय एक द्वितीयक इंडेक्स बना सकते हैं और तेज़ पहुँच प्राप्त करने के लिए "mnesia: index_read" का उपयोग कर सकते हैं, हालाँकि इसके लिए अधिक मेमोरी की आवश्यकता होती है।

उदाहरण

-record(person, {idno, name, age, occupation}).
        ...
{atomic, ok} = 
mnesia:create_table(person, [{index,[#person.age]},
                              {attributes,
                                    record_info(fields, person)}]),
{atomic, ok} = mnesia:add_table_index(person, age), 
...

PersonsAge42 =
     mnesia:dirty_index_read(person, 42, #person.age),
...

लेन-देन

लेन-देन का उपयोग यह गारंटी देने का एक तरीका है कि वितरित Mnesia डेटाबेस सुसंगत रहता है, तब भी जब कई विभिन्न प्रक्रियाएं इसे समानांतर में अपडेट करती हैं। हालांकि, यदि आपके पास वास्तविक समय की आवश्यकताएं हैं, तो लेनदेन के बजाय dirty संचालन का उपयोग करने की सिफारिश की जाती है। dirty संचालन का उपयोग करते समय, आप स्थिरता की गारंटी खो देते हैं; यह आमतौर पर केवल एक प्रक्रिया को तालिका को अपडेट करने के द्वारा हल किया जाता है। अन्य प्रक्रियाओं को उस प्रक्रिया के लिए अद्यतन अनुरोध भेजना चाहिए।

उदाहरण

...
% Using transaction

Fun = fun() ->
          [mnesia:read({Table, Key}),
           mnesia:read({Table2, Key2})]
      end, 

{atomic, [Result1, Result2]}  = mnesia:transaction(Fun),
...

% Same thing using dirty operations
...

Result1 = mnesia:dirty_read({Table, Key}),
Result2 = mnesia:dirty_read({Table2, Key2}),
...