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


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

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

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

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

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

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

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

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

5 98

5 ответов:

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

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

  • "тупой" подход: 1 пользователь = 1 Состояние = 1 полная рабочая копия репозитория, который сервер поддерживает для пользователя. Даже если речь идет о довольно небольшом документе база данных (например, 100s MiBs) с ~100K пользователей, поддерживая полный клон репозитория для всех из них, делает использование диска проходящим через крышу (т. е. 100K пользователей раз 100MiB ~ 10 TiB). Что еще хуже, клонирование репозитория 100 MiB каждый раз занимает несколько секунд времени, даже если это делается в довольно эффективном maneer (т. е. не используется git и распаковка-переупаковка материала), что неприемлемо, IMO. И что еще хуже-каждое редактирование, которое мы применяем к главному дереву, должно быть потянуто к каждому пользователю репозиторий, который является (1) ресурсом hog, (2) может привести к неразрешенным конфликтам редактирования в общем случае.

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

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

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

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

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

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

тем не менее, обратите внимание, что я намеренно рассчитал количество довольно небольших баз данных и пользовательской базы: 100K пользователей, 1K активных пользователей, 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 и хранить в значительной степени полную проверку на стороне клиента, а не на стороне сервера.

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

  • сама картина наличия" Толстого " состояния редактирования пользователя спорна с точки зрения обычных ORMs, таких как ActiveRecord, Hibernate, DataMapper, Tower и т. д.
  • сколько Я искал, есть нулевая существующая бесплатная кодовая база для такого подхода к git из популярных фреймворков.
  • есть хотя бы одна услуга, которая каким - то образом удается сделать это эффективно-это очевидно github - но, увы, их кодовая база является закрытым исходным кодом, и я сильно подозреваю, что они не используют обычные методы хранения git-серверов / РЕПО внутри, т. е. они в основном реализовали альтернативные "большие данные" git.

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

действительно интересный подход. Я бы сказал, что если вам нужно хранить данные, используйте базу данных, а не репозиторий исходного кода, который предназначен для очень конкретной задачи. Если вы можете использовать git out-of-the-box, то это нормально, но вам, вероятно, нужно построить слой репозитория документов над ним. Таким образом, вы могли бы построить его над традиционной базой данных, а также, не так ли? И если это встроенный контроль версий, который вас интересует, почему бы просто не использовать один из хранилище документов с открытым исходным кодом инструменты? Есть много, чтобы выбрать из.

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

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

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

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

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

пока еще идет работа ( ...и слегка пренебречь ) превратился версии просто так .

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

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

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

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

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

из коктейля выше, вот что я сейчас завариваю

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

некоторые забавные факты - git фактически делает четкие вещи для оптимизации хранения, такие как сжатие и хранение только дельт между ревизиями объектов - Да, git хранит только наборы изменений или дельты между ревизиями объектов данных, где это применимо (он знает, когда и как) . Ссылка: packfiles, глубоко в кишки Git internals - Обзор хранения объектов git (content-addressable filesystem), показывает поразительное сходство ( с точки зрения концепции) с базами данных noSQL, такими как mongoDB. Опять же, за счет пота капитала, это может обеспечить более интересные возможности для интеграции 2, и производительность настройки

Если Вы зашли так далеко, позвольте мне, если вышеизложенное может быть применимо к вашему случаю, и предполагая , что это будет так, как это будет соответствовать некоторым аспектам в вашем последнем всестороннем анализе производительности

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

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

фокус в том, чтобы объединить в Git GIT_INDEX_FILE переменная окружения с инструментами для создания 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 но разрешение конфликта, возможно, самая трудная часть здесь.

идеи, чтобы сделать его легче приветствуются.

реализовал Руби библиотека на libgit2 Что делает это довольно легко реализовать и исследовать. Есть некоторые очевидные ограничения, но это также довольно освобождающая система, так как вы получаете полную цепочку инструментов git.

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