Як створити базу даних для зберігання відсортованого списку?


42

Я хочу зберігати відсортований список всередині бази даних. Наступні операції хочу виконати ефективно.

  1. Вставити (x) - Вставити запис x у таблицю
  2. Видалити (x) - Видалити запис x з таблиці
  3. До (x, n) - Повернення записів 'n' перед записом x у відсортованому списку.
  4. Після (x, n) - Повернення записів 'n', що завершили запис x, у відсортованому списку.
  5. Перший (n) - Повернення перших записів 'n' зі списку відсортованих.
  6. Останнє (n) - Повернення останніх «n» записів із відсортованого списку.
  7. Порівняйте (x, y) - Давши два записи x і y з таблиці, знайдіть, якщо x> y.

Простий метод, про який я міг би придумати, - це зберігати якийсь атрибут 'rank' у таблиці та запитувати шляхом сортування за цим атрибутом. Але в цьому методі вставлення / зміна запису з рангом стає дорогою операцією. Чи є кращий метод?

Зокрема, я хочу реалізувати таблицю за допомогою SimpleDB Amazon. Але загальна відповідь на реляційну базу даних також повинна бути корисною.

Оновлення профілю завантаження:

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

Якщо є 100 тис. Активних користувачів (супер оптимізм: P), то моя дуже приблизна оцінка за день

500k вибирає, 100k вставляє та видаляє, 500k оновлення

Я б очікував, що стіл виросте в цілому до 500 тис.

Я хочу оптимізувати оновлення, вставити та порівняти операції. Ранг елементів буде постійно змінюватися, і мені потрібно постійно оновлювати таблицю.


Детально опрацюйте очікуваний профіль навантаження. Скільки вибору / вставок / оновлень на день? Для яких операцій ви хочете найбільше оптимізувати? Наскільки ви очікуєте, що таблиця буде рости за день або отримувати загальну суму?
Nick Chammas

Це для дошки гравців? У будь-якому разі я оновив свою відповідь нижче зворотним зв'язком на основі вашого прогнозованого профілю завантаження.
Нік Чаммас

ні, це не табло гравців.
Чітті

Який підхід ви в кінцевому підсумку використали?
Nick Chammas

Я навіть не впевнений у тому, що тут просять або що вам не потрібно робити зі списку білизни речей, які вам потрібно зробити.
Еван Керролл

Відповіді:


22

Якщо ранг не є абсолютно довільним, але натомість є похідним з якогось іншого властивості (наприклад, ім'я, рахунок гравця тощо), тоді добре погляньте на відповідь Джоела .

Якщо це довільне властивість даних, то , що має бути збережене в якості стовпця в таблиці рекордів. Якщо припустити, що SimpleDB Amazon схожий на типовий RDBMS, ви можете проіндексувати цей стовпець і швидко задовольнити всі ваші вищезазначені запити відповідною стратегією індексації. Це нормально для RDBMS.

Зважаючи на те, що ви очікуєте високої активності вставки та оновлення, а також відносно високої активності читання, рекомендую зробити наступне:

  • Кластеризуйте таблицю за рангом, особливо якщо переважна більшість ваших запитів проти рангу. Якщо ні, або якщо вибір кластеризації недоступний у SimpleDB, просто створіть індекс з рангом як провідний стовпець. Це задовольнило б запити 3-6.
  • Індекс на записі спочатку, а потім ранжування (або, у світі SQL Server, лише запис та INCLUDE-ing ранг, або просто запис, якщо ви кластеризовані на ранг), задовольнив би запит 7.
  • Операції 1 і 2 можна оптимізувати, розставивши ваші дані належним чином (тобто встановивши FILLFACTORв SQL Server). Це особливо важливо, якщо ви кластерите за рангом.
  • Коли ви вставляєте чи оновлюєте ранги, зберігайте якомога більше проміжку між номерами рангів, щоб мінімізувати таку можливість, що вам знадобиться перевпорядкувати існуючу запис для розміщення вставки чи оновлення. Наприклад, якщо ви класифікуєте свої записи за кроком 1000, ви залишаєте достатньо місця для приблизно половини стільки змін і вставок з мінімальними шансами, вам знадобиться перевпорядкувати запис, який безпосередньо не бере участь у цих змінах.
  • Кожен вечір проводить повторне ранжування всіх записів, щоб скидати прогалини між ними.
  • Ви можете налаштувати частоту масових перерейтингів, а також розмір розриву рангів, щоб вмістити очікувану кількість вставок або оновлень відносно кількості існуючих записів. Тож якщо у вас є записи в 100 тис. І очікуєте, що вставки та оновлення становитимуть 10% від цього, залиште достатньо місця для 10-кілограмових нових рангів та перейдіть на ночі.
  • Переоцінка записів 500K - це дорога операція, але виконана один раз на день або тиждень у неробочий час має бути чудовою для такої бази даних. Таке масове переозброєння неробочих годин для підтримання прогалин у рейтингах - це те, що дозволяє заощадити багато рекордів для кожного оновлення чи вставки протягом нормальних і пікових годин.

Якщо ви очікуєте, що 100K + прочитає таблицю розміром 100 К +, я не рекомендую використовувати підхід зв'язаного списку. Він не підходить до таких розмірів.


Ранги можуть змінюватися. Я очікую, що чини будуть постійно змінюватися, і нові записи постійно вставляються. Мене хвилює випадок, коли я вставляю новий елемент з рангом, тоді ряди всіх записів нижче нового запису в порядку сортування потрібно змінювати. Це не дорога операція, коли в моїй базі даних є тисячі записів?
Чітті

@chitti - Ах, це питання. Ви можете виділити рейтинги (наприклад, 0, 1000, 2000, 3000, ...) і періодично переосмислювати всі записи по мірі заповнення прогалин у рейтингах. Це не буде масштабуватися, якщо ви очікуєте набагато більше, ніж кілька десятків тисяч записів.
Нік Шамма

1
@chitti - насправді це смішно. Це саме проблема, з якою вирішуються двигуни бази даних під час індексації даних, оскільки вони впорядковують їх та переупорядковують, коли дані додаються чи змінюються. Якщо ви подивитесь вгору, FILLFACTORви побачите, що в основному мається на увазі створити додатковий простір для записів в індексі, подібно до того, як я описав прогалини в рейтингах, створюють простір для змін чи вставок.
Нік Чаммас

2
Дякуємо за оновлену відповідь. "Ранг" - це довільна властивість моїх даних. Я майже впевнений, що те, що я вимагаю, - це спеціальний стовпчик індексу. Ознайомтесь із цим посиланням SO із подібним запитанням. У верхній відповіді наведено рекомендації щодо обробки такої колонки.
Чітті

@chitti - прийнята відповідь на це питання ТАК чудова. Він пропонує той самий підхід, який я детально описав тут, з додатковою пропозицією використовувати десяткові знаки замість цілих чисел, щоб значно розширити вашу гнучкість при призначенні та зміні рангів. Чудова знахідка.
Нік Шамм

13

Я, як правило, використовую описаний вами метод "рангу". Замість того, щоб поплутатися з оновленням рядків, коли елементи потрібно було впорядкувати, я часто мав змогу піти зі видалення всіх записів у списку та повторної вставки нових елементів у належному порядку. Цей метод чітко оптимізований для пошуку.

Альтернативним підходом було б моделювання записів як зв'язаного списку за допомогою рефлексивного стовпчика "попередник" на таблиці:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

Ви можете легко отримати список і додавати та видаляти елементи з невеликими накладними витратами, але вивести записи в належному порядку буде складним. Можливо, є розумний спосіб зробити це за один запит, ймовірно, з великою кількістю приєднаних таблиць.

Цей останній підхід я часто використовую, коли моделюю відносини у стилі дерева (категорії, папки, набори та підмножини). У мене в основному була якась рекурсивна функція реконструкції повного дерева в моїй програмі.


2
Модель пов'язаного списку акуратна. Для отримання такої ієрархії в порядку в SQL Server ви використовували б рекурсивний CTE .
Nick Chammas

Однак побудова цієї ієрархії буде досить дорогою для високого столу. Перевага полягає в тому, що зміни рангів / вставок / тощо можна легко здійснити. Залежно від очікуваного профілю навантаження Чітті, це насправді може бути найкращим підходом.
Nick Chammas

Параметр пов'язаного списку виглядає як найкраща ідея для всіх операцій, крім Порівняння. Будь-яка ідея, як я б реалізував Порівняти, не простежуючи шлях між двома елементами, які порівнюються?
Чітті

Якщо у вас є ідентифікатори елементів, я думаю, що Порівняти () було б просто, якщо тільки я неправильно зрозумів, що ви мали на увазі під Порівнянням (). Коли ви сказали: "знайти, якщо х> у", ви мали на увазі "знайти, якщо х передує у"? Я не можу зрозуміти, що це легко без спеціального індексу або збереженої процедури, яка б перейшла до списку (або тієї цікавої функції CTE, згаданої @Nick).
bpanulla

5
Цей тип рішення також наближає модель графічних даних ( en.wikipedia.org/wiki/Graph_theory ). Система зберігання даних, оптимізована для зберігання вузлів та країв графіка, може бути кращим рішенням, ніж RDBMS. Потрійні та Quad-магазини та бази даних графіків, такі як Neo4J, у цьому досить непогані.
bpanulla

6

Я думаю, що потрібно зробити, це зберегти властивість або властивості, які використовуються для обчислення рангу, а потім побудувати індекс над ними. Замість того, щоб намагатися змусити базу даних фізично зберігати дані в ранговому порядку або використовувати зв’язаний вручну список, чому б не дозволити двигуну бази даних робити те, що було призначено?


2
Що робити, якщо "властивості, які використовуються для обчислення рангу", є довільними? Напр.: Набір записів кошика, який упорядковується на основі довільних дій користувача.
Чітті

Коли ви говорите, що ранг довільний, що ви маєте на увазі? Повинно бути алгоритм, який ви використовуєте для обчислення того, яким повинен бути ранг. Наприклад: "на основі записів кошика" - як на основі? У базі даних повинно бути щось, що є драйвером для підрахунку рангу. Це може бути поєднання декількох речей, але ці речі повинні якимось чином зберігатися в таблиці клієнтів або в таблицях, пов’язаних із замовником. Якщо він є в даних, то ви можете створити функцію, яка його обчислює. Якщо ви можете обчислити його, ви можете зберігати його та індексувати його.
Джоел Браун

Скажімо, нам потрібно підтримувати порядок товарів у кошику для покупок, і користувач може «довільно» змінити користувач за допомогою веб-інтерфейсу. Як би ви зберігали такий список елементів у базі даних і як би ви підтримували порядок сортування?
Чітті

Якщо я вас правильно зрозумів, "довільно змінюючи" порядок товарів у кошику для покупок, ви маєте на увазі, що користувач може перетягувати предмети вгору та вниз у списку та викидати їх куди хоче. Я думаю, що це вражає мене, як трохи надуманого. Чому користувачі роблять це? Якби вони могли це зробити, чи зробили б це багато? Чи справді використання простої послідовності предметів у кошику викликає велику ефективність? Мені здається, що порядковий номер від одного до кількості предметів у кошику + FK до замовлення дав би вам потрібний індекс. Просто оновіть елементи, коли вас перетягують.
Джоел Браун

3
Кошик - це лише приклад, який я наводив, щоб показати, що є випадки, коли «ранг» може бути довільним. Можливо, це був не чудовий приклад. Черга DVD-мереж netflix може бути кращим прикладом. Тільки заради аргументу уявіть мережу netflix зі 100 к. Елементами, яку користувач може довільно упорядкувати, і він робить це щохвилини. Як би ви створили базу даних для зберігання упорядкованого списку фільмів у цій гіпотетичній програмі?
Чітті

1

Це обмеження не-RDBMS, як simpleDB. Необхідні функції не можуть бути реалізовані на стороні БД у simpleDB, вони повинні бути реалізовані з боку програмування / програми.

Для таких RDBMS, як SQL serverпотрібні функції, рудиментарні до кластерного індексу.

  • Вставка (x) - Вставте запис x у таблицю> Проста вставка.
  • Видалити (x) - Видалити запис x із таблиці> Просте видалення.
  • До (x, n) - Повернення записів 'n' перед записом x у відсортованому списку. > Виберіть верхній результат n, де x менше значення та порядок за пунктом.

  • Після (x, n) - Повернення записів 'n', що завершили запис x, у відсортованому списку. > Виберіть верхній результат n, де x більше значення та порядок за пунктом.

  • Перший (n) - Повернення перших записів 'n' зі списку відсортованих. > Виберіть топ n результатів.

  • Останнє (n) - Повернення останніх «n» записів із відсортованого списку. > Виберіть топ n результатів після замовлення за дес.

  • Порівняйте (x, y) - Давши два записи x і y з таблиці, знайдіть, якщо x> y. > Оператор TSQL IF.

SimpleDB забезпечує автоматичні покажчики, сортування та основну мову запитів . Моя проблема залишиться, навіть якщо я оберу RDBMS. Проблема полягає в тому, що ранжування даних у моїй базі даних змінюється довільно, і вони не можуть бути захоплені як єдине властивість (якщо я не використовую спеціальний стовпець рейтингу), який можна індексувати.
Чітті

0

Ось що я використовував для перевпорядкування таблиці Postgres після кожної вставки:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

Що стосується мого використання, продуктивність не викликає занепокоєння, але важлива впевненість у тому, що вона ніколи не зламається чи діятиме дивно.

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