使用C++ Poco库从SQL Server获取200万行数据时遇std::bad_alloc异常
解决Poco ODBC检索大数据量时的内存溢出问题
你遇到的std::bad_alloc是因为一次性把200万行数据全部加载到内存容器中,导致内存耗尽。在Poco框架下,有以下几种方法可以安全处理大数据量查询:
1. 逐行读取数据
直接利用Poco Statement的next()方法逐行获取结果,每次内存中仅保留一行数据,处理完成后即可释放。这种方式内存占用最低,适合超大规模数据集。
Poco::Data::ODBC::Connector::registerConnector(); Poco::Data::Session session("ODBC", "Driver={SQL Server};Server=localhost;Database=sample_db;UID=<username>;PWD=<pwd>"); std::string sql = "SELECT id, timestamp, type, int_value, float_value, string_value, bStatus " "FROM sample_tbl " "WHERE id = ? AND timestamp >= ? AND timestamp <= ?"; Poco::Data::Statement select(session); // 用单个变量存储单条数据,而非容器 int id; std::string timestamp; int datatype; Poco::Nullable<int> int_value; Poco::Nullable<float> float_value; std::string string_value; bool status; select << sql, Poco::Data::Keywords::into(id), Poco::Data::Keywords::into(timestamp), Poco::Data::Keywords::into(datatype), Poco::Data::Keywords::into(int_value), Poco::Data::Keywords::into(float_value), Poco::Data::Keywords::into(string_value), Poco::Data::Keywords::into(status), Poco::Data::Keywords::use(tagid), Poco::Data::Keywords::use(starttime), Poco::Data::Keywords::use(endtime); select.execute(); // 循环逐行读取并处理 while (select.next()) { // 处理当前行数据:写入文件、计算、入库等 // ... }
2. 分批分页查询
借助SQL Server的ROW_NUMBER()窗口函数实现分页,每次查询固定行数的结果,循环处理所有批次。这种方式可以灵活控制每批数据的内存占用,适合需要批量处理的场景。
const int PAGE_SIZE = 100000; // 每批处理10万行 int current_page = 1; bool has_more_data = true; Poco::Data::ODBC::Connector::registerConnector(); Poco::Data::Session session("ODBC", "Driver={SQL Server};Server=localhost;Database=sample_db;UID=<username>;PWD=<pwd>"); while (has_more_data) { std::vector<int> ids; std::vector<std::string> timestamps; std::vector<int> datatypes; std::vector<Poco::Nullable<int>> int_values; std::vector<Poco::Nullable<float>> float_values; std::vector<std::string> string_values; std::vector<bool> bStatus; // 带分页逻辑的SQL std::string sql = "WITH PaginatedData AS (" " SELECT *, ROW_NUMBER() OVER (ORDER BY timestamp) AS RowNum " " FROM sample_tbl " " WHERE id = ? AND timestamp >= ? AND timestamp <= ?" ") " "SELECT id, timestamp, type, int_value, float_value, string_value, bStatus " "FROM PaginatedData " "WHERE RowNum BETWEEN ? AND ?"; Poco::Data::Statement select(session); int start_row = (current_page - 1) * PAGE_SIZE + 1; int end_row = current_page * PAGE_SIZE; select << sql, Poco::Data::Keywords::into(ids), Poco::Data::Keywords::into(timestamps), Poco::Data::Keywords::into(datatypes), Poco::Data::Keywords::into(int_values), Poco::Data::Keywords::into(float_values), Poco::Data::Keywords::into(string_values), Poco::Data::Keywords::into(bStatus), Poco::Data::Keywords::use(tagid), Poco::Data::Keywords::use(starttime), Poco::Data::Keywords::use(endtime), Poco::Data::Keywords::use(start_row), Poco::Data::Keywords::use(end_row); select.execute(); if (ids.empty()) { has_more_data = false; } else { // 处理当前批次数据 // ... current_page++; } }
3. 设置批量获取大小(Fetch Size)
通过Poco Statement的setFetchSize()方法,指定每次从ODBC驱动拉取的行数,让Poco分批次加载数据到内存。这种方式无需修改SQL,只需调整参数,平衡内存占用和查询效率。
Poco::Data::ODBC::Connector::registerConnector(); Poco::Data::Session session("ODBC", "Driver={SQL Server};Server=localhost;Database=sample_db;UID=<username>;PWD=<pwd>"); std::vector<int> ids; std::vector<std::string> timestamps; // ... 其他容器定义 std::string sql = "SELECT id, timestamp, type, int_value, float_value, string_value, bStatus " "FROM sample_tbl " "WHERE id = ? AND timestamp >= ? AND timestamp <= ?"; Poco::Data::Statement select(session); select << sql, Poco::Data::Keywords::into(ids), Poco::Data::Keywords::into(timestamps), // ... 其他into和use绑定 // 设置每次从驱动拉取10万行 select.setFetchSize(100000); select.execute(); // 处理已加载的批量数据 // ...
注意事项
- 分页查询时,
ORDER BY的字段建议创建索引,避免大页码时的性能损耗; - 逐行读取的效率相对较低,适合内存紧张但对性能要求不极致的场景;
setFetchSize的实际效果依赖ODBC驱动的支持,部分驱动可能会忽略该参数,需测试验证。
内容的提问来源于stack exchange,提问作者user29674386
相关产品推荐
相关产品推荐

