4

我必须通过以下代码对 Postgres 执行 SQL 查询。该查询返回大量行(40M 或更多)并具有 4 个整数字段:当我使用 32Gb 的工作站时,一切正常,但在 16Gb 的工作站上,查询非常慢(我猜是由于交换)。有没有办法告诉 C++ 批量加载行,而无需等待整个数据集?由于可能更好的 JDBC 驱动程序,使用 Java 之前我从未遇到过这些问题。

try {
        work W(*Conn);
        result r = W.exec(sql[sqlLoad]);
        W.commit();

        for (int rownum = 0; rownum < r.size(); ++rownum) {
            const result::tuple row = r[rownum];
            vid1 = row[0].as<int>();
            vid2 = row[1].as<int>();
            vid3 = row[2].as<int>();
            ..... 

    } catch (const std::exception &e) {
        std::cerr << e.what() << std::endl;
    }

我正在使用 PostgreSQL 9.3,在那里我看到了这个http://www.postgresql.org/docs/9.3/static/libpq-single-row-mode.html,但我不知道如何在我的 C++ 代码上使用它。您的帮助将不胜感激。

编辑:这个查询只运行一次,用于创建必要的主内存数据结构。因此, tt 无法优化。此外,pgAdminIII 可以在同一台(或使用更小的 RAM)PC 上在一分钟内轻松获取这些行。此外,Java 可以轻松处理两倍的行数(使用 Statent.setFetchSize() http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#setFetchSize%28int%29)因此,这确实是 libpqxx 库的问题,而不是应用程序问题。有没有办法在 C++ 中强制执行此功能,而无需手动显式设置限制/偏移量?

4

3 回答 3

2

使用光标

另请参阅FETCH。我收集到,光标将在幕后为您使用它,但以防万一,您始终可以使用FETCH手动编码流检索。

于 2014-04-28T10:06:21.317 回答
1

为了回答我自己的问题,我改编了如何使用 libpqxx 中的 pqxx::stateless_cursor 类?

try {
        work W(*Conn);
        pqxx::stateless_cursor<pqxx::cursor_base::read_only, pqxx::cursor_base::owned>
                cursor(W, sql[sqlLoad], "mycursor", false);
        /* Assume you know total number of records returned */
        for (size_t idx = 0; idx < countRecords; idx += 100000) {
            /* Fetch 100,000 records at a time */
            result r = cursor.retrieve(idx, idx + 100000);
            for (int rownum = 0; rownum < r.size(); ++rownum) {
                const result::tuple row = r[rownum];
                vid1 = row[0].as<int>();
                vid2 = row[1].as<int>();
                vid3 = row[2].as<int>();
                .............
            }
        }
    } catch (const std::exception &e) {
        std::cerr << e.what() << std::endl;
    }
于 2014-05-04T16:38:45.653 回答
0

光标是一个很好的起点。这是另一个游标示例,使用 do-while()

     const std::conStr("user=" + opt::dbUser + " password=" + opt::dbPasswd + " host=" + opt::dbHost + " dbname=" + opt::dbName);                                            

      pqxx::connection conn(connStr);
      pqxx::work txn(conn);
      std::string selectString = "SELECT id, name FROM table_name WHERE condition";

      pqxx::stateless_cursor<pqxx::cursor_base::read_only, pqxx::cursor_base::owned> 
      cursor(txn, selectString, "myCursor", false);

      //cursor variables
      size_t idx = 0;       //starting location
      size_t step = 10000;  //number of rows for each chunk
      pqxx::result result;
      do{
        //get next cursor chunk and update the index
        result = cursor.retrieve( idx, idx + step );
        idx += step;

        size_t records = result.size();
        cout << idx << ": records pulled = " << records << endl;

        for( pqxx::result::const_iterator row : result ){
          //iterate over cursor rows
        }
      }
      while( result.size() == step ); //if the result.size() != step, we're on our last loop
      cout << "Done!" << endl;

我在我的应用程序中迭代了大约 3300 万行。除了使用游标之外,我还使用了以下方法:

  1. 将数据拆分成更小的块。对我来说,那是使用边界框来抓取给定区域的数据。
  2. 构造一个查询以获取该块,并使用游标对其进行迭代。
  3. 将块存储在堆上并在处理完给定块的数据后释放它们。

我知道这是对您问题的一个非常晚的答案,但我希望这可能对某人有所帮助!

于 2020-02-17T22:20:39.340 回答