代码之家  ›  专栏  ›  技术社区  ›  Piotr Czapla

RDBMS是否像Hadoop:The权威指南中描述的那样糟糕?

  •  11
  • Piotr Czapla  · 技术社区  · 14 年前

    我在读汤姆·怀特的《Hadoop:权威指南》。在第13.6章“HBase vs 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;
    

    (注意每个查询的“限制10”。顺便说一下,我知道我不能限制和命令联合,但为了可读性,我已经去掉了包装选择)

    每个子查询的运行速度应与在索引O(logN)中找到正确的位置并返回10个项一样快。如果我们重复这K次,我们得到O(KlogN)。

    即使query planner非常糟糕,无法优化第一个查询,我们也可以将其转换为具有联合的查询,并在不使用pl/python编写任何内容的情况下获得所需的性能。

    为了再次检查我的计算结果,我运行了一个postgresql上面的查询,里面有9000000条测试记录。结果证实了我的预期,第一个查询的速度是100毫秒,第二个查询的速度是300毫秒(有联合的查询)。

    因此,如果查询在100毫秒内运行9000000条记录(logn=23),那么对于900000000条记录(logn=33),它应该在140毫秒内运行。

    问题

    • 你认为上述推理有什么缺陷吗?
    • 您能想象一个需要在pl/python中重写上述查询的数据集吗?
    • 您是否看到这样的查询在O(K log n)中不起作用的情况?
    4 回复  |  直到 14 年前
        1
  •  6
  •   araqnid    14 年前

    他们认为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被索引,因此它完全按照需要执行-返回索引,应用过滤器,并在输出达到限制时停止。

    如果ordering列没有索引,则需要进行表扫描和排序,但只需要进行一次表扫描:

    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个结果,这听起来完全像他们自己编写的解决方案。

        2
  •  4
  •   duffymo    14 年前

    我不认为TomWhite说关系数据库是“坏的”;它们对非关系的、非基于集合的数据不是最佳的。

    很长一段时间以来,人们都知道深层对象图不适合关系数据库。它们通常出现在诸如几何数据的CAD表示之类的问题中,在这些问题中,装配是由零件装配的装配组成的。参考链确实很长。

    对象和图形数据库是解决这类问题的方法,因为我早在90年代就意识到了它们。

    关系数据库对于基于集的关系数据来说是非常棒的。但并非所有数据都属于这一类。这就是为什么NoSQL获得了思想共享。

    我想你举的例子就是这么说的。

        3
  •  1
  •   JeffO    14 年前

    RDBMS是用于您没有想到的查询的。一旦你确定了你想要什么,你就可以应用最理想的解决方案。

        4
  •  1
  •   Tom Clarkson    14 年前

    无论使用SQL还是NoSQL,如果以错误的方式设计查询,性能都会很糟糕。

    我将通过在where子句中添加对timestamp的检查来修复该示例。如果你有很多数据,你可以假设最近的10个条目来自最后一分钟,那么为什么要尝试读取和排序上个月的所有条目呢?

    我可以同样轻松地设计同一个示例,使NoSQL看起来很糟糕,因为默认情况下,您只能通过ID来查找记录,因此需要加载整个数据集来查找所需的记录,而忽略了设置各种辅助/自定义索引的能力,这些索引将使您在重要的查询中获得比SQL更好的性能。