Thi*_*uda 5 sql database acid nosql document-database
我正在实现一个服务,每个用户必须拥有自己的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
)
Run Code Online (Sandbox Code Playgroud)
每个用户都有一个包含这两个表的数据库,用户必须声明他需要查询哪些字段,以便系统可以正确填充"索引"表.因此,如果用户"A"将其帐户配置为按"名称"和"年龄"启用查询,则每次用户插入具有"名称"或"年龄"属性的文档时,系统也会将记录插入"索引" table,'property'列包含name/age,'value'将包含属性值,'document_id'将指向相应的文档.
例如,假设用户插入以下文档:
'{"name" : "Foo", "age" 43}'
Run Code Online (Sandbox Code Playgroud)
这将导致对'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);
Run Code Online (Sandbox Code Playgroud)
然后,假设用户'A'向服务发送了以下查询:
'{"name": "Foo", "age": 43}' //(the queries are also json documents).
Run Code Online (Sandbox Code Playgroud)
此查询将转换为以下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')
Run Code Online (Sandbox Code Playgroud)
我的问题:
你的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
Run Code Online (Sandbox Code Playgroud)
这假设(document_id, property, value)是独一无二的.否则一个文档可能有('name', 'foo')两次,所以通过该COUNT(*)子句.
| 归档时间: |
|
| 查看次数: |
3196 次 |
| 最近记录: |