Golang中database/sql包使用问题探讨
Golang中database/sql包使用问题探讨 我编写了一段代码,用于处理大量数据,这些大数据需要逐条检查数据库中是否已存在记录,如果存在则跳过,如果不存在则保存记录。
这里的问题是,这段代码在一定程度上可以工作,但过一段时间后就开始保存重复记录,有人能帮助我吗?
func CreateRecords(cryptoFilteredResult []*gjson.CryptoFiltered) {
db, _ := sql.Open("sqlite3", "database/database.db")
db.Exec("CREATE TABLE IF NOT EXISTS CryptoAll (id INTEGER PRIMARY KEY AUTOINCREMENT, Symbol TEXT, Price REAL)")
var i int
for i < len(cryptoFilteredResult) {
cryptoAll := CryptoAll{
Crypto: cryptoFilteredResult[i],
}
rows, _ := db.Query("SELECT Symbol FROM CryptoAll WHERE Symbol = ?", cryptoAll.Crypto.Symbol)
if rows.Next() { // true
fmt.Println("1 - Record Exist")
} else { // false
db.Exec("INSERT INTO CryptoAll (Symbol, Price) VALUES (?, ?)", cryptoAll.Crypto.Symbol, cryptoAll.Crypto.Price)
aux.AppendFile("log/program-log.txt", cryptoAll.Crypto.Symbol, cryptoAll.Crypto.Price) // record a simple log
fmt.Println("0 - Record Created")
}
i++
}
}
我使用了一个功能强大的API和一个Web Socket (gorilla/websocket) 并解析了JSON数据, 如果你需要更多信息或代码,请告诉我
更多关于Golang中database/sql包使用问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
4 回复
谢谢,它起作用了。 我修改了一次,它就正常运行了!
...
var symbol string
...
尝试以下代码
func CreateRecords(cryptoFilteredResult []*gjson.CryptoFiltered) {
db, _ := sql.Open("sqlite3", "database/database.db")
defer db.Close()
db.Exec("CREATE TABLE IF NOT EXISTS CryptoAll (id INTEGER PRIMARY KEY AUTOINCREMENT, Symbol TEXT, Price REAL)")
var symbol int
for i:=0; i < len(cryptoFilteredResult); i++ {
cryptoAll := CryptoAll{
Crypto: cryptoFilteredResult[i],
}
row := db.QueryRow("SELECT Symbol FROM CryptoAll WHERE Symbol = ? LIMIT 1", cryptoAll.Crypto.Symbol)
switch err := row.Scan(&symbol); err {
case sql.ErrNoRows:
fmt.Println("0 - Record Created")
db.Exec("INSERT INTO CryptoAll (Symbol, Price) VALUES (?, ?)", cryptoAll.Crypto.Symbol, cryptoAll.Crypto.Price)
aux.AppendFile("log/program-log.txt", cryptoAll.Crypto.Symbol, cryptoAll.Crypto.Price) // record a simple log
case nil:
fmt.Println("1 - Record Exist")
default:
panic(err)
}
}
}
你的代码存在并发数据竞争问题。当多个goroutine同时执行查询和插入操作时,可能会在检查记录是否存在和实际插入记录之间产生时间窗口,导致重复记录被插入。
以下是修正后的代码示例,使用数据库的唯一约束和事务来确保原子性:
func CreateRecords(cryptoFilteredResult []*gjson.CryptoFiltered) {
db, err := sql.Open("sqlite3", "database/database.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 添加唯一约束防止重复Symbol
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS CryptoAll (
id INTEGER PRIMARY KEY AUTOINCREMENT,
Symbol TEXT UNIQUE,
Price REAL
)`)
if err != nil {
log.Fatal(err)
}
// 使用预处理语句提高性能
insertStmt, err := db.Prepare("INSERT OR IGNORE INTO CryptoAll (Symbol, Price) VALUES (?, ?)")
if err != nil {
log.Fatal(err)
}
defer insertStmt.Close()
// 批量处理数据
for _, crypto := range cryptoFilteredResult {
result, err := insertStmt.Exec(crypto.Symbol, crypto.Price)
if err != nil {
log.Printf("插入失败: %v", err)
continue
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected > 0 {
aux.AppendFile("log/program-log.txt", crypto.Symbol, crypto.Price)
fmt.Println("0 - Record Created")
} else {
fmt.Println("1 - Record Exist")
}
}
}
如果需要更精确的控制,可以使用事务:
func CreateRecordsWithTransaction(cryptoFilteredResult []*gjson.CryptoFiltered) {
db, err := sql.Open("sqlite3", "database/database.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
stmt, err := tx.Prepare("INSERT INTO CryptoAll (Symbol, Price) VALUES (?, ?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for _, crypto := range cryptoFilteredResult {
// 在事务中检查记录是否存在
var existingSymbol string
err := tx.QueryRow("SELECT Symbol FROM CryptoAll WHERE Symbol = ?", crypto.Symbol).Scan(&existingSymbol)
if err == sql.ErrNoRows {
_, err = stmt.Exec(crypto.Symbol, crypto.Price)
if err != nil {
// 如果是唯一约束冲突,记录已存在
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
fmt.Println("1 - Record Exist")
continue
}
log.Printf("插入失败: %v", err)
} else {
aux.AppendFile("log/program-log.txt", crypto.Symbol, crypto.Price)
fmt.Println("0 - Record Created")
}
} else if err == nil {
fmt.Println("1 - Record Exist")
} else {
log.Printf("查询失败: %v", err)
}
}
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
}
主要改进点:
- 在数据库层面添加
UNIQUE约束 - 使用
INSERT OR IGNORE语句自动处理重复 - 使用事务确保操作的原子性
- 添加错误处理避免静默失败
- 使用预处理语句提高性能

