PostgreSQL:有效地将数据加载到Star Schema中

时间:2011-01-03 11:53:16

标签: postgresql normalization etl star-schema fact-table

想象一下PostgreSQL 9.0上具有以下结构的表:

create table raw_fact_table (text varchar(1000));

为了简化起见,我只提到一个文本列,实际上它有十几个。该表有100亿行,每列有很多重复。该表是使用COPY FROM从平面文件(csv)创建的。

为了提高性能,我想转换为以下星型模式结构:

create table dimension_table (id int, text varchar(1000));

事实表将被替换为如下的事实表:

create table fact_table (dimension_table_id int);

我当前的方法是基本上运行以下查询来创建维度表:

Create table dimension_table (id int, text varchar(1000), primary key(id));

然后创建填充我使用的维度表:

insert into dimension_table (select null, text from raw_fact_table group by text);

之后我需要运行以下查询:

select id into fact_table from dimension inner join raw_fact_table on (dimension.text = raw_fact_table.text);

想象一下,通过多次将所有字符串与所有其他字符串进行比较,我得到了可怕的表现。

在MySQL上我可以在COPY FROM期间运行存储过程。这可以创建字符串的哈希值,并且所有后续字符串比较都是在哈希而不是长原始字符串上完成的。这在PostgreSQL上似乎不可能,我该怎么办?

示例数据将是包含类似内容的CSV文件(我也使用整数和双精度的引号):

"lots and lots of text";"3";"1";"2.4";"lots of text";"blabla"
"sometext";"30";"10";"1.0";"lots of text";"blabla"
"somemoretext";"30";"10";"1.0";"lots of text";"fooooooo"

5 个答案:

答案 0 :(得分:6)

  

想象一下可怕的表现   我通过比较所有字符串来获得   其他字符串数次。

当你这样做了一段时间后,你停止想象性能,然后开始测量它。 “过早优化是万恶之源。”

“十亿”对你意味着什么?对我来说,在美国,这意味着1,000,000,000(或1e9)。如果你也是这样,你可能会看到1到7太字节的数据。

  

我目前的方法基本上是   运行以下查询以创建   维度表:

Create table dimension_table (id int, text varchar(1000), primary key(id));

你如何将100亿行放入一个使用整数作为主键的表中?我们甚至可以说一半的行是重复的。当你这样做时,算术如何运作?

不要想象。先阅读。然后测试。

阅读Data Warehousing with PostgreSQL。我怀疑这些演示幻灯片会给你一些想法。

另请阅读Populating a Database,并考虑要实施哪些建议。

按照“分而治之”的过程,测试一百万(1e6)行。也就是说,不要一次尝试加载一百万;编写一个程序,将其分解为更小的块。运行

EXPLAIN <sql statement>

您已经说过估计至少有99%的重复行。从广义上讲,有两种方法可以摆脱欺骗

  1. 在数据库内部,不一定是您用于生产的同一平台。
  2. 在数据库之外,在文件系统中,不一定是用于生产的相同文件系统。
  3. 如果你仍然有你加载的文本文件,我会考虑先在数据库外面尝试。这个awk单行将从每个文件输出唯一的行。它相对经济,因为它只对数据进行一次传递。

    awk '!arr[$0]++' file_with_dupes > file_without_dupes
    

    如果你真的有99%的傻瓜,那么在这个过程结束时你应该把你的1到7太比特减少到大约50演出。并且,完成此操作后,您还可以对每个唯一行进行编号,并在将其复制到数据仓库之前创建制表符分隔文件。那是另一个单线:

    awk '{printf("%d\t%s\n", NR, $0);}' file_without_dupes > tab_delimited_file
    

    如果你必须在Windows下执行此操作,我会使用Cygwin

    如果必须在数据库中执行此操作,我会尽量避免使用生产数据库或生产服务器。但也许我太谨慎了。移动几兆兆字节是件昂贵的事情。

    但我要测试

    SELECT DISTINCT ...
    

    使用GROUP BY之前。我或许可以为你做一些大数据集测试,但本周可能不会。 (我通常不会使用太字节大小的文件。这很有趣。如果你可以等。)

答案 1 :(得分:2)

你最后在那里省略了一些细节,但我不认为一定存在问题。没有证据表明所有字符串实际上与所有其他字符串进行了比较。如果你进行连接,PostgreSQL可以很好地选择一个更聪明的连接算法,例如散列连接,这可能会给你在MySQL解决方案中实现的相同散列。 (再一次,你的细节很朦胧。)

答案 2 :(得分:2)

-- add unique index
CREATE UNIQUE INDEX uidx ON dimension_table USING hash(text);
-- for non case-sensitive hash(upper(text))

尝试哈希(文本);和btree(文本)看哪一个更快

答案 3 :(得分:2)

仅提问:   - 是否需要以1或2步转换数据?   - 我们可以在转换时修改表吗?

运行更简单的查询可以提高性能(以及服务器负载)

一种方法是:

  1. 生成dimension_table(如果我理解正确,你没有性能问题)(可能还有一个额外的临时布尔字段......)
  2. repeat:从dimension_table中选择一个先前未选择的条目,从包含它的raw_fact_table中选择每一行并将它们插入fact_table。将dimension_table记录标记为已完成,接下来......您可以将其写为存储过程,它可以在后台转换您的数据,占用最少的资源......
  3. 或另一个(可能更好):

    1. 将fact_table创建为raw_fact_table和一个dimension_id的每条记录。 (所以包括dimension_text和dimension_id行)
    2. create dimension_table
    3. 为fact_table创建一个after插入触发器,其中:
      • 在fact_table
      • 中搜索dimension_text
      • 如果未找到,则在dimension_table
      • 中创建新记录
      • 将dimension_id更新为此ID
    4. 在simle循环中,将每个记录从raw_fact_table插入fact_table

答案 4 :(得分:1)

我看到了解决问题的几种方法 PostgreSql中有md5函数 md5(string)计算字符串的MD5哈希值,以十六进制返回结果

插入维度_表格(从文本中选择null,md5(文本),raw_fact_table组中的文本)

将md5字​​段添加到raw_fact_table中 从维度内部联接raw_fact_table中选择id到fact_table(dimension.md5 = raw_fact_table.md5);

MD5上的索引也可能有所帮助

或者您可以在加载数据时动态计算MD5。 例如,我们的ETL工具Advanced ETL处理器可以为您完成。 此外,它可以同时将数据加载到多个表中。

我们的网站上提供了许多在线教程 例如,这个演示了加载缓慢变化的维度

http://www.dbsoftlab.com/online-tutorials/advanced-etl-processor/advanced-etl-processor-working-with-slow-changing-dimension-part-2.html