Golang中ORA-01000错误:超出最大打开游标数的问题解决

Golang中ORA-01000错误:超出最大打开游标数的问题解决 我有一个运行堆栈的服务。为此,它通过在Oracle中执行查询来读取这个堆栈。问题是,执行几个小时后,我收到以下消息:

ORA-01000: 超出最大打开游标数

我想知道可能是什么原因。我有一个用于打开连接的全局变量,我总是检查其连接状态并将其复制到局部变量。

根据我的理解,在 Rows.Next() 中,当它检查到没有更多行时会关闭游标,所以我不应该累积这么多游标以至于超出限制。

数据库连接:

type GERENCIACON struct {
DataBase *sql.DB
}

func (gc *GERENCIACON) F_FECHAR_CONEXAO() {
gc.DataBase.Close()
}

func (gc *GERENCIACON) F_ABRIR_CONEXAO()  {	
if gc.DataBase == nil || gc.DataBase.Ping() != nil {	
	gc.DataBase, _ = sql.Open("goracle", "X/X@10.0.254.10:1521/orcl")
}
}

var VGGerenciaConexao GERENCIACON

用于查询的结构体:

type GERENCIACONSULTA struct {
DataBase *sql.DB
Rows *sql.Rows
}

func (gc *GERENCIACONSULTA) F_EXECUTA_CONSULTA(pSql string) {
VGGerenciaConexao.F_ABRIR_CONEXAO()
gc.DataBase = VGGerenciaConexao.DataBase
gc.Rows, _ = gc.DataBase.Query(pSql)
}

服务获取堆栈:

var vGerenciaConsulta CertanoLabsPackage.GERENCIACONSULTA
var vSQL string

for {
	vSQL = "select * from stack "	

	vGerenciaConsulta.F_EXECUTA_CONSULTA(vSQL)

	for vGerenciaConsulta.Rows.Next() {
	...
	}

	time.Sleep(time.Minute)
}

可能是什么问题呢?


更多关于Golang中ORA-01000错误:超出最大打开游标数的问题解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

将数据库连接的打开和关闭操作移出循环。

更多关于Golang中ORA-01000错误:超出最大打开游标数的问题解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在调用 vGerenciaConsulta.Rows.Next() 时可能发生错误,导致其返回 false 并退出循环。在这种情况下,Close() 不会被自动调用。

这是一个典型的游标泄漏问题。你的代码没有正确关闭 sql.Rows 对象,导致数据库游标一直保持打开状态。

主要问题在于:

  1. GERENCIACONSULTA.F_EXECUTA_CONSULTA 方法执行查询后没有处理可能的错误
  2. 循环中没有关闭 Rows 对象
  3. 没有使用 defer 确保资源释放

这是修复后的代码示例:

type GERENCIACONSULTA struct {
    DataBase *sql.DB
    Rows     *sql.Rows
}

func (gc *GERENCIACONSULTA) F_EXECUTA_CONSULTA(pSql string) error {
    VGGerenciaConexao.F_ABRIR_CONEXAO()
    gc.DataBase = VGGerenciaConexao.DataBase
    
    var err error
    gc.Rows, err = gc.DataBase.Query(pSql)
    return err
}

func (gc *GERENCIACONSULTA) F_FECHAR_CONSULTA() {
    if gc.Rows != nil {
        gc.Rows.Close()
        gc.Rows = nil
    }
}

// 服务代码修改
var vGerenciaConsulta CertanoLabsPackage.GERENCIACONSULTA
var vSQL string

for {
    vSQL = "select * from stack"
    
    err := vGerenciaConsulta.F_EXECUTA_CONSULTA(vSQL)
    if err != nil {
        // 处理错误
        continue
    }
    
    // 确保Rows被关闭
    defer vGerenciaConsulta.F_FECHAR_CONSULTA()
    
    for vGerenciaConsulta.Rows.Next() {
        // 处理数据
    }
    
    // 显式关闭当前查询的Rows
    vGerenciaConsulta.F_FECHAR_CONSULTA()
    
    time.Sleep(time.Minute)
}

更健壮的实现应该使用上下文并确保每次迭代都清理资源:

func processStack() error {
    for {
        ctx := context.Background()
        
        VGGerenciaConexao.F_ABRIR_CONEXAO()
        if VGGerenciaConexao.DataBase == nil {
            return fmt.Errorf("database connection is nil")
        }
        
        rows, err := VGGerenciaConexao.DataBase.QueryContext(ctx, "select * from stack")
        if err != nil {
            return fmt.Errorf("query failed: %w", err)
        }
        
        // 使用defer确保Rows关闭
        defer rows.Close()
        
        for rows.Next() {
            // 处理数据
        }
        
        // 检查迭代过程中的错误
        if err := rows.Err(); err != nil {
            return fmt.Errorf("rows iteration error: %w", err)
        }
        
        // 显式关闭
        rows.Close()
        
        time.Sleep(time.Minute)
    }
}

关键点:

  1. 每次查询后必须调用 Rows.Close()
  2. 使用 defer 作为安全网,防止在错误发生时资源泄漏
  3. 检查查询和迭代过程中的错误
  4. 考虑使用 QueryContext 以便可以取消长时间运行的查询

在你的原始代码中,每次循环都创建新的 Rows 对象但没有关闭之前的,导致游标数量持续增长直到达到数据库限制。

回到顶部