यदि किसी संग्रह में अनुक्रमिक अनुक्रमित मानों वाले दस्तावेज़ शामिल हैं, तो क्लाउड फायरस्टोर लेखन दर को 500 लेखन प्रति सेकंड तक सीमित कर देता है। यह पृष्ठ वर्णन करता है कि इस सीमा को पार करने के लिए दस्तावेज़ फ़ील्ड को कैसे शार्प किया जाए। सबसे पहले, आइए परिभाषित करें कि "अनुक्रमिक अनुक्रमित फ़ील्ड" से हमारा क्या मतलब है और स्पष्ट करें कि यह सीमा कब लागू होती है।
अनुक्रमिक अनुक्रमित फ़ील्ड
"अनुक्रमिक अनुक्रमित फ़ील्ड" का अर्थ दस्तावेज़ों का कोई भी संग्रह है जिसमें एक समान रूप से बढ़ने या घटने वाले अनुक्रमित फ़ील्ड शामिल हैं। कई मामलों में, इसका मतलब एक timestamp
फ़ील्ड है, लेकिन कोई भी नीरस रूप से बढ़ने या घटने वाला फ़ील्ड मान 500 राइट्स प्रति सेकंड की लेखन सीमा को ट्रिगर कर सकता है।
उदाहरण के लिए, यदि ऐप userid
userid
मान निर्दिष्ट करता है तो सीमा अनुक्रमित फ़ील्ड उपयोगकर्ता आईडी वाले user
दस्तावेज़ों के संग्रह पर लागू होती है:
-
1281, 1282, 1283, 1284, 1285, ...
दूसरी ओर, सभी timestamp
फ़ील्ड इस सीमा को ट्रिगर नहीं करते हैं। यदि timestamp
फ़ील्ड बेतरतीब ढंग से वितरित मानों को ट्रैक करता है, तो लिखने की सीमा लागू नहीं होती है। फ़ील्ड का वास्तविक मूल्य भी मायने नहीं रखता, केवल यह कि फ़ील्ड एकरस रूप से बढ़ रही है या घट रही है। उदाहरण के लिए, नीरस रूप से बढ़ते फ़ील्ड मानों के निम्नलिखित दोनों सेट लेखन सीमा को ट्रिगर करते हैं:
-
100000, 100001, 100002, 100003, ...
-
0, 1, 2, 3, ...
टाइमस्टैम्प फ़ील्ड साझा करना
मान लें कि आपका ऐप एक नीरस रूप से बढ़ते timestamp
फ़ील्ड का उपयोग करता है। यदि आपका ऐप किसी भी प्रश्न में timestamp
फ़ील्ड का उपयोग नहीं करता है, तो आप टाइमस्टैम्प फ़ील्ड को अनुक्रमित न करके प्रति सेकंड 500 लिखने की सीमा को हटा सकते हैं। यदि आपको अपने प्रश्नों के लिए timestamp
फ़ील्ड की आवश्यकता है, तो आप शार्डेड टाइमस्टैम्प का उपयोग करके सीमा के आसपास काम कर सकते हैं:
-
timestamp
फ़ील्ड के साथ एकshard
फ़ील्ड जोड़ें।shard
फ़ील्ड के लिए1..n
विशिष्ट मानों का उपयोग करें। इससे संग्रह के लिए लिखने की सीमा500*n
तक बढ़ जाती है, लेकिन आपकोn
प्रश्नों को एकत्रित करना होगा। - प्रत्येक दस्तावेज़ को बेतरतीब ढंग से एक
shard
मान निर्दिष्ट करने के लिए अपने लेखन तर्क को अपडेट करें। - साझा परिणाम सेटों को एकत्रित करने के लिए अपनी क्वेरीज़ को अपडेट करें।
-
shard
फ़ील्ड औरtimestamp
फ़ील्ड दोनों के लिए सिंगल-फ़ील्ड इंडेक्स अक्षम करें। मौजूदा समग्र अनुक्रमणिका को हटाएं जिसमेंtimestamp
फ़ील्ड शामिल है। - अपने अद्यतन प्रश्नों का समर्थन करने के लिए नई समग्र अनुक्रमणिकाएँ बनाएँ। इंडेक्स में फ़ील्ड का क्रम मायने रखता है, और
shard
फ़ील्ड कोtimestamp
फ़ील्ड से पहले आना चाहिए। कोई भी इंडेक्स जिसमेंtimestamp
फ़ील्ड शामिल है, उसमेंshard
फ़ील्ड भी शामिल होना चाहिए।
आपको शार्प्ड टाइमस्टैम्प को केवल उन उपयोग के मामलों में लागू करना चाहिए जिनमें प्रति सेकंड 500 से ऊपर निरंतर लेखन दर हो। अन्यथा, यह एक पूर्व-परिपक्व अनुकूलन है। timestamp
फ़ील्ड को साझा करने से 500 राइट्स प्रति सेकंड का प्रतिबंध हट जाता है, लेकिन क्लाइंट-साइड क्वेरी एकत्रीकरण की आवश्यकता समाप्त हो जाती है।
निम्नलिखित उदाहरण दिखाते हैं कि timestamp
फ़ील्ड को कैसे शार्प किया जाए और शार्प किए गए परिणाम सेट को कैसे क्वेरी किया जाए।
उदाहरण डेटा मॉडल और क्वेरीज़
उदाहरण के तौर पर, मुद्राओं, सामान्य स्टॉक और ईटीएफ जैसे वित्तीय साधनों के वास्तविक समय के विश्लेषण के लिए एक ऐप की कल्पना करें। यह ऐप instruments
संग्रह के लिए दस्तावेज़ इस प्रकार लिखता है:
नोड.जे.एस
async function insertData() { const instruments = [ { symbol: 'AAA', price: { currency: 'USD', micros: 34790000 }, exchange: 'EXCHG1', instrumentType: 'commonstock', timestamp: Timestamp.fromMillis( Date.parse('2019-01-01T13:45:23.010Z')) }, { symbol: 'BBB', price: { currency: 'JPY', micros: 64272000000 }, exchange: 'EXCHG2', instrumentType: 'commonstock', timestamp: Timestamp.fromMillis( Date.parse('2019-01-01T13:45:23.101Z')) }, { symbol: 'Index1 ETF', price: { currency: 'USD', micros: 473000000 }, exchange: 'EXCHG1', instrumentType: 'etf', timestamp: Timestamp.fromMillis( Date.parse('2019-01-01T13:45:23.001Z')) } ]; const batch = fs.batch(); for (const inst of instruments) { const ref = fs.collection('instruments').doc(); batch.set(ref, inst); } await batch.commit(); }
यह ऐप timestamp
फ़ील्ड द्वारा निम्नलिखित क्वेरीज़ और ऑर्डर चलाता है:
नोड.जे.एस
function createQuery(fieldName, fieldOperator, fieldValue, limit = 5) { return fs.collection('instruments') .where(fieldName, fieldOperator, fieldValue) .orderBy('timestamp', 'desc') .limit(limit) .get(); } function queryCommonStock() { return createQuery('instrumentType', '==', 'commonstock'); } function queryExchange1Instruments() { return createQuery('exchange', '==', 'EXCHG1'); } function queryUSDInstruments() { return createQuery('price.currency', '==', 'USD'); }
insertData() .then(() => { const commonStock = queryCommonStock() .then( (docs) => { console.log('--- queryCommonStock: '); docs.forEach((doc) => { console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`); }); } ); const exchange1Instruments = queryExchange1Instruments() .then( (docs) => { console.log('--- queryExchange1Instruments: '); docs.forEach((doc) => { console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`); }); } ); const usdInstruments = queryUSDInstruments() .then( (docs) => { console.log('--- queryUSDInstruments: '); docs.forEach((doc) => { console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`); }); } ); return Promise.all([commonStock, exchange1Instruments, usdInstruments]); });
कुछ शोध के बाद, आप यह निर्धारित करते हैं कि ऐप को प्रति सेकंड 1,000 से 1,500 उपकरण अपडेट प्राप्त होंगे। यह अनुक्रमित टाइमस्टैम्प फ़ील्ड वाले दस्तावेज़ों वाले संग्रह के लिए अनुमत 500 राइट्स प्रति सेकंड से अधिक है। राइट थ्रूपुट बढ़ाने के लिए, आपको 3 शार्ड मानों की आवश्यकता है, MAX_INSTRUMENT_UPDATES/500 = 3
। यह उदाहरण शार्ड मान x
, y
, और z
का उपयोग करता है। आप अपने शार्ड मानों के लिए संख्याओं या अन्य वर्णों का भी उपयोग कर सकते हैं।
एक शार्ड फ़ील्ड जोड़ना
अपने दस्तावेज़ों में एक shard
फ़ील्ड जोड़ें। shard
फ़ील्ड को मान x
, y
, या z
पर सेट करें जो संग्रह पर लिखने की सीमा को प्रति सेकंड 1,500 लिखने तक बढ़ाता है।
नोड.जे.एस
// Define our 'K' shard values const shards = ['x', 'y', 'z']; // Define a function to help 'chunk' our shards for use in queries. // When using the 'in' query filter there is a max number of values that can be // included in the value. If our number of shards is higher than that limit // break down the shards into the fewest possible number of chunks. function shardChunks() { const chunks = []; let start = 0; while (start < shards.length) { const elements = Math.min(MAX_IN_VALUES, shards.length - start); const end = start + elements; chunks.push(shards.slice(start, end)); start = end; } return chunks; } // Add a convenience function to select a random shard function randomShard() { return shards[Math.floor(Math.random() * Math.floor(shards.length))]; }
async function insertData() { const instruments = [ { shard: randomShard(), // add the new shard field to the document symbol: 'AAA', price: { currency: 'USD', micros: 34790000 }, exchange: 'EXCHG1', instrumentType: 'commonstock', timestamp: Timestamp.fromMillis( Date.parse('2019-01-01T13:45:23.010Z')) }, { shard: randomShard(), // add the new shard field to the document symbol: 'BBB', price: { currency: 'JPY', micros: 64272000000 }, exchange: 'EXCHG2', instrumentType: 'commonstock', timestamp: Timestamp.fromMillis( Date.parse('2019-01-01T13:45:23.101Z')) }, { shard: randomShard(), // add the new shard field to the document symbol: 'Index1 ETF', price: { currency: 'USD', micros: 473000000 }, exchange: 'EXCHG1', instrumentType: 'etf', timestamp: Timestamp.fromMillis( Date.parse('2019-01-01T13:45:23.001Z')) } ]; const batch = fs.batch(); for (const inst of instruments) { const ref = fs.collection('instruments').doc(); batch.set(ref, inst); } await batch.commit(); }
शार्प्ड टाइमस्टैम्प को क्वेरी करना
shard
फ़ील्ड जोड़ने के लिए आवश्यक है कि आप अपने प्रश्नों को समग्र शार्ड परिणामों के लिए अद्यतन करें:
नोड.जे.एस
function createQuery(fieldName, fieldOperator, fieldValue, limit = 5) { // For each shard value, map it to a new query which adds an additional // where clause specifying the shard value. return Promise.all(shardChunks().map(shardChunk => { return fs.collection('instruments') .where('shard', 'in', shardChunk) // new shard condition .where(fieldName, fieldOperator, fieldValue) .orderBy('timestamp', 'desc') .limit(limit) .get(); })) // Now that we have a promise of multiple possible query results, we need // to merge the results from all of the queries into a single result set. .then((snapshots) => { // Create a new container for 'all' results const docs = []; snapshots.forEach((querySnapshot) => { querySnapshot.forEach((doc) => { // append each document to the new all container docs.push(doc); }); }); if (snapshots.length === 1) { // if only a single query was returned skip manual sorting as it is // taken care of by the backend. return docs; } else { // When multiple query results are returned we need to sort the // results after they have been concatenated. // // since we're wanting the `limit` newest values, sort the array // descending and take the first `limit` values. By returning negated // values we can easily get a descending value. docs.sort((a, b) => { const aT = a.data().timestamp; const bT = b.data().timestamp; const secondsDiff = aT.seconds - bT.seconds; if (secondsDiff === 0) { return -(aT.nanoseconds - bT.nanoseconds); } else { return -secondsDiff; } }); return docs.slice(0, limit); } }); } function queryCommonStock() { return createQuery('instrumentType', '==', 'commonstock'); } function queryExchange1Instruments() { return createQuery('exchange', '==', 'EXCHG1'); } function queryUSDInstruments() { return createQuery('price.currency', '==', 'USD'); }
insertData() .then(() => { const commonStock = queryCommonStock() .then( (docs) => { console.log('--- queryCommonStock: '); docs.forEach((doc) => { console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`); }); } ); const exchange1Instruments = queryExchange1Instruments() .then( (docs) => { console.log('--- queryExchange1Instruments: '); docs.forEach((doc) => { console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`); }); } ); const usdInstruments = queryUSDInstruments() .then( (docs) => { console.log('--- queryUSDInstruments: '); docs.forEach((doc) => { console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`); }); } ); return Promise.all([commonStock, exchange1Instruments, usdInstruments]); });
सूचकांक परिभाषाएँ अद्यतन करें
500 राइट्स प्रति सेकंड की बाधा को दूर करने के लिए, मौजूदा एकल-फ़ील्ड और मिश्रित इंडेक्स को हटा दें जो timestamp
फ़ील्ड का उपयोग करते हैं।
समग्र सूचकांक परिभाषाएँ हटाएँ
फायरबेस कंसोल
फायरबेस कंसोल में क्लाउड फायरस्टोर कंपोजिट इंडेक्स पेज खोलें।
timestamp
फ़ील्ड वाले प्रत्येक इंडेक्स के लिए, बटन पर क्लिक करें और हटाएं पर क्लिक करें।
जीसीपी कंसोल
Google क्लाउड प्लेटफ़ॉर्म कंसोल में, डेटाबेस पृष्ठ पर जाएँ।
डेटाबेस की सूची से आवश्यक डेटाबेस का चयन करें।
नेविगेशन मेनू में, इंडेक्स पर क्लिक करें और फिर कंपोजिट टैब पर क्लिक करें।
timestamp
फ़ील्ड वाली इंडेक्स परिभाषाओं को खोजने के लिए फ़िल्टर फ़ील्ड का उपयोग करें।इनमें से प्रत्येक अनुक्रमणिका के लिए,
बटन पर क्लिक करें और हटाएं पर क्लिक करें।
फायरबेस सीएलआई
- यदि आपने फायरबेस सीएलआई सेट नहीं किया है, तो सीएलआई स्थापित करने और
firebase init
कमांड चलाने के लिए इन निर्देशों का पालन करें ।init
कमांड के दौरान,Firestore: Deploy rules and create indexes for Firestore
। - सेटअप के दौरान, फायरबेस सीएलआई आपकी मौजूदा इंडेक्स परिभाषाओं को डिफ़ॉल्ट रूप से,
firestore.indexes.json
नामक फ़ाइल में डाउनलोड करता है। उदाहरण के लिए,
timestamp
फ़ील्ड वाली किसी भी इंडेक्स परिभाषा को हटा दें:{ "indexes": [ // Delete composite index definition that contain the timestamp field { "collectionGroup": "instruments", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "exchange", "order": "ASCENDING" }, { "fieldPath": "timestamp", "order": "DESCENDING" } ] }, { "collectionGroup": "instruments", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "instrumentType", "order": "ASCENDING" }, { "fieldPath": "timestamp", "order": "DESCENDING" } ] }, { "collectionGroup": "instruments", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "price.currency", "order": "ASCENDING" }, { "fieldPath": "timestamp", "order": "DESCENDING" } ] }, ] }
अपनी अद्यतन अनुक्रमणिका परिभाषाएँ परिनियोजित करें:
firebase deploy --only firestore:indexes
एकल-फ़ील्ड अनुक्रमणिका परिभाषाएँ अद्यतन करें
फायरबेस कंसोल
फायरबेस कंसोल में क्लाउड फायरस्टोर सिंगल फील्ड इंडेक्स पेज खोलें।
छूट जोड़ें पर क्लिक करें.
संग्रह आईडी के लिए,
instruments
दर्ज करें। फ़ील्ड पथ के लिए,timestamp
दर्ज करें।क्वेरी स्कोप के अंतर्गत, संग्रह और संग्रह समूह दोनों का चयन करें।
अगला पर क्लिक करें
सभी इंडेक्स सेटिंग्स को अक्षम पर टॉगल करें। सहेजें पर क्लिक करें.
shard
फ़ील्ड के लिए समान चरणों को दोहराएं।
जीसीपी कंसोल
Google क्लाउड प्लेटफ़ॉर्म कंसोल में, डेटाबेस पृष्ठ पर जाएँ।
डेटाबेस की सूची से आवश्यक डेटाबेस का चयन करें।
नेविगेशन मेनू में, इंडेक्स पर क्लिक करें और फिर सिंगल फ़ील्ड टैब पर क्लिक करें।
सिंगल फील्ड टैब पर क्लिक करें।
छूट जोड़ें पर क्लिक करें.
संग्रह आईडी के लिए,
instruments
दर्ज करें। फ़ील्ड पथ के लिए,timestamp
दर्ज करें।क्वेरी स्कोप के अंतर्गत, संग्रह और संग्रह समूह दोनों का चयन करें।
अगला पर क्लिक करें
सभी इंडेक्स सेटिंग्स को अक्षम पर टॉगल करें। सहेजें पर क्लिक करें.
shard
फ़ील्ड के लिए समान चरणों को दोहराएं।
फायरबेस सीएलआई
अपनी अनुक्रमणिका परिभाषा फ़ाइल के
fieldOverrides
अनुभाग में निम्नलिखित जोड़ें:{ "fieldOverrides": [ // Disable single-field indexing for the timestamp field { "collectionGroup": "instruments", "fieldPath": "timestamp", "indexes": [] }, ] }
अपनी अद्यतन अनुक्रमणिका परिभाषाएँ परिनियोजित करें:
firebase deploy --only firestore:indexes
नए समग्र सूचकांक बनाएं
timestamp
वाले सभी पिछले इंडेक्स को हटाने के बाद, आपके ऐप के लिए आवश्यक नए इंडेक्स को परिभाषित करें। timestamp
फ़ील्ड वाले किसी भी इंडेक्स में shard
फ़ील्ड भी शामिल होना चाहिए। उदाहरण के लिए, उपरोक्त प्रश्नों का समर्थन करने के लिए, निम्नलिखित अनुक्रमणिकाएँ जोड़ें:
संग्रह | फ़ील्ड अनुक्रमित | क्वेरी का दायरा |
---|---|---|
उपकरण | शार्ड, प्राइस.मुद्रा, टाइमस्टैम्प | संग्रह |
उपकरण | शार्ड, एक्सचेंज, टाइमस्टैम्प | संग्रह |
उपकरण | शार्ड, इंस्ट्रूमेंट टाइप, टाइमस्टैम्प | संग्रह |
त्रुटि संदेश
आप अद्यतन क्वेरीज़ चलाकर इन अनुक्रमणिका का निर्माण कर सकते हैं।
प्रत्येक क्वेरी फायरबेस कंसोल में आवश्यक इंडेक्स बनाने के लिए एक लिंक के साथ एक त्रुटि संदेश लौटाती है।
फायरबेस सीएलआई
अपनी अनुक्रमणिका परिभाषा फ़ाइल में निम्नलिखित अनुक्रमणिका जोड़ें:
{ "indexes": [ // New indexes for sharded timestamps { "collectionGroup": "instruments", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "shard", "order": "DESCENDING" }, { "fieldPath": "exchange", "order": "ASCENDING" }, { "fieldPath": "timestamp", "order": "DESCENDING" } ] }, { "collectionGroup": "instruments", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "shard", "order": "DESCENDING" }, { "fieldPath": "instrumentType", "order": "ASCENDING" }, { "fieldPath": "timestamp", "order": "DESCENDING" } ] }, { "collectionGroup": "instruments", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "shard", "order": "DESCENDING" }, { "fieldPath": "price.currency", "order": "ASCENDING" }, { "fieldPath": "timestamp", "order": "DESCENDING" } ] }, ] }
अपनी अद्यतन अनुक्रमणिका परिभाषाएँ परिनियोजित करें:
firebase deploy --only firestore:indexes
सीमा अनुक्रमिक अनुक्रमित फ़ील्ड के लिए लेखन को समझना
अनुक्रमिक अनुक्रमित फ़ील्ड के लिए लेखन दर की सीमा इस बात से आती है कि क्लाउड फायरस्टोर इंडेक्स मानों को कैसे संग्रहीत करता है और इंडेक्स लेखन को मापता है। प्रत्येक इंडेक्स लिखने के लिए, क्लाउड फायरस्टोर एक कुंजी-मूल्य प्रविष्टि को परिभाषित करता है जो दस्तावेज़ नाम और प्रत्येक अनुक्रमित फ़ील्ड के मान को जोड़ता है। क्लाउड फायरस्टोर इन इंडेक्स प्रविष्टियों को टैबलेट नामक डेटा के समूहों में व्यवस्थित करता है। प्रत्येक क्लाउड फायरस्टोर सर्वर में एक या अधिक टैबलेट होते हैं। जब किसी विशेष टैबलेट पर लिखने का भार बहुत अधिक हो जाता है, तो क्लाउड फायरस्टोर टैबलेट को छोटे टैबलेट में विभाजित करके और नए टैबलेट को विभिन्न क्लाउड फायरस्टोर सर्वर पर फैलाकर क्षैतिज रूप से स्केल करता है।
क्लाउड फायरस्टोर एक ही टैबलेट पर लेक्सिकोग्राफ़िक रूप से बंद इंडेक्स प्रविष्टियाँ रखता है। यदि किसी टैबलेट में इंडेक्स मान एक-दूसरे के बहुत करीब हैं, जैसे कि टाइमस्टैम्प फ़ील्ड के लिए, तो क्लाउड फायरस्टोर टैबलेट को कुशलतापूर्वक छोटे टैबलेट में विभाजित नहीं कर सकता है। यह एक हॉट स्पॉट बनाता है जहां एक ही टैबलेट को बहुत अधिक ट्रैफ़िक प्राप्त होता है, और हॉट स्पॉट पर पढ़ने और लिखने का कार्य धीमा हो जाता है।
टाइमस्टैम्प फ़ील्ड को साझा करके, आप क्लाउड फायरस्टोर के लिए कई टैबलेट में कार्यभार को कुशलतापूर्वक विभाजित करना संभव बनाते हैं। हालाँकि टाइमस्टैम्प फ़ील्ड के मान एक साथ करीब रह सकते हैं, संयोजित शार्ड और इंडेक्स मान क्लाउड फायरस्टोर को इंडेक्स प्रविष्टियों के बीच प्रविष्टियों को कई टैबलेट के बीच विभाजित करने के लिए पर्याप्त स्थान देते हैं।
आगे क्या होगा
- पैमाने के लिए डिज़ाइन करने की सर्वोत्तम प्रक्रियाएँ पढ़ें
- एकल दस्तावेज़ में उच्च लेखन दर वाले मामलों के लिए, डिस्ट्रब्ड काउंटर देखें
- क्लाउड फायरस्टोर के लिए मानक सीमाएँ देखें