O que é uma Capped Collection? Casos de uso e restrições.
- Postado por Adriano Bonacin
- Categorias mongodb
- Data 31/01/2022
- Comentários 0 comentário
Vamos falar de um tipo especial de collection, chamada Capped Collection. Se você quiser dar uma revisada no que é uma collection e como é dividido logicamente o MongoDB, escrevi um artigo a respeito e você pode revisar aqui. Vamos falar também de suas vantagens e restrições.
Características
A principal característica de uma Capped Collection é que ela tem um tamanho máximo fixo, seja em bytes ou em número de documentos. O tamanho em bytes é obrigatório, o número de registros não. O mais restritivo vence, se você definir 1GB e 4 documentos, ela terá apenas 4 documentos.
O comando para criação é semelhante a criação de qualquer outra collection, com excessão do parâmetro capped (dentro de um documento). Nosso DB yadax havia sido removido em algum dos artigos anteriores, então estamos trabalhando em um ambiente novinho e vazio. Vamos criar nosso Capped Collection com no máximo 4 documentos e re-inserir nossos docs de carros.
rsYadax0 [direct: primary] yadax> show dbs
admin 176 kB
config 201 kB
local 537 kB
rsYadax0 [direct: primary] admin> use yadax
switched to db yadax
rsYadax0 [direct: primary] yadax> db.createCollection("cars", {capped:true, size:10000, max:4})
{ ok: 1 }
rsYadax0 [direct: primary] yadax> db.cars.insertMany([
... {"marca":"HONDA", "modelo":"CIVIC", "ano_fabricacao": 2015 , "ano_modelo": 2016, "cor": "Prata", "preco": 60000},
... {"marca":"HONDA", "modelo":"Civic", "ano_fabricacao": 2018 , "ano_modelo": 2018, "cor": "Branco", "preco": 80000},
... {"marca":"HONDA", "modelo":"FIT", "ano_fabricacao": 2019 , "ano_modelo": 2019, "cor": "Branco", "preco": 50000},
... {"marca":"FIAT", "modelo":"UNO", "ano_fabricacao": 2000 , "ano_modelo": 2001, "cor": "Preto", "preco": 10000},
... {"marca":"FIAT", "modelo":"PALIO", "ano_fabricacao": 2006 , "ano_modelo": 2007, "cor": "Prata", "preco": 11000},
... {"marca":"FIAT", "modelo":"STRADA", "ano_fabricacao": 2018 , "ano_modelo": 2018, "cor": "Branco", "preco": 40000},
... {"marca":"FIAT", "modelo":"TORO", "ano_fabricacao": 2020 , "ano_modelo": 2020, "cor": "Preto", "preco": 90000},
... {"marca":"FIAT", "modelo":"TORO", "ano_fabricacao": 2019 , "ano_modelo": 2020, "cor": "Prata", "preco": 80000},
... {"marca":"VW", "modelo":"GOL", "ano_fabricacao": 2012 , "ano_modelo": 2012, "cor": "Preto", "preco": 20000},
... {"marca":"VW", "modelo":"POLO", "ano_fabricacao": 2020 , "ano_modelo": 2020, "cor": "Branco", "preco": 60000},
... {"marca":"VW", "modelo":"GOLF", "ano_fabricacao": 2019 , "ano_modelo": 2019, "cor": "Prata", "preco": 90000},
... {"marca":"VW", "modelo":"GOL", "ano_fabricacao": 2003 , "ano_modelo": 2003, "cor": "Branco", "preco": 10000},
... {"marca":"FORD", "modelo":"FIESTA", "ano_fabricacao": 2005 , "ano_modelo": 2006, "cor": "Prata", "preco": 11000},
... {"marca":"FORD", "modelo":"FIESTA", "ano_fabricacao": 2007 , "ano_modelo": 2007, "cor": "Prata"},
... {"marca":"FORD", "modelo":"PAMPA", "ano_fabricacao": 1989, "ano_modelo": 1989, "cor": "Prata", "preco": null}
... ])
{
acknowledged: true,
insertedIds: {
'0': ObjectId("61f7d30fe224569f4449262a"),
'1': ObjectId("61f7d30fe224569f4449262b"),
'2': ObjectId("61f7d30fe224569f4449262c"),
'3': ObjectId("61f7d30fe224569f4449262d"),
'4': ObjectId("61f7d30fe224569f4449262e"),
'5': ObjectId("61f7d30fe224569f4449262f"),
'6': ObjectId("61f7d30fe224569f44492630"),
'7': ObjectId("61f7d30fe224569f44492631"),
'8': ObjectId("61f7d30fe224569f44492632"),
'9': ObjectId("61f7d30fe224569f44492633"),
'10': ObjectId("61f7d30fe224569f44492634"),
'11': ObjectId("61f7d30fe224569f44492635"),
'12': ObjectId("61f7d30fe224569f44492636"),
'13': ObjectId("61f7d30fe224569f44492637"),
'14': ObjectId("61f7d30fe224569f44492638")
}
}
rsYadax0 [direct: primary] yadax>
rsYadax0 [direct: primary] yadax> db.cars.find()
[
{
_id: ObjectId("61f7d30fe224569f44492635"),
marca: 'VW',
modelo: 'GOL',
ano_fabricacao: 2003,
ano_modelo: 2003,
cor: 'Branco',
preco: 10000
},
{
_id: ObjectId("61f7d30fe224569f44492636"),
marca: 'FORD',
modelo: 'FIESTA',
ano_fabricacao: 2005,
ano_modelo: 2006,
cor: 'Prata',
preco: 11000
},
{
_id: ObjectId("61f7d30fe224569f44492637"),
marca: 'FORD',
modelo: 'FIESTA',
ano_fabricacao: 2007,
ano_modelo: 2007,
cor: 'Prata'
},
{
_id: ObjectId("61f7d30fe224569f44492638"),
marca: 'FORD',
modelo: 'PAMPA',
ano_fabricacao: 1989,
ano_modelo: 1989,
cor: 'Prata',
preco: null
}
]
rsYadax0 [direct: primary] yadax>
Por fim, podemos ver como só restaram 4 documentos. Mas como funciona esse tamanho máximo fixo? Este tipo de collection mantem a ordem (temporal) dos documentos inseridos e ao atingirmos o tamanho máximo, os documentos mais antigos começam ser substituídos.
Sua estrutura é como uma fila de documentos, com boa performance para escrita e leitura na ordem de inserção. E como a leitura é baseado na ordem de inserção, não há necessidade de índice para esta operação, embora por padrão ainda exista o index no campo _id.
Casos de uso
Os principais casos de uso são:
Armazenar alto volume de escrita e consumi-lo no modelo FIFO (First In First Out), que o primeiro registro inserido é o primeiro a ser lido. Há uma estrutura chamada Tailable Cursor (que falaremos em outro artigo) que consegue ir extraindo documentos à medida que são inseridos, de forma rápida e sem depender de índices.
Outro caso bastante usado é onde é necessário cache dos registros mais recentes ou Top N em alguma característica, como os mais vendidos. Assim, ao invés de ler toda sua collection, baseada em um índice, você mantem em uma Capped Collection apenas os 20 itens mais vendidos.
Restrições
Há várias restrições quando estamos trabalhando com Capped Collection, aqui vou citar as mais relevantes (no meu ponto de vista).A lista inteira pode ser consultada na documentação oficial.
Os Updates não podem alterar o tamanho do documento. Vamos tentar alterar o valor de dois carros, um que já possui o campo preço com valor não nulo e outro que este campo está nulo. Outro ponto importante a se considerar é que geralmente não temos índices nas Capped Collections, então se você precisar alterar algum doc fique alerta com as Full Collections Scans.
// o tamanho do documento não pode mudar
rsYadax0 [direct: primary] yadax> db.cars.update({_id: ObjectId("61f7d30fe224569f44492638")},{$set:{preco: 15000}})
DeprecationWarning: Collection.update() is deprecated. Use updateOne, updateMany, or bulkWrite.
MongoServerError: Cannot change the size of a document in a capped collection: 114 != 118
// sem alterar o size, tudo ocorre bem
rsYadax0 [direct: primary] yadax> db.cars.update({_id: ObjectId("61f7d30fe224569f44492636")},{$set:{preco: 15000}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
Outra restrição é que não podemos deletar um registro de uma Capped Collection. Você pode dropar a collection, mas não eliminar apenas um documento.
rsYadax0 [direct: primary] yadax> db.cars.deleteOne({_id: ObjectId("61f7d30fe224569f44492636")})
MongoServerError: cannot remove from a capped collection: yadax.cars
Por fim, as Capped Collections não podem ser “shardeadas”, os valores de MaxSize e MaxDocs não podem ser alterados. As collections tradicionais podem ser convertidas em Capped, mas não o contrário. Vou falar um pouco mais detalhadamente sobre a conversão na próxima sessão. Também há um método que nos informa se a collection é Capped ou não.
Converter uma Collection para Capped
Este é um caminho literalmente sem volta. Você pode fazer a conversão de uma collection tradicional em Capped, mas não o contrário. Uma restrição nesse processo é que você não pode informar o número máximo de documentos, somente informar o Max Size.
rsYadax0 [direct: primary] yadax> db.cars2.isCapped()
false
rsYadax0 [direct: primary] yadax> db.runCommand({"convertToCapped": "cars2", size: 100000, max: 4});
{
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1643636914, i: 32 }),
signature: {
hash: Binary(Buffer.from("474bbabbdf9a9218cbc0e5938d39e070c9e96ed5", "hex"), 0),
keyId: Long("7058320868807540741")
}
},
operationTime: Timestamp({ t: 1643636914, i: 32 })
}
rsYadax0 [direct: primary] yadax> db.cars2.isCapped()
true
Uma alternativa para descobrir informações de uma collection, seja ela capped ou não, é com o db.collection.stats(). Ele vai mostrar algumas métricas interessantes.
rsYadax0 [direct: primary] yadax> db.cars2.stats()
{
ns: 'yadax.cars2',
size: 1746,
count: 15,
avgObjSize: 116,
storageSize: 4096,
freeStorageSize: 0,
capped: true,
max: 0,
maxSize: 40960,
...
Com certeza ainda há muito a se explorar no tema Capped Collection, mas o texto para uma breve introdução. A documentação é rica e também há inúmeros posts mundo afora. Em seguida vou conversar um pouco sobre tailable cursores, uma feature que aliada à Capped Collection é o coração do nosso ReplicaSet. Até lá.
Tag:mongodb