Як швидко шукати дуже великий список рядків / записів у базі даних


32

У мене є така проблема: у мене є база даних, що містить понад 2 мільйони записів. У кожному записі є рядкове поле X, і я хочу відобразити список записів, для якого поле X містить певний рядок. Кожен запис має розмір близько 500 байт.

Щоб зробити це більш конкретним: у графічному інтерфейсі моєї програми у мене є текстове поле, куди я можу ввести рядок. Над текстовим полем я маю таблицю із відображенням (перший N, наприклад 100) записів, які відповідають рядку в текстовому полі. Коли я набираю або видаляю один символ у текстовому полі, вміст таблиці повинен бути оновлений на льоту.

Цікаво, чи існує ефективний спосіб зробити це за допомогою відповідних індексних структур та / або кешування. Як було пояснено вище, я хочу лише показати перші N елементів, які відповідають запиту. Тому для N досить малих, це не повинно бути великим питанням завантаження відповідних елементів із бази даних. Крім того, кешування елементів в основній пам'яті може зробити пошук швидшим.

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

EDIT

Я провів перший експеримент. Я розділив записи на різні текстові файли (максимум 200 записів на файл) і розмістив файли в різних каталогах (я використовував вміст одного поля даних для визначення дерева каталогу). У кінцевому підсумку я маю близько 50000 файлів у приблизно 40000 каталогах. Потім я запустив Lucene, щоб індексувати файли. Пошук рядка за допомогою демо-програми Lucene досить швидкий. Розщеплення та індексація зайняли кілька хвилин: це для мене цілком прийнятно, оскільки це статичний набір даних, який я хочу запитувати.

Наступним кроком є ​​інтеграція Lucene в основну програму та використання звернень, повернутих Lucene, для завантаження відповідних записів у основну пам'ять.


2
2 мільйони записів * 500 байт = 1 ГБ даних. Це дуже багато даних для пошуку, незалежно від того, який шлях ви будете використовувати - чи кожне значення X може бути унікальним, або у вас буде багато записів з однаковим значенням X?

1
Це також було б багато даних для спроби зберігати в пам'яті як кеш для швидкого пошуку. Це було б більше ніж 1 Гб за сеанс користувача.
maple_shaft

Мій попередній коментар передбачає веб-додаток. Це веб-додаток?
maple_shaft

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

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

Відповіді:


20

Замість того, щоб розміщувати свої дані всередині БД, ви можете зберігати їх як набір документів (текстові файли) окремо і зберігати посилання (шлях / URL тощо) у БД.

Це важливо, оскільки запит SQL за дизайном буде дуже повільним як у пошуку підрядків, так і в пошуку.

Тепер ваша проблема сформульована як необхідність пошуку текстових файлів, що містять набір рядків. Тут є дві можливості.

  1. Збіг підрядків Якщо текстові краплі - це одне слово або слово (без пробілів), і вам потрібно шукати в ньому довільну підрядку. У таких випадках потрібно проаналізувати кожен файл, щоб знайти найкращі можливі файли, які відповідають. Один використовує такі алгоритми, як алгоритм Boyer Moor. Детальніше дивіться у цьому та цьому . Це також рівнозначно grep - тому що grep використовує подібні речі всередині. Але ви все одно можете зробити принаймні 100+ греп (найгірший випадок 2 мільйони) перед поверненням.

  2. Індексований пошук. Тут ви припускаєте, що текст містить набір слів, а пошук обмежений фіксованою довжиною слова. У цьому випадку документ індексується за всіма можливими зустрічами слів. Це часто називається "Повний текст пошуку". Існує кількість алгоритмів для цього та кількість проектів з відкритим кодом, які можна використовувати безпосередньо. Багато з них, а також підтримка дикого пошуку карти, приблизний пошук і т.д. , як зазначено нижче:
    а. Apache Lucene: http://lucene.apache.org/java/docs/index.html
    b. OpenFTS: http://openfts.sourceforge.net/
    c. Сфінкс http://sphinxsearch.com/

Швидше за все, якщо вам потрібні "фіксовані слова" як запити, підхід два буде дуже швидким та ефективним.


2
Це цікава концепція, але, мабуть, малоймовірно, що розробник може легко шукати 1 Гб текстових даних швидше та ефективніше, ніж механізм баз даних. Набагато розумніші люди, ніж ви, і я працювали над оптимізаторами запитів, щоб робити саме це, і трохи наївно думати, що ви можете якось зробити це більш ефективно.
maple_shaft

4
@maple_shaft Наведені мною приклади не є двигунами бази даних RDBMS. Вони більше схожі на "пошукові системи", якщо ви хочете це назвати. Існує величезна концептуальна різниця між збиранням списку з індексу (або хеш-таблиці) і пошуком 1 Гб даних знову і знову кожного разу, коли виникає запит. Тож те, що я пропоную, - це не незначна зміна.
Діпан Мехта

Це здається цікавою ідеєю, але мені цікаво, як би це працювало. У мене було б понад 2 000 000 файлів, розміром близько півкілобайт. Або ви пропонуєте мати більше одного запису на файл? Яка буде різниця у базі даних?
Джорджіо

Я не впевнений, що це обов'язково краще, ніж, скажімо, повнотекстовий індекс SQL.
Кірк Бродхерст

@Giorgio - так, так працювали б повнотекстові пошукові системи. Ключова відмінність тут - попередньо проіндексовані сторінки та пошук у пам'яті (знову щоразу, коли надходить запит).
Діпан Мехта

21

Ви шукаєте технологію повнотекстової індексації. Більшість RDBMS мають якісь вбудовані можливості, які можуть працювати тут, або ви можете використовувати щось на зразок Lucene, якщо хочете отримати більш фантазії та / або просто запустити його в пам'яті.


1
На мою думку, параметри повного тексту в будь-якій RDBMS є вирішенням для того, щоб змусити його робити те, на що він не призначений: "пошук у якійсь купі неструктурованих непов'язаних даних". Якщо ви будуєте searchchengine, ви просто не використовуєте RDBMS. Це може працювати для невеликих наборів даних, але не потребує будь-якого масштабування. Пошук по палі неструктурованих даних - це не цвях, тому не використовуйте молоток. Використовуйте правильний інструмент для роботи.
Пітер Б

8

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


1
ТАК! Я думав про структуру дерева, і я згадав, що щось подібне могло б мені подобатися, але я не пам’ятав трійки, бо я ніколи їх не використовував. Щодо вимоги пам’яті: пам’ятайте, що мені потрібно отримати лише перші N записів (наприклад, N = 100), оскільки немає сенсу заповнювати таблицю з 20000 зверненнями. Отже кожен вузол трійки вказував би на щонайбільше N записів. Також я забув зазначити, що мені потрібен швидкий доступ, але мені не потрібно швидкого оновлення, оскільки дані завантажуються лише один раз. Ідея трійки на перестановленому індексі справді могла б працювати!
Джорджіо

1
Хороша відповідь, але, як ви зазначаєте, трие чудово підходить для відповідності початку ваших слів, але швидко вийде складним і дуже великим, якщо відповідати будь-якій підрядці ...
Кірк Бродхерст

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

5

Я хотів би додати відповідь Уайата Барнетта, що рішення RDBMS з повнотекстовим індексуванням у відповідному стовпчику буде працювати, але якщо ви хочете використовувати локальний кеш попередньо отриманих записів, тоді вам потрібно скласти план використання цих кешованих записів. на вашу користь.

Один із варіантів - зібрати унікальні ідентифікатори цих записів, які ви НАЙКРАЙНО не бажаєте отримувати з запиту та включати їх, можливо, в a NOT INчи a NOT EXISTS.

Хоча слово обережності, використання NOT INабо, NOT EXISTSяк правило, не є дешевим, і МОЖЕ негативно впливати на ефективність запиту або план запитів, залежно від того, який механізм бази даних ви використовуєте. Запустіть план пояснення щодо остаточного запиту, щоб переконатися, що всі ваші індекси на постраждалих стовпцях використовуються

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


maple_shaft та @Wyatt Barnett: Дякую за пропозиції. Мені доведеться почитати і випробувати різні рішення. Не всі бази даних підтримують повну індексацію, MySQL (яку я зараз використовую) робить ( dev.mysql.com/doc/refman/5.5/uk/fulltext-search.html ). Я спробую зробити кілька тестів, а потім звітувати тут.
Джорджіо

2

Про всяк випадок, коли ви його пропустили. Якщо ви використовуєте Lucene для своєї бази даних замість текстового пошуку, що підтримується в DB, ​​вам доведеться бути дуже обережними під час внесення змін до своєї БД. Як ви переконаєтесь у тому, що ви можете мати атомність, коли вам доведеться вносити зміни як у БД, так і на зовнішні ресурси (Lucene)? Так, це можна зробити, але роботи буде багато.

Коротше кажучи, ви втрачаєте підтримку транзакцій БД, якщо помістити Lucene у свою схему даних.


1
Як заявлена ​​проблема, так чи інакше не підходить для RDMS.
Пітер Б

1

Ви розглядали сфінкса? http://sphinxsearch.com, якщо ви можете скористатись стороннім інструментом, це було б ідеально для того, що ви намагаєтесь досягти, його набагато ефективніше при повнотекстовому пошуку, ніж будь-які RDBMS, якими я особисто користувався.


3
а голос "за" - за?
прутик

1

Дещо дивно, що жодна з відповідей не представляла термін "перевернутий індекс" , технологію, що лежить в основі всіх рішень, аналогічних Apache Lucene та ін.

Перевернутий індекс - це зіставлення від слів до документів ("інвертований індекс рівня" запису) або навіть точних розташувань слів у документі ("інвертований індекс на рівні слів").

І ІЛИ АБО логічні операції є тривіальними для реалізації. Якщо у вас є точні місця розташування слів, можна шукати суміжні слова, таким чином роблячись можливим пошук фрази.

Отже, подумайте про індекс, що містить (слово, файл, місцезнаходження) кортежі. Якщо у вас є напр. ("Перевернутий", "foo.txt", 123), ви просто перевіряєте, чи ("індекс", "foo.txt", 124) є частиною індексу для пошуку повної фрази "перевернутий індекс" .

Хоча я не рекомендую вам повторно реалізувати повнотекстову пошукову систему з нуля, корисно знати, як працюють такі технології, як Apache Lucene.

Отже, моя рекомендація - дізнатися, як працюють перевернуті індекси та вибрати технологію, використовуючи їх, наприклад Apache Lucene. Тоді ви, принаймні, добре розумієте, що можна зробити, а що не можна зробити.

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