Использование репозитория git как серверной части базы данных


119

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

Пользователи этой системы выполняют несколько типов операций:

  • получение этих документов по ID
  • поиск документов по некоторым из структурированных атрибутов внутри них
  • редактирование документов (т.е. добавление / удаление / переименование / объединение); каждая операция редактирования должна быть записана как транзакция с некоторым комментарием
  • просмотр истории записанных изменений для конкретного документа (включая просмотр того, кто, когда и почему изменил документ, получение более ранней версии - и, возможно, возврат к этой, если потребуется)

Конечно, традиционным решением для этой проблемы было бы использование какой-то базы данных документов (например, CouchDB или Mongo), однако эта вещь с контролем версий (история) соблазнила меня на дикая идея - почему бы мне не использовать gitрепозиторий в качестве серверная часть базы данных для этого приложения?

На первый взгляд, это можно решить так:

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

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

В принципе, скажите мне, будет ли это решение работать и почему оно будет работать или нет?


Ответы:


58

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

Как правило, первый главный момент, который упускается из виду, заключается в том, что я имею дело с многопользовательской системой, которая работает параллельно, одновременно с использованием моего сервера с тонким клиентом (т.е. просто веб-браузером). Таким образом, я должен поддерживать состояние для всех. Есть несколько подходов к этому, но все они либо слишком требовательны к ресурсам, либо слишком сложны для реализации (и, таким образом, в некотором роде убивают первоначальную цель выгрузки всей сложной реализации на git):

  • «Тупой» подход: 1 пользователь = 1 состояние = 1 полная рабочая копия репозитория, который сервер поддерживает для пользователя. Даже если мы говорим о довольно небольшой базе данных документов (например, 100 МБ) с ~ 100 тыс. Пользователей, поддержание полного клона репозитория для всех из них делает использование диска чрезмерным (т.е. 100 тыс. Пользователей умножить на 100 МБ ~ 10 ТиБ) , Что еще хуже, клонирование репозитория 100 MiB каждый раз занимает несколько секунд, даже если оно выполняется достаточно эффективно (то есть без использования git и распаковки-переупаковки), что неприемлемо, IMO. И что еще хуже - каждое редактирование, которое мы применяем к основному дереву, должно быть перенесено в репозиторий каждого пользователя, что (1) потребляет ресурсы, (2) может привести к неразрешенным конфликтам редактирования в общем случае.

    По сути, это может быть так же плохо, как O (количество правок × данные × количество пользователей) с точки зрения использования диска, и такое использование диска автоматически означает довольно высокую загрузку ЦП.

  • Подход «Только активные пользователи»: поддерживать рабочую копию только для активных пользователей. Таким образом, вы обычно храните не полный репо-клон для каждого пользователя, а:

    • Когда пользователь входит в систему, вы клонируете репозиторий. Это занимает несколько секунд и ~ 100 Мбайт дискового пространства на одного активного пользователя.
    • По мере того как пользователь продолжает работать на сайте, он работает с данной рабочей копией.
    • Когда пользователь выходит из системы, его клон репозитория копируется обратно в основной репозиторий в виде ветки, таким образом сохраняются только его «непримененные изменения», если таковые имеются, что довольно экономно.

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

  • Подход «разреженной проверки»: создание «разреженной проверки» вместо полномасштабного клона репо для каждого активного пользователя не очень помогает. Это может примерно в 10 раз сэкономить дисковое пространство, но за счет гораздо большей нагрузки на ЦП / диск при выполнении операций, связанных с историей, что убивает цель.

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

Тем не менее, обратите внимание, что я намеренно рассчитал количество довольно небольшой базы данных и базы пользователей: 100 000 пользователей, 1 000 активных пользователей, общая база данных 100 MiB + история изменений, 10 MiB рабочей копии. Если вы посмотрите на более известные краудсорсинговые проекты, там гораздо больше цифр:

│              │ 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 и сохранять почти полную проверку на стороне клиента, а не на стороне сервера.

Есть и другие моменты, которые я упустил, но они не так уж плохи по сравнению с первым:

  • Очень образец, имеющий редактировать состояние «толстых» пользователя, является спорным с точки зрения обычных ORMs, таких как ActiveRecord, Hibernate, DataMapper, башни и т.д.
  • Насколько я искал, в популярных фреймворках не существует бесплатной базы кода для реализации такого подхода к git.
  • Есть по крайней мере одна служба, которая каким-то образом справляется с этим эффективно - это, очевидно, github - но, увы, их кодовая база закрыта, и я сильно подозреваю, что они не используют внутри обычные методы хранения git-серверов / репо, т.е. они в основном реализованы альтернативный "big data" git.

Таким образом, нижняя строка : это является возможным, но для большинства современных usecases не будет в любом месте вблизи оптимального решения. Лучшей альтернативой, вероятно, будет сворачивание вашей собственной реализации документа-редактирования-истории-в-SQL или попытка использовать любую существующую базу данных документов.


16
Возможно, немного опоздал на вечеринку, но у меня было аналогичное требование к этому, и я фактически пошел по git-маршруту. Покопавшись во внутренностях git, я нашел способ заставить его работать. Идея состоит в том, чтобы работать с голым репозиторием. Есть некоторые недостатки, но я считаю, что это работоспособно. Я написал все в сообщении, которое вы, возможно, захотите проверить (во всяком случае, ради интереса): kenneth-truyers.net/2016/10/13/git-nosql-database
Kenneth

Еще одна причина, по которой я этого не делаю, - это возможности запросов. Хранилища документов часто индексируют документы, что упрощает поиск в них. Это будет непросто с git.
FrankyHollywood

12

Действительно интересный подход. Я бы сказал, что если вам нужно хранить данные, используйте базу данных, а не репозиторий исходного кода, который предназначен для очень конкретной задачи. Если бы вы могли использовать Git из коробки, то все в порядке, но вам, вероятно, потребуется построить над ним слой репозитория документов. Значит, вы могли бы построить его и на традиционной базе данных, верно? И если вас интересует встроенный контроль версий, почему бы просто не использовать один из инструментов репозитория документов с открытым исходным кодом ? Есть из чего выбирать.

Что ж, если вы все равно решите использовать бэкэнд Git, то в основном он будет работать в соответствии с вашими требованиями, если вы реализуете его, как описано. Но:

1) Вы упомянули «кластер серверов, которые толкают / подтягивают друг друга» - я думал об этом некоторое время, но все еще не уверен. Вы не можете нажать / вытащить несколько репозиториев как атомарную операцию. Интересно, могла ли быть возможность некоторого беспорядка слияния во время одновременной работы.

2) Возможно, вам это не нужно, но очевидная функциональность репозитория документов, который вы не указали, - это контроль доступа. Вы можете ограничить доступ к некоторым путям (= категориям) через подмодули, но, вероятно, вы не сможете легко предоставить доступ на уровне документа.


11

мои 2 пенса стоят. Немного тоскует, но ... У меня было подобное требование в одном из моих инкубационных проектов. Как и ваши, мои ключевые требования, когда база данных документов (xml в моем случае) с управлением версиями документов. Это было для многопользовательской системы с множеством вариантов использования для совместной работы. Я предпочел использовать доступные решения с открытым исходным кодом, которые поддерживают большинство ключевых требований.

Вкратце: я не смог найти ни одного продукта, который обеспечивал бы и то, и другое, достаточно масштабируемым (количество пользователей, объемы использования, хранилище и вычислительные ресурсы). Я был склонен к git из-за всех многообещающих возможностей, и (вероятные) решения, которые можно было бы найти из этого. По мере того, как я больше играл с опцией git, переход от перспективы одного пользователя к перспективе нескольких (милли) пользователей стал очевидной проблемой. К сожалению, мне не удалось провести серьезный анализ производительности, как это сделали вы. (.. ленив / рано бросить .... для версии 2, мантра) Power to you !. Как бы то ни было, моя предвзятая идея с тех пор трансформировалась в следующую (все еще предвзятую) альтернативу: набор инструментов, которые являются лучшими в своих отдельных сферах, базах данных и контроле версий.

Пока работа еще продолжается (... и немного игнорируется), преобразованная версия просто такая.

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

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

Как это будет (должно) работать, так это то, что основной обмен данными многопользовательского интерфейса осуществляется через базу данных. СУБД будет обрабатывать все забавные и сложные проблемы, такие как многопользовательский, параллелизм, атомарные операции и т. Д. На бэкэнде VCS будет выполнять контроль версий для одного набора объектов данных (без параллелизма или многопользовательских проблем). Для каждой действующей транзакции в базе данных контроль версий осуществляется только для тех записей данных, которые должны были эффективно измениться.

Что касается связующего клея, то он будет в форме простой функции взаимодействия между базой данных и VCS. С точки зрения дизайна, в качестве простого подхода будет интерфейс, управляемый событиями, с обновлением данных из базы данных, запускающим процедуры контроля версий (подсказка: предполагая , что Mysql, использование триггеров и sys_exec () бла-бла ...). С точки зрения сложности реализации, он будет варьироваться от простого и эффективного (например, создание сценариев) до сложного и замечательного (некоторый программный интерфейс коннектора). Все зависит от того, насколько сумасшедшим вы хотите пойти с этим, и сколько пота капитала вы готовы потратить. Я считаю, что волшебство должно творить простые сценарии. А для доступа к конечному результату, различным версиям данных, простой альтернативой является заполнение клона базы данных (скорее, клона структуры базы данных) данными, на которые ссылается тег версии / id / hash в 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но разрешение конфликтов, пожалуй, самая сложная часть здесь.

Приветствуются идеи по упрощению.


Обычно такой подход ни к чему не ведет, если только вы не хотите иметь полноценную концепцию транзакции и пользовательского интерфейса для ручного разрешения конфликтов. Общая идея конфликтов состоит в том, чтобы заставить пользователя разрешить их прямо при фиксации (т.е. «извините, кто-то другой редактировал этот документ, который вы редактировали -> просмотрите его правки и ваши правки и объедините их»). Когда вы позволяете двум пользователям успешно выполнить фиксацию, а затем обнаруживаете в задании async cron, что все пошло не так, как правило, нет никого, кто мог бы решить проблему.
GreyCat
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.