पहले भाग में, मैंने वर्णन किया कि कैसे ZFS में vdev पर डेटा का आयोजन किया जाता है। दूसरा भाग वर्णन करता है कि वास्तविक स्थान को चुनने के लिए एल्गोरिथ्म कैसे काम करता है जहां रिकॉर्डिंग काम करेगी।
यहाँ मैं कार्य को थोड़ा जटिल कर दूंगा - पहले भाग में केवल एक वीदेव का वर्णन किया गया था; यहाँ हम उनमें से कई होंगे, क्योंकि एल्गोरिथ्म दोनों vdev का चयन करना चाहिए, जहाँ हम डेटा ब्लॉक, और महादेव के अंदर मेटास्लैब लिखेंगे। एक उत्पादन प्रणाली में कई दर्जनों vdevs हो सकते हैं, और उन सभी में डेटा को सही ढंग से वितरित करना महत्वपूर्ण है - हम सभी डेटा को कॉपी किए बिना अब उन्हें रिबैलेंस नहीं कर सकते हैं। सही एल्गोरिदम का लक्ष्य डेटा को समानांतर करना है ताकि प्रत्येक डिवाइस पर लगभग समान मात्रा हो, असमान भरने को सुचारू करने के लिए, लेकिन उपकरणों में से एक को अधिभारित करने के लिए भी नहीं (यह पूरे पूल में रिकॉर्डिंग को धीमा कर देगा)।
NAME STATE READ WRITE CKSUM tank ONLINE 0 0 0 c1t6d0 ONLINE 0 0 0 c1t5d0 ONLINE 0 0 0
के साथ शुरू करने के लिए, एक महत्वपूर्ण नोट: ZFS यह सुनिश्चित करने के लिए डिज़ाइन किया गया है कि पूल में सभी उपकरण समान आकार के हों। अन्यथा, उदाहरण के लिए, यदि आप डिस्क के 1TB के पूल में 2TB डिस्क जोड़ते हैं, तो 2TB डिस्क के परिणामस्वरूप दोगुना डेटा होगा, और यह सिस्टम के कुल IOP को प्रभावित करना शुरू कर देगा - आवंटनकर्ता एल्गोरिथ्म बाइट्स में डेटा की मात्रा के बजाय भरने का प्रतिशत खाता है।
ZFS में वर्तमान में चार एलोकेटर एल्गोरिदम हैं। चर
zfs_metaslab_ops
में
space_map_ops_t
संरचना के लिए एक सूचक होता है, जिसमें सात फ़ंक्शन
space_map_ops_t
होते हैं जो प्रत्येक विशिष्ट एल्गोरिथ्म का उपयोग करता है। उदाहरण के लिए, Illumos
metaslab_df
एल्गोरिथ्म का उपयोग करता है, और फ़ंक्शन पॉइंटर्स वाला संबंधित पृष्ठ इस तरह दिखता है:
static space_map_ops_t metaslab_df_ops = { metaslab_pp_load, metaslab_pp_unload, metaslab_df_alloc, metaslab_pp_claim, metaslab_pp_free, metaslab_pp_maxsize, metaslab_df_fragmented };
इन कार्यों में से पांच का उपयोग सभी एल्गोरिदम में किया जाता है; अंतर वास्तव में केवल
metaslab_*_alloc()
और
metaslab_*_fragmented()
- आबंटक ही, और एक फ़ंक्शन जो यह तय करता है कि किसी विशेष मेटाबेस में कितना खाली स्थान विखंडित है। उपयोग किए जा सकने वाले एलोकेटर: डीएफ (डायनामिक-फिट), एफएफ (फर्स्ट-फिट), और दो प्रायोगिक वाले, सीडीएफ और एनडीएफ - कोई नहीं जानता कि उनका क्या मतलब है।
उनमें से एफएफ सबसे सरल है - यह एवीएल पेड़ को क्रम में ट्रेस करते समय पहले उपलब्ध खाली स्थान पर डेटा के टुकड़े लिखता है, और यदि वे फिट नहीं होते हैं तो रिकॉर्डिंग ब्लॉकों को खंडों में विभाजित करता है। इसके कारण, एफएफ एक बहुत ही धीमा एल्गोरिदम है, क्योंकि एक ब्लॉक को लिखने के लिए इसे पूरे पेड़ का पता लगाना चाहिए जब तक कि पर्याप्त संख्या में सेगमेंट टाइप न हो जाएं। उदाहरण के लिए, 1GB डेटा रिकॉर्ड करने के लिए, सबसे खराब स्थिति 512 बाइट्स के 20 मिलियन सेगमेंट और परिणामस्वरूप बहुत मजबूत डेटा विखंडन है। FF का उपयोग अंतिम विकल्प के रूप में अन्य एल्गोरिदम द्वारा किया जाता है, यदि वे अलग-अलग जगह नहीं पा सकते हैं - उदाहरण के लिए, DFF FF का उपयोग करेगा यदि इस मेटास्लैब (
int metaslab_df_free_pct = 4;
) में 4% से कम खाली स्थान है
int metaslab_df_free_pct = 4;
एफएफ का एकमात्र प्लस यह है कि यह एकमात्र ऐसा टुकड़ा है जो एक खंडित मेटास्लैब को 100% भर सकता है।
* डीएफ एल्गोरिदम अलग तरीके से काम करते हैं - वे
freemap
एक
freemap
फ्री स्पेस
freemap
बनाते हैं जो वर्तमान में रिकॉर्ड किया जा रहा है, इसे आकार और / या निरंतर मुक्त स्थान के टुकड़ों की निकटता द्वारा सॉर्ट करें, और गति के संदर्भ में सबसे इष्टतम डेटा प्लेसमेंट विकल्प चुनने का प्रयास करें। रिकॉर्डिंग, डिस्क सिर के आंदोलनों की संख्या और रिकॉर्ड किए गए डेटा का न्यूनतम विखंडन।
उदाहरण के लिए,
metaslab_weight()
फ़ंक्शन उन सभी के लिए काम करता है, जो डिस्क प्लेट के बाहरी क्षेत्रों (शॉर्ट-स्ट्रोक प्रभाव के लिए) पर स्थित मेटास्लैब को एक छोटी प्राथमिकता देता है। यदि आप केवल SSDs का उपयोग करते हैं, तो यह एल्गोरिथ्म के इस हिस्से को अक्षम करके ZFS को ट्यून करने के लिए समझ में आता है, क्योंकि SSDs पर शॉर्ट-स्ट्रोकिंग लागू नहीं है।
तो, डेटा
ZIO पाइपलाइन से आवंटन एल्गोरिदम में जाता है - वहां से,
metaslab_alloc()
फ़ंक्शंस (लेखन के लिए खुद को आबंटक) और
metaslab_free()
(मुक्त स्थान, कचरा इकट्ठा)।
metaslab_alloc(spa_t *spa, metaslab_class_t *mc, uint64_t psize, blkptr_t *bp, int ndvas, uint64_t txg, blkptr_t *hintbp, int flags)
इसे वहां पारित किया जाता है:
*spa
- डेटा ऐरे (ज़ूल) की संरचना के लिए एक संकेतक; * mc - मेटास्लैब का एक वर्ग, जिसमें, अन्य बातों के अलावा,
zfs_metaslab_ops
लिए एक सूचक है;
psize
- डेटा आकार;
*bp
- सूचक ही ब्लॉक करने के लिए;
ndvas
- डेटा की स्वतंत्र प्रतियों की संख्या जो किसी दिए गए ब्लॉक के लिए आवश्यक है (1 डेटा के लिए; 2 अधिकांश मेटाडेटा के लिए; 3 मेटाडेटा के लिए कुछ मामलों में जो AVL ट्री में उच्च हैं। मेटाडेटा की नकल करने का बिंदु यह है कि अगर मेटाडेटा के साथ केवल एक ब्लॉक है। एक पेड़ के खंड के लिए खो दिया है, हम इसके तहत है कि सब कुछ खो देते हैं। ऐसे ब्लॉकों को डिट्टो ब्लॉक कहा जाता है, और एल्गोरिथ्म उन्हें विभिन्न vdevs को लिखने की कोशिश करता है)।
अगला,
txg
लेनदेन समूह की अनुक्रम संख्या है जिसे हम लिख रहे हैं;
*hintbp
- एक संकेत यह सुनिश्चित करने के लिए उपयोग किया जाता है कि जो ब्लॉक उनके बगल में हैं वे भी तार्किक रूप से डिस्क के बगल में हैं, और उसी vdev पर जाएं;
flags
- 5 बिट्स जो आवंटितकर्ता को यह जानने की अनुमति देते हैं कि क्या किसी विशिष्ट आवंटन विकल्पों का उपयोग करना है
*hintbp
का उपयोग करना या अनदेखा करना, और
*hintbp
का उपयोग करना है (कृपया अधिक वीवीडी काम के लिए उनके हेडर के रूप में एक ही vdev के लिए बच्चे ब्लॉक का एक समूह लिखें) ZFS प्रीफैच और vdev कैश)।
define METASLAB_HINTBP_FAVOR 0x0 define METASLAB_HINTBP_AVOID 0x1 define METASLAB_GANG_HEADER 0x2 define METASLAB_GANG_CHILD 0x4 define METASLAB_GANG_AVOID 0x8
/* * Allow allocations to switch to gang blocks quickly. We do this to * avoid having to load lots of space_maps in a given txg. There are, * however, some cases where we want to avoid "fast" ganging and instead * we want to do an exhaustive search of all metaslabs on this device. * Currently we don
/* * If we are doing gang blocks (hintdva is non-NULL), try to keep * ourselves on the same vdev as our gang block header. That * way, we can hope for locality in vdev_cache, plus it makes our * fault domains something tractable. */
अगला, वास्तव में, कोड का सबसे महत्वपूर्ण हिस्सा है, जिसमें लिखने के लिए मेटास्लैब चुनने का तर्क
metaslab_alloc_dva()
। फ़ंक्शन में ट्रिकी कोड की लगभग 200 लाइनें हैं, जिसे मैं समझाने की कोशिश करूंगा।
सबसे पहले, हम सभी vdevs के लिए मेटाबेस के सभी समूहों को लेते हैं, और उनके साथ एक आवंटन चक्र (
mg_rotor
) के माध्यम से जाते हैं, यदि कोई हो, तो संकेत का उपयोग करना। हम vdevs को छोड़ते हैं जिसके लिए रिकॉर्डिंग वर्तमान में अवांछनीय है, उदाहरण के लिए, उनमें से जिनमें से एक डिस्क की मृत्यु हो गई है, या रेड्ज़ समूह को पुनर्स्थापित किया जा रहा है। (दोषपूर्ण उपकरणों से आवंटित नहीं होता है।) हम उन डिस्क को भी छोड़ देते हैं, जिन पर कुछ प्रकार की लिखने की त्रुटियां थीं, जिनके डेटा के लिए डिस्क पर केवल एक प्रति होगी। (असफल वीदेव को सिंगल-कॉपी डेटा लिखने से बचें।)
रोटर एक सर्कल में काम करता है जब तक कि रिकॉर्डिंग के लिए भेजा गया डेटा रन आउट न हो जाए। इस चक्र के अंदर, इष्टतम vdev को बारी-बारी से चुना जाता है, फिर इसमें,
metaslab_group_alloc()
का उपयोग करते हुए, सबसे अच्छा metaslab चुना जाता है, फिर हम तय करते हैं कि इस मेटाबेस पर कितना डेटा लिखना है, दूसरों के साथ vdev'a उपयोग के प्रतिशत की तुलना करना। कोड का यह हिस्सा बहुत महत्वपूर्ण है, इसलिए मैं इसे पूरी तरह से देता हूं:
offset = metaslab_group_alloc(mg, psize, asize, txg, distance, dva, d, flags); if (offset != -1ULL) { if (mc->mc_aliquot == 0) { vdev_stat_t *vs = &vd->vdev_stat; int64_t vu, cu; vu = (vs->vs_alloc * 100) / (vs->vs_space + 1); cu = (mc->mc_alloc * 100) / (mc->mc_space + 1); mg->mg_bias = ((cu - vu) * (int64_t)mg->mg_aliquot) / 100; }
उदाहरण के लिए, अगर हमें दो डिस्क की एक सरणी में 1MB डेटा लिखने की आवश्यकता है,
जिसमें से एक 20% पूर्ण और दूसरा 80% पूर्ण है, तो हम पहले 819KB और दूसरे को 205KB लिखेंगे। सुधार: पूल में मुक्त स्थान और vdev में मुक्त स्थान की तुलना करता है, इसलिए परिणाम थोड़ा अलग होगा। यहां, एक बहुत ही दिलचस्प बात की जा सकती है - कुछ महीने पहले मैंने ZFS में प्रत्येक vdeva के लिए विलंबता के आँकड़े जोड़े (यह
vdev_stat_t->vs_latency[]
NexentaStor में है
vdev_stat_t->vs_latency[]
, अभी तक Illumos में नहीं जोड़ा गया है), और इसे एक के रूप में इस्तेमाल किया जा सकता है; नए डेटा को रिकॉर्ड करते समय कारकों में से किसी एक के अनुपात में या इसके खाली स्थान को ध्यान में रखते हुए या केवल इसका उपयोग करके। मैंने इस तरह के एक संशोधित एल्गोरिदम को भी लिखा था, लेकिन इसका उपयोग उत्पादन प्रणालियों में अभी तक नहीं किया गया है। यह तब समझ में आता है जब या तो सरणी में विभिन्न प्रकार और गति के डिस्क होते हैं, या जब डिस्क में से एक मरने लगता है (धीमा), लेकिन अभी तक यह इतना बुरा नहीं है और इस पर कोई त्रुटियां नहीं हैं।
अंत में, पुनरावृति के बाद पुनरावृत्ति, लूप मेटासलैब समूह को लिखने के लिए सभी डेटा भेजता है, और इस लेनदेन समूह (txg) के लिए समाप्त होता है, जबकि
metaslab_weight()
सिस्टम मेटास्लैब समूह में काम करता है (लेख की शुरुआत देखें), और
space map
सिस्टम के माध्यम से, मैक्सफ्री पर विचार कर रहा है। (निरंतर मुक्त स्थान का अधिकतम टुकड़ा), एवीएल पेड़ों के अनुरेखण और संबंधित एल्गोरिथ्म (डीएफ, एफएफ, सीडीएफ, एनडीएफ) का उपयोग करते हुए, डेटा को एल्गोरिथम के लिए एक इष्टतम तरीके से धकेलता है, जिसके बाद अंत में हमें उस ब्लॉक का भौतिक पता मिलता है जिसमें हम डिस्क पर लिखेंगे। और डेटा
sd
(Scsi Device) ड्राइवर को लिखने के लिए कतारबद्ध है ।