Birçok gerçek zamanlı uygulamanın sayaç görevi gören belgeleri vardır. Örneğin, bir gönderideki 'beğenileri' veya belirli bir öğenin 'favorilerini' sayabilirsiniz.
Cloud Firestore'da tek bir belgeyi sınırsız oranda güncelleyemezsiniz. Tek bir belgeye dayalı bir sayacınız varsa ve bu sayaçta yeterince sık artışlar yapılıyorsa, sonunda belgedeki güncellemeler konusunda çekişme göreceksiniz. Bkz. Tek bir belgedeki güncellemeler .
Çözüm: Dağıtılmış sayaçlar
Daha sık sayaç güncellemelerini desteklemek için dağıtılmış bir sayaç oluşturun. Her sayaç, "parçaların" alt koleksiyonunu içeren bir belgedir ve sayacın değeri, parçaların değerinin toplamıdır.
Yazma verimi, parça sayısıyla doğrusal olarak artar; bu nedenle, 10 parçalı dağıtılmış bir sayaç, geleneksel bir sayacın 10 katı kadar yazma işlemi gerçekleştirebilir.
ağ
// counters/${ID}
{
"num_shards": NUM_SHARDS,
"shards": [subcollection]
}
// counters/${ID}/shards/${NUM}
{
"count": 123
}
Süratli
// counters/${ID} struct Counter { let numShards: Int init(numShards: Int) { self.numShards = numShards } } // counters/${ID}/shards/${NUM} struct Shard { let count: Int init(count: Int) { self.count = count } }
Amaç-C
// counters/${ID} @interface FIRCounter : NSObject @property (nonatomic, readonly) NSInteger shardCount; @end @implementation FIRCounter - (instancetype)initWithShardCount:(NSInteger)shardCount { self = [super init]; if (self != nil) { _shardCount = shardCount; } return self; } @end // counters/${ID}/shards/${NUM} @interface FIRShard : NSObject @property (nonatomic, readonly) NSInteger count; @end @implementation FIRShard - (instancetype)initWithCount:(NSInteger)count { self = [super init]; if (self != nil) { _count = count; } return self; } @end
Kotlin+KTX
// counters/${ID} data class Counter(var numShards: Int) // counters/${ID}/shards/${NUM} data class Shard(var count: Int)
Java
// counters/${ID} public class Counter { int numShards; public Counter(int numShards) { this.numShards = numShards; } } // counters/${ID}/shards/${NUM} public class Shard { int count; public Shard(int count) { this.count = count; } }
Python
Python
Node.js
Geçerli değil, aşağıdaki sayaç artış pasajına bakın.
Gitmek
PHP
Geçerli değil, aşağıdaki sayaç başlatma pasajına bakın.
C#
Aşağıdaki kod dağıtılmış bir sayacı başlatır:
ağ
function createCounter(ref, num_shards) { var batch = db.batch(); // Initialize the counter document batch.set(ref, { num_shards: num_shards }); // Initialize each shard with count=0 for (let i = 0; i < num_shards; i++) { const shardRef = ref.collection('shards').doc(i.toString()); batch.set(shardRef, { count: 0 }); } // Commit the write batch return batch.commit(); }
Süratli
func createCounter(ref: DocumentReference, numShards: Int) { ref.setData(["numShards": numShards]){ (err) in for i in 0...numShards { ref.collection("shards").document(String(i)).setData(["count": 0]) } } }
Amaç-C
- (void)createCounterAtReference:(FIRDocumentReference *)reference shardCount:(NSInteger)shardCount { [reference setData:@{ @"numShards": @(shardCount) } completion:^(NSError * _Nullable error) { for (NSInteger i = 0; i < shardCount; i++) { NSString *shardName = [NSString stringWithFormat:@"%ld", (long)shardCount]; [[[reference collectionWithPath:@"shards"] documentWithPath:shardName] setData:@{ @"count": @(0) }]; } }]; }
Kotlin+KTX
fun createCounter(ref: DocumentReference, numShards: Int): Task<Void> { // Initialize the counter document, then initialize each shard. return ref.set(Counter(numShards)) .continueWithTask { task -> if (!task.isSuccessful) { throw task.exception!! } val tasks = arrayListOf<Task<Void>>() // Initialize each shard with count=0 for (i in 0 until numShards) { val makeShard = ref.collection("shards") .document(i.toString()) .set(Shard(0)) tasks.add(makeShard) } Tasks.whenAll(tasks) } }
Java
public Task<Void> createCounter(final DocumentReference ref, final int numShards) { // Initialize the counter document, then initialize each shard. return ref.set(new Counter(numShards)) .continueWithTask(new Continuation<Void, Task<Void>>() { @Override public Task<Void> then(@NonNull Task<Void> task) throws Exception { if (!task.isSuccessful()) { throw task.getException(); } List<Task<Void>> tasks = new ArrayList<>(); // Initialize each shard with count=0 for (int i = 0; i < numShards; i++) { Task<Void> makeShard = ref.collection("shards") .document(String.valueOf(i)) .set(new Shard(0)); tasks.add(makeShard); } return Tasks.whenAll(tasks); } }); }
Python
Python
Node.js
Geçerli değil, aşağıdaki sayaç artış pasajına bakın.
Gitmek
PHP
C#
Yakut
Sayacı artırmak için rastgele bir parça seçin ve sayımı artırın:
ağ
function incrementCounter(ref, num_shards) { // Select a shard of the counter at random const shard_id = Math.floor(Math.random() * num_shards).toString(); const shard_ref = ref.collection('shards').doc(shard_id); // Update count return shard_ref.update("count", firebase.firestore.FieldValue.increment(1)); }
Süratli
func incrementCounter(ref: DocumentReference, numShards: Int) { // Select a shard of the counter at random let shardId = Int(arc4random_uniform(UInt32(numShards))) let shardRef = ref.collection("shards").document(String(shardId)) shardRef.updateData([ "count": FieldValue.increment(Int64(1)) ]) }
Amaç-C
- (void)incrementCounterAtReference:(FIRDocumentReference *)reference shardCount:(NSInteger)shardCount { // Select a shard of the counter at random NSInteger shardID = (NSInteger)arc4random_uniform((uint32_t)shardCount); NSString *shardName = [NSString stringWithFormat:@"%ld", (long)shardID]; FIRDocumentReference *shardReference = [[reference collectionWithPath:@"shards"] documentWithPath:shardName]; [shardReference updateData:@{ @"count": [FIRFieldValue fieldValueForIntegerIncrement:1] }]; }
Kotlin+KTX
fun incrementCounter(ref: DocumentReference, numShards: Int): Task<Void> { val shardId = Math.floor(Math.random() * numShards).toInt() val shardRef = ref.collection("shards").document(shardId.toString()) return shardRef.update("count", FieldValue.increment(1)) }
Java
public Task<Void> incrementCounter(final DocumentReference ref, final int numShards) { int shardId = (int) Math.floor(Math.random() * numShards); DocumentReference shardRef = ref.collection("shards").document(String.valueOf(shardId)); return shardRef.update("count", FieldValue.increment(1)); }
Python
Python
Node.js
Gitmek
PHP
C#
Yakut
Toplam sayımı almak için tüm parçaları sorgulayın ve count
alanlarını toplayın:
ağ
function getCount(ref) { // Sum the count of each shard in the subcollection return ref.collection('shards').get().then((snapshot) => { let total_count = 0; snapshot.forEach((doc) => { total_count += doc.data().count; }); return total_count; }); }
Süratli
func getCount(ref: DocumentReference) { ref.collection("shards").getDocuments() { (querySnapshot, err) in var totalCount = 0 if err != nil { // Error getting shards // ... } else { for document in querySnapshot!.documents { let count = document.data()["count"] as! Int totalCount += count } } print("Total count is \(totalCount)") } }
Amaç-C
- (void)getCountWithReference:(FIRDocumentReference *)reference { [[reference collectionWithPath:@"shards"] getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) { NSInteger totalCount = 0; if (error != nil) { // Error getting shards // ... } else { for (FIRDocumentSnapshot *document in snapshot.documents) { NSInteger count = [document[@"count"] integerValue]; totalCount += count; } NSLog(@"Total count is %ld", (long)totalCount); } }]; }
Kotlin+KTX
fun getCount(ref: DocumentReference): Task<Int> { // Sum the count of each shard in the subcollection return ref.collection("shards").get() .continueWith { task -> var count = 0 for (snap in task.result!!) { val shard = snap.toObject<Shard>() count += shard.count } count } }
Java
public Task<Integer> getCount(final DocumentReference ref) { // Sum the count of each shard in the subcollection return ref.collection("shards").get() .continueWith(new Continuation<QuerySnapshot, Integer>() { @Override public Integer then(@NonNull Task<QuerySnapshot> task) throws Exception { int count = 0; for (DocumentSnapshot snap : task.getResult()) { Shard shard = snap.toObject(Shard.class); count += shard.count; } return count; } }); }
Python
Python
Node.js
Gitmek
PHP
C#
Yakut
Sınırlamalar
Yukarıda gösterilen çözüm, Cloud Firestore'da paylaşılan sayaçlar oluşturmanın ölçeklenebilir bir yoludur ancak aşağıdaki sınırlamaların farkında olmalısınız:
- Parça sayısı - Parça sayısı, dağıtılan sayacın performansını kontrol eder. Çok az sayıda parça varsa, bazı işlemlerin başarılı olmadan önce yeniden denenmesi gerekebilir ve bu da yazma işlemlerini yavaşlatır. Çok fazla parça olduğunda okumalar daha yavaş ve daha pahalı hale gelir. Sayaç toplamını daha yavaş bir tempoda güncellenen ayrı bir özet belgede tutarak ve müşterilerin toplamı elde etmek için bu belgeden okumasını sağlayarak okuma masrafını dengeleyebilirsiniz. Bunun dezavantajı, müşterilerin herhangi bir güncellemeden hemen sonra tüm parçaları okuyarak toplamı hesaplamak yerine, özet belgenin güncellenmesini beklemek zorunda kalacak olmasıdır.
- Maliyet - Parça alt koleksiyonunun tamamının yüklenmesi gerektiğinden, sayaç değerini okumanın maliyeti parça sayısıyla doğrusal olarak artar.