Використання git-сховища як резервного бази даних


119

Я роблю проект, який займається структурованою базою документів. У мене є дерево категорій (~ 1000 категорій, до ~ 50 категорій на кожному рівні), кожна категорія містить кілька тисяч (до, скажімо, ~ 10000) структурованих документів. Кожен документ має кілька кілобайт даних у структурованому вигляді (я б вважав за краще YAML, але він може бути так само JSON або XML).

Користувачі цієї системи виконують кілька типів операцій:

  • отримання цих документів за ідентифікацією
  • пошук документів за деякими структурованими атрибутами всередині них
  • редагування документів (тобто додавання / видалення / перейменування / об'єднання); кожну операцію редагування слід записувати як транзакцію з деяким коментарем
  • перегляд історії записаних змін для конкретного документа (включаючи перегляд того, хто, коли і навіщо змінив документ, отримання попередньої версії - і, ймовірно, повернення до цієї, якщо вимагається)

Звичайно, традиційним рішенням було б використовувати якусь базу даних документів (наприклад, CouchDB чи Mongo) для цієї проблеми - однак, ця версія контролю версій (історія) спокусила мене дикою ідеєю - чому я не повинен використовувати gitсховище як база даних для цього додатка?

На перший погляд, це можна було вирішити так:

  • категорія = каталог, документ = файл
  • отримання документа за ідентифікатором => зміна каталогів + читання файлу в робочій копії
  • редагування документів із редагуванням коментарів => прийняття комітетів різними користувачами + зберігання повідомлень про фіксацію
  • історія => звичайний журнал git та пошук старих транзакцій
  • search => це трохи складніша частина, я думаю, що це потребує періодичного експорту категорії в реляційну базу даних з індексуванням стовпців, які ми дозволимо шукати за

Чи є в цьому рішенні інші загальні підводні камені? Хтось уже намагався реалізувати такий бекенд (тобто для будь-яких популярних фреймворків - RoR, node.js, Django, CakePHP)? Чи має це рішення якісь можливі наслідки для продуктивності чи надійності - тобто чи доведено, що git буде набагато повільніше, ніж традиційні рішення баз даних, або будуть якісь підводні масштаби масштабування / надійності? Я припускаю, що кластер таких серверів, які штовхають / витягують сховища один одного, повинен бути досить надійним та надійним.

В основному, скажіть, чи буде це рішення спрацювати і чому воно буде чи ні?


Відповіді:


58

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

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

  • "Тупий" підхід: 1 користувач = 1 стан = 1 повна робоча копія сховища, яке сервер підтримує для користувача. Навіть якщо ми говоримо про досить невелику базу даних документів (наприклад, 100s MiB) з ~ 100K користувачів, підтримуючи повний клон сховища для всіх них, зйомка диска працює через дах (тобто 100K користувачів разів 100MiB ~ 10 TiB) . Що ще гірше, що клонування сховища 100 MiB щоразу займає кілька секунд часу, навіть якщо це робиться досить ефективним маневром (тобто, не використовуючи git та розпаковуючи-перепаковуючи речі), що не прийнятно, IMO. І ще гірше - кожне редагування, яке ми застосовуємо до головного дерева, має бути перетягнуто на сховище кожного користувача, що (1) ресурс свиней, (2) може призвести до невирішених конфліктів редагування в цілому.

    В основному це може бути настільки ж погано, як O (кількість редагувань × дані × кількість користувачів) щодо використання диска, і таке використання диска автоматично означає досить високе використання процесора.

  • Підхід "Тільки активних користувачів": зберігати робочу копію лише для активних користувачів. Таким чином, ви зазвичай зберігаєте не повного репо-клонування на користувача, але:

    • Після входу користувача ви клонуєте сховище. Це займає кілька секунд і ~ 100 МіБ місць диска на активного користувача.
    • Оскільки користувач продовжує працювати на сайті, він працює з даною робочою копією.
    • Коли користувач виходить із системи, його клон сховища копіюється назад у основне сховище у вигляді гілки, таким чином зберігаючи лише його "не застосовані зміни", якщо такі є, що є досить просторовим.

    Таким чином, використання диска в цьому випадку досягає максимального значення O (кількість правок × даних × кількість активних користувачів), що зазвичай ~ 100..1000 разів менше кількості загальної кількості користувачів, але це робить вхід / вихід складнішим та повільнішим , оскільки це включає клонування гілки кожного користувача при кожному вході в систему і витягнення цих змін назад під час виходу з системи або закінчення сеансу (що слід робити транзакційно => додає ще один рівень складності). В абсолютних цифрах це знижується на 10 TiB використання диска до 10..100 Гбіт в моєму випадку, що може бути прийнятним, але, знову ж таки, ми зараз говоримо про досить невелику базу даних на 100 МБ.

  • Підхід "розрідженої каси": виготовлення "рідкої каси" замість повнорозмірного клона репо на кожного активного користувача не дуже допомагає. Це може заощадити ~ 10x використання дискового простору, але за рахунок набагато більшого завантаження процесора / диска на операціях, пов’язаних з історією, що вбиває мету.

  • Підхід "Workers pool": замість того, щоб робити щоденно повноцінних клонів для активної людини, ми можемо тримати пул "робітничих" клонів, готових до використання. Таким чином, кожен раз, коли користувач входить в систему, він займає одного "працівника", витягуючи туди свою гілку з головного репо, і, коли він виходить, він звільняє "працівника", який робить розумний git hard reset, щоб стати знову просто головний клон репо, готовий до використання іншим користувачем, який входить у систему. Не дуже допомагає використання диска (він все ще досить високий - лише повний клон на активного користувача), але принаймні робить вхід / вихід швидше, як рахунок ще більше складності.

Враховуючи це, зауважте, що я навмисно підраховував кількість досить невеликої бази даних та бази користувачів: 100 КК користувачів, 1 К активних користувачів, 100 МБ загальної бази даних + історія редагувань, 10 МБ робочої копії. Якщо ви подивитеся на більш відомі проекти з натовпу, тут є набагато більша кількість:

│              │ Users │ Active users │ DB+edits │ DB only │
├──────────────┼───────┼──────────────┼──────────┼─────────┤
│ MusicBrainz  │  1.2M │     1K/week  │   30 GiB │  20 GiB │
│ en.wikipedia │ 21.5M │   133K/month │    3 TiB │  44 GiB │
│ OSM          │  1.7M │    21K/month │  726 GiB │ 480 GiB │

Очевидно, що для таких обсягів даних / діяльності такий підхід був би абсолютно неприйнятним.

Як правило, це спрацювало б, якби можна було використовувати веб-браузер як "товстий" клієнт, тобто видавати git-операції та зберігати майже повний замовлення на стороні клієнта, а не на стороні сервера.

Є й інші моменти, які я пропустив, але вони не такі вже й погані порівняно з першим:

  • Сама структура наявності "товстого" режиму редагування користувача є суперечливою щодо нормальних ORM, таких як ActiveRecord, Hibernate, DataMapper, Tower тощо.
  • Скільки всього, що я шукав, немає нульової вільної бази коду для виконання такого підходу до git з популярних фреймворків.
  • Існує хоча б одна служба, яка якось вдається зробити це ефективно - це, очевидно, github - але, на жаль, їх база коду є закритим джерелом, і я сильно підозрюю, що вони не використовують звичайні git-сервери / методи зберігання репо, всередині, тобто вони в основному реалізовані альтернативні "великі дані" git.

Таким чином, нижня рядок : це є можливим, але для більшості сучасних usecases НЕ буде в будь-якому місці поблизу оптимального рішення. Згортання власної реалізації документа-редагування-історія-SQL або намагання використовувати будь-яку існуючу базу даних документів, можливо, буде кращою альтернативою.


16
Напевно, трохи спізнився на вечірку, але я мав подібну вимогу до цього і насправді пішов по git-route. Після деякого копання з внутрішніми органами git я знайшов спосіб змусити його працювати. Ідея полягає в роботі з голим сховищем. Є деякі недоліки, але я вважаю, що це працює. Я написав усе у дописі, який ви можете перевірити (якщо що, заради інтересів): kenneth-truyers.net/2016/10/13/git-nosql-database
Кеннет

Ще одна причина, чому я цього не роблю, - це можливості запиту. У магазинах документів часто індексуються документи, що полегшує пошук у них. Це не буде прямо з git.
FrankyHollywood

12

Дійсно цікавий підхід. Я б сказав, що якщо вам потрібно зберігати дані, використовуйте базу даних, а не сховище вихідного коду, розроблене для дуже конкретного завдання. Якщо ви можете використовувати Git поза вікном, то це добре, але вам, ймовірно, потрібно створити шар сховища документів над ним. Таким чином, ви можете створити його і над традиційною базою даних, правда? І якщо це вбудований контроль версій, який вас цікавить, чому б не просто використати один із інструментів сховища з відкритим кодом ? Є з чого вибрати.

Ну, якщо ви все-таки вирішите піти на Git backend, то в основному це спрацювало б під ваші вимоги, якби ви реалізували його, як описано. Але:

1) Ви згадали про "кластер серверів, які штовхають / тягнуть один одного" - я про це думав деякий час і досі не впевнений. Ви не можете натиснути / потягнути кілька репостів як атомну операцію. Цікаво, чи може бути можливість деякого безладу злиття під час одночасних робіт.

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


11

мій 2 пенси варто. Трохи туга, але ...... я мав подібну вимогу в одному зі своїх інкубаційних проектів. Як у ваших, мої ключові вимоги, коли база даних документів (xml в моєму випадку) з версією документа. Це було для багатокористувацької системи з безліччю випадків використання співпраці. Моєю перевагою було використання доступних рішень з відкритим кодом, які підтримують більшість ключових вимог.

Щоб вирішити переслідування, я не зміг знайти жодного продукту, який би забезпечив обидва способи, таким чином, що був достатньо масштабованим (кількість користувачів, обсяги використання, сховища та ресурси для обчислення). Я був упереджений до git за всі перспективні можливості, і (ймовірні) рішення, з яких можна було б вийти з нього. По мірі того, як я більше грав з git-варіантом, очевидним викликом стало перехід від точки зору одного користувача до перспективного (мілі) користувачів. На жаль, мені не вдалося зробити істотний аналіз ефективності, як ви. (.. лінивий / кинь рано ... для версії 2, мантра) Могу вам! У будь-якому разі, моя упереджена ідея з того часу перетворилася на наступну (все ще необ'єктивну) альтернативу: мережу інструментів, які є найкращими у своїх окремих сферах, базах даних та контролю версій.

Поки все ще працює (... і трохи нехтується), морфована версія просто така.

  • на фронті: (користувальницьке використання) використовувати базу даних для зберігання 1-го рівня (взаємодія з користувацькими додатками)
  • на бекенді, використовуйте систему управління версіями (VCS) (наприклад, git) для виконання версій об'єктів даних у базі даних

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

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

Що стосується клею, що поєднується, він буде виконуватись у вигляді простої функції взаємодії між базою даних та VCS. З точки зору дизайну, як простий підхід був би інтерфейс, керований подіями, оновлення даних із бази даних запускає процедури контролю версій (підказка: припускаючи Mysql, використання тригерів і sys_exec () blah blah ...). З точки зору складності реалізації, вона буде варіюватися від простого та ефективного (наприклад, сценаріїв) до складного та чудового (деякий програмований інтерфейс роз'єму). Все залежить від того, наскільки божевільний ви хочете піти з цим, і скільки потужного капіталу ви готові витратити. Я вважаю, що прості сценарії повинні робити магію. А для отримання кінцевого результату, різних версій даних, простою альтернативою є заповнення клона бази даних (більше клону структури бази даних) з даними, на які посилається тег версії / id / хеш у VCS. знову цей біт стане простим запитом / перекладом / картою завдання інтерфейсу.

Є ще деякі проблеми та невідомі, з якими потрібно вирішити, але я вважаю, що вплив та актуальність більшості з них значною мірою залежатимуть від ваших вимог та випадків використання. Деякі з них можуть просто не мати проблем. Деякі питання включають відповідність продуктивності між двома ключовими модулями, базою даних та VCS, для програми з активністю оновлення даних високої частоти, масштабування ресурсів (потужність зберігання та обробки) протягом часу на стороні git як даних, так і користувачів зростають: стійкі, експоненціальні або з часом плато

З коктейлю вище, ось що я зараз готую

  • використання Git для VCS (спочатку вважався старим хорошим CVS за рахунок використання лише наборів змін або дельт між двома версіями)
  • використання mysql (через сильно структурований характер моїх даних, xml зі строгими схемами xml)
  • пограти з MongoDB (спробувати базу даних NoSQl, яка тісно відповідає рідній структурі бази даних, що використовується в git)

Деякі цікаві факти - git насправді робить зрозумілі речі для оптимізації сховища, такі як стиснення та зберігання лише дельта між ревізією об'єктів - ТАК, git зберігає лише набори змін або дельти між ревізіями об'єктів даних, де це застосовно (він знає коли і як). Довідка: packfiles, глибоко в кишечнику внутрішніх приміщень Git - Огляд зберігання об'єктів git (файлова система, адресована вмістом), показує чітку схожість (з точки зору концепції) на базі даних noSQL, таких як mongoDB. Знову ж таки, за рахунок потужного капіталу, це може забезпечити більш цікаві можливості інтеграції 2 та налаштування продуктивності

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


4

Я впровадив бібліотеку Ruby, на вершині libgit2якої це дуже легко здійснити та дослідити. Є деякі очевидні обмеження, але це також досить звільняюча система, оскільки ви отримуєте повний інструментарій git.

Документація включає деякі ідеї щодо ефективності, компромісів тощо.


2

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

  • немає необхідності в окремих робочих копіях (використання диска обмежено зміненими файлами)
  • немає необхідності у трудомістких підготовчих роботах (за сеанс користувача)

Трюк полягає в поєднанні GIT_INDEX_FILEзмінної середовища середовища Git з інструментами для створення Git-коміктів вручну:

Наступний контур рішення (фактичні хеші SHA1, опущені з команд):

# Initialize the index
# N.B. Use the commit hash since refs might changed during the session.
$ GIT_INDEX_FILE=user_index_file git reset --hard <starting_commit_hash>

#
# Change data and save it to `changed_file`
#

# Save changed data to the Git object database. Returns a SHA1 hash to the blob.
$ cat changed_file | git hash-object -t blob -w --stdin
da39a3ee5e6b4b0d3255bfef95601890afd80709

# Add the changed file (using the object hash) to the user-specific index
# N.B. When adding new files, --add is required
$ GIT_INDEX_FILE=user_index_file git update-index --cacheinfo 100644 <changed_data_hash> path/to/the/changed_file

# Write the index to the object db. Returns a SHA1 hash to the tree object
$ GIT_INDEX_FILE=user_index_file git write-tree
8ea32f8432d9d4fa9f9b2b602ec7ee6c90aa2d53

# Create a commit from the tree. Returns a SHA1 hash to the commit object
# N.B. Parent commit should the same commit as in the first phase.
$ echo "User X updated their data" | git commit-tree <new_tree_hash> -p <starting_commit_hash>
3f8c225835e64314f5da40e6a568ff894886b952

# Create a ref to the new commit
git update-ref refs/heads/users/user_x_change_y <new_commit_hash>

Залежно від ваших даних, ви можете використовувати cron завдання для об'єднання нових відкликів до, masterале вирішення конфлікту, мабуть, найважча тут.

Ідеї, щоб зробити це простіше, вітаються.


Це взагалі підхід, який нікуди не веде, якщо ви не хочете мати повноцінну концепцію транзакцій та інтерфейс користувача для ручного вирішення конфлікту. Загальна ідея конфліктів полягає в тому, щоб змусити користувача вирішувати це право під час фіксації (тобто "вибачте, хтось інший редагував цей документ, який ви редагували -> будь ласка, перегляньте його правки та ваші правки та з'єднайте їх"). Коли ви дозволяєте двом користувачам успішно вчиняти та потім виявляти в асинхронній роботі, що все пішло на південь, зазвичай не існує жодного доступу до вирішення речей.
GreyCat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.