问题描述
我有一个有点复杂的 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_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。
解决方法
向我们展示声明。
如果其中一个参数用于 OFFSET
,请注意必须跨过所有“偏移量”行。
如果语句是 DELETE ... LIMIT
,那么它可能会越来越远地搜索以找到所需的行。
等!