所以我编写了一个python程序来处理一些数据处理
任务。
下面是一个非常简短的规范,使用我想要的计算语言:

parse "%s %lf %s" aa bb cc | group_by aa | quickselect --key=bb 0:5 | \
    flatten | format "%s %lf %s" aa bb cc

也就是说,对于每一行,解析出一个单词、一个浮点数和另一个单词。把它们想象成一个球员ID、一个得分和一个约会。我想要每个球员的前五名得分和日期。数据大小不小,但不大;大约630兆字节。
我想知道我应该用什么真正的、可执行的语言来写它
让它同样短(如下面的Python),但要快得多。
#!/usr/bin/python
# -*- coding: utf-8; -*-
import sys

top_5 = {}

for line in sys.stdin:
    aa, bb, cc = line.split()

    # We want the top 5 for each distinct value of aa.  There are
    # hundreds of thousands of values of aa.
    bb = float(bb)
    if aa not in top_5: top_5[aa] = []
    current = top_5[aa]
    current.append((bb, cc))

    # Every once in a while, we drop the values that are not in
    # the top 5, to keep our memory footprint down, because some
    # values of aa have thousands of (bb, cc) pairs.
    if len(current) > 10:
        current.sort()
        current[:-5] = []

for aa in top_5:
    current = top_5[aa]
    current.sort()
    for bb, cc in current[-5:]:
        print aa, bb, cc

以下是一些示例输入数据:
3 1.5 a
3 1.6 b
3 0.8 c
3 0.9 d
4 1.2 q
3 1.5 e
3 1.8 f
3 1.9 g

以下是我从中得到的输出:
3 1.5 a
3 1.5 e
3 1.6 b
3 1.8 f
3 1.9 g
4 1.2 q

3有七个值,因此我们删除了cd值。
因为他们的bb值将他们排在前5位。因为4
只有一个值,它的“前5”只包含一个值。
这比在MySQL中执行相同的查询更快(至少
我们已经找到了解决问题的方法)但我很确定这是在花
大部分时间都在Python字节码解释器中。我觉得在
另一种语言,我可以让它处理数百个
每秒数千行,而不是每分钟。所以我想
用实现速度更快的语言编写它。
但我不知道该选哪种语言。
我还没有弄清楚如何在SQL中将其表示为单个查询,以及
实际上,我对MySQL的能力甚至仅仅是
select * from foo into outfile 'bar';输入数据。
C是一个明显的选择,但是像line.split()这样的事情,排序一个列表
对于2元组,生成哈希表需要编写一些代码,
不是在标准库中,所以我最终会得到100行代码
或更多,而不是14。
C++似乎是一个更好的选择(它有字符串,地图,
对,和标准库中的向量),但它看起来像代码
会让STL更加混乱。
OCAML很好,但它是否具有相当于
我会为地图的表现感到难过吗?
普通的口齿不清可能奏效?
有没有类似于matlab的数据库计算工具?
让我把循环向下推成快速代码?有人试过吗?
(编辑:通过提供一些示例输入和输出数据来响应Davethgr8的评论,并修复了python程序中的错误!)
(补充编辑:哇,这条评论线到目前为止真是太棒了。谢谢大家!)
编辑:
有一个(谢谢,雷纳!),这里有一个will hartung的line.split()脚本,用于生成一些测试数据(尽管它没有真实数据的zipfian分布):
BEGIN {
 for (i = 0; i < 27000000; i++) {
  v = rand();
  k = int(rand() * 100);
  print k " " v " " i;
 }
 exit;
}

最佳答案

我很难相信任何不了解数据的脚本(不像mysql那样预先加载了这样的信息)都会比SQL方法更快。
除了解析输入所花费的时间外,脚本还需要“保持”按数组排序等…
下面是对在SQL中应该以适当的速度工作的第一个猜测,假设表的a a、bb、cc列按该顺序有一个索引(*)。(可能的替代方案是“AA,BB DESC,CC”索引
(*)此索引是否可以聚集,不影响以下查询。是否选择集群,以及是否需要“aa、bb、cc”独立索引取决于用例、表中行的大小等。

SELECT T1.aa, T1.bb, T1.cc , COUNT(*)
FROM tblAbc T1
LEFT OUTER JOIN tblAbc T2 ON T1.aa = T2.aa AND
         (T1.bb < T2.bb OR(T1.bb = T2.bb AND T1.cc < T2.cc))
GROUP BY T1.aa, T1.bb, T1.cc
HAVING COUNT(*) < 5  -- trick, remember COUNT(*) goes 1,1,2,3,...
ORDER BY T1.aa, T1.bb, T1.cc, COUNT(*) DESC

我们的想法是计算在给定的a a值内有多少个记录小于self。不过,有一个小技巧:我们需要使用左外部联接,以免丢弃具有最大bb值或最后一个bb值的记录(可能恰好是前5个)。由于左连接,计数(*)值计数1、1、2、3、4等,因此HAVING测试“为了模拟op的示例输出,order by使用count()上的desc,可以删除它以获得更传统的前5种类型的列表。另外,如果需要,可以删除选择列表中的count(),这不会影响查询的逻辑和正确排序的能力。
还要注意,这个查询在处理关系方面是确定的,即当给定的一组记录的bb值相同时(在a a组中);我认为当输入数据的顺序发生更改时,python程序可能提供稍微不同的输出,这是因为它的偶然性。排序字典的NAL截断。
真正的解决方案:基于SQL的过程方法
上面描述的自连接方法演示了如何使用声明性语句来表示OP的需求。然而,这种方法在某种意义上是幼稚的,因为它的性能大致上与每个AA“类别”中记录计数的平方和绑定在一起。(不是o(n^2),但大致是o((n/a)^2),其中a是aa列的不同值的数目),换句话说,它对数据的表现很好,这样,与给定aa值相关的记录的数目平均不会超过几十条。如果数据是这样的AA列不是选择性的,那么下面的方法是非常多的!-更合适。它利用了SQL的高效排序框架,同时实现了一种简单的算法,这种算法很难用声明的方式表达。通过在光标中向前(有时向后…)查找下一个a a值,引入一个简单的二进制搜索,这种方法可以进一步改进每个/大多数aa“类别”记录数量特别大的数据集。对于AA“类别”相对于tblabc中的总行数较低的情况,请参阅下一种方法之后的另一种方法。
DECLARE @aa AS VARCHAR(10), @bb AS INT, @cc AS VARCHAR(10)
DECLARE @curAa AS VARCHAR(10)
DECLARE @Ctr AS INT

DROP TABLE  tblResults;
CREATE TABLE tblResults
(  aa VARCHAR(10),
   bb INT,
   cc VARCHAR(10)
);

DECLARE abcCursor CURSOR
  FOR SELECT aa, bb, cc
  FROM tblABC
  ORDER BY aa, bb DESC, cc
  FOR READ ONLY;

OPEN abcCursor;

SET @curAa = ''

FETCH NEXT FROM abcCursor INTO @aa, @bb, @cc;
WHILE @@FETCH_STATUS = 0
BEGIN
    IF @curAa <> @aa
    BEGIN
       SET @Ctr = 0
       SET @curAa = @aa
    END
    IF @Ctr < 5
    BEGIN
       SET @Ctr = @Ctr + 1;
       INSERT tblResults VALUES(@aa, @bb, @cc);
    END
    FETCH NEXT FROM AbcCursor INTO @aa, @bb, @cc;
END;

CLOSE abcCursor;
DEALLOCATE abcCursor;

SELECT * from tblResults
ORDER BY aa, bb, cc    -- OR .. bb DESC ... for a more traditional order.

当aa非常不选择性时,可选择上述情况。换句话说,当我们有相对较少的AA“类别”时。其思想是浏览不同类别的列表,并为每个值运行“limit”(mysql)“top”(mssql)查询。
为了便于参考,以下代码在63秒内运行,用于6100万条记录的tblabc,除以45个a a值,在MSSQL8.0上,在相对较旧/较弱的主机上。
DECLARE @aa AS VARCHAR(10)
DECLARE @aaCount INT

DROP TABLE  tblResults;
CREATE TABLE tblResults
(  aa VARCHAR(10),
   bb INT,
   cc VARCHAR(10)
);

DECLARE aaCountCursor CURSOR
  FOR SELECT aa, COUNT(*)
  FROM tblABC
  GROUP BY aa
  ORDER BY aa
  FOR READ ONLY;
OPEN aaCountCursor;


FETCH NEXT FROM aaCountCursor INTO @aa, @aaCount
WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT tblResults
       SELECT TOP 5 aa, bb, cc
       FROM tblproh
       WHERE aa = @aa
       ORDER BY aa, bb DESC, cc

    FETCH NEXT FROM aaCountCursor INTO @aa, @aaCount;
END;

CLOSE aaCountCursor
DEALLOCATE aaCountCursor

SELECT * from tblResults
ORDER BY aa, bb, cc    -- OR .. bb DESC ... for a more traditional order.

关于是否需要索引的问题。(参考操作说明)
当只运行“select*from mytable”时,表扫描实际上是最快的Appraoch,无需费心索引。然而,SQL通常更适合这种情况的主要原因(除了数据最初是在存储库中积累的,而任何外部解决方案都需要考虑导出相关数据的时间)是它可以依赖于空扫描。许多通用语言更适合处理原始处理,但它们正在与SQL进行不公平的斗争,因为它们需要重新构建SQL在其数据收集/导入阶段收集的数据的任何先前知识。由于排序通常是一项耗时且有时占用空间的任务,因此SQL及其相对较慢的处理能力通常会先于其他解决方案。
此外,即使没有预先构建的索引,现代查询优化器也可以决定一个涉及创建临时索引的计划。而且,由于排序是DDMS的固有部分,所以SQL服务器在该领域通常是高效的。
所以…SQL更好吗?
这就是说,如果我们试图比较纯ETL作业的SQL和其他语言,即处理作为输入的堆(未索引的表),以执行各种转换和过滤,那么很可能多线程的实用程序是用say c编写的,并利用高效的排序库,可能会更快。决定SQL与非SQL方法的决定性问题是数据位于何处以及最终应位于何处。如果我们仅仅是转换一个文件来提供“链”下的外部程序更适合。如果我们在SQL Server中拥有或需要数据,那么只有很少的情况下才有值得从外部导出和处理的数据。

关于python - 我可以使用什么语言来快速执行此数据库摘要任务?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1467898/

10-09 15:48
查看更多