我有一个有点复杂的 SQL 命令,它使用了十几个常用的表表达式和需要重复调用的窗口函数。因此,我正在尝试使用准备好的语句。它有 10 个输入绑定和 6 个输出绑定。
它涉及几张表,最大的是几 GB,有 1500 万条记录。查询的单次执行需要 50-200 毫秒。
当我将代码构造为:
MYSQL_STMT* statement = mysql_stmt_init(db_connection);
Assert(statement);
char* sql = ...
s32 status = mysql_stmt_prepare(statement, sql, sql_length);
Assert(status == 0);
// ...setup input binding (10 params)...
s32 input_bind_result = mysql_stmt_bind_param(statement, input_bind);
Assert(input_bind_result == 0);
// ...setup output binding (6 columns)..
s32 output_bind_result = mysql_stmt_bind_result(statement, output_bind);
Assert(output_bind_result == 0);
for (int i = 0; i < 1000; i++)
{
s32 execute_result = mysql_stmt_execute(statement);
Assert(execute_result == 0);
while (true)
{
s32 fetch_result = mysql_stmt_fetch(statement);
if (fetch_result == MYSQL_NO_DATA)
break;
Assert(fetch_result == 0);
// ...process row...
// ...printf(timestamp and query output)...
}
// ...increment input parameters...
}
mysql_stmt_close(statement);
每次执行查询时,它都会变得越来越慢,而且速度非常快:
2021-06-30 21:24:40: 0.996904
2021-06-30 21:24:40: 0.995356
2021-06-30 21:24:41: 0.000000
2021-06-30 21:24:41: 0.000000
2021-06-30 21:24:41: 0.000000
2021-06-30 21:24:41: 0.000000
<hangs indefinitely>
它在从 mysql_stmt_execute() 返回时挂起。它不是确定性的。如果我重置查询缓存并重新启动,它可能会在第 5 次或第 8 次查询时挂起。
相比之下,如果我将代码构造为:
// ...setup input binding (10 params)...
// ...setup output binding (6 columns)...
for (int i = 0; i < 1000; i++)
{
MYSQL_STMT* statement = mysql_stmt_init(db_connection);
Assert(statement);
char* sql = ...
s32 status = mysql_stmt_prepare(statement, sql, sql_length);
Assert(status == 0);
s32 input_bind_result = mysql_stmt_bind_param(statement, input_bind);
Assert(input_bind_result == 0);
s32 output_bind_result = mysql_stmt_bind_result(statement, output_bind);
Assert(output_bind_result == 0);
s32 execute_result = mysql_stmt_execute(statement);
Assert(execute_result == 0);
while (true)
{
s32 fetch_result = mysql_stmt_fetch(statement);
if (fetch_result == MYSQL_NO_DATA)
break;
Assert(fetch_result == 0);
// ...process row...
// ...printf(timestamp and query output)...
}
// ...increment input parameters...
mysql_stmt_close(statement);
}
也就是说,从字面上看,每次迭代都创建和释放准备好的语句,没有这样的退化,我很快地处理了每次迭代:
2021-06-30 21:26:59: 0.996904
2021-06-30 21:27:00: 0.995356
2021-06-30 21:27:00: 0.000000
2021-06-30 21:27:00: 0.000000
2021-06-30 21:27:00: 0.000000
2021-06-30 21:27:00: 0.000000
2021-06-30 21:27:00: 0.994969
2021-06-30 21:27:00: 0.997291
2021-06-30 21:27:00: 0.997678
2021-06-30 21:27:00: 0.998452
2021-06-30 21:27:00: 0.998452
2021-06-30 21:27:00: 0.998452
2021-06-30 21:27:00: 0.998839
2021-06-30 21:27:01: 0.998839
2021-06-30 21:27:01: 0.998839
2021-06-30 21:27:01: 0.998454
2021-06-30 21:27:01: 0.999230
2021-06-30 21:27:01: 0.999230
2021-06-30 21:27:01: 0.999230
2021-06-30 21:27:01: 0.998459
2021-06-30 21:27:01: 0.998844
2021-06-30 21:27:01: 0.998844
2021-06-30 21:27:02: 0.999230
2021-06-30 21:27:02: 0.999231
2021-06-30 21:27:02: 0.999231
2021-06-30 21:27:02: 0.999231
2021-06-30 21:27:02: 0.999615
2021-06-30 21:27:02: 1.000000
2021-06-30 21:27:02: 1.000000
2021-06-30 21:27:02: 0.999232
2021-06-30 21:27:02: 0.999617
2021-06-30 21:27:02: 0.999617
2021-06-30 21:27:02: 0.999617
...continues at this speed for 1000+ iterations...
但这对我来说没有多大意义,并且否定了使用准备好的语句的主要好处之一。关于可能导致这种情况的任何见解?
感觉有些资源没有被释放,但我尝试添加:
mysql_stmt_free_result(statement);
或者
mysql_stmt_reset(statement);
在每次迭代结束时,降级仍然存在。
运行 MariaDB 10.5.10、mariadb-connector 3.1.13。