Все документы в коллекции 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, поскольку оно уже содержит понятие созданного в и индексируется по умолчанию. Копать это?