RE:NODE

ბაზები14 წუთის საკითხავი

MongoDB-ის სქემის დიზაინი და ინდექსები, რომლებიც თავს იმართლებს

Embedding თუ referencing, 16 MB ლიმიტი, compound ინდექსები და ESR წესი, explain()-ის კითხვა, პროფაილერი და რამდენი RAM სჭირდება შენს working set-ს.

0 მკითხველი

MongoDB-ს სქემა არ აქვს, რაც არ ნიშნავს, რომ შენს მონაცემებს ფორმა არ აქვს. ეს ნიშნავს, რომ ფორმა შენს აპლიკაციასა და ინდექსებში ცხოვრობს და არა სერვერზე, და რომ იმას, რომ ცუდი არჩევანი გააკეთე, production-ში გებულობ და არა migration-ის დროს. ორი გადაწყვეტილება თითქმის ყველაფერს ფარავს: ჩააშენე ის მონაცემები, რომლებსაც ყოველთვის ერთად კითხულობ, და მიუთითე ისინი, რომლებსაც ცალ-ცალკე კითხულობ; და აწყე თითო compound ინდექსი თითო query-ის ფორმაზე, ველების რიგით: equality, sort, range.

ქვემოთ ყველაფერი ამ ორი წინადადების მიზეზებია, პლუს ის, როგორ შეამოწმო შენი საქმე explain()-ითა და პროფაილერით. მაგალითები მაღაზიაზეა: customers, orders, products. თუ დოკუმენტურ და რელაციურ საცავს შორის არჩევანი ჯერ არ გაგიკეთებია, Postgres თუ MongoDB უფრო მოკლე არგუმენტაციაა; ეს პოსტი ვარაუდობს, რომ გადაწყვეტილება მიღებულია.

ჩაშენება თუ მითითება#

კითხვა ის არ არის, რომელია სწორი. ის არის, ორიდან რომელ ფასს ხარ მზად გადაიხადო.

ჩაშენება (embedding) დაკავშირებულ მონაცემებს მშობელი დოკუმენტის შიგნით ათავსებს. ერთი წაკითხვა ყველაფერს იძლევა, join არ არის, და ჩაწერა ატომურია, რადგან MongoDB-ში ერთი დოკუმენტის განახლება ატომურია, რაგინდ ღრმა იყოს ცვლილება. ფასი დუბლირება და ზრდაა: ჩაშენებული პროდუქტის ერთი და იგივე სახელი ათი ათას შეკვეთაში ცხოვრობს, ამიტომ პროდუქტის გადარქმევა ათი ათასი დოკუმენტის შეხებას ნიშნავს, ხოლო დოკუმენტი, რომელიც დროთა განმავლობაში იზრდება, საბოლოოდ დისკზე გადასატანი ხდება.

მითითება (referencing) ინახავს იდენტიფიკატორს და მეორე დოკუმენტს ცალკე იღებს, ან აგრეგაციაში $lookup-ით. არაფერი მეორდება, განახლებები ერთ ადგილას ხდება და მშობელი დოკუმენტი პატარა რჩება. ფასი მეორე round trip-ია, ან $lookup, რომელიც სწორად უნდა იყოს დაინდექსებული, რომ ნელისგან განსხვავებული იყოს.

ხელის წესები, რომლებიც რეალურ აპლიკაციებთან შეხებას უძლებს:

  • ერთი-რამდენიმეზე, ერთად იკითხება, იშვიათად იცვლება: ჩააშენე. მისამართები მომხმარებელზე. პოზიციები შეკვეთაზე. შეკვეთის პოზიციები ისედაც ისტორიული ჩანაწერია - თუ პროდუქტის ფასი შეიცვალა, შეკვეთა არ უნდა შეიცვალოს.
  • ერთი-ბევრზე, სადაც "ბევრს" ჭერი არ აქვს: მიუთითე. კომენტარები პოსტზე, მოვლენები მომხმარებელზე, შეტყობინებები არხში. შვილობილი დოკუმენტი მშობლის _id-ს ინახავს და მასზეა დაინდექსებული.
  • ბევრი-ბევრზე: მიუთითე იმ მხრიდან, საიდანაც query-ს აკეთებ. შეინახე tag-ების იდენტიფიკატორების მასივი სტატიაზე, თუ კითხულობ "რა tag-ები აქვს ამ სტატიას", და დააინდექსე მასივი, რომ უკუკითხვასაც პასუხი ჰქონდეს.
  • საჭიროა სიის ხედში: გაიმეორე ორი-სამი ველი, რომელსაც სია აჩვენებს. შეკვეთა, რომელიც productId-ის გვერდით productName-სა და unitPrice-ს ინახავს, კალათას products-ის შეხების გარეშე აჩვენებს. ეს extended reference პატერნია და დუბლირება განზრახულია.

უნდა უფრთხილდე შეუზღუდავ მასივს. user დოკუმენტი events მასივით, რომელიც მომხმარებლის ყოველ ქმედებაზე იზრდება, დოკუმენტია, რომელიც სამი თვე მშვენივრად მუშაობს და შემდეგ კედელს ეჯახება. თუ მასივისთვის მაქსიმუმს ვერ ასახელებ, მას საკუთარი კოლექცია ეკუთვნის.

ლიმიტები, რომლებიც ფორმას წყვეტს#

ლიმიტიმნიშვნელობაპრაქტიკაში რას ნიშნავს
BSON დოკუმენტის ზომა16 MBჩაშენების მკაცრი ჭერი
ჩადგმის სიღრმე100 დონეშემთხვევით არასოდეს მიიღწევა
ველები compound ინდექსში32გონივრულად არასოდეს მიიღწევა
მასივის ველები compound ინდექსზე1multikey შეზღუდვა, ქვემოთ
ინდექსები კოლექციაზე64ახლოსაც არ უნდა იყო

თექვსმეტი მეგაბაიტი უზარმაზარი ჩანს, სანამ მასივი საზღვრის გარეშე იზრდება. პრაქტიკული ზღვარი გაცილებით დაბალია: დოკუმენტი, რომელსაც ხშირად ანახლებ, პატარა უნდა დარჩეს, რადგან ყოველი განახლება მას თავიდან წერს, ხოლო 4 MB დოკუმენტი, რომელიც ყოველ დამატებაზე თავიდან იწერება, ერთი შეცვლილი ველისთვის ძალიან დიდი დისკისა და ქეშის ხარჯია. რამდენიმე ასეული კილობაიტი ის წერტილია, სადაც მასივი უკვე გატანილი უნდა გქონდეს.

თუ ერთ მნიშვნელობას მართლა 16 MB-ზე მეტი სჭირდება, სწორედ ამისთვის არის GridFS - ის ფაილს ორ კოლექციაში ნაწილებად ყოფს. აპლიკაციების უმეტესობისთვის უკეთესი პასუხია ფაილი object storage-ში ინახებოდეს, მითითება კი MongoDB-ში.

პატერნები, რომლებიც უნდა იცოდე#

ოთხი სახელდებული პატერნი ფარავს შემთხვევების უმეტესობას, სადაც მარტივი პასუხი არ მუშაობს.

Subset. მშობელში შეინახე ყველაზე ახალი ან ყველაზე მნიშვნელოვანი რამდენიმე, დანარჩენი კი სხვა კოლექციაში. პროდუქტი თავის ათ ყველაზე ახალ მიმოხილვას ინახავს პროდუქტის გვერდისთვის; reviews ყველას ინახავს "ყველას ნახვის" გვერდისთვის. ორ ადგილას წერას იღებ ერთი დოკუმენტის წაკითხვის სანაცვლოდ იმ გვერდზე, რომელსაც ყველა ხსნის.

Bucket. დააჯგუფე ბევრი პატარა ჩანაწერი ერთ დოკუმენტად დროის ან გასაღების მიხედვით. წუთში ათას სენსორულ ჩვენებას, რომელიც ერთ დოკუმენტად წუთზე ინახება და არა ერთად თითო ჩვენებაზე, დღეში მილიონი დოკუმენტი 1,440-ად გადააქვს და ინდექსებს მკვეთრად ამცირებს. MongoDB 5.0-სა და შემდგომ ვერსიებს აქვს time series კოლექციები, რომლებიც ამას შენს მაგივრად აკეთებენ, და როცა ისინი ერგება, ისაა უკეთესი ვარიანტი.

Computed. შეინახე პასუხი და არა შემავალი მონაცემები, როცა პასუხი ბევრად უფრო ხშირად იკითხება, ვიდრე იცვლება. მიმდინარე orderCount და totalSpent მომხმარებელზე უკეთესია, ვიდრე აგრეგაცია ყველა შეკვეთაზე გვერდის ყოველ ჩატვირთვაზე. განაახლე იმავე ოპერაციაში, რომელიც შეკვეთას ქმნის, $inc-ით.

Schema version. პირველივე დღიდან ჩაწერე schemaVersion ველი ყველა დოკუმენტზე. დიდი კოლექციის ადგილზე მიგრაცია მაწუხარეა; ზარმაცი მიგრაცია, სადაც აპლიკაცია ორივე ფორმას ამუშავებს და დოკუმენტს თავიდან წერს, როცა მას ეხება, არა. მიგრაციები გათიშვის გარეშე ამ იდეის ზოგადი ფორმაა.

ასევე შეგიძლია, სერვერმა თავად აიძულოს ფორმა, რაც ღირს, როგორც კი სქემა კვირაობით აღარ იცვლება:

javascript
db.runCommand({  collMod: "orders",  validator: { $jsonSchema: {    bsonType: "object",    required: ["customerId", "status", "createdAt"],    properties: {      status: { enum: ["pending", "paid", "shipped", "cancelled"] },      createdAt: { bsonType: "date" }    }  }},  validationLevel: "moderate",  validationAction: "warn"})

დაიწყე validationAction: "warn"-ით, რომელიც დარღვევებს წერს და ჩაწერებს არ უარყოფს, წაიკითხე ლოგი ერთი კვირა და მხოლოდ შემდეგ გადადი error-ზე.

როგორ მუშაობს MongoDB-ის ინდექსები#

ყველა კოლექციას _id-ზე ინდექსი აქვს. ის უნიკალურია, ვერ წაიშლება და სწორედ ის არის მიზეზი, რატომ არის იდენტიფიკატორით ძებნა ყოველთვის სწრაფი. ყველა სხვა ინდექსს შენ ქმნი, და თითოეული B-tree-ა, რომელიც სერვერმა უნდა განაახლოს ყოველ ჩაწერაზე, რომელიც მის ველებს ეხება.

ტიპიიქმნებარისთვის
Single field{ email: 1 }Equality და range ერთ ველზე
Compound{ status: 1, createdAt: -1 }ერთი query-ის ფორმა, იხილე ქვემოთ
Multikeyნებისმიერი ინდექსი მასივის ველზემასივის შიგნით ელემენტების შესატყვისობა
Unique{ email: 1 }, { unique: true }უნიკალურობის დაცვა
PartialpartialFilterExpressionმხოლოდ იმ ჩანაწერების დაინდექსება, რომლებსაც ეძებ
TTL{ createdAt: 1 }, { expireAfterSeconds: 604800 }ძველი დოკუმენტების ვადის გასვლა
Text{ description: "text" }უხეში სრულტექსტური ძებნა, ერთი კოლექციაზე
Wildcard{ "attrs.$**": 1 }ნამდვილად წინასწარ უცნობი ველების სახელები
2dsphere{ location: "2dsphere" }გეოსივრცული query-ები

მიმართულებას ერთველიან ინდექსზე მნიშვნელობა არ აქვს: სერვერს B-tree-ს ორივე მიმართულებით გავლა შეუძლია. მიმართულებას compound ინდექსზე მნიშვნელობა მხოლოდ მაშინ აქვს, როცა მის ერთზე მეტ ველს შერეული მიმართულებებით ალაგებ.

ორი ქცევა MongoDB-სთვის სპეციფიკურია და დასამახსოვრებელია. ინდექსი მასივის ველზე multikey ინდექსია: ის მასივის ყოველ ელემენტზე ერთ ჩანაწერს ინახავს, ამიტომ დოკუმენტი ორმოცდაათი tag-ით ორმოცდაათ ჩანაწერს ამატებს. ეს ნორმალურია და სწორედ ამიტომ შეგიძლია მასივის შიგნით query, მაგრამ ამიტომაც შეიძლება compound ინდექსს მხოლოდ ერთი მასივის ველი ჰქონდეს. და partial ინდექსი partialFilterExpression: { status: "pending" }-ით მხოლოდ მოლოდინში მყოფ შეკვეთებს ინდექსავს, რაც ცხრილზე, სადაც სტრიქონების 99% shipped-ია, მეათედი ზომა და ჩაწერის მეათედი ფასია. Partial ინდექსებს მიანიჭე უპირატესობა sparse-ებთან შედარებით; ისინი უფრო ფართო შესაძლებლობისაა.

TTL ინდექსი სესიების, token-ებისა და ლოგების ვადის გასვლის ყველაზე იაფი გზაა. ფონური thread, რომელიც ვადაგასულ დოკუმენტებს შლის, დაახლოებით წუთში ერთხელ ეშვება, ამიტომ წაშლა მიახლოებითია, ველი კი BSON date უნდა იყოს და არა რიცხვი.

Compound ინდექსები და ESR წესი#

ერთი compound ინდექსი მთელ query-ების ოჯახს ემსახურება prefix წესის გამო: ინდექსი { a: 1, b: 1, c: 1 } ემსახურება query-ებს a-ზე, a-სა და b-ზე, და a-ზე, b-სა და c-ზე, მაგრამ არა მხოლოდ b-ზე. თუ ველებს სწორად დაალაგებ, სამი ინდექსი ერთი ხდება.

დალაგების წესია equality, sort, range:

  1. Equality ველები პირველი - ისინი, რომლებიც ზუსტ მნიშვნელობას ემთხვევა. ისინი სკანირებას ინდექსის გასაღებების უწყვეტ მონაკვეთამდე ავიწროებს.
  2. Sort ველები შემდეგ. თუ დალაგება equality ველებს ინდექსში მოსდევს, შედეგები უკვე დალაგებული გამოდის და ცალკე sort ნაბიჯი არ არის.
  3. Range ველები ბოლოს - $gt, $lt, $in მონაკვეთზე, $regex პრეფიქსით. ინდექსის შუაში მდგარი range მის შემდეგ ყველაფრის რიგს არღვევს.

db.orders.find({ status: "paid", total: { $gt: 100 } }).sort({ createdAt: -1 })-სთვის ინდექსია:

javascript
db.orders.createIndex({ status: 1, createdAt: -1, total: 1 })

Equality status-ზე, sort createdAt-ზე, range total-ზე. total-ის createdAt-მდე დაყენება უფრო ბუნებრივი ჩანს და იძლევა გეგმას მაბლოკირებელი SORT ეტაპით, რაც ერთ მილიწამსა და რამდენიმე ასეულს შორის განსხვავებაა.

Covered query შემდეგი საფეხურია: თუ ყველა ველი, რომელიც query-ს სჭირდება, ინდექსშია, სერვერი დოკუმენტებს საერთოდ არ ეხება. რადგან _id ნაგულისხმევად ბრუნდება და ჩვეულებრივ შენს ინდექსში არ არის, დასაფარად მისი projection-ით მოცილებაა საჭირო: .find({ status: "paid" }, { _id: 0, status: 1, createdAt: 1 }). როცა მუშაობს, დაინახავ totalDocsExamined: 0-ს.

ყველაფერს ნუ დააინდექსებ. ყოველი ინდექსი იწერება ყოველ შესაბამის insert-სა და update-ზე, ქეშს ჭამს, რომელსაც შენი დოკუმენტები გამოიყენებდა, და restore-ისას თავიდან უნდა აშენდეს. სამი კარგი compound ინდექსი თორმეტ ერთველიან ინდექსს სჯობს, და MongoDB ერთი query-სთვის ორ ინდექსს იშვიათად აერთიანებს, თუმცა ტექნიკურად შეუძლია.

explain()-ის კითხვა#

javascript
db.orders.find({ customerId: 4821, createdAt: { $gte: since } })         .sort({ createdAt: -1 }).limit(20)         .explain("executionStats")

ინდექსამდე:

code
nReturned: 20executionTimeMillis: 412totalKeysExamined: 0totalDocsExamined: 2400000stage: SORT  sortPattern: { createdAt: -1 }  inputStage: COLLSCAN    filter: { customerId: { $eq: 4821 } ... }

createIndex({ customerId: 1, createdAt: -1 })-ის შემდეგ:

code
nReturned: 20executionTimeMillis: 1totalKeysExamined: 20totalDocsExamined: 20stage: LIMIT  inputStage: FETCH    inputStage: IXSCAN      indexName: customerId_1_createdAt_-1

ოთხი რიცხვი თითქმის ყველაფერს გეუბნება. nReturned არის ის, რაც query-მ გამოიტანა. totalKeysExamined არის წაკითხული ინდექსის ჩანაწერები. totalDocsExamined არის ამოღებული დოკუმენტები. გინდა, რომ სამივე დაახლოებით ტოლი იყოს. გასაღებები დაბრუნებულზე ბევრად მეტი ნიშნავს, რომ ინდექსი საკმარისად სელექტიური არ არის; დოკუმენტები გასაღებებზე ბევრად მეტი ნიშნავს, რომ სერვერი დოკუმენტებს იღებს ფილტრის გამოსაყენებლად, რომელიც ინდექსში უნდა ყოფილიყო; COLLSCAN მილიონობით შემოწმებული დოკუმენტით ნიშნავს, რომ გამოსადეგი ინდექსი საერთოდ არ არის.

ეტაპების სახელები, რომლებიც მნიშვნელოვანია: COLLSCAN კოლექციას კითხულობს, IXSCAN ინდექსს გადის, FETCH დოკუმენტებს მათი ინდექსის ჩანაწერებით იღებს, SORT მაბლოკირებელი დალაგებაა მეხსიერებაში, ხოლო PROJECTION_COVERED ნიშნავს, რომ არაფერი აღებულა. SORT ეტაპი, რომელიც სერვერის sort მეხსიერების ლიმიტს აჭარბებს, სრულად ვარდება შეცდომით Sort exceeded memory limit; ლიმიტი ვერსიის მიხედვით 32 MB და 100 MB იყო, და შეცდომა ზუსტ მნიშვნელობას ბეჭდავს. გამოსავალია ინდექსი, რომელიც რიგს იძლევა და არა უფრო დიდი ლიმიტი.

explain("executionStats") query-ს ასრულებს. explain() მარტო queryPlanner დეტალურობას იყენებს და მხოლოდ გეგმავს, რაც production-ის წინააღმდეგ უსაფრთხო ვარიანტია. აგრეგაციებსაც აქვთ explain, db.orders.explain("executionStats").aggregate([...])-ით, და პირველი $group-ის ან $sort-ის შემდეგ მდგარი pipeline ეტაპები ინდექსს ვერ იყენებენ, ამიტომ $match პირველ ადგილზე დააყენე და დააინდექსე ის, რასაც ის ემთხვევა.

ნელი query-ების პოვნა პროფაილერით#

MongoDB ყველა ოპერაციას, რომელიც slowms-ზე ნელია და ნაგულისხმევად 100 მილიწამია, სერვერის ლოგში წერს, პროფაილინგი ჩართულია თუ არა. ეს ლოგი პირველი ადგილია, სადაც უნდა ნახო. რაიმე ისეთისთვის, რაზეც query-ს გააკეთებ, ჩართე პროფაილერი, რომელიც თითო მონაცემთა ბაზაზეა:

javascript
db.setProfilingLevel(1, { slowms: 50 })     // 1 = slow ops only, 2 = everythingdb.system.profile.find().sort({ ts: -1 }).limit(5).pretty()db.setProfilingLevel(0)

system.profile capped კოლექციაა, ნაგულისხმევად პატარა, ამიტომ ის ახალ ფანჯარას ინახავს და არა ისტორიას. დონე 2 ყველა ოპერაციას წერს და დეველოპმენტის ინსტრუმენტია და არა ის, რაც ჩართული უნდა დატოვო.

იმის გასაგებად, რომელი ინდექსებია შესანახად ღირსი, ჰკითხე სერვერს, რამდენჯერ იქნა თითოეული გამოყენებული ბოლო restart-იდან:

javascript
db.orders.aggregate([{ $indexStats: {} }])

ინდექსი, რომლის accesses.ops თვეზე მეტი uptime-ის შემდეგ ნულია, არაფრისთვის გიჯდება ჩაწერებსა და ქეშს. სანახავად, რა ღირს, გვერდით შეამოწმე db.orders.stats().indexSizes. ხოლო db.currentOp() გაჩვენებს, რა მუშაობს ახლა, და db.killOp(opid) - აგრეგაციისთვის, რომელიც ვიღაცამ ხელით გაუშვა production-ის წინააღმდეგ.

ინდექსების უსაფრთხოდ აგება და წაშლა#

MongoDB 4.2-დან ინდექსის აგება ექსკლუზიურ lock-ს მხოლოდ მოკლედ იღებს დასაწყისსა და ბოლოს და შუაში ჩვეულებრივ კითხულობს და წერს, ამიტომ ძველი background: true პარამეტრი აღარაფერს ნიშნავს. ეს აგებას უფასოს არ ხდის: ის მთელ კოლექციას კითხულობს და მთელი ხნის განმავლობაში CPU-სა და მეხსიერებას იყენებს. ააგე მშვიდ საათში და პროგრესს db.currentOp()-ით უყურე.

ინდექსის წაშლა ის ადგილია, სადაც ხალხი ნერვიულობს, და MongoDB 4.4-მა სწორი ინსტრუმენტი დაამატა:

javascript
db.orders.hideIndex("status_1_total_1")   // invisible to the planner, still maintaineddb.orders.unhideIndex("status_1_total_1") // instant undodb.orders.dropIndex("status_1_total_1")   // once you are sure

დამალე, ერთი დღე უყურე შენს latency-ს და წაშალე, თუ არაფერი შეცვლილა. დამალვის გაუქმება მყისიერია; წაშლილი ინდექსის ხელახლა აგება დიდ კოლექციაზე არა.

ერთი შენიშვნა restore-ებზე: ინდექსები თავიდან შენდება, როცა dump აღდგება, და ეს ხშირად ოპერაციის ყველაზე ნელი ნაწილია. mongodump და mongorestore მოიცავს flag-ებს, რომლებიც ამას აკონტროლებს.

მეხსიერება, working set და WiredTiger-ის ქეში#

MongoDB სწრაფია, როცა working set - ინდექსები პლუს დოკუმენტები, რომლებსაც რეალურად ეხები - მეხსიერებაში ეტევა, და მკვეთრად უარესდება, როცა არა, რადგან ყოველი miss დისკის წაკითხვა ხდება. საცავის ძრავა ქეშს ამარაგებს ფორმულით max(50% of (RAM - 1 GB), 256 MB):

სერვერის RAMნაგულისხმევი WiredTiger ქეში
1 GB256 MB
2 GB512 MB
4 GB1.5 GB
8 GB3.5 GB
14 GB6.5 GB

დანარჩენი მეხსიერება კავშირებს, აგრეგაციის framework-ს, ინდექსების აგებას და ოპერაციული სისტემის საკუთარ page cache-ს ეკუთვნის, რასაც MongoDB-ც სარგებლობს. დააყენე storage.wiredTiger.engineConfig.cacheSizeGB პირდაპირ, თუ გინდა მნიშვნელობაში დარწმუნებული იყო და არა გამოთვლაში იმისა, რაც პროცესს მანქანის შესახებ ჰგონია.

ზომა განსაზღვრე ინდექსებით და არა მონაცემებით. 40 GB კოლექცია, რომელსაც მხოლოდ _id-ით ეკითხებიან, პატარა სერვერზე კომფორტულია; 4 GB კოლექცია ექვსი ინდექსითა და ანალიტიკური query-ებით არა. db.stats().indexSize გაძლევს რიცხვს, რომელიც მნიშვნელოვანია, და თუ ის ქეშზე დიდია, ჩვეულებრივ query-ებზე დისკის წაკითხვები უნდა ელოდო.

ამოწურვა მოხდენილი არ არის. RE:NODE-ზე კონტეინერი, რომელიც მეხსიერების ლიმიტს აღწევს, kernel-ის მიერ ჩერდება და swap-ში დატოვების ნაცვლად სუფთად იტვირთება, რაც ბაზისთვის გაწყვეტილ კავშირებსა და ცივ ქეშს ნიშნავს და არა მანქანას, რომელიც ნელ-ნელა წყვეტს პასუხს. ეს უკეთესი მარცხია, მაგრამ მაინც მარცხია, ამიტომ დატოვე მარაგი, აპლიკაციაში pool-ების ზომები რეალისტური გქონდეს - connection pool-ები და ლიმიტები შეიცავს არითმეტიკას - და გადადი მომდევნო დონეზე ქეშის გავსებამდე და არა მის შემდეგ. პანელის მეხსიერების გრაფიკი ლიმიტის წინააღმდეგ არის რიცხვი, რომელსაც უნდა უყურო.

FAQ#

ჩავაშენო თუ მივუთითო?

ჩააშენე, როცა შვილობილი მონაცემები მშობელთან ერთად იკითხება, მხოლოდ მშობელს ეკუთვნის და ცნობილი მაქსიმალური ზომა აქვს. მიუთითე, როცა ის ცალკე იკითხება, მშობლებს შორის გაზიარებულია ან შეუზღუდავია. როცა ორივე დასაცავია, ჩააშენე - ერთი წაკითხვა ორზე იაფია და მასივის გატანა მოგვიანებითაც შეგიძლია.

რამდენი ინდექსია ბევრი?

ერთი რიცხვი არ არსებობს, მაგრამ თუ კოლექციას query-ის ფორმებზე მეტი ინდექსი აქვს, ზოგიერთი გამოუყენებელია. გაუშვი $indexStats, იპოვე ისინი, ვისაც ბოლო restart-იდან ნულოვანი წვდომა აქვს, ერთი კვირა დამალე და წაშალე. ჩაწერები უფრო სწრაფი ხდება და restore-ები უფრო მოკლე.

რატომ არის ჩემი query ნელი, თუმცა ინდექსი შევქმენი?

ჩვეულებრივ ველების რიგის გამო. ინდექსი { createdAt: -1, status: 1 } ეფექტურად ვერ ემსახურება query-ს, რომელიც status-ზე ფილტრავს და createdAt-ით ალაგებს, რადგან equality ველი prefix არ არის. გაუშვი explain("executionStats") და შეადარე totalDocsExamined nReturned-ს; თუ სხვაობა დიდია, ინდექსი ის არ არის, რაც query-ს სჭირდება.

სჭირდება MongoDB-ს სქემა?

სერვერი მას არ აიძულებს, თუ JSON Schema validator-ს არ დაამატებ, მაგრამ შენს აპლიკაციას ის აქვს, ჩაწერე თუ არა. დაამატე validator, როცა ფორმა დასტაბილურდება, warn რეჟიმით დაწყებული, და დაამატე schemaVersion ველი, რომ აზრის შეცვლა მოგვიანებით ფონური სამუშაო იყოს და არა გათიშვა.

რას ნიშნავს ჩემთვის 16 MB დოკუმენტის ლიმიტი?

იმას, რომ დოკუმენტის შიგნით ნებისმიერ მასივს ჭერი სჭირდება, რომლის დასახელებაც შეგიძლია. 16 MB-მდე ბევრად ადრე დიდი დოკუმენტების განახლება ძვირია, რადგან მთელი დოკუმენტი თავიდან იწერება. გაიტანე ყველაფერი, რაც განუსაზღვრელად იზრდება, საკუთარ კოლექციაში, მშობლის იდენტიფიკატორზე ინდექსით.

რამდენი RAM სჭირდება ჩემს MongoDB სერვერს?

საკმარისი შენი ინდექსებისთვის პლუს დოკუმენტებისთვის, რომლებსაც რეგულარულად კითხულობ. შეამოწმე db.stats().indexSize თითო მონაცემთა ბაზაზე, დაამატე მონაცემების ის ნაწილი, რომელიც რეალურად ცხელია, და შეადარე ზემოთ მოცემული ცხრილის ქეშის მნიშვნელობებს. თუ მარტო ინდექსები ქეშზე დიდია, მომდევნო დონე უფრო იაფია, ვიდრე latency, რომელსაც იხდი.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000