Все документы в коллекции MongoDB имеют первичный ключ, названный _id . Это поле автоматически присваивается документу при вставке, поэтому редко требуется его предоставлять. Что интересно в поле _id это то, что оно основано на времени . Таким образом, базовый тип _id , который является ObjectId , является 12-байтовым типом BSON , и 4 из этих байтов представляют секунды с начала эпохи Unix.
Особенностью поля _id является то, что оно автоматически индексируется, как вы можете видеть ниже, вызывая getIndexes для любой коллекции.
Все коллекции MongoDB имеют поле _id в качестве индекса:
|
01
02
03
04
05
06
07
08
09
10
11
|
> db.things.getIndexes()[ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.things", "name" : "_id_" }] |
И как все помнят из традиционных РСУБД, индексы важны, потому что они могут ускорить поиск документов; тем не менее, индексы действительно потребляют память, и при вставке документов наблюдается небольшое снижение производительности, поскольку все соответствующие индексы должны быть обновлены. Таким образом, хотя вы должны серьезно рассмотреть вопрос об использовании индексов, вы должны быть экономными в их использовании.
Естественно, поиск по _id документа удобен только тогда, когда вы его знаете . Чаще всего документы ищутся в других полях, и если вы обнаружите, что ищете по временным рядам , таким как created_at то вас created_at угощение.
Представьте себе коллекцию дублированных logs которая содержит простые документы, фиксирующие различные сообщения журнала. Пример документа может выглядеть так:
Простой документ в коллекции журналов:
|
1
2
3
4
5
6
|
{ "_id" : ObjectId("51c4ab6d4d6906d494460728"), "message" : "crashed, no such method exception", "type" : "crash", "created_at" : ISODate("2013-06-21T19:37:17.992Z")} |
Что если я захочу найти все сообщения журнала за определенную дату, как сегодня? Я мог бы написать свой запрос так:
Поиск всех журналов, созданных с 20 июня 2013 года:
|
1
|
db.logs.find({created_at:{'$gt': new Date(2013, 5, 20)}}) |
Если я добавлю объяснение к этому запросу, я увижу, что, поскольку у меня нет индекса для created_at , используется базовый курсор, и все документы в коллекции были отсканированы, чтобы получить мой результат.
План объяснения прилагается к моей находке:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
> db.logs.find({created_at:{'$gt': new Date(2013, 5, 20)}}).explain(){ "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 2, "nscannedObjects" : 4, "nscanned" : 4, "nscannedObjectsAllPlans" : 4, "nscannedAllPlans" : 4, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { }, "server" : "ghome-computer.home:27017"} |
Как вы можете видеть, поиск через поле created_at может быть неэффективным; таким образом, у вас может возникнуть желание добавить индекс в это поле. Это, естественно, сделает этот конкретный запрос более эффективным, однако вы понесете стоимость нового индекса, который потребляет больше памяти, а вставки будут немного медленнее из-за обновления этого недавно созданного индекса.
Как выясняется, поскольку поле _id встраивает в него эпоху Unix, вы можете с такой же легкостью создать выражение find, не включая поле created_at . Например, драйвер Ruby MongoDB позволяет вам создавать ObjectId из Time следующим образом:
Создание нового ObjectId с помощью метода фабрики from_time:
|
1
2
3
|
yesterday = Time.now - (60*60*(24*1))custom_id = BSON::ObjectId.from_time(yesterday)=> BSON::ObjectId('51c397800000000000000000') |
Как видите, я создал новый ObjectId помощью from_time фабрики from_time . 51c397800000000000000000 — это шестнадцатеричное представление, а первые 8 цифр представляют время, а все остальное обнуляется.
Теперь я могу использовать свой custom_id в любом выражении find . Через драйвер Ruby я также могу прикрепить explain , которое продемонстрирует использование свободного индекса _id .
Использование производного по дате ObjectId заставляет находку использовать индекс _id:
|
1
2
3
|
mongodb[:logs].find({_id: {'$gt' => custom_id}}).explain=> {"cursor"=>"BtreeCursor _id_", "isMultiKey"=>false, "n"=>1, "nscannedObjects"=>1, "nscanned"=>1, ....} |
Если вы видите BtreeCusor , то вы знаете, что используете индекс; если вы видите BasicCursor , вы знаете, что нет.
Таким образом, если вы обнаружите, что выполняете запросы и создаете индексы для некоторого поля времени или даты, такого как created_at , вам может быть лучше просто использовать поле _id Mongo, поскольку оно уже содержит понятие созданного в и индексируется по умолчанию. Копать это?