Порядок InsertManyResult.inserted_ids в случае неупорядоченной массовой вставки - PullRequest
1 голос
/ 25 сентября 2019

Мне интересно, соответствует ли порядок идентификаторов InsertManyResult.inserted_ids, возвращаемых неупорядоченной массовой вставкой , порядку входного массива?т.е. что первый идентификатор из InsertManyResult.inserted_ids соответствует идентификатору первого элемента входного массива

На самом деле, если я рассмотрю следующий пример:

from pymongo import *

connection = MongoClient()

users = [{'name': 'John'}, {'name': 'Bob'}, {'name': 'Hulk'}]
r = connection.testdb.user.insert_many(users, ordered=False)

print(r.inserted_ids)
print(users)

, который печатает следующее:

[ObjectId('5d8bc14424c4cb24ac1d2334'),
 ObjectId('5d8bc14424c4cb24ac1d2335'),
 ObjectId('5d8bc14424c4cb24ac1d2336')]

[{'_id': ObjectId('5d8bc14424c4cb24ac1d2334'), 'name': 'John'},
 {'_id': ObjectId('5d8bc14424c4cb24ac1d2335'), 'name': 'Bob'},
 {'_id': ObjectId('5d8bc14424c4cb24ac1d2336'), 'name': 'Hulk'}]

В этом простом примере порядок соблюден, но из-за неупорядоченного характера вставки мне интересно, гарантируется ли она всегда

1 Ответ

1 голос
/ 26 сентября 2019

Короткая версия: да, за исключением редких ситуаций

Mongo _ids имеют "4-байтовое значение, представляющее секунды с начала эпохи Unix, 5-байтовое случайное значениеи 3-байтовый счетчик, начиная со случайного значения. " Каждая из этих частей будет обычно (но не всегда) упорядочена (метка времени и счетчик) или постоянна (5-байтовое случайное значение).

По умолчанию драйверы базы данных mongo будут генерировать _id с, поэтому вы можете положиться на те _id с одинаковым случайным значением и, как правило, полагаться на метку времени и счетчик, перемещающийся вперед.https://docs.mongodb.com/manual/core/document/

4-байтовая временная метка

Если вы посмотрите на код генерации ObjectId для узла , вы увидите, что он использует Date для генерацииначальная часть метки времени ObjectId & Date может возвращаться назад при обновлении часов.Я бы предположил, что он действует одинаково на всех драйверах, поэтому у них, скорее всего, будут неправильные _ids, если вы делаете вставку в то же время, когда обновляются часы.

По результатам тестирования кажется, что даже если вы принудительно сгенерировали идентификаторы , insertMany с ordered=false на сервере (то есть базе данных), все равно сохраните порядок _id.Mongo не выполняет размазывание обновлений часов, поэтому, если на сервере, на котором запущен Mongo, есть обновления часов, _ids также может быть не в порядке в массиве вставок.

The 5случайное значение байта

Это должно оставаться постоянным, но если у вас есть монго (а не драйвер), сгенерируйте _ids и есть сбой, я думаю, что это изменится.Я не уверен, как это будет взаимодействовать со вставками.

3-байтовый счетчик

3-байтовый счетчик будет сброшен после подсчета 2 ^ 24 (~ 16 миллионов) документов.Это означает, что если ваш драйвер (если _ids генерируется там) или mongo (если _ids генерируется там) достигает этой точки, документы, вставленные в течение этой секунды в массовой вставке (упорядоченные или неупорядоченные), не будут упорядочены_id.

Mongo's recordId

Внутренне mongo использует recordId вместо _id в качестве первичного ключа.Если вы делаете неупорядоченную вставку и смотрите на recordIds, _ids будет в том порядке, в котором они были в массиве вставок, но recordIds будет упорядочен в том порядке, в котором они фактически были вставлены, и не будетсоответствовать порядку _id.

  const conn = await mongodb.connect("mongodb://mongo:27017/");
  const db = conn.db("test");
  await db.collection("test").remove({});
  const toInsert = [];
  for (let i = 0; i < 100; i++) {
    toInsert.push({i})
  }
  await db.collection("test").insertMany(toInsert, {ordered: false, forceServerObjectId: true});
  const results = await db.collection("test").find({}).showRecordId(true).sort({_id: 1}).toArray();
  let prev;
  for (const result of results) {
    if (prev) {
      // this will throw because recordids are not ordered as expected
      assert(prev < result.$recordId)
    }
    prev = result.$recordId;
  }
...