tpcc-mysql problem: 持续输出 “ordstat thread_num:7 0, 00000, “

问题

当我使用tpcc-mysql tpcc-mysql 进行数据库测试时,遇到了以下问题
问题截图

源码探索

在网上我找不到出错原因,所以我研究了源码。并将错误定位到下面的代码(ordstat.c line206-257):

/* find the most recent order for this customer */
        proceed = 7;
        /*EXEC_SQL SELECT o_id, o_entry_d, COALESCE(o_carrier_id,0)
                INTO :o_id, :o_entry_d, :o_carrier_id
                FROM orders
                WHERE o_w_id = :c_w_id
                AND o_d_id = :c_d_id
                AND o_c_id = :c_id
                AND o_id = (SELECT MAX(o_id)
                                FROM orders
                                WHERE o_w_id = :c_w_id
                              AND o_d_id = :c_d_id
                                AND o_c_id = :c_id);*/
        mysql_stmt = stmt[t_num][23];
        memset(param, 0, sizeof(MYSQL_BIND) * 6); /* initialize */
        param[0].buffer_type = MYSQL_TYPE_LONG;
        param[0].buffer = &c_w_id;
        param[1].buffer_type = MYSQL_TYPE_LONG;
        param[1].buffer = &c_d_id;
        param[2].buffer_type = MYSQL_TYPE_LONG;
        param[2].buffer = &c_id;
        param[3].buffer_type = MYSQL_TYPE_LONG;
        param[3].buffer = &c_w_id;
        param[4].buffer_type = MYSQL_TYPE_LONG;
        param[4].buffer = &c_d_id;
        param[5].buffer_type = MYSQL_TYPE_LONG;
        param[5].buffer = &c_id;
        if( mysql_stmt_bind_param(mysql_stmt, param) ) goto sqlerr;
        if( mysql_stmt_execute(mysql_stmt) ) goto sqlerr;
        if( mysql_stmt_store_result(mysql_stmt) ) goto sqlerr;
        memset(column, 0, sizeof(MYSQL_BIND) * 3); /* initialize */
        column[0].buffer_type = MYSQL_TYPE_LONG;
        column[0].buffer = &o_id;
        column[1].buffer_type = MYSQL_TYPE_STRING;
        column[1].buffer = o_entry_d;
        column[1].buffer_length = sizeof(o_entry_d);
        column[2].buffer_type = MYSQL_TYPE_LONG;
        column[2].buffer = &o_carrier_id;
        if( mysql_stmt_bind_result(mysql_stmt, column) ) goto sqlerr;
        switch( mysql_stmt_fetch(mysql_stmt) ) {
            case 0: //SUCCESS
                break;
            case 1: //ERROR
            case MYSQL_NO_DATA: //NO MORE DATA
            default:
                mysql_stmt_free_result(mysql_stmt);
                goto sqlerr;
        }
        mysql_stmt_free_result(mysql_stmt);
sqlerr:
        fprintf(stderr, "ordstat %d:%d\n",t_num,proceed);
    error(ctx[t_num],mysql_stmt);
        /*EXEC SQL WHENEVER SQLERROR GOTO sqlerrerr;*/
    /*EXEC_SQL ROLLBACK WORK;*/
    mysql_rollback(ctx[t_num]);
sqlerrerr:
    return (0);

在sqlerr中,程序输出了我遇到的报错语句。但我不能确定是上述代码中的哪一句导向了sqlerr。于是我修改了源码,添加了一些输出信息,并且最后我将报错定位到了该函数:
mysql_stmt_fetch(mysql_stmt)
返回了值 MYSQL_NO_DATA(100)
我查阅了资料(mysql官网),发现该返回码是因为sql语句返回的结果为空
又通过日志找到了引发错误的语句:

SELECT o_id, o_entry_d, COALESCE(o_carrier_id,0) 
FROM orders WHERE o_w_id = 600 AND
o_d_id = 6 AND o_c_id = 886 AND 
o_id = (SELECT MAX(o_id) FROM orders 
WHERE o_w_id = 600 AND o_d_id = 6 AND o_c_id = 886);

在这里插入图片描述
去数据库中进行复现:
在这里插入图片描述
这句sql语句在tpcc的设计中属于Order-Status:查询客户最近交易的状态,包括最近一次交易的订单信息以及对应的商品信息。(具体见TPC-C模型简介 SQL加注释 )

# 取出该客户最新一次交易(即订单id最大)的订单信息
SELECT o_id, o_entry_d, o_carrier_id 
FROM bmsql_oorder 
WHERE o_w_id = ? AND o_d_id = ? AND o_c_id = ? AND 
o_id = (SELECT max(o_id) 
FROM bmsql_oorder 
WHERE o_w_id = ? AND o_d_id = ? AND o_c_id = ?);

那么如果遇到这种返回结果为空的情况应该怎么处理呢,tpcc-mysql源码中的解决方案是引发sqlerr,回滚事务。
但我个人认为,这是正常的查询结果,实际应用场景中可以将空结果返回给用户(该customer没有最新的交易信息)。而源码中进行回滚后,从输出结果看,又反复“执行-回滚-再执行”了多次,一定程度上影响了数据库性能。
影响程度有多大呢,我们继续看代码,在执行完sqlerr的回滚后,该函数结束,返回0,程序跳回到上一层(driver.c的do_ordstat函数):

#define MAX_RETRY 2000
for (i = 0; i < MAX_RETRY; i++) 
{
      ret = ordstat(t_num, w_id, d_id, byname, c_id, c_last);
      clk2 = clock_gettime(CLOCK_MONOTONIC, &tbuf2 );
      if(ret)
      {
          rt = (double)(tbuf2.tv_sec * 1000.0 + tbuf2.tv_nsec/1000000.0-tbuf1.tv_sec * 1000.0 - tbuf1.tv_nsec/1000000.0);
          if(rt > max_rt[2])
                max_rt[2]=rt;
          total_rt[2] += rt;
          hist_inc(2, rt);
          if(counting_on)
          {
                if( rt < rt_limit[2])
                {
                  success[2]++;
                  success2[2][t_num]++;
                }
                else
                {
                  late[2]++;
                  late2[2][t_num]++;
                }
           }
           return (1); /* end */
      }
      else
      {
          if(counting_on){
            retry[2]++;
            retry2[2][t_num]++;
          }
      }
}

ordstat返回0后,会跳到28行的else语句进行执行,增加重做次数后,进入下一轮循环,尝试再次做该事务,最大循环次数为2000,也就是说该事务会“执行-回滚-再执行”2000次,这在一定程度上影响了性能。
所以我对tpcc-mysql源码产生了疑惑,希望有大佬来给我解惑。

补充

一位同事帮忙看了另一个tpcc工具(sysbench-tpcc)对该情况的处理,发现是没有回滚的

处理方案

最后的处理:修改了代码(ordstat.c line 248处的switch语句):

switch( mysql_stmt_fetch(mysql_stmt) ) {
            case 0: //SUCCESS
            case MYSQL_NO_DATA: //NO MORE DATA
                break;
            case 1: //ERROR
            default:
                mysql_stmt_free_result(mysql_stmt);
                goto sqlerr;
  }

此外,在tpcc-mysql中还缺少一些功能,联系作者无果后,我自己开了个仓库进行修改,地址如下:https://github.com/qingsuijiu/tpcc-mysql-pro

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值