使用Golang协程执行多查询的实现方法
使用Golang协程执行多查询的实现方法 如何使用 Go 协程执行多个 SQL 查询
如今,所有的笔记本电脑和服务器都拥有核心和线程。
如果我们在不同的线程上运行独立的查询,速度会更快。这是我的思考过程。
更多关于使用Golang协程执行多查询的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Goroutine 1 从 emp 表中选择所有数据 Goroutine 2 从 dep 表中选择所有数据
除非 Goroutine 1 和 2 都执行完毕,否则应该在此处停止。
Goroutine 3 我将处理数据
这似乎是一个关于Go语言并发性的相当宽泛的问题。请参阅Go语言之旅的并发章节:
https://go.dev/tour/concurrency/1
以及Go by Example中的goroutines示例:
可以说,正确的答案(正如 @Dean_Davidson 所说)确实是:
- 使用主例程
但同时也要:
- 使用适当的 SQL 查询 进行 数据处理(仅使用 Go 进行最终步骤)
毕竟,SQL 基本上 就是 一种特定领域的数据处理语言。
此外,数据库系统有它们自己的并发机制(事务、ACID 等),因此你可能不需要为此依赖 Go 的机制。
事情并非那么简单。
- 瓶颈(例如,可能你的数据库正在等待前一个请求完成,那么你的 goroutine 就只是在等待)
- 竞态条件:每当多个线程访问相同的数据时,就存在损坏数据的巨大风险(一个正确的程序胜过一个快速但错误的程序)
Go 语言凭借其独特的通道(以及 WaitGroup 等其他特性)使得避免竞态条件比大多数其他语言更容易,但这并不意味着你应该总是编写多线程程序。它仍然比单线程程序更复杂,因为你必须注意线程/goroutine 之间的同步问题。
让多个 goroutine 运行任务并同步它们是一个相对容易解决的问题。既然你提到了阻塞直到两个任务完成,可以查看一下 sync.WaitGroup:
sync package - sync - Go Packages
sync 包提供了基本的同步原语,例如互斥锁。
从文档中:
除了 Once 和 WaitGroup 类型,大部分原语是供底层库例程使用的。更高级别的同步最好通过通道和通信来完成。
因此,为此目的,请参考我上面链接的 Go 并发之旅部分,其中演示了通道的使用。话虽如此,我倾向于认为你过早地进行了优化。如果我是你,我会先尝试运行查询,只有在你有具体问题需要解决时,才开始考虑并发。
这取决于您想使用哪个数据库。
这里有一个教程:http://go-database-sql.org/
还有这个教程:https://go.dev/doc/tutorial/database-access
这两个教程都使用了这个 mysql 驱动:https://github.com/go-sql-driver/mysql
您也可以尝试使用 sqlite。
那么您需要这个驱动:https://github.com/mattn/go-sqlite3,但它有一个问题,即依赖于 C 编译器(cgo),因此它并不总是在每个系统上都能编译(Linux 可能可以,但 macOS 或 Windows 会困难一些)。
另一个流行的数据库是 postgresql,那么我推荐这个驱动:https://github.com/jackc/pgx,它也可以与标准的 import "database/sql" 一起使用。
该库还提供了对 pg 数据库的低级访问,这使得它速度更快一些,并支持更多 postgresql 特有的功能,但那样您就不是在使用 database/sql 了。
如果您想要 sqlite 但不想依赖 C 编译器(cgo),您可以使用 https://pkg.go.dev/modernc.org/sqlite,它使用 modernc 将原始的 sqlite 转换为完全在纯 Go 环境中运行的东西。
您也可以使用专门为 sqlite 设计的库,它们支持所有(或大部分)sqlite 的功能,但那样您就又脱离了 Go 的标准 database/sql 包。有 https://github.com/crawshaw/sqlite,但它又依赖于 cgo;还有 https://pkg.go.dev/zombiezen.com/go/sqlite,它受 crawshaw 启发,但也使用了 modernc,因此不需要 cgo。
编辑:使链接使用 URL
在Go中利用协程并发执行多个SQL查询可以有效提升数据库操作的效率。以下是一个典型实现示例:
package main
import (
"database/sql"
"fmt"
"log"
"sync"
_ "github.com/go-sql-driver/mysql"
)
type QueryResult struct {
ID int
Name string
Error error
}
func executeQuery(db *sql.DB, query string, id int, results chan<- QueryResult, wg *sync.WaitGroup) {
defer wg.Done()
var name string
err := db.QueryRow(query, id).Scan(&name)
results <- QueryResult{
ID: id,
Name: name,
Error: err,
}
}
func main() {
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
log.Fatal(err)
}
defer db.Close()
queries := []struct {
SQL string
ID int
}{
{"SELECT name FROM users WHERE id = ?", 1},
{"SELECT name FROM products WHERE id = ?", 2},
{"SELECT name FROM orders WHERE id = ?", 3},
}
results := make(chan QueryResult, len(queries))
var wg sync.WaitGroup
for _, q := range queries {
wg.Add(1)
go executeQuery(db, q.SQL, q.ID, results, &wg)
}
go func() {
wg.Wait()
close(results)
}()
for result := range results {
if result.Error != nil {
fmt.Printf("查询ID %d 失败: %v\n", result.ID, result.Error)
} else {
fmt.Printf("查询ID %d 结果: %s\n", result.ID, result.Name)
}
}
}
关键要点:
- 每个查询在独立的goroutine中执行
- 使用sync.WaitGroup等待所有查询完成
- 通过channel安全地收集结果
- 共享单个数据库连接(需确保驱动支持并发)
对于需要不同参数的查询,可以使用以下变体:
func executeDynamicQuery(db *sql.DB, query string, args []interface{}, resultChan chan<- interface{}) {
rows, err := db.Query(query, args...)
if err != nil {
resultChan <- err
return
}
defer rows.Close()
// 处理结果集
var results []map[string]interface{}
// ... 解析逻辑
resultChan <- results
}
注意:实际使用时应添加连接池配置、超时控制和错误处理机制。

