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 回复

这确实有效。谢谢!

更多关于Golang中database/sql包使用问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


谢谢,它起作用了。 我修改了一次,它就正常运行了!

...
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)
    }
}

主要改进点:

  1. 在数据库层面添加UNIQUE约束
  2. 使用INSERT OR IGNORE语句自动处理重复
  3. 使用事务确保操作的原子性
  4. 添加错误处理避免静默失败
  5. 使用预处理语句提高性能
回到顶部