需要一种在SQL数据库中存储/查询json的有效方法

时间:2012-06-25 15:20:44

标签: sql database nosql acid document-database

我正在实现一项服务,每个用户必须拥有自己的json / document数据库。除了让用户通过示例查询json文档之外,数据库还必须支持涉及多个文档的ACID事务,因此我放弃使用Couch / Mongo或其他NoSQL数据库(不能使用RavenDB,因为它必须在Unix系统上运行)。 / p>

考虑到这一点,我一直在努力想办法在SQL数据库之上实现它。这是我到目前为止所提出的:

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

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

每个用户都有一个包含这两个表的数据库,用户必须声明他需要查询哪些字段,以便系统可以正确填充“索引”表。因此,如果用户“A”将其帐户配置为按“名称”和“年龄”启用查询,则每次用户插入具有“名称”或“年龄”属性的文档时,系统也会将记录插入“索引” table,'property'列包含name / age,'value'将包含属性值,'document_id'将指向相应的文档。

例如,假设用户插入以下doc:

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

这将导致对'documents'表的插入以及对'indexes'表的两次插入:

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);

然后假设用户'A'向服务发送了以下查询:

'{"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 AND条件),这会导致子查询嵌套非常高,上面的SELECT查询效率最高数据库系统(postgres,mysql ......)?
  • 上述解决方案是否适用于最终将包含数百万/亿亿个json文档的数据库?
  • 有更好的方法来满足我的要求吗?
  • 是否有可扩展的文档数据库可以执行涉及多个文档的ACID事务并在Unix系统上运行?

1 个答案:

答案 0 :(得分:5)

您的indexes表格就是所谓的Entity-Attribute-Value

EAV表适用于存储信息并在您了解实体时调用它。 (在您的情况下,当您知道indexes时,查找所有document_id行。)

但是反过来它们可怕:提供属性 - 值组合来搜索实体。这正是您在最终查询中所拥有的。随着越来越多的实体共享相同的属性值组合(例如name=foo,查询性能会下降。

那么,回答你的前两个问题:
  1.编写的查询在搜索n属性时需要n个子查询。随着n的增长,这将扩展得很差   随着记录数量的增加,它将会降低,特别是有数百万/十亿记录。

一般来说,如果您阅读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(*)子句。