Golang中多协程访问Oracle数据库的实现与优化
Golang中多协程访问Oracle数据库的实现与优化 我们有一个服务,在一个无限循环中执行多个SQL语句,每5秒暂停一次。我想知道我在数据库处理方面是否采用了正确的流程。我创建了一个全局变量,在服务启动时,我们也启动与数据库的连接并将其保存在这个全局变量中。然后对于SQL的执行,我创建了一个局部变量来执行查询或更新、删除、插入(DML)操作,这些局部变量使用全局变量中的数据库来执行。
以下是代码:
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", "XXXX/XXXX@10.0.254.10:1521/orcl")
}
}
var VGGerenciaConexao GERENCIACON
全局变量是 VGGerenciaCON。
现在是我在代码中创建的用于查询和DML的局部变量代码:
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)
}
type GERENCIADML struct {
DataBase *sql.DB
Stmt *sql.Stmt
Result sql.Result
Error error
}
func (gd *GERENCIADML) F_EXECUTA_DML(pSql string) {
VGGerenciaConexao.F_ABRIR_CONEXAO()
gd.DataBase = VGGerenciaConexao.DataBase
gd.Stmt, gd.Error = gd.DataBase.Prepare(pSql)
gd.Result, gd.Error = gd.Stmt.Exec()
}
如你所见,我检查全局变量的连接,并在出错时启动它,然后将数据库的值分配给局部变量。我在所有服务中都使用这种结构。
这样做可以吗?我遇到了一些崩溃,所以我仍在尝试找出原因。
更多关于Golang中多协程访问Oracle数据库的实现与优化的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更好的做法是创建一个例程来返回你的数据库连接对象,在 goroutine 中将其作为局部变量使用,然后在 defer 中关闭该连接…
我认为 sql.DB 已经处理了数据库连接池。
更多关于Golang中多协程访问Oracle数据库的实现与优化的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你的代码存在几个关键问题,特别是在多协程环境下访问Oracle数据库时。以下是主要问题和改进方案:
主要问题
- 连接池竞争:
sql.DB是线程安全的,但你的F_ABRIR_CONEXAO()方法存在竞态条件 - 错误处理缺失:忽略了
sql.Open、Query、Prepare等操作的错误 - 连接泄漏:没有关闭
Rows和Stmt - 全局状态管理不当:
Ping()调用可能在高并发下成为瓶颈
改进后的实现
import (
"database/sql"
"sync"
"time"
_ "github.com/godror/godror" // 推荐使用godror驱动
)
type DBManager struct {
db *sql.DB
mu sync.RWMutex
config string
}
var (
globalDB *DBManager
once sync.Once
)
func GetDBManager(dsn string) *DBManager {
once.Do(func() {
globalDB = &DBManager{
config: dsn,
}
globalDB.initialize()
})
return globalDB
}
func (dm *DBManager) initialize() {
dm.mu.Lock()
defer dm.mu.Unlock()
if dm.db != nil {
return
}
db, err := sql.Open("godror", dm.config)
if err != nil {
panic(err)
}
// 配置连接池
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(2 * time.Minute)
// 测试连接
if err := db.Ping(); err != nil {
panic(err)
}
dm.db = db
}
func (dm *DBManager) GetDB() *sql.DB {
dm.mu.RLock()
defer dm.mu.RUnlock()
return dm.db
}
func (dm *DBManager) Close() {
dm.mu.Lock()
defer dm.mu.Unlock()
if dm.db != nil {
dm.db.Close()
dm.db = nil
}
}
// 查询执行器
type QueryExecutor struct {
db *sql.DB
}
func NewQueryExecutor() *QueryExecutor {
dbMgr := GetDBManager("user/pass@host:port/service")
return &QueryExecutor{
db: dbMgr.GetDB(),
}
}
func (qe *QueryExecutor) ExecuteQuery(query string, args ...interface{}) (*sql.Rows, error) {
return qe.db.Query(query, args...)
}
func (qe *QueryExecutor) ExecuteQueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
return qe.db.QueryContext(ctx, query, args...)
}
// DML执行器
type DMLExecutor struct {
db *sql.DB
}
func NewDMLExecutor() *DMLExecutor {
dbMgr := GetDBManager("user/pass@host:port/service")
return &DMLExecutor{
db: dbMgr.GetDB(),
}
}
func (de *DMLExecutor) ExecuteDML(query string, args ...interface{}) (sql.Result, error) {
return de.db.Exec(query, args...)
}
func (de *DMLExecutor) ExecuteDMLContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
return de.db.ExecContext(ctx, query, args...)
}
// 使用示例
func worker(id int, stop chan bool) {
qe := NewQueryExecutor()
de := NewDMLExecutor()
for {
select {
case <-stop:
return
default:
// 执行查询
rows, err := qe.ExecuteQuery("SELECT * FROM users WHERE status = ?", "active")
if err != nil {
log.Printf("Worker %d query error: %v", id, err)
continue
}
defer rows.Close()
// 处理结果...
// 执行DML
result, err := de.ExecuteDML("UPDATE users SET last_seen = ? WHERE id = ?", time.Now(), id)
if err != nil {
log.Printf("Worker %d update error: %v", id, err)
continue
}
affected, _ := result.RowsAffected()
log.Printf("Worker %d updated %d rows", id, affected)
time.Sleep(5 * time.Second)
}
}
}
// 主函数
func main() {
stop := make(chan bool)
// 启动多个协程
for i := 0; i < 10; i++ {
go worker(i, stop)
}
// 运行一段时间后停止
time.Sleep(30 * time.Second)
close(stop)
time.Sleep(1 * time.Second)
// 关闭数据库连接
if globalDB != nil {
globalDB.Close()
}
}
关键优化点
- 单例模式:确保只有一个数据库连接池实例
- 连接池配置:合理设置连接池参数
- 上下文支持:使用
QueryContext和ExecContext支持超时和取消 - 错误处理:正确处理所有可能返回的错误
- 资源清理:确保关闭
Rows和Stmt - 线程安全:使用读写锁保护共享状态
崩溃可能的原因
- 连接泄漏:没有关闭查询结果
- 竞态条件:多个协程同时调用
F_ABRIR_CONEXAO() - 空指针引用:当
Ping()失败时,gc.DataBase可能为nil - 驱动问题:建议使用维护更活跃的
godror驱动替代goracle
这个改进方案应该能解决你遇到的崩溃问题,并提供更好的性能和稳定性。

