我正在阅读Hadoop:汤姆怀特的权威指南。在第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实现和
查询计划者的策略......
预期的穿孔和expermiental结果
我无法想象会导致此类问题的数据集,您必须编写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;
(请注意每个查询中的“限制10”。顺便说一下,我知道我不能限制和订购工会,但为了便于阅读,我已经删除了包装选项)
每个子查询的运行速度应与在索引O(logN)中找到正确的位置并返回10个项目一样快。如果我们重复K次,我们得到O(KlogN)。
即使查询规划器非常糟糕以至于它无法优化第一个查询,我们总是可以将其转换为带有联合的查询并获得所需的性能而无需在pl / python中编写任何内容。
为了仔细检查我的计算,我运行了一个postgresql上面的查询,其中填充了9,000,000个测试记录。结果证实了我的期望,对于第一个查询,两个查询都非常快100毫秒,第二个查询是300毫秒(具有联合的查询)。
因此,如果查询在100ms内运行9,000,000(logn = 23)个记录,那么对于9,000,000,000(logn = 33)记录,它应该在140ms内运行。
问题
- 你认为上述推理存在任何缺陷吗?
- 您能想象一下您需要在pl / python中重写上述查询的数据集吗?
- 你看到这种查询在O(K log n)中不起作用的情况吗?
他们断言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个结果,这听起来就像他们自己写的解决方案。
我不认为汤姆怀特说关系数据库是“坏”;它们不适用于非关系型,非基于数据的非关系数据。
众所周知,深度对象图不适合关系数据库。它们通常存在于几何数据的CAD表示等问题中,其中组件由零件组件组成。实际上,参考链很长。
自从我在90年代早期意识到这些问题以来,对象和图形数据库一直是解决这类问题的方法。
关系数据库对于基于集合的关系数据非常重要。但所有数据都不属于该类别。这就是NoSQL获得大脑共享的原因。
我认为这就是你引用的例子。
RDBMS用于您没有想到的查询。一旦确定您想要什么,您就可以应用最佳解决方案。
使用SQL或NoSQL,如果以错误的方式设计查询,性能将会非常糟糕。
我会通过在where子句中添加时间戳检查来修复该示例。如果您有大量数据,您可以假设最近的10个条目来自最后一分钟 - 那么为什么要尝试阅读和排序上个月的所有内容?
我可以轻松地设计相同的例子,使NoSQL看起来很糟糕,因为默认情况下你只能按ID找到记录,你需要加载整个数据集来找到你需要的记录,忽略设置各种二级的能力/自定义索引将使您比重要的查询的SQL性能更好。