Хороша структура даних для індексу пам'яті


12

Я проектую базу даних об'єктів пам'яті для дуже конкретного випадку використання. Це один автор, але повинен підтримувати ефективні паралельні читання. Читання повинні бути ізольовані. Мова запитів відсутня, база даних підтримує лише:

  • отримати object / -s за атрибутом / набором атрибутів (може бути підтримка виразів, наприклад x.count < 5)
  • отримати атрибут об'єкта

Запит - це імперативний сценарій, що складається з довільної кількості вищезазначених операцій. Розмір даних буде << пам'яттю, тому всі об'єкти та індекси на більшості атрибутів повинні зручно розміщуватися, не змінюючи місцями.

Мені потрібна структура даних для індексу атрибутів об'єкта, який може бути O (n) при записі, не підтримує одночасність запису, але в ідеалі повинен підтримувати O (1) знімки (можливо, копіювати при запису) та O (logN) доступу. В ідеалі це дозволило б забезпечити високу конкурентоспроможність для читання з максимальним структурним обміном між версіями.

Я дивився на CTries , паралельні BST та паралельні дерева Splay , але я не впевнений, чи справді я дивлюсь у правильному напрямку. Вищезазначені структури приділяють багато уваги складності вставок, які мене не цікавлять.

Питання : чи є відома структура даних, яка найкраще підходить для мого випадку використання?

EDIT : після продумування ще здається, що стійке дерево BST / Splay спрацює. Письменник оновлює "головну" копію, і запити отримають дерево на момент початку виконання та викинуть його після того, як вони будуть виконані. Однак мене все ще цікавить, чи є краще рішення.


1
Вам потрібні знімки в пам'яті або потрібно зберегти їх на диску / мережі? Суто функціональна структура даних автоматично дає знімки в пам'яті, тож якщо це саме те, що вам потрібно, це найкращий варіант.
Жиль 'ТАК - перестань бути злим'

Це все в пам’яті. Мені було цікаво, можливо, є ефективна змінна версія з знімком постійного часу (як CTrie, тільки без одночасних записів).
dm3

2
Вашою проблемою може бути не вибір структури даних, а тип контролю паралельності.
Рафаель

Це може бути, ви могли б детальніше зупинитися на цьому?
dm3

Відповіді:


5

Використовуйте будь-яку стійку / незмінну (тобто функціональну) структуру даних на основі дерева. Ключовим моментом є отримання права блокування, на що @Raphael зазначив у коментарях.

Приємна річ у функціональних / стійких структурах даних на основі дерев - це те, що ви отримуєте «знімки» безкоштовно. Припустимо, ви використовуєте treap (рандомізоване дерево бінарного пошуку) для вашої структури даних. Ось приклад одного, написаного на Go: https://github.com/steveyen/gtreap . Автор так описує це:

Порушуючи незмінні, будь-які оновлення / вилучення з трансакцією повертають нову treap, яка може спільно використовувати внутрішні вузли з попередньою. Усі вузли в цій реалізації читаються лише після їх створення. Це дозволяє одночасно читачам безпечно працювати з одночасними авторами, оскільки модифікації створюють лише нові структури даних і ніколи не змінюють існуючі структури даних. Це простий підхід до досягнення контролю за одночасністю MVCC або багатоверсійної версії.

У будь-який момент часу "поточний" стан дерева представлений вказівником на корінь дерева. Вставки не мутують. Натомість вставка зберігає попередню версію дерева повністю недоторканою, створює нові вузли для шляху від кореня до правильної точки вставки з вказівниками назад на вузли попередньої версії, якими можна поділитися.O(logn)

Ви використовуєте блокування для захисту вказівника на корінь. Оскільки структура даних є незмінною, читання можна робити одночасно, і ви можете зберегти вказівники до старих знімків. Читання:

lock
tmp = ptr_to_root
unlock
value = search(tmp, <value to search for>)
return value

Незважаючи на те, що пошук може зайняти деякий час, ви тримаєте замок лише під час копіювання вказівника, тож пошук може відбуватися одночасно.

Писати:

lock
old_ptr_to_root = ptr_to_root
ptr_to_root = insert(old_ptr_to_root, <new key/value pair>)
unlock

У цій версії Write Do потрібно утримувати замок протягом усього процесу створення нової версії дерева. Ви можете покращити ефективність читання (ціною іноді не вдалося зробити транзакцію запису), змінивши запис на щось подібне:

top:
  lock
  old_ptr_to_root = ptr_to_root
  unlock
  new_ptr_to_root = insert(old_ptr_to_root, <new key/value pair>)
  lock
  if (ptr_to_root == old_ptr_to_root)   # make sure no other write happened in the interim
    ptr_to_root = new_ptr_to_root
    unlock
  else                                  # transaction fails, try again
    unlock
    goto top

Ви можете зробити це навіть трохи краще (зробіть його «замкненим»), якщо у вашій мові програмування є атомні змінні з атомною операцією порівняння та заміни. (Наприклад, використовуючи C ++ 11-і atomic<T*>.)


Дякую за детальну відповідь. Я якось це знав, можливо, я не поставив цього досить чітко у самому питанні. Однак відповідь все-таки чудова!
dm3

Ваша "вдосконалена" версія залежить від моделі пам'яті використовуваної системи. Цілком може знадобитися, щоб verables був оголошений непостійним у певній системі, і для отримання правильного кодування потрібна велика майстерність.
Ян Рінроуз

1

Microsoft опублікувала детальну інформацію про їх нову в базі даних пам’яті, в ній є індекси, які не блокують читання під час запису.

Наприклад:

Джастін Левандоскі, Девід Ломет та Судіпта Сенгупта, дерево дерева: дерево дерева для нового обладнання, в 2013 році, 29-а міжнародна конференція IEEE з інженерії даних (ICDE), Міжнародна конференція з інженерії даних, 8 квітня 2013 року.

Перегляньте http://research.microsoft.com/en-us/projects/main-memory_dbs/ список їх публікацій.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.