0

我有一个有点复杂的 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。

4

1 回答 1

0

向我们展示声明。

如果参数之一是 for OFFSET,请注意必须跨过所有“偏移”行。

如果语句是DELETE ... LIMIT,那么它可能会越来越远地搜索以找到所需的行。

ETC!

于 2021-06-30T17:32:41.453 回答