golang高性能Oracle数据库驱动插件库godror的使用

golang高性能Oracle数据库驱动插件库godror的使用

概述

godror是一个用于连接Oracle数据库的database/sql/driver.Driver实现,它使用了Anthony Tuininga的优秀OCI包装器ODPI-C。

构建要求

  • Go 1.15或更高版本
  • 启用了CGO的C编译器(CGO_ENABLED=1),因此跨平台编译较困难

运行时要求

  • Oracle客户端库 - 请参考ODPI-C的安装文档

虽然编译时不需要Oracle客户端库,但在运行时是必需的。可以从Oracle官网下载免费的Basic或Basic Light包。

安装

运行以下命令安装godror:

go get github.com/godror/godror@latest

然后安装Oracle客户端库即可使用。

连接Oracle数据库

使用sql.Open("godror", dataSourceName)连接Oracle数据库,其中dataSourceName是一个logfmt编码的参数列表。至少需要指定"user"、"password"和"connectString"参数。例如:

db, err := sql.Open("godror", `user="scott" password="tiger" connectString="dbhost:1521/orclpdb1"`)

connectString可以是SQL*Plus或Oracle Call Interface(OCI)接受的任何内容:服务名、Easy Connect字符串如host:port/service_name,或连接描述符如(DESCRIPTION=...)

可以通过"?connect_timeout=15"指定连接超时秒数(注意connect_timeout需要至少19c客户端)。

连接池

Oracle的OCI客户端(godror底层使用)在快速连接-重连循环中存在问题,可能导致C库中的SIGSEGV。因此请使用某种连接池 - 可以是db.SetMaxIdleConns设置大于0的值,或使用驱动内置的会话池(standaloneConnection=0, poolMinSessions=1)。

完整示例

以下是一个完整的godror使用示例:

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"time"

	_ "github.com/godror/godror"
)

func main() {
	// 连接Oracle数据库
	db, err := sql.Open("godror", `user="scott" password="tiger" connectString="dbhost:1521/orclpdb1"`)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 设置连接池参数
	db.SetMaxIdleConns(5)
	db.SetMaxOpenConns(10)
	db.SetConnMaxLifetime(time.Minute * 5)

	// 检查连接
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := db.PingContext(ctx); err != nil {
		log.Fatal(err)
	}

	// 查询示例
	rows, err := db.QueryContext(ctx, "SELECT empno, ename FROM emp WHERE deptno = :1", 10)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		var empno int
		var ename string
		if err := rows.Scan(&empno, &ename); err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Employee %d: %s\n", empno, ename)
	}
	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}

	// 调用存储过程示例(返回游标)
	var rset driver.Rows
	const query = `BEGIN Package.StoredProcA(123, :1); END;`
	
	stmt, err := db.PrepareContext(ctx, query)
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()
	
	if _, err := stmt.ExecContext(ctx, sql.Out{Dest: &rset}); err != nil {
		log.Fatal(err)
	}
	defer rset.Close()

	// 处理返回的游标数据
	cols := rset.(driver.RowsColumnTypeScanType).Columns()
	dests := make([]driver.Value, len(cols))
	for {
		if err := rset.Next(dests); err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
		fmt.Println(dests)
	}
}

注意事项

  1. sql.NullString:不支持,因为Oracle DB不区分空字符串("")和NULL

  2. NUMBER类型:作为字符串传输到Go底层,确保不丢失精度

  3. CLOB/BLOB:从2.9.0开始,默认返回为string/[]byte

  4. TIMESTAMP:所有time.Time都绑定为DATE,因此会丢失小数秒

  5. 时区:默认使用数据库OS的时区

  6. 上下文超时:始终尽快关闭sql.Rows

贡献

与其他Go项目一样,你不需要更改导入路径,但可以在本地修改库代码。设置不同的远程仓库后即可提交Pull Request。

第三方工具

  • oracall:生成用于调用存储过程的服务器

更多关于golang高性能Oracle数据库驱动插件库godror的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能Oracle数据库驱动插件库godror的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


godror - Golang高性能Oracle数据库驱动使用指南

godror是Oracle官方推荐的Go语言Oracle数据库驱动,基于Oracle的ODPI-C接口实现,具有高性能和稳定性。下面详细介绍其使用方法。

安装

go get github.com/godror/godror

需要先安装Oracle客户端库,并设置相关环境变量:

  1. 下载Oracle Instant Client Basic或Basic Light包
  2. 解压并设置LD_LIBRARY_PATH(linux)或PATH(windows)

基本使用

连接数据库

package main

import (
	"database/sql"
	"fmt"
	"log"
	_ "github.com/godror/godror"
)

func main() {
	// 连接字符串格式: user/pass@host:port/service_name
	dsn := "scott/tiger@localhost:1521/ORCLPDB1?connect_timeout=30"
	db, err := sql.Open("godror", dsn)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 测试连接
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to Oracle database successfully!")
}

查询数据

func queryData(db *sql.DB) {
	rows, err := db.Query("SELECT employee_id, first_name, last_name FROM employees WHERE department_id = :1", 50)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		var (
			id        int
			firstName string
			lastName  string
		)
		if err := rows.Scan(&id, &firstName, &lastName); err != nil {
			log.Fatal(err)
		}
		fmt.Printf("ID: %d, Name: %s %s\n", id, firstName, lastName)
	}

	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}
}

执行DML操作

func insertData(db *sql.DB) {
	result, err := db.Exec(
		"INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, job_id) "+
			"VALUES (:1, :2, :3, :4, :5, :6)",
		207, "John", "Doe", "JDOE", time.Now(), "IT_PROG")
	if err != nil {
		log.Fatal(err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Inserted %d row(s)\n", rowsAffected)
}

高级特性

批量插入

func batchInsert(db *sql.DB) {
	// 准备语句
	stmt, err := db.Prepare("INSERT INTO employees (employee_id, first_name, last_name) VALUES (:1, :2, :3)")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	// 批量插入
	for i := 0; i < 10; i++ {
		_, err = stmt.Exec(300+i, fmt.Sprintf("First%d", i), fmt.Sprintf("Last%d", i))
		if err != nil {
			log.Fatal(err)
		}
	}
}

事务处理

func transactionExample(db *sql.DB) {
	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}

	// 执行事务操作
	_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE account_id = 1")
	if err != nil {
		tx.Rollback()
		log.Fatal(err)
	}

	_, err = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE account_id = 2")
	if err != nil {
		tx.Rollback()
		log.Fatal(err)
	}

	err = tx.Commit()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Transaction completed successfully")
}

使用Oracle特有类型

func oracleTypesExample(db *sql.DB) {
	var (
		id      int
		name    string
		hireDate time.Time
		salary  float64
		photo   []byte
	)
	
	err := db.QueryRow(`
		SELECT employee_id, first_name, hire_date, salary, photo 
		FROM employees 
		WHERE employee_id = :1`, 100).Scan(&id, &name, &hireDate, &salary, &photo)
	if err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("ID: %d, Name: %s, Hire Date: %v, Salary: %.2f, Photo Size: %d\n",
		id, name, hireDate, salary, len(photo))
}

性能优化技巧

  1. 连接池配置
db.SetMaxOpenConns(25)  // 最大连接数
db.SetMaxIdleConns(5)   // 最大空闲连接数
db.SetConnMaxLifetime(time.Hour)  // 连接最大存活时间
  1. 使用预编译语句:重复执行的SQL应使用Prepare

  2. 批量操作:使用数组绑定提高批量操作性能

  3. 适当设置fetch size

rows, err := db.Query("SELECT /*+ godror.fetchSize=100 */ * FROM large_table")
  1. 使用连接上下文
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

rows, err := db.QueryContext(ctx, "SELECT * FROM slow_query")

常见问题解决

  1. ORA-12545: Connect failed because target host or object does not exist

    • 检查连接字符串格式
    • 确认Oracle监听服务运行正常
  2. ORA-01017: invalid username/password; logon denied

    • 检查用户名密码
    • 确认用户有连接权限
  3. DPI-1047: Oracle Client library cannot be loaded

    • 确认Oracle Instant Client已安装
    • 检查环境变量设置

godror提供了高性能的Oracle数据库访问能力,通过合理配置和使用其高级特性,可以满足企业级应用的需求。更多详细配置和选项请参考官方文档。

回到顶部