我尝试逐行读取纯文本文件,构造SQL INSERT语句,执行查询,然后继续。目前,我已经有了一个解决方案,可以在我4岁的桌面上每秒完成大约200行。但是,我已经获得了大约1.2亿行,并且希望将其作为日常任务来实现。花几个小时来完成它会很好,但是花一个星期的时间不是一个选择。
这些行将包含一个字符串,范围从5到9个整数,范围从布尔值(我编码为TINYINT(1))到午夜(BIGINT)以来的微秒。
从文件中读入后(通过getline()),这些行被这个函数标记为:
#define MAX_TOKENS 10
#define MAX_TOKEN_LENGTH 32
char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH];
//...
void split_line(const string &s)
{
char raw_string[MAX_TOKENS * MAX_TOKEN_LENGTH];
char *rest;
char *token_string;
strcpy(raw_string, s.c_str());
if(tokens[0][0] != '\0')
{
fill(tokens[0], tokens[0]+(MAX_TOKENS*MAX_TOKEN_LENGTH), '\0');
}
for(uint32_t token = 0; token < MAX_TOKENS; token++)
{
if(token == 0) token_string = strtok_r(raw_string, " ", &rest);
else token_string = strtok_r(nullptr, " ", &rest);
if(token_string == nullptr) break;
if(token >= 1)
{
//if it's not a number...
if(token_string[0] < 48 || token_string[0] > 57)
{
if(token_string[0] != 45) //negative numbers are allowed
{
clear_tokens();
break;
}
}
}
strcpy(tokens[token], token_string);
}
}
我曾尝试使用该标记器的更多STL派生版本,但这证明太慢了。它仍然在调用图中排名很高,但不如正确的STL字符串那么高。
无论如何,下一步是构建SQL查询。为此,我尝试了一些事情。一个选项是stringstreams。
string insert_query = "INSERT INTO data_20170222";
stringstream values;
string query;
while(getline(input_stream, input_stream_line))
{
split_line(input_stream_line);
if(tokens[5][0] != '\0') //the smallest line will have six tokens
{
try
{
query = insert_query;
uint32_t item_type = stoi(tokens[2]);
switch(item_type)
{
case 0: //one type of item
case 1: //another type of item
{
values << " (valueA, valueB, valueC, valueD, valueE, valueF,"
" valueG, valueH) values('"
<< tokens[0] << "', " << tokens[1] << ", "
<< tokens[2] << ", " << tokens[3] << ", "
<< tokens[4] << ", " << tokens[5] << ", "
<< tokens[6] << ", " << tokens[7] << ")";
break;
}
//...
}
query.append(values.str());
values.str(string());
values.clear();
if(mysql_query(conn, query.c_str()))
{
string error(mysql_error(conn));
mysql_close(conn);
throw runtime_error(error);
}
}
catch(exception &ex)
{
cerr << "Error parsing line\n '" << input_stream_line
<< "'\n" << " " << ex.what() << endl;
throw;
}
}
当我运行这个版本时,我看到30%的callgrind样本是在std :: operator&lt;&lt;中测量的。在std :: basic_ostream中。
我最初尝试使用字符串,ala:
string values;
values = " (valueA, valueB, valueC, valueD, valueE, valueF,"
" valueG, valueH) values('" +
string(tokens[0]) + "', " + tokens[1] + ", "
tokens[2] + ", " + tokens[3] + ", "
tokens[4] + ", " + tokens[5] + ", "
tokens[6] + ", " + tokens[7] + ")";
证明实际上速度相同,但这次将30%的样本从std :: basic_string分配给std :: operator +。
最后,我切换到直接sprintf()。
char values[MAX_TOKENS * MAX_TOKEN_LENGTH];
sprintf(values, " (valueA, valueB, valueC, valueD, valueE, valueF,"
" valueG, valueH) values('%s', %s, %s, %s, %s, %s, %s, %s)",
tokens[0], tokens[1], tokens[2], tokens[3],
tokens[4], tokens[5], tokens[6], tokens[7]);
stringstream比字符串略快(但是在合理的误差范围内)。 sprintf()比两者快10%左右,但这还不够快。
当然,有一种完善的方法可以用如此大的数据集完成这项任务。我现在感激任何指导。
我想这已成为一个问题,为什么MariaDB现在似乎运行得如此缓慢。我在这个系统中有一个好的SSD,16GB内存等等。这让我觉得我的硬件不太可能阻止它。
更加好奇。在此先感谢您的帮助!
答案 0 :(得分:0)
批INSERTing
每行INSERT
语句100行将以10倍的速度运行。
您需要每天插入120M行吗?那是每秒1400。你有没有计算过剩磁盘空间的时间?
让我们看看SHOW CREATE TABLE
。当BIGINT
(4个字节)就足够时,不要使用INT
(8个字节)。当INT
(3个字节)执行时,请勿使用MEDIUMINT
。等
您将如何处理数十亿行数据?请记住,即使使用SSD,对于索引不佳的表格形成不良的SELECT
也需要很长时间。
可以对一个字符串进行规范化吗?
考虑将一堆布尔值打包成SET
(每8个布尔值1个字节)或一些大小的int。
让我们看看SHOW CREATE TABLE
和主SELECTs
。
innodb_flush_log_at_trx_commit
的价值是多少?使用2。