कड़ाई से बोलते हुए, विशिष्टता के लिए आपका परीक्षण समवर्ती भार के तहत विशिष्टता की गारंटी नहीं देगा। समस्या यह है कि आप उस स्थान से पहले (और अलग से) विशिष्टता की जांच करते हैं जहां आप अपने नए जेनरेट किए गए पासकोड का "दावा" करने के लिए एक पंक्ति डालते हैं। एक और प्रक्रिया एक ही समय में एक ही काम कर सकती है। यहां बताया गया है कि यह कैसे होता है...
दो प्रक्रियाएं एक ही पासकोड उत्पन्न करती हैं। वे प्रत्येक विशिष्टता के लिए जाँच करके शुरू करते हैं। चूंकि न तो प्रक्रिया ने (अभी तक) तालिका में एक पंक्ति डाली है, दोनों प्रक्रियाओं को डेटाबेस में कोई मिलान पासकोड नहीं मिलेगा, और इसलिए दोनों प्रक्रियाएं मान लेंगी कि कोड अद्वितीय है। अब जैसे-जैसे प्रक्रियाएं अपना काम जारी रखती हैं, अंततः वे दोनों files
. में एक पंक्ति डालें उत्पन्न कोड का उपयोग करके तालिका -- और इस प्रकार आपको एक डुप्लीकेट मिलता है।
इसके आसपास जाने के लिए, आपको जांच करनी होगी, और एक "परमाणु" ऑपरेशन में सम्मिलित करना होगा। इस दृष्टिकोण की व्याख्या निम्नलिखित है:
यदि आप चाहते हैं कि पासकोड अद्वितीय हो, तो आपको अपने डेटाबेस में कॉलम को UNIQUE
के रूप में परिभाषित करना चाहिए . यह डुप्लिकेट पासकोड का कारण बनने वाली पंक्ति को सम्मिलित करने से इनकार करके विशिष्टता (भले ही आपका PHP कोड न हो) सुनिश्चित करेगा।
CREATE TABLE files (
id int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
filename varchar(255) NOT NULL,
passcode varchar(64) NOT NULL UNIQUE,
)
अब, mysql के SHA1()
का उपयोग करें और NOW()
अपना पासकोड के भाग . के रूप में जेनरेट करने के लिए डालने का बयान। इसे INSERT IGNORE ...
. के साथ मिलाएं (दस्तावेज़
), और एक पंक्ति के सफलतापूर्वक सम्मिलित होने तक लूप करें:
do {
$query = "INSERT IGNORE INTO files
(filename, passcode) values ('whatever', SHA1(NOW()))";
$res = mysql_query($query);
} while( $res && (0 == mysql_affected_rows()) )
if( !$res ) {
// an error occurred (eg. lost connection, insufficient permissions on table, etc)
// no passcode was generated. handle the error, and either abort or retry.
} else {
// success, unique code was generated and inserted into db.
// you can now do a select to retrieve the generated code (described below)
// or you can proceed with the rest of your program logic.
}
नोट: उपरोक्त उदाहरण को टिप्पणी अनुभाग में @martinstoeckli द्वारा पोस्ट की गई उत्कृष्ट टिप्पणियों के लिए संपादित किया गया था। निम्नलिखित परिवर्तन किए गए:
- बदला हुआ
mysql_num_rows()
(दस्तावेज़ ) सेmysql_affected_rows()
(docs ) -- num_rows इन्सर्ट पर लागू नहीं होता है।mysql_affected_rows()
. के तर्क को भी हटा दिया , क्योंकि यह फ़ंक्शन कनेक्शन स्तर पर संचालित होता है, न कि परिणाम स्तर पर (और किसी भी स्थिति में, किसी सम्मिलन का परिणाम बूलियन होता है, संसाधन संख्या नहीं)। - लूप कंडीशन में एरर चेकिंग को जोड़ा, और लूप से बाहर निकलने के बाद एरर/सफलता के लिए एक टेस्ट जोड़ा। त्रुटि प्रबंधन महत्वपूर्ण है, क्योंकि इसके बिना, डेटाबेस त्रुटियां (जैसे खोए हुए कनेक्शन, या अनुमति समस्याएं), लूप को हमेशा के लिए स्पिन करने का कारण बनेंगी। ऊपर दिखाया गया तरीका (
IGNORE
. का इस्तेमाल करके , औरmysql_affected_rows()
, और परीक्षण$res
त्रुटियों के लिए अलग से) हमें इन "वास्तविक डेटाबेस त्रुटियों" को अद्वितीय बाधा उल्लंघन (जो तर्क के इस खंड में पूरी तरह से मान्य गैर-त्रुटि स्थिति है) से अलग करने की अनुमति देता है।
यदि आपको पासकोड जनरेट होने के बाद प्राप्त करने की आवश्यकता है, तो बस फिर से रिकॉर्ड का चयन करें:
$res = mysql_query("SELECT * FROM files WHERE id=LAST_INSERT_ID()");
$row = mysql_fetch_assoc($res);
$passcode = $row['passcode'];
संपादित करें :mysql फ़ंक्शन LAST_INSERT_ID()
का उपयोग करने के लिए उपरोक्त उदाहरण में बदलाव किया गया है , PHP के फ़ंक्शन के बजाय। यह एक ही चीज़ को पूरा करने का एक अधिक कुशल तरीका है, और परिणामी कोड क्लीनर, स्पष्ट और कम अव्यवस्थित है।