Странная проблема производительности с UPDATE в ArangoDB - PullRequest
2 голосов
/ 25 марта 2020

Я создаю Node.js приложение, которое работает с ArangoDB в качестве хранилища данных. По сути, в качестве структуры данных у меня есть две таблицы: одна для управления так называемым instances, другая для entities. Я делаю следующее:

  • В коллекции instances есть документ для каждого instance, который у меня есть.
  • Всякий раз, когда я добавляю объект в entities коллекции, я также хочу отслеживать сущности, которые принадлежат конкретному c экземпляру.
  • Итак, каждый документ instance имеет поле массива для entities, а я pu sh Идентификатор объекта в этом массиве.

Следующий код показывает общую схему:

// Connect to ArangoDB.
db = new Database(...);
db.useBasicAuth(user, password);

// Use the database.
await db.createDatabase(database);
db.useDatabase(database);

// Create the instance collection.
instanceCollection = db.collection(`instances-${uuid()}`);
await instanceCollection.create();

// Create the entities collection.
entityCollection = db.collection(`entities-${uuid()}`);
await entityCollection.create();

// Setup an instance.
instance = {
  id: uuid(),
  entities: []
};

// Create the instance in the database.
await db.query(aql`
  INSERT ${instance} INTO ${instanceCollection}
`);

// Add lots of entities.
for (let i = 0; i < scale; i++) {
  // Setup an entity.
  const entity = {
    id: uuid()
  };

  // Update the instance.
  instance.entities.push(entity);

  // Insert the entity in the database.
  await db.query(aql`
    INSERT ${entity} INTO ${entityCollection}
  `);

  // Update the instance in the database.
  await db.query(aql`
    FOR i IN ${instanceCollection}
      FILTER i.id == ${instance.id}
      UPDATE i WITH ${instance} IN ${instanceCollection} OPTIONS { mergeObjects: false }
  `);
}

Проблема сейчас в том, что это становится чрезвычайно медленным, чем больше объектов я добавляю. Он в основном имеет экспоненциальный рост, хотя я бы ожидал линейного роста:

Running benchmark 'add and update'
  100 Entities:   348.80ms [+0.00%]
 1000 Entities:  3113.55ms [-10.74%]
10000 Entities: 90180.18ms [+158.54%]

Добавление индекса оказывает влияние, но ничего не меняет в общей проблеме:

Running benchmark 'add and update with index'
  100 Entities:   194.30ms [+0.00%]
 1000 Entities:  2090.15ms [+7.57%]
10000 Entities: 89673.52ms [+361.52%]

проблему можно отследить до оператора UPDATE. Если вы пропустите это и будете использовать только оператор базы данных INSERT, все будет линейно масштабироваться. Так что, похоже, что-то не так с самим обновлением. Однако я не понимаю, в чем проблема.

Это то, что я хотел бы понять: почему оператор UPDATE значительно замедляется со временем? Я использую это неправильно? Это известная проблема в ArangoDB? …?

То, что я не заинтересован, обсуждает этот подход: пожалуйста, примите как дано. Давайте сосредоточимся на производительности оператора UPDATE. Любые идеи?

ОБНОВЛЕНИЕ

Как просили в комментариях, здесь некоторая информация о настройке системы:

  • ArangoDB 3.4.6, 3.6.2.1, и 3.7.0-alpha.2 (все работают в Docker, в macOS и Linux)
  • Настройка на одном сервере
  • Aran goJS 6.14.0 (мы также было это с более ранними версиями, хотя я не могу сказать точную версию)

1 Ответ

2 голосов
/ 29 марта 2020

Поиск проблемы

Вы пытались объяснить или профилировать запрос?

Аран go описания плана объяснения превосходны. Вы можете получить доступ к explain, используя встроенный веб-интерфейс администратора Aardvark или db._explain(query). Вот как выглядит ваш:

Execution plan:
 Id   NodeType                  Est.   Comment
  1   SingletonNode                1   * ROOT
  5   CalculationNode              1     - LET #5 = { "_key" : "123", "_id" : "collection/123", "_rev" : "_aQcjewq---", ...instance }   /* json expression */   /* const assignment */
  2   EnumerateCollectionNode      2     - FOR i IN collection   /* full collection scan, projections: `_key`, `id` */   FILTER (i.`id` == "1")   /* early pruning */
  6   UpdateNode                   0       - UPDATE i WITH #5 IN pickups 

Indexes used:
 By   Name      Type      Collection   Unique   Sparse   Selectivity   Fields       Ranges
  6   primary   primary   pickups      true     false       100.00 %   [ `_key` ]   i

Проблема

Ключевой частью плана является - FOR i IN collection /*full collection scan Полное сканирование коллекции будет ... медленный. Он должен расти линейно с размером вашей коллекции. Так что с вашими итерациями for l oop из scale это определенно означает экспоненциальный рост с увеличением размера коллекции.

Решение

Индексирование id должно помочь, но я думаю, что это зависит от того, как вы создали индекс.

Использование _key вместо индекса меняет план на показ primary

- FOR i IN pickups   /* primary index scan, index only, projections: `_key` */    

Это должно быть постоянное время, поэтому с вашим for l oop из scale итерации, это должно означать линейное время.

...