想象一下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"
答案 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%的重复行。从广义上讲,有两种方法可以摆脱欺骗
如果你仍然有你加载的文本文件,我会考虑先在数据库外面尝试。这个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步转换数据? - 我们可以在转换时修改表吗?
运行更简单的查询可以提高性能(以及服务器负载)
一种方法是:
或另一个(可能更好):
答案 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处理器可以为您完成。 此外,它可以同时将数据加载到多个表中。
我们的网站上提供了许多在线教程 例如,这个演示了加载缓慢变化的维度