【问题标题】:Speed up processing from CSV file加快 CSV 文件的处理速度
【发布时间】:2011-08-30 17:23:34
【问题描述】:

我有一个项目,我必须改进它的性能。我有一个由巨大的 CSV 文件(1 亿行)构建的大型 Mysql 数据库。插入时间不是问题,但请求的响应时间非常重要,有时带有 2 个连接的查询需要大约 20 小时...

为了减少响应时间,我尝试将我的数据库迁移到 Cassandra,但没有成功:我的数据模型不适用于 Cassandra 概念。然后我想尝试另一种提高性能的方法:并行虚拟文件系统。而是在 Mysql 数据库中插入数据并发送然后发送查询,我尝试使用多线程读取整个 csv 文件并进行计算。但结果并不好:仅 1 000 000 行 2m20s。

目前,我的计算非常简单:在 C++ 中使用 MPI-IO API,我只计算 2 列中不同对值的数量。为了执行该计算,我使用了一个哈希图,其中每个键都是来自 csv 文件的一对值。最后,我返回哈希图大小。 这里有一个小代码:

 MPI::Init(argc,argv); 
 cout << " INFO init done" << endl;
 int myrank = MPI::COMM_WORLD.Get_rank(); 
 int numprocs = MPI::COMM_WORLD.Get_size(); 
 get_filename(path_name, myrank);
 cout << " INFO open file : " << path_name << endl;
 MPI::File thefile = MPI::File::Open(MPI::COMM_WORLD, path_name.c_str(), 
                  MPI::MODE_RDONLY, 
                  MPI::INFO_NULL); 
 MPI::Offset offset = 101;
 MPI::Offset limit = thefile.Get_size();
 cout << " INFO go computing" << endl;
 do {
   thefile.Read_at(offset, buf, bufsize, MPI_CHAR, status);
   temp.assign(buf);
   Tokenize(temp,tokens,"\n");
   line.assign(tokens.at(0));
   tokens.clear();

   Tokenize(line,tokens,"\t");
   nidt_count(tokens);
   tokens.clear();
   offset += (line.size() + 1);
 }while(offset < limit);
 count = status.Get_count(MPI_INT);
 cout << "process " << myrank << " reads " << nidt_hash.size() << " nidt" << endl; 

我在一台 4 核、8GB 内存的服务器上工作。我的数据在我的服务器上安装在 NFS 或 Samba 中的 NAS 上。我可以添加 2 或 3 台服务器进行处理,但目前我只是在一台服务器上尝试了一个小文件(100 万行)来衡量性能。

最后我的问题是:

  • 对于我的问题,考虑更改为 PVFS 类型是一种好方法吗?我想说我将处理更复杂的查询,例如:选择具有特定日期(范围小时)的所有行,以及来自特定列的特定对值。
  • 您知道其他可以帮助我改进 csv 文件处理的方法吗?我正在考虑使用 Hadoop、Pytables 或 FasterCSV。

这是我由 2 个 csv 文件组成的数据样本:

最大的(1亿行)组成如下:

ID        DATE             NUM_1        NUM_2     NB_UNITE TYPUNIT CODE_1 CODE_2

0  2007-05-13 15:37:48  33671624244  33698802900    547      s       0      17
0  2007-05-13 15:52:22  33671624244  33672211799      5      s       0      17 
....

第二个更简单更小(90 000),它就像一本字典,从code_1code_2 我得到一个名为 CODEVAL 的值:

CODE_1 CODE_2 CODEVAL

  0       17     VS
  0       34     SS

如您所料,通常我为每个文件创建 2 个表,一个典型的查询是:

Select CODEVAL, hour(date) AS HEURE, COUNT(*) AS NBSMSSOR 
From Tables_1 Join CODEVAL using(CODE_1,CODE_2) 
Where CODEVAL='SS'

对不起,我不知道如何制作数组。


这是我由 2 个 csv 文件组成的数据样本:

  • 最大的(1亿行)组成如下:

    ID DATE NUM_1 NUM_2 NB_UNITE TYPUNIT CODE_1 CODE_2

    0 2007-05-13 15:37:48 33671624244 33698802900 547 秒 0 17
    0 2007-05-13 15:52:22 33671624244 33672211799 5 秒 0 17 ....

  • 第二个更简单更小(90 000),它就像一个字典,从 code_1 和 code_2 我得到一个名为 CODEVAL 的值:

    CODE_1 CODE_2 CODEVAL

    0 17 VS

    0 34 不锈钢

如您所料,通常我为每个文件创建 2 个表,一个典型的查询是:

  • 选择 CODEVAL,小时(日期)作为 HEURE,计数(*)作为 NBSMSSOR 来自 Tables_1 使用 (CODE_1,CODE_2) 加入 CODEVAL 其中 CODEVAL='SS'

对不起,我不知道如何制作数组。

【问题讨论】:

  • 有没有试过分析调优MySql:forge.mysql.com/wiki/Top10SQLPerformanceTips
  • 如果您使用的不是最新版本的 MySQL,请考虑升级,并阅读 @ChrisWue 的链接,这是很棒的东西。
  • 我会更关心您的数据布局以及您如何访问它们。您能否粘贴一个包含 10 行的示例以及您使用的最常见查询?
  • 感谢您的帮助,您可以在下面看到我用 CSV 文件编写的数据示例。我也写了一个简单的查询。
  • 一亿行听起来不像是一个大数据库,即使按照 MySQL 标准。

标签: c++ multithreading performance csv mpi


【解决方案1】:

在我看来你是 I/O 绑定的。您的数据通过网络传输也无济于事。我怀疑如果你只是添加更多的机器,那么你的性能会因为额外的争用而下降。请记住,仍然只有一个主轴和一个 HD 磁头读取您的数据。对于 MPI 解决方案,我建议制作多个数据副本并将它们放在服务器本身上。

对于 MySQL,我听到你在说什么。我发现 MySQL 在连接方面效率很低。在我看来,当它可以在没有它们的情况下逃脱时,它会进行全表扫描。我记得 MySQL 在一个查询上花了一分钟多的时间,而 Oracle 只需要不到一秒的时间。也许试试 PostgreSQL?我不确定它是否更好。 另一种方法是让数据库为您对数据进行排序,这样您就可以在没有哈希图的情况下进行扫描。

除非您的记录非常庞大,否则 1 亿条记录应该不会那么糟糕。

【讨论】:

  • 感谢您的帮助。我怀疑我被带有磁头磁盘的 I/O 绑定......但我希望重点是我的 NAS 是由 RAID 6 中的几个磁盘组成的,我希望 PVFS 能够平衡不同的数据我的 NAS 中的磁盘,然后跨磁盘传播 I/O。我可能错了……
  • 哈哈哈不,我不会告诉你的,对不起。 :) 第二个是最慢的。但是我有一些更复杂的查询,例如:我想按一小时的范围和 CODEVAL 将每一行存储在一个 csv 文件中。在 Mysql 数据库中,很容易在查询中转换我的需求,但需要很长时间。这就是为什么我尝试用 MPI-IO 的 C++ 程序这样的编程方式来提高性能,但它看起来更难设置......
【解决方案2】:

如果您从 CSV 读取数据,我认为它不会经常更改。因此,您也可以在 CSV 数据上构建自己的索引,而不是将其加载到通用数据库产品中。还是您需要完整的 SQL 支持?

除此之外,您还提到要返回不同 K、V 对的 NUMBER。但是,您确实计算了实际的对。我不知道您是否需要它们用于其他目的,但您也可以将该数字作为#distinctKeys x #distinctValues 获得,而无需实际构建HashMap。

假设你为表格的每一列建立一个索引

value -> {r | r is a byteOffset of a row that has "value" in the index column}

你可以回答很多很多的查询,尤其是确定不同对的数量应该只需要几毫秒。

我希望这个答案会有所帮助,因为我不确定必须满足哪些其他要求。此解决方案的功能明显不如支持 SQL 的数据库(尤其是插入会使事情变得更加复杂),但至少确定不同对的数量应该快几个数量级

【讨论】:

    【解决方案3】:

    分而治之 一百个小型数据库应该更快。 你决定如何分解它 - 使用 split() 或 slice() 我目前正在使用每行第一个单词的第一个字符,所以曾经有一个巨大的慢速数据库现在有 (A - Z + a - z + 0 - 9) 62 个更快的小型数据库。另一个优势是笔记本电脑现在可以完成以前只有功能强大、价格昂贵的 PC 才能完成的工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-17
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 2021-11-06
      • 2016-11-28
      • 2018-04-07
      • 2019-06-29
      相关资源
      最近更新 更多