Parçalı zaman damgaları

Bir koleksiyon sıralı olarak dizine eklenmiş değerlere sahip dokümanlar içeriyorsa Cloud Firestore, yazma hızını saniyede 500 yazma işlemiyle sınırlandırır. Bu sayfa , bu sınırı aşmak için bir belge alanının nasıl kırılacağını açıklar. İlk olarak "sıralı dizine eklenmiş alanlar" ile ne kastettiğimizi açıklayın ve bu sınırın ne zaman geçerli olur.

Dizine eklenen sıralı alanlar

"Sıralı dizine eklenen alanlar" içinde herhangi bir belgenin bulunması durumunda dizine eklenen alanda tekdüze bir şekilde artan veya azalan. Birçok durumda bu, timestamp alanı ancak alan değeri monoton olarak artan veya azalan tüm alanlar saniyede 500 yazma sınırını tetikleyebilir.

Örneğin, sınır şu türde user dokümandan oluşan bir koleksiyon için geçerlidir: Uygulama aşağıdaki gibi userid değerleri atarsa dizine eklenen userid alanı:

  • 1281, 1282, 1283, 1284, 1285, ...

Diğer yandan, tüm timestamp alanları bu sınırı tetiklemez. timestamp alan adı rastgele dağıtılan değerleri izler, yazma sınırı geçerlidir. Alanın gerçek değeri de değil, yalnızca alanın değeri monoton olarak artıyor veya azalıyor. Örneğin, aşağıdaki monoton olarak artan alan değeri kümelerinin her ikisi de yazma sınırı:

  • 100000, 100001, 100002, 100003, ...
  • 0, 1, 2, 3, ...

Zaman damgası alanını parçalama

Uygulamanızın monoton olarak artan bir timestamp alanı kullandığını varsayalım. Uygulamanız hiçbir sorguda timestamp alanını kullanmıyorsa şunu kaldırabilirsiniz: Zaman damgası alanı dizine eklenmediğinden saniyede 500 yazma sınırı. CANNOT TRANSLATE timestamp alanı gerektiriyorsa parçalanmış zaman damgaları kullanılarak:

  1. timestamp alanının yanına shard alanı ekleyin. 1..n farklı kullan değerleri (shard alanı için) Böylece kullanıcılar daha çok sınırı 500*n olarak ayarlandı, ancak n sorguyu toplamanız gerekiyor.
  2. Yazma mantığınızı, her birine rastgele bir shard değeri atayacak şekilde güncelleyin. uygulayacaksınız.
  3. Sorgularınızı, parçalanmış sonuç kümelerini toplayacak şekilde güncelleyin.
  4. Hem shard alanı hem de timestamp için tek alanlı dizinleri devre dışı bırakın girin. timestamp öğesini içeren mevcut bileşik dizinleri silin girin.
  5. Güncellenen sorgularınızı desteklemek için yeni birleşik dizinler oluşturun. Sıra, dizindeki alanlar önemlidir ve shard alanı, timestamp alanı. timestamp alanı, shard alanını da içermelidir.

Parçalanmış zaman damgalarını yalnızca sürekli saniyede 500'den fazla yazma hızına sahiptir. Aksi takdirde bu, ortaya çıkmış olabilir. Bir timestamp alanının parçalanması 500 yazma işlemini kaldırır Bu kısıtlama, isteğe bağlı olarak istemci taraflı sorguya ihtiyaç için kullanılır.

Aşağıdaki örneklerde bir timestamp alanının nasıl parçalanacağı ve bir parçalanmış sonuç kümesi.

Örnek veri modeli ve sorgular

Örneğin, Yeşil Ofis’te sunulan finansal verilerin neredeyse gerçek zamanlı analizine farklı enstrümanlar kullanabilirsiniz. Bu uygulama yazıyor aşağıdaki gibi bir instruments koleksiyonuna gönderebilirsiniz:

Node.js
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();
}

Bu uygulama, timestamp alanına göre aşağıdaki sorguları ve siparişleri çalıştırır:

Node.js
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]);
    });

Biraz araştırma yaptıktan sonra, Saniyede 1.000 ve 1.500 araç güncellemesi. Bu, her birinde 500 yazmadan daha fazladır dizine eklenen zaman damgasına sahip dokümanlar içeren koleksiyonlar için saniye cinsinden izin alanları. Yazma işleme hızını artırmak için 3 shard değerine ihtiyacınız vardır: MAX_INSTRUMENT_UPDATES/500 = 3 Bu örnekte x kırık değerleri kullanılmaktadır, y ve z. Ayrıca kırığınız için sayı veya başka karakterler de kullanabilirsiniz değerler.

Kırık alanı ekleme

Dokümanlarınıza shard alanı ekleyin. shard alanını ayarlayın Koleksiyondaki yazma sınırını yükselten x, y veya z değerlerine saniyede 1.500 yazma işlemine çıktı.

Node.js
// 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();
}

Parçalı zaman damgasını sorgulama

shard alanı eklemek için sorgularınızı veri toplayacak şekilde güncellemeniz gerekir parçalanmış sonuçlar:

Node.js
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]);
    });

Dizin tanımlarını güncelleme

Saniyede 500 yazma kısıtlamasını kaldırmak için mevcut tek alanı silin. ve timestamp alanını kullanan bileşik dizinler.

Bileşik dizin tanımlarını silin

Firebase Konsolu

  1. Firebase konsolunda Cloud Firestore Birleşik Dizinler sayfasını açın.

    Bileşik Dizinler'e git

  2. timestamp alanını içeren her dizin için düğmesini ve Sil'i tıklayın.

GCP Console

  1. Google Cloud Platform Console'da Veritabanları sayfasına gidin.

    Veritabanlarına Git

  2. Veritabanı listesinden gerekli veritabanını seçin.

  3. Gezinme menüsünde Dizinler'i, ardından Bileşik sekmesini tıklayın.

  4. Filter alanını kullanarak timestamp alanı için geçerlidir.

  5. Bu dizinlerin her biri için simgesini tıklayın. düğmesini ve Sil'i tıklayın.

Firebase CLI

  1. Firebase CLI'ı ayarlamadıysanız bu talimatları uygulayarak KSA ve firebase init komutunu çalıştırın. init komutu sırasında Firestore: Deploy rules and create indexes for Firestore seçeneğini belirlediğinizden emin olun.
  2. Firebase CLI, kurulum sırasında mevcut dizin tanımlarınızı aşağıdaki klasöre indirir varsayılan olarak firestore.indexes.json adlı bir dosya oluşturulur.
  3. için timestamp alanını içeren tüm dizin tanımlarını kaldırın örnek:

    {
    "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"
          }
        ]
      },
     ]
    }
    
  4. Güncellenen dizin tanımlarınızı dağıtın:

    firebase deploy --only firestore:indexes
    

Tek alanlı dizin tanımlarını güncelleme

Firebase Konsolu

  1. Cloud Firestore Tek Alan Dizinleri sayfasını Firebase konsolu.

    Tek Alan Dizinleri'ne git

  2. Muafiyet Ekle'yi tıklayın.

  3. Koleksiyon Kimliği için instruments girin. Alan yolu için, timestamp girin.

  4. Sorgu kapsamı altında, hem Koleksiyon'u hem de Koleksiyon grubu.

  5. Sonraki'yi tıklayın.

  6. Tüm dizin ayarlarını Devre dışı yapın. Kaydet'i tıklayın.

  7. Aynı adımları shard alanı için tekrarlayın.

GCP Console

  1. Google Cloud Platform Console'da Veritabanları sayfasına gidin.

    Veritabanlarına Git

  2. Veritabanı listesinden gerekli veritabanını seçin.

  3. Gezinme menüsünde Dizinler'i ve ardından Tek Alan sekmesini tıklayın.

  4. Tek Alan sekmesini tıklayın.

  5. Muafiyet Ekle'yi tıklayın.

  6. Koleksiyon Kimliği için instruments girin. Alan yolu için, timestamp girin.

  7. Sorgu kapsamı altında, hem Koleksiyon'u hem de Koleksiyon grubu.

  8. Sonraki'yi tıklayın.

  9. Tüm dizin ayarlarını Devre dışı yapın. Kaydet'i tıklayın.

  10. Aynı adımları shard alanı için tekrarlayın.

Firebase CLI

  1. Aşağıdakileri, dizin tanımlarınızın fieldOverrides bölümüne ekleyin. dosya:

    {
     "fieldOverrides": [
       // Disable single-field indexing for the timestamp field
       {
         "collectionGroup": "instruments",
         "fieldPath": "timestamp",
         "indexes": []
       },
     ]
    }
    
  2. Güncellenen dizin tanımlarınızı dağıtın:

    firebase deploy --only firestore:indexes
    

Yeni birleşik dizinler oluşturun

timestamp öğesini içeren önceki tüm dizinleri kaldırdıktan sonra, uygulamanızın gerektirdiği yeni dizinleri tanımlayın. timestamp alanı, shard alanını da içermelidir. Örneğin, yukarıdaki sorgulara aşağıdaki dizinleri ekleyin:

Toplama Dizine eklenen alanlar Sorgu kapsamı
enstrümanlar parça, price.currency, zaman damgası Toplama
enstrümanlar parça, exchange, zaman damgası Toplama
enstrümanlar parça, instrumentType, zaman damgası Toplama

Hata Mesajları

Bu dizinleri, güncellenen sorguları çalıştırarak oluşturabilirsiniz.

Her sorgu, gerekli dizini oluşturun.

Firebase CLI

  1. Dizin tanım dosyanıza aşağıdaki dizinleri ekleyin:

     {
       "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"
             }
           ]
         },
       ]
     }
    
  2. Güncellenen dizin tanımlarınızı dağıtın:

    firebase deploy --only firestore:indexes
    

Sıralı dizine eklenmiş alanları sınırlama için yazma işlemini anlama

Sıralı dizine eklenmiş alanlar için yazma hızı sınırı, Cloud Firestore, dizin değerlerini depolar ve dizin yazma işlemlerini ölçeklendirir. Her bir dizine ekleme işlemi yaptığınızda, Cloud Firestore, bilgileri depolayarak dokümanın adı ve dizine eklenen her alanın değeri. Cloud Firestore bu dizin girişlerini tablet adı verilen veri grupları halinde düzenler. Her biri Cloud Firestore sunucusu bir veya daha fazla tablet barındırır. Yazma işlemi ne zaman bir tablet çok yükseldiğinde Cloud Firestore yatay olarak ölçeklendirilir tableti daha küçük tabletlere bölüp yeni tabletleri dağıtarak kolayca uygulayabilirsiniz.

Cloud Firestore, aynı tablete yerleştiriyoruz. Bir tabletteki dizin değerleri birbirine çok yakınsa, örneğin zaman damgası alanları, Cloud Firestore verimli bir şekilde bölünemez daha küçük tabletlere dönüştürüyor. Bu şekilde tek bir tabletin çok fazla trafik aldığı için sabit sunucuya okuma ve yazma işlemleri spot ağırlığı yavaşlar.

Bir zaman damgası alanını parçalayarak Cloud Firestore'un iş yüklerini birden fazla kullanıcıya etkili bir şekilde bölmesini sağlar. tabletler. Zaman damgası alanının değerleri birbirine yakın kalabilse de birleştirilen parça ve dizin değeri, Cloud Firestore'a yeterli alan sağlar dizin girişleri arasında, girişleri birden çok tablet arasında bölmek için kullanılabilir.

Sırada ne var?