2016-08-12 9 views
8

पृष्ठभूमिएक "संस्था" विशिष्ट अनुक्रम

मैं अलग "बातें" (एक डोमेन विशिष्ट आइटम/संस्था/अधीन) है कि "बात" मालिकों (मानव) के लिए दिखाई दे रहे हैं की एक बहुत कुछ है। मालिक अपनी "चीजें" को एक संख्या के साथ पहचानने जा रहे हैं। इसके बजाए एक बड़ा "यादृच्छिक" नंबर दिखा रहा है, मैं उन्हें एक छोटी संख्या (विशेष रूप से 1 से शुरू होने वाला अनुक्रम) दिखाना चाहता हूं जो मनुष्यों के लिए आसान है। मालिक "मेरे फू 37" और "उसकी बार 128" के बारे में बात करने में बहुत सहज हैं। वहां "अनुक्रम" में अंतराल हो सकता है लेकिन संलग्न वस्तु को "चीज" के जीवनकाल के दौरान समान रहना पड़ता है। इसलिए मुझे "चीज़" उत्पन्न करने का एक तरीका चाहिए + मालिक विशिष्ट आईडी (जिसे वर्तमान में "दृश्य आईडी" कहा जाता है)।

"चीज़" + मालिक संयोजन की संख्या 10k + के पैमाने पर है। वर्तमान में नई "चीजें" गतिशील रूप से जेनरेट नहीं की जा सकती हैं लेकिन मालिक हो सकते हैं।

प्रति मालिक "चीज़" उदाहरणों की संख्या अपेक्षाकृत छोटी है, प्रति मालिक लगभग दसियों, लेकिन व्यापार नियमों से प्राप्त कोई हार्ड कैप नहीं है। नई "चीज़" उदाहरण अक्सर बनाए जाते हैं और हटा दिए जाते हैं।

माना विकल्पों

मैंने पाया एक में एक अच्छा चर्चा अतः Oracle Partitioned Sequence कि बहुत ज्यादा एक ही मुद्दा रहा है तुम्हारे पास के पते सवाल उठाते हैं।

अब तक मैं पर विचार किया है निम्नलिखित विकल्पों:

  1. मुझे लगता है कि एक मानक डेटाबेस अनुक्रम एक बिल्कुल ठीक हो सकती है, लेकिन है कि मुझे गतिशील रूप से "बात" + मालिक विशिष्ट दृश्यों की एक बड़ी संख्या बनाने के लिए की आवश्यकता होगी और डालने के दौरान अनुक्रम नाम को भी हल करें। (और मालिक चलाए जाने पर अनुक्रमों को छोड़ दें।) मुझे यकीन नहीं है कि अनुक्रमों की एक बड़ी संख्या बनाना एक अच्छा अभ्यास है (मेरे लिए 10k + डेटाबेस ऑब्जेक्ट्स एक बड़ी संख्या है)।
  2. मैंने कुख्यात max(visible_id) + 1 भी कुख्यात माना लेकिन हम इसके साथ सामान्य समवर्ती मुद्दों में भाग लेंगे, इसलिए यह कोई नहीं है।
  3. मालिक विशिष्ट आईडी को डेटाबेस में सेव न करें बल्कि इसके बजाय suggestedAdam Musch द्वारा इसे चुनें। यह एक अद्भुत विचार है लेकिन दुर्भाग्यवश आईडी को "चीज़" उदाहरण जीवनकाल के दौरान समान होना चाहिए।
  4. मालिकों को "चीज़" नाम देकर पूरी समस्या से बचें। लेकिन उन्हें इस विचार को बिल्कुल पसंद नहीं आया - "मुझे परेशान क्यों होना चाहिए, यह foo 16. कहना इतना आसान है।"!

प्रश्न

वहाँ इस मुद्दे के समाधान के लिए या मैं गतिशील दृश्यों का निर्माण शुरू समझने का कोई अन्य रास्ता नहीं है? अगर अनुक्रम उत्तर हैं तो कृपया बताएं कि क्या नुकसान हो सकता है (जैसे डीडीएल में निहित प्रतिबद्धता)।

मुझे ओरेकल 11 जीआर 2 और 12 सी समाधान दोनों में दिलचस्पी है (यदि वे अलग हैं)।

छद्म कोड सवाल वर्णन करने के लिए

create table foo (
id number primary key -- the key for computers 
,owner_id number 
,visible_id number -- the key for humans 
,data_ varchar2(20) 
); 

create constraint foo_u1 unique foo(owner_id, visible_id); 

-- primary key sequence 
create sequence foo_id_seq; 

insert into foo values(
foo_id_seq.nextval 
,1 
,1 -- what to put here ? 
,'lorem ipsum' 
); 

insert into foo values(
foo_id_seq.nextval 
,2 
,1 -- what to put here ? 
,'dolor sit amet' 
); 

select visible_id, data_ from foo where owner = 2 order by visible_id; 
+1

विकल्प 3 लागू क्यों नहीं है? चीजें उदाहरण हटा दिए जा सकते हैं? क्या होगा यदि उन्हें हटाए गए के रूप में चिह्नित करें (उदाहरण के लिए अतिरिक्त कॉलम के माध्यम से)? –

+1

मैं यह देखने में असफल रहा हूं कि क्यों # 2 समेकन समस्याएं बनाएगा जब MAX (visual_id) किसी विशेष स्वामी के लिए बाध्य होता है (आप निश्चित रूप से पूरी तालिका को अधिकतम नहीं चाहते हैं)। क्या यह संभावना है कि मालिक एक ही समय में 1 से अधिक आवेदन उदाहरण से अपने आइटम अपडेट कर रहे हैं? ऐसा लगता है कि आपको टेबल, मालिक, चीज़िड पर प्राथमिक कुंजी की आवश्यकता है। थिंगिड को आपके द्वारा उत्पन्न किया जाना होगा, इसलिए मैं अपडेट/डालने/हटाने के लिए संग्रहीत प्रक्रिया का उपयोग करने का सुझाव दूंगा ताकि आप अनुक्रम का प्रबंधन कर सकें, हालांकि आप सीधे डीएमएल ऑपरेशंस को कॉल करने के बजाय – Matt

+0

@ कॉन्स्टेंटिन सोरोकिन वेल ... क्योंकि, त्रुटि ... मैं बस इतना हो सकता हूं कि मैं उस विकल्प के लिए अंधेरा था। हां, मुझे पता है [सॉफ्ट डिलीट] (http://stackoverflow.com/q/2549839/272735) और हाँ मेरा पहला विचार यह है कि यहां काफी अच्छा फिट हो सकता है। मुझे इस विचार और लाभ और कमियों पर अधिक विचार करने की जरूरत है। – user272735

उत्तर

2

के बाद से अंतराल ठीक हैं, तो आप "विकल्प 2" का एक परिवर्तन लागू करना चाहिए।अंतराल की अनुमति का मतलब है कि आपका सिंक्रनाइज़ेशन जल्दी से किया जा सकता है: प्रतिस्पर्धी सत्र केवल दूसरों को प्रतिबद्ध या रोलबैक देखने के लिए इंतजार करने के बजाय प्रतीक्षा करने और आगे बढ़ने के बजाय।

यदि ओरेकल ने INSERT INTO..NOWAIT विकल्प प्रदान किया है, तो यह आसान होगा। जैसा कि चीजें हैं, मैं शायद DBMS_LOCK शामिल करूंगा। यहां मेरा लेना है कि आपका एपीआई कैसा दिखता है।

यह आपके द्वारा देखी गई अधिकतम दृश्य आईडी के बारे में कुछ धारणाएं बनाता है क्योंकि आपने अपनी मूल पोस्ट में उन धारणाएं की हैं।

CREATE OR REPLACE PACKAGE foo_api AS 
    PROCEDURE create_foo (p_owner_id NUMBER, p_data VARCHAR2); 
END foo_api; 

CREATE OR REPLACE PACKAGE BODY foo_api AS 
    -- We need to call allocate_unique in an autonomous transaction because 
    -- it commits and the calling program may not want to commit at this time 
    FUNCTION get_lock_handle (p_owner_id NUMBER, p_visible_id NUMBER) 
    RETURN VARCHAR2 IS 
    PRAGMA AUTONOMOUS_TRANSACTION; 
    l_lock_handle VARCHAR2 (128); 
    BEGIN 
    DBMS_LOCK.allocate_unique (
     lockname => 'INSERT_FOO_' || p_owner_id || '_' || p_visible_id, 
     lockhandle => l_lock_handle 
    ); 
    COMMIT; 
    RETURN l_lock_handle; 
    END; 


    PROCEDURE create_foo (p_owner_id NUMBER, p_data VARCHAR2) IS 
    -- This is the highest visible ID you'd ever want. 
    c_max_visible_id NUMBER := 1000; 
    BEGIN 
    <<id_loop>> 
    FOR r_available_ids IN (SELECT a.visible_id 
          FROM (SELECT ROWNUM visible_id 
            FROM DUAL 
            CONNECT BY ROWNUM <= c_max_visible_id) a 
            LEFT JOIN foo 
            ON foo.owner_id = p_owner_id 
            AND foo.visible_id = a.visible_id 
          WHERE foo.visible_id IS NULL) LOOP 
     -- We found a gap 
     -- We could try to insert into it. If another session has already done so and 
     -- committed, we'll get an ORA-00001. If another session has already done so but not 
     -- yet committed, we'll wait. And waiting is bad. 
     -- We'd like an INSERT...NO WAIT, but Oracle doesn't provide that. 
     -- Since this is the official API for creating foos and we have good application 
     -- design to ensure that foos are not created outside this API, we'll manage 
     -- the concurrency ourselves. 
     -- 
     -- Try to acquire a user lock on the key we're going to try an insert. 
     DECLARE 
     l_lock_handle  VARCHAR2 (128); 
     l_lock_result  NUMBER; 
     l_seconds_to_wait NUMBER := 21600; 
     BEGIN 
     l_lock_handle := get_lock_handle (
      p_owner_id => p_owner_id, 
      p_visible_id => r_available_ids.visible_id 
     ); 

     l_lock_result := DBMS_LOCK.request (lockhandle => l_lock_handle, 
              lockmode => DBMS_LOCK.x_mode, 
              timeout => 0, -- Do not wait 
              release_on_commit => TRUE); 

     IF l_lock_result = 1 THEN 
      -- 1 => Timeout -- this could happen. 
      -- In this case, we want to move onto the next available ID. 
      CONTINUE id_loop; 
     END IF; 

     IF l_lock_result = 2 THEN 
      -- 2 => Deadlock (this should never happen, but scream if it does). 
      raise_application_error (
      -20001, 
       'A deadlock occurred while trying to acquire Foo creation lock for ' 
      || p_owner_id 
      || '_' 
      || r_available_ids.visible_id 
      || '. This is a programming error.'); 
     END IF; 

     IF l_lock_result = 3 THEN 
      -- 3 => Parameter error (this should never happen, but scream if it does). 
      raise_application_error (
      -20001, 
       'A parameter error occurred while trying to acquire Foo creation lock for ' 
      || p_owner_id 
      || '_' 
      || r_available_ids.visible_id 
      || '. This is a programming error.'); 
     END IF; 

     IF l_lock_result = 4 THEN 
      -- 4 => Already own lock (this should never happen, but scream if it does). 
      raise_application_error (
      -20001, 
       'Attempted to create a Foo creation lock and found lock already held by session for ' 
      || p_owner_id 
      || '_' 
      || r_available_ids.visible_id 
      || '. This is a programming error.'); 
     END IF; 

     IF l_lock_result = 5 THEN 
      -- 5 => Illegal lock handle (this should never happen, but scream if it does). 
      raise_application_error (
      -20001, 
       'An illegal lock handle error occurred while trying to acquire Foo creation lock for ' 
      || p_owner_id 
      || '_' 
      || r_available_ids.visible_id 
      || '. This is a programming error.'); 
     END IF; 
     END; 

     -- If we get here, we have an exclusive lock on the owner_id/visible_id 
     -- combination. Attempt the insert 
     BEGIN 
     INSERT INTO foo (id, 
         owner_id, 
         visible_id, 
         data_) 
     VALUES (foo_id_seq.NEXTVAL, 
       p_owner_id, 
       r_available_ids.visible_id, 
       p_data); 

     -- If we get here, we are done. 
     EXIT id_loop; 
     EXCEPTION 
     WHEN DUP_VAL_ON_INDEX THEN 
      -- Unfortunately, if this happened, we would have waited until the competing 
      -- session committed or rolled back. But the only way it 
      -- could have happened if the competing session did not use our API to create 
      -- or update the foo. 
      -- TODO: Do something to log or alert a programmer that this has happened, 
      -- but don't fail. 
      CONTINUE id_loop; 
     END; 
    END LOOP; 
    END create_foo; 
END foo_api; 
+0

मैं आज ही इस उत्तर पर नजर डालने में सक्षम था। यह एक आदर्श मैच की तरह दिखता है और मैं इस तरह इकाई विशिष्ट अनुक्रम को लागू करूंगा। यदि मैं ऐसी आवश्यकता में भाग लेता हूं तो बोनस के रूप में यह संभावित रूप से इकाइयों की संख्या को सीमित करने के लिए उपयोग किया जा सकता है। धन्यवाद ! – user272735

संबंधित मुद्दे