Статьи

Основные ключи MongoDB — ваш друг

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

Справка: первичные ключи MongoDB — ваш друг от нашего партнера по JCG Эндрю Гловера в блоге The Disco Blog .