我正在阅读Hadoop:Tom White的权威指南。在第13.6节“HBase与RDMS”中,他说如果你有很多数据,即使是简单的查询,比如获得10个最近的项目也要非常昂贵,他们不得不使用python和PL / SQL重写它们。
他提供了以下查询作为示例:
SELECT id, stamp, type FROM streams
WHERE type IN ('type1','type2','type3','type4',...,'typeN')
ORDER BY stamp DESC LIMIT 10 OFFSET 0;
并说:“RDBMS查询计划程序将此查询视为如下:
MERGE (
SELECT id, stamp, type FROM streams
WHERE type = 'type1' ORDER BY stamp DESC,
...,
SELECT id, stamp, type FROM streams
WHERE type = 'typeK' ORDER BY stamp DESC
) ORDER BY stamp DESC LIMIT 10 OFFSET 0;
这里的问题是我们追求的 只有前10个ID,但查询 计划者实际上实现了一个 整个合并然后限制在 结束。 ......实际上我们竟然如此 编写自定义PL / Python脚本 表演了一个堆。 ......在... 几乎所有情况下,这都表现优异 本机SQL实现和 查询计划者的策略......
预期的穿孔和精子结果
我无法想象会导致此类问题的数据集,您必须编写pl / python来执行此类简单查询。所以我已经玩了一段时间来解决这个问题并得出以下观察结果:
此类查询的性能受O(KlogN)限制。因为它可以翻译成如下:
SELECT * FROM (
SELECT id, stamp, type FROM streams
WHERE type = 'type1' ORDER BY stamp DESC LIMIT 10,
UNION
...,
SELECT id, stamp, type FROM streams
WHERE type = 'typeK' ORDER BY stamp DESC LIMIT 10
) t ORDER BY stamp DESC LIMIT 10;
(请注意每个查询的'LIMIT 10'。顺便说一句,我知道我不能限制和订购工会,但为了便于阅读,我已经删除了包装选项)
每个子查询的运行速度应该与在索引O(logN)中找到正确的位置并返回10个项目一样快。如果我们重复K次,我们得到O(KlogN)。
即使查询规划器非常糟糕以至于它无法优化第一个查询,我们总是可以将其转换为带有联合的查询并获得所需的性能而无需在pl / python中编写任何内容。
为了仔细检查我的计算,我运行了一个postgresql上面的查询,其中填充了9,000,000条测试记录。结果证实了我的期望,对于第一个查询,两个查询都非常快100毫秒,第二个查询是300毫秒(具有联合的查询)。
因此,如果查询在100毫秒内运行9,000,000(logn = 23)个记录,那么对于9,000,000,000(logn = 33)个记录,它应该在140毫秒内运行。
问题
答案 0 :(得分:6)
他们断言RDMBS查询规划器将该解决方案用于查询是不正确的,至少对于Postgresql 9.0而言,我应该想象其他平台。我用类似的查询进行了快速测试:
explain select * from client_attribute where client_attribute_type_code in ('UAG', 'RFR', 'IPA', 'FVD') order by client_attribute_id desc limit 10;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..0.93 rows=10 width=85)
-> Index Scan Backward using client_attribute_pkey on client_attribute (cost=0.00..15516.47 rows=167234 width=85)
Filter: (client_attribute_type_code = ANY ('{UAG,RFR,IPA,FVD}'::bpchar[]))
(3 rows)
这里对client_attribute_id进行索引,因此它完全符合要求 - 遍历索引,应用过滤器并在输出达到限制时停止。
如果未对订购列编制索引,则需要进行表扫描和排序,但只能进行一次表扫描:
explain analyze select * from client_attribute where client_attribute_type_code in ('UAG', 'RFR', 'IPA', 'FVD') order by updated desc limit 10;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=13647.00..13647.03 rows=10 width=85) (actual time=180.961..180.964 rows=10 loops=1)
-> Sort (cost=13647.00..14065.09 rows=167234 width=85) (actual time=180.960..180.961 rows=10 loops=1)
Sort Key: updated
Sort Method: top-N heapsort Memory: 26kB
-> Seq Scan on client_attribute (cost=0.00..10033.14 rows=167234 width=85) (actual time=0.010..106.791 rows=208325 loops=1)
Filter: (client_attribute_type_code = ANY ('{UAG,RFR,IPA,FVD}'::bpchar[]))
这使用了一个heapsort来保持顺序扫描过程中的前10个结果,这听起来就像他们自己编写的解决方案。
答案 1 :(得分:4)
我认为Tom White并不认为关系数据库“糟糕”;对于非关系型,非基于数据的数据,它们不是最佳的。
很长一段时间以来,众所周知,深度对象图不适合关系数据库。它们通常存在于几何数据的CAD表示等问题中,其中组件由零件组件组成。实际上,参考链很长。
对象和图形数据库一直是这类问题的解决方案,因为我在90年代初就意识到了这些问题。
关系数据库对于基于集合的关系数据非常重要。但所有数据都不属于该类别。这就是NoSQL获得大脑共享的原因。
我认为这就是你引用的例子。
答案 2 :(得分:1)
RDBMS用于您没有想到的查询。一旦确定您想要什么,就可以应用最佳解决方案。
答案 3 :(得分:1)
使用SQL或NoSQL,如果以错误的方式设计查询,性能将会非常糟糕。
我会通过在where子句中添加时间戳检查来修复该示例。如果您有大量数据,您可以假设最近的10个条目来自最后一分钟 - 那么为什么要尝试阅读和整理上个月的所有内容?
我可以轻松地设计相同的例子,使NoSQL看起来很糟糕,因为默认情况下你只能按ID找到记录,你需要加载整个数据集来找到你需要的记录,忽略设置的能力各种辅助/自定义索引可以使您比重要的查询的SQL性能更好。