เริ่มต้นในเวอร์ชัน 3.2 MongoDB ติดตามสถิติการใช้งานสำหรับทุกดัชนี ในการเข้าถึงสถิติเหล่านี้ MongoDB เสนอขั้นตอนไปป์ไลน์การรวม $indexStats ข้อควรพิจารณา 6 ข้อเมื่อค้นหาดัชนีที่ไม่ได้ใช้ใน MongoDB
ตัวอย่างเช่น คำสั่งต่อไปนี้จะให้สถิติดัชนีสำหรับคอลเล็กชัน “test.foo”:
db.foo.aggregate( [ { $indexStats:{ } } ] )
https://bit.ly/2seXnzo
เราจะไม่อธิบายผลลัพธ์ของ $indexStats เนื่องจากมีเอกสารประกอบมากมายและบทความดีๆ มากมายที่ครอบคลุมหัวข้อนี้ แต่เราจะให้ข้อควรพิจารณาหกประการในพื้นที่ต่างๆ เมื่อใช้ตัวดำเนินการ $indexStats
ข้อควรพิจารณาที่ 1:สถิติจะถูกรีเซ็ตทุกครั้งที่เริ่มบริการใหม่
ให้ความสนใจเป็นพิเศษกับช่อง "accesses.since" เสมอเมื่อใช้ตัวดำเนินการ $indexStats รูปแบบข้อความค้นหาบางรูปแบบอาจไม่บ่อยนัก เช่น กระบวนการชุดงานเมื่อสิ้นสุดวันหรือรายงานประจำสัปดาห์ ดังนั้น ตรวจสอบให้แน่ใจว่าระยะเวลาของสถิติที่คุณจะประเมินครอบคลุมความต้องการของคุณ สคริปต์ต่อไปนี้ช่วยให้คุณกำหนดเกณฑ์ (เป็นชั่วโมง) และจะพิมพ์คำเตือนหากดัชนีที่ไม่ได้ใช้ไม่เป็นไปตามข้อกำหนด
threshold_hours=24; db.foo.aggregate( [ { $indexStats:{ } } ] ).forEach(function(f){if (f.accesses.ops==0) {if (ISODate()-f.accesses.sinceเกณฑ์ที่เหมาะสมสำหรับคุณคืออะไร? ไม่มีคำตอบง่ายๆ ไม่ใช่รูปแบบคำสั่งทั้งหมดที่ทำงานด้วยความถี่เดียวกัน ดังนั้นเกณฑ์ที่ถูกต้องจะแตกต่างกันไปในแต่ละแอปพลิเคชัน และอาจแตกต่างกันไปตามคอลเลกชันภายในฐานข้อมูลเดียวกัน
ข้อควรพิจารณา 2:การอ่านรอง
โดยค่าเริ่มต้น $indexStats จะอ่านจากหลัก หากแอปพลิเคชันของคุณอ่านเฉพาะจาก Secondaries การเรียกใช้ $indexStats กับ Primary จะทำให้ได้ข้อสรุปที่ไม่ถูกต้อง เพื่อที่จะอ่านผลลัพธ์จาก Secondaries คุณสามารถใช้สคริปต์จากการพิจารณาครั้งแรกข้างต้น แต่ดำเนินการ db.getMongo().setReadPref('secondary') ก่อนที่จะรันซึ่งบังคับให้คำสั่งอ่านจาก Secondaries
คำถามตอนนี้คือ “จะเกิดอะไรขึ้นถ้าคอลเล็กชันได้รับการอ่านทั้งระดับประถมศึกษาและมัธยมศึกษา” เมื่อใช้สคริปต์ต่อไปนี้ คุณสามารถเติมอาร์เรย์ idx ด้วยดัชนีที่ไม่ได้ใช้ทั้งในหลักและรอง:
idx=[];db.foo.getIndexes().forEach(function(f){idx.push(f.name)})db.getMongo().setReadPref('primary');db.foo รวม ( [ { $indexStats:{ } } ] ).forEach(function(f){if (f.accesses.ops>0) { var index =idx.indexOf(f.name); if (index> -1) {idx.splice(index, 1);};}})db.getMongo().setReadPref('secondary');db.foo.aggregate( [ { $indexStats:{ } } ] ).forEach(function(f) ){if (f.accesses.ops>0) { var index =idx.indexOf(f.name); if (index> -1) {idx.splice(index, 1);};}})เนื้อหาของอาร์เรย์ idx จะมีดัชนีที่ไม่ได้ใช้ทั้งในหลักและรอง กรอบเวลาการปฏิบัติตามข้อกำหนด (threshold_hours) เป็นอย่างไร การปรับเปลี่ยนสคริปต์เล็กน้อยจากการพิจารณา 1 (ส่วนย่อยก่อนหน้า) จะช่วยให้เราปฏิบัติตามเวลาได้ เพียงแทนที่ส่วนการรวมด้วยส่วนที่ให้ไว้ด้านล่างและเรียกใช้สคริปต์กับส่วนหลักและส่วนรองโดยใช้ setReadPref:
db.foo.aggregate( [ { $indexStats:{ } } ,{$match:{"name" :{$in:idx}}}] )หากระดับประถมศึกษาและมัธยมศึกษาแสดงผลลัพธ์การปฏิบัติตามข้อกำหนดต่างกัน วิธีที่ดีที่สุดคือรอให้ทั้งสองระดับปฏิบัติตาม
การใช้ค่ากำหนดการอ่านรองอาจนำไปสู่ "ผลลัพธ์ที่ไม่ถูกต้อง" ในกรณีที่มีการเริ่มระบบรองใหม่เมื่อเร็วๆ นี้ และสคริปต์จะเลือกรายการรองเพื่อดึงสถิติ สำหรับกรณีนี้ วิธีที่ดีคือการตรวจสอบ db.serverStatus().uptime และเลือกรองที่มีเวลาทำงานที่สูงขึ้น เมธอด uptime ต้องใช้แนวทางอื่นในสคริปต์ ซึ่งจะนำไปใช้ในการแก้ไขบล็อกโพสต์นี้ในอนาคต
ข้อควรพิจารณา 3:แท็กตั้งค่าแบบจำลอง
แท็กชุดแบบจำลองสามารถใช้ได้ในหลายกรณี แต่ส่วนใหญ่จะใช้สำหรับการอ่านในพื้นที่ในสถานการณ์จำลองแบบทางภูมิศาสตร์และสำหรับการกำหนดเป้าหมายปริมาณงานเฉพาะไปยังโหนดเฉพาะ (เช่น การดำเนินการวิเคราะห์จำนวนมาก) เมื่อพูดถึงการกำหนดเป้าหมายภาระงาน โหนดที่ติดแท็กจะต้องได้รับการตรวจสอบแยกกัน เนื่องจากอาจใช้ดัชนีหรือดัชนีที่โหนดอื่นไม่ได้ใช้ กรณีนี้อาจเกิดขึ้นในการจำลองแบบทางภูมิศาสตร์ด้วย แม้ว่าจะพบได้ยากกว่าก็ตาม สคริปต์จากการพิจารณา 1 อาจใช้เพื่อตรวจสอบสมาชิกที่แท็กด้วยการเพิ่ม readPreference ที่จุดเริ่มต้นของสคริปต์ ต่อไปนี้คือตัวอย่างสำหรับการตั้งค่าการอ่านค่ารองที่ทำเครื่องหมายสำหรับการวิเคราะห์:
db.getMongo().setReadPref('secondary', [ { "workload":"analytics" } ] )แนวทางในอนาคตที่อธิบายไว้ในย่อหน้าสุดท้ายของการพิจารณาที่สองข้างต้น สามารถขยายเพื่อตรวจสอบรองที่ติดแท็กได้เช่นกัน
ข้อควรพิจารณาที่ 4:ดัชนี TTL
ดัชนี TTL ให้บริการการดำเนินการ แต่การใช้งานหลักคือการตัดข้อมูล เป็นไปได้มากที่ $indexStats จะรายงานดัชนีเหล่านี้ว่าไม่ได้ใช้ เนื่องจากจอภาพ TTL ไม่นับเป็นการดำเนินการดัชนี คุณคงไม่ต้องการวางดัชนี TTL ตามผลการวิจัยของรายงาน $indexStats ดังนั้นสคริปต์จะต้องยกเว้นดัชนีประเภทนี้
การแก้ไขเล็กน้อยบนสคริปต์อาร์เรย์ idx ที่เติมจากส่วนที่สองจะใช้งานได้ แทนที่บรรทัดที่สองด้วยบรรทัดต่อไปนี้ และอาร์เรย์ idx จะไม่มีดัชนี TTL ใดๆ
db.foo.getIndexes().forEach(function(f){if (f.expireAfterSeconds==undefined) {idx.push(f.name)}})การสนทนาเกี่ยวกับดัชนีที่ถูกแยกออกไปอีกขั้นหนึ่ง เราสามารถอ้างได้ว่า _id อยู่ในหมวดหมู่นี้ด้วย แน่นอน คุณไม่สามารถวางดัชนี _id แม้ว่าจะไม่ได้ใช้งานก็ตาม คอลเล็กชันที่ไม่เคยแตะต้องดัชนี _id อาจต้องออกแบบใหม่
ข้อพิจารณา 5:คลัสเตอร์ที่มีการแบ่งส่วน
เมื่อพูดถึงคลัสเตอร์ที่แบ่งส่วนข้อมูล มีสองสิ่งที่ต้องพิจารณาก่อนประเมินผลลัพธ์ของ $indexStats ประการแรก เมื่อใช้ $indexStats กับคอลเล็กชันชาร์ด มีความเป็นไปได้ที่ดัชนีชาร์ดคีย์ถูกจัดประเภทว่าไม่ได้ใช้ ตัวอย่างเช่น คอลเล็กชันที่มีการเขียนจำนวนมากซึ่งแบ่งส่วนย่อยใน {_id:”hashed”} (สำหรับการกระจายการเขียนที่เท่ากัน) แต่ไม่มีการดำเนินการอ่าน/อัปเดต/ลบใดๆ ที่สามารถใช้ดัชนี _id ในกรณีนี้ {_id:”hashed”} รายงานว่าไม่ได้ใช้ ไม่ควรทิ้งดัชนีเนื่องจากจะทำให้คลัสเตอร์ที่แยกส่วนของคุณเสียหาย หากคุณต้องการยกเว้นชาร์ดคีย์สำหรับคอลเล็กชัน 'stats.foo' คุณสามารถแนบสคริปต์ต่อไปนี้กับวิธีการที่อธิบายไว้ในการพิจารณาสองครั้ง (การอ่านสำรอง) และชาร์ดคีย์จะไม่รวมอยู่ในอาร์เรย์ idx
shardkey=db.getSiblingDB('config').collections.findOne({_id:'stats.foo'},{_id:0,key:1});db.foo.getIndexes().forEach(ฟังก์ชัน (f){if (JSON.stringify(f.key)!=JSON.stringify(shardkey.key)) {printjson(shardkey.key);printjson(f.key);idx.push(f.name)}} )ข้อพิจารณาอีกประการหนึ่งเกี่ยวข้องกับผลลัพธ์ของ $indexStats เนื่องจากตอนนี้ส่งคืนสถิติจากชาร์ดแต่ละรายการที่คอลเล็กชันมีอยู่ แม้ว่าจะไม่ใช่เรื่องปกติ แต่ก็มีบางกรณีที่ดัชนีรายงานว่าไม่ได้ใช้ในส่วนแบ่งข้อมูลสองสามส่วนในขณะที่ส่วนย่อยอื่นๆ ใช้งาน ชาร์ดคีย์ที่ไม่ดีอาจเป็นสาเหตุได้ แต่สถานการณ์ทั่วไปส่วนใหญ่มีชื่อว่า "ดัชนีการครอบคลุม"
ต่อไปนี้คือตัวอย่างเพื่อให้เข้าใจถึงดัชนีที่ครอบคลุมมากขึ้น:ดัชนี {a:1} และ {a:1,b:1} สามารถแสดงการจับคู่ที่เท่าเทียมกันในช่อง 'a' หากเครื่องมือเพิ่มประสิทธิภาพของ shardA เลือก {a:1} และตัวเลือกของ shardB {a:1,b:1} ดัชนีทั้งสองจะรายงานว่าไม่ได้ใช้งานอย่างน้อยหนึ่งส่วน
ความท้าทายคือการค้นหาดัชนีที่ไม่ได้ใช้งานทั่วโลก การแทนที่ส่วนการรวม (ตามด้วยลูป forEach) ของการพิจารณา 2 สคริปต์ (การอ่านรอง) ด้วยสคริปต์ต่อไปนี้จะทำงาน:
db.foo.aggregate( [ { $indexStats:{ } } , {$group:{_id:"$name",number :{$sum:"$accesses.ops"}}}] ).forEach( function(f){if (f.number>0) { var index =idx.indexOf(f._id); if (index> -1) {idx.splice(index, 1);};}})ก่อน>ความท้าทายอีกประการหนึ่งคือการค้นหาดัชนีที่มีการใช้งานเพียงบางส่วน ข้อมูลชิ้นนี้อาจมีประโยชน์เมื่อพูดถึงดัชนีที่ซ้ำซ้อนหรือการระบุดัชนีทำงานผิดปกติ การใช้อาร์เรย์ idx การรวม/สคริปต์ต่อไปนี้จะถูกเติมด้วยดัชนีที่ไม่ได้ใช้ทั่วโลก และรายงานดัชนีบางส่วนที่ใช้งาน:
db.foo.aggregate( [ { $indexStats:{ } },{$match:{name:{$nin:idx},"accesses.ops":0}}]).forEach(function(f) {พิมพ์ ("ดัชนี "+f.name+" รายงานว่าไม่ได้ใช้บางส่วนในชาร์ด/โฮสต์ " +f.host)})
ข้อควรพิจารณาที่ 6:ดัชนีที่ใช้น้อยที่สุด
การค้นหาและปล่อยดัชนีที่ไม่ได้ใช้มีความสำคัญ แต่การประเมินดัชนีที่ใช้น้อยที่สุดก็มีความสำคัญเช่นกัน หากดัชนีเข้าถึงได้เพียงหนึ่งหรือสองครั้งในหนึ่งสัปดาห์หรือหลายเดือน อาจหมายความว่าดัชนีนั้นไม่จำเป็นหรือเป็นประโยชน์ต่อปริมาณงานของคุณ แนวทางปฏิบัติที่ดีคือการตรวจสอบดัชนีที่ใช้น้อยที่สุดเป็นระยะโดยใช้การรวมต่อไปนี้:
db.foo.aggregate( [ { $indexStats:{ } },{$match:{"accesses.ops":{$gt:0}}},{$group:{_id:"$name", หมายเลข :{$sum:"$accesses.ops"}}},{$sort:{number:1}}] )จากนั้น ดำเนินการที่เหมาะสมซึ่งอาจเป็นการยกเลิกดัชนี เปลี่ยนคำจำกัดความของดัชนี หรือแม้แต่เปลี่ยนตรรกะสคีมา/แอปพลิเคชันของคุณเพื่อทำให้ดัชนีซ้ำซ้อน
เราอยู่ที่นี่เพื่อคุณ
เราพร้อมช่วยคุณล้างข้อมูลดัชนีสำหรับอินสแตนซ์ MongoDB ของคุณ สิ่งที่คุณต้องทำคือสร้างตั๋วไปที่ [email protected] และให้เราช่วยคุณปรับปรุงดัชนีของคุณ!