Нужен эффективный способ хранения/запроса json в базе данных SQL

Я реализую службу, в которой каждый пользователь должен иметь свою собственную базу данных json/document. Помимо предоставления пользователю возможности запрашивать документы json на примере, база данных также должна поддерживать транзакции ACID с участием нескольких документов, поэтому я отказался от использования Couch/Mongo или других баз данных NoSQL (не могу использовать RavenDB, поскольку он должен работать в системах Unix).

Имея это в виду, я пытался найти способ реализовать это поверх базы данных SQL. Вот что я придумал до сих пор:

CREATE TABLE documents (
  id INTEGER PRIMARY KEY,
  doc TEXT
);

CREATE TABLE indexes (
  id INTEGER PRIMARY KEY,
  property TEXT,
  value TEXT,
  document_id INTEGER
)

У каждого пользователя будет база данных с этими двумя таблицами, и пользователь должен будет объявить, какие поля ему нужно запрашивать, чтобы система могла правильно заполнить таблицу «Индексы». Таким образом, если пользователь «А» настраивает свою учетную запись для включения запросов по «имени» и «возрасту», каждый раз, когда этот пользователь вставляет документ со свойством «имя» или «возраст», система также будет вставлять запись в «индексы». таблица, где столбец «свойство» будет содержать имя/возраст, «значение» будет содержать значение свойства, а «document_id» будет указывать на соответствующий документ.

Например, предположим, что пользователь вставляет следующий документ:

'{"name" : "Foo", "age" 43}'

Это приведет к вставке в таблицу «документы» и еще двум вставкам в таблицу «индексы»:

INSERT INTO documents (id,doc) VALUES (1, '{"name" : "Foo", "age" 43}');
INSERT INTO indexes (property, value, document_id) VALUES ('name', 'foo', 1);
INSERT INTO indexes (property, value, document_id) VALUES ('age', '43', 1);

Затем предположим, что пользователь «А» отправил сервису следующий запрос:

'{"name": "Foo", "age": 43}' //(the queries are also json documents).

Этот запрос будет переведен в следующий SQL:

SELECT doc FROM documents
WHERE id IN (SELECT document_id FROM indexes
             WHERE document_id IN (SELECT document_id FROM indexes
                                   WHERE property = 'name' AND value = 'Foo')
             AND property = 'age' AND value = '43') 

Мои вопросы:

  • Зная, что пользователь может использовать большое количество условий в своих запросах (скажем, 20-30 условий И), что приведет к очень высокой вложенности подзапросов, насколько эффективным будет приведенный выше запрос SELECT в большинстве систем баз данных ( постгрес, mysql...)?
  • Подходит ли вышеуказанное решение для базы данных, которая в конечном итоге будет содержать миллионы/миллиарды документов json?
  • Есть ли лучший способ удовлетворить мои требования?
  • Существует ли масштабируемая база данных документов, которая может выполнять ACID-транзакции с несколькими документами и работает в системах Unix?

person Thiago de Arruda    schedule 25.06.2012    source источник
comment
PostgreSQL 9.2 будет поддерживать тип данных JSON, и с некоторыми функциями (например, написанными на JavaScript) вышеперечисленное должно быть возможно. См. пример здесь: people.planetpostgresql.org/andrew/index.php?/archives/   -  person a_horse_with_no_name    schedule 25.06.2012
comment
Посмотрите, подойдет ли вам CouchDB: CouchDB предоставляет семантику ACID. Это достигается за счет реализации формы контроля параллелизма нескольких версий, что означает, что CouchDB может без конфликтов обрабатывать большой объем одновременных операций чтения и записи.   -  person Void Ray    schedule 25.06.2012
comment
Интересный совет про PostgreSQL, посмотрю, спасибо   -  person Thiago de Arruda    schedule 25.06.2012
comment
Дмитрий, CouchDB является ACID только для операций с одним документом.   -  person Thiago de Arruda    schedule 25.06.2012


Ответы (1)


Ваша таблица indexes известна как Entity-Attribute-Value.

Таблицы EAV прекрасно подходят для хранения информации и ее вызова, когда вы знаете сущность. (В вашем случае найти все indexes строки, когда вы знаете document_id.)

Но они ужасны наоборот: предоставление комбинаций атрибут-значение для поиска сущности. Это именно то, что у вас есть в вашем последнем запросе. По мере того как все больше и больше сущностей используют одни и те же комбинации атрибутов и значений (например, name=foo), производительность запросов снижается.

Итак, чтобы ответить на первые два вопроса:
1. Запрос, как написано, требует n подзапросов при поиске n свойств. Это будет очень плохо масштабироваться по мере роста n.
2. По мере роста количества записей оно будет ухудшаться, особенно с миллионами/миллиардами записей.

В общем, если вы читали о EAV, люди настоятельно рекомендуют избегать его.


И, что еще хуже, в SQL нет хорошей альтернативы. Стандартным способом оптимизации поиска является индекс, который можно легко смоделировать как отсортированный набор данных. Но тогда вам потребуется много индексов:
– Индекс по (fieldX, fieldY, fieldZ) прекрасен, если вы выполняете поиск по всем трем столбцам.
– Но он отстой, если у вас есть для поиска только fieldZ.


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

Если вы не можете исправить количество столбцов (постоянно появляются новые properties) и/или у вас нет места для всех различных комбинаций индекса, вы, похоже, застряли с EAV . Это сработает, но не очень масштабируется с точки зрения "мгновенных" результатов.

ПРИМЕЧАНИЕ. Если вы придерживаетесь EAV, протестировали ли вы эту структуру запроса?

  SELECT
    document_id
  FROM
    indexes
  WHERE
       (property = 'name' AND value = 'Foo')
    OR (property = 'age'  AND value = '43' )
  GROUP BY
    document_id
  HAVING
    COUNT(*) = 2

Это предполагает, что (document_id, property, value) уникален. В противном случае один документ может иметь ('name', 'foo') дважды, и таким образом передать предложение COUNT(*).

person MatBailie    schedule 25.06.2012
comment
Я не думаю, что таблица «индексы» моделирует данные с использованием метода «сущность-атрибут-значение», это просто способ «вручную» индексировать бессхемные данные в таблице «документы». Я забыл упомянуть, что столбцы имени и значения также будут проиндексированы, вам не кажется, что это ускорит выполнение запросов? - person Thiago de Arruda; 25.06.2012
comment
@ThiadodeArruda - К сожалению, это именно EAV. Ваши Documents — это Entities. Ваши Properties - это Attributes. И ваши Values, ну, я думаю, вы поняли. Индексация (property, value, document_id), безусловно, улучшит ситуацию по сравнению с ее отсутствием, но это минимальное рабочее предположение. У вас все еще есть все трудности EAV. Он всегда будет значительно медленнее, чем «традиционный» стол. И чем больше записей имеют одинаковое значение для любого заданного свойства, тем медленнее оно будет работать. И чем больше объектов вы ищете, тем медленнее. - person MatBailie; 25.06.2012