You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go操作Oracle存储过程:3亿游标数据读取挂起且无数据

问题:使用godror读取3亿条Oracle游标记录时挂起且结果为空

我基于database/sql结合github.com/godror/godror库,通过存储过程游标读取3亿条Oracle记录。程序处理9百万条数据时运行正常,但读取3亿条时,rows.Next()会挂起17分钟,结束后rbgMap长度为0。

生成sql.Rows的代码

func (o *OracleDb) OpenCursorTX(tx *sql.Tx, sqlStmt string) (*sql.Rows, error) {
    var cur driver.Rows
    ctx := context.Background()
    stmt, err := tx.PrepareContext(ctx, sqlStmt)
    if err != nil {
        logrus.Fatalf("error parsing cursor %s: %s", sqlStmt, err.Error())
        return nil, err
    }
    
    if _, err := stmt.ExecContext(ctx, sql.Out{Dest: &cur}, godror.PrefetchCount(100000), godror.FetchArraySize(100000)); err != nil {
        logrus.Fatalf("error exec cursor %s: %s", sqlStmt, err.Error())
        return nil, err
    }
    rows, err := godror.WrapRows(ctx, tx, cur)
    
    if err != nil {
        logrus.Fatalf("error cursor wrap rows %s: %s", sqlStmt, err.Error())
        return nil, err
    }
    if err := stmt.Close(); err != nil {
        return nil, err
    }
    return rows, nil
}  

读取行的代码

rows, err := l.oracleDb.OpenCursorTX(tx, "begin drs_router_read.get_rate_b_groups(po_rate_b_groups => :po_rate_b_groups); end;")
if err != nil {
    return err
}

var rbg domain.RmsGroupHist
var gwgrId, direction int
var dialCode, key, rbgDBegin, rbgDEnd string
rbgMap := make(map[string][]domain.RmsGroupHist)
i:=0
for rows.Next() {
    if err := rows.Scan(&gwgrId, &direction, &dialCode, &rbg.RmsgId, &rbgDBegin, &rbgDEnd); err != nil {
        return fmt.Errorf("error scan db rows loadRateBGroups %v", err)
    }
    rbg.DBegin = util.StrToInt64(rbgDBegin)
    rbg.DEnd = util.StrToInt64(rbgDEnd)
    key = domain.RBObjectKey + ":" + strconv.Itoa(gwgrId) + ":" + strconv.Itoa(direction) + ":" + dialCode
    rbgMap[key] = append(rbgMap[key], rbg)
    i++
    if i%100000 == 0 {
        logrus.Infof("rows %d", i)
    }
}

排查思路

  • 验证存储过程游标输出
    在Oracle客户端直接执行drs_router_read.get_rate_b_groups,确认游标是否能正常返回3亿条数据,排查存储过程内部是否存在大数据量下的逻辑异常(比如游标未正确打开、提前关闭,或执行超时)。

  • 调整Stmt.Close()时机
    当前代码在WrapRows后立即关闭stmt,但sql.Rows可能依赖底层stmt资源。尝试将stmt.Close()延迟到rows处理完成后执行(比如用defer stmt.Close()),避免提前释放资源导致读取失败。

  • 优化预取参数
    当前设置的PrefetchCount(100000)和FetchArraySize(100000)可能过大,导致单次预取占用过多数据库或内存资源,引发性能瓶颈。尝试降低参数值(比如10000),观察挂起情况是否缓解。

  • 添加上下文超时
    当前使用无超时的context.Background(),大数据量读取时可能因数据库端执行时间过长触发隐式超时。尝试使用context.WithTimeout设置合理超时,并捕获超时错误,确认是否是超时导致的异常。

  • 监控内存与资源
    3亿条数据存入rbgMap会占用巨量内存,可能导致GC频繁、程序假死甚至数据丢失。考虑分批次处理数据(比如每处理一定量后写入外部存储,或清理部分map),避免一次性加载全量数据到内存。

  • 检查Scan映射正确性
    确认rows.Scan的参数顺序、数据类型与存储过程游标返回的字段完全匹配。大数据量下类型不匹配可能引发静默失败,导致无数据存入map。

  • 捕获循环后错误
    在rows.Next()循环结束后,必须调用rows.Err()检查是否存在循环过程中未捕获的错误(比如网络中断、数据库连接异常),这类错误可能导致循环提前终止且无数据输出。

内容的提问来源于stack exchange,提问作者Vadim Voronin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:09:29