我正在实现一项服务,每个用户必须拥有自己的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')
我的问题:
答案 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(*)
子句。