R / Richie全部文章 ↑

Go · 2 分钟阅读

Go 操作数据

目录

  • 数据库
  • 文件读写
  • 并发
    Go 操作数据无非这么几块:内置数据结构(数组、切片、map、struct)、数据库、文件、网络、并发。下面挑几个常用的写法贴出来。

数据库

database/sql 是标准库的统一接口,配驱动用。MySQL 配 github.com/go-sql-driver/mysql:

package main

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

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // sql.Open 只验证参数格式,真正建连要 Ping 一下
    if err := db.Ping(); err != nil {
        log.Fatal(err)
    }

    rows, err := db.Query("SELECT id, name FROM users")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

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

    result, err := db.Exec("INSERT INTO users(name) VALUES(?)", "John Doe")
    if err != nil {
        log.Fatal(err)
    }
    id, err := result.LastInsertId()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Inserted ID: %d\n", id)
}

sql.Open 不会真的连数据库,只是检查 DSN 格式对不对,得 Ping() 一下才能确认通不通。新手经常踩这个坑,写了 Open 就以为连上了。

错误处理用 log.Fatal 比 panic 优雅一点,至少会打印错误信息再退出,调试方便。

文件读写

Go 1.16 起 ioutil 包废弃了,读写文件直接用 os 包:

package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    data := []byte("Hello, Go!")
    if err := os.WriteFile("example.txt", data, 0644); err != nil {
        log.Fatal(err)
    }

    content, err := os.ReadFile("example.txt")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(content))

    if err := os.Remove("example.txt"); err != nil {
        log.Fatal(err)
    }
}

os.WriteFile 和 os.ReadFile 就是原来 ioutil 那两个函数搬过来了,签名没变,换个包名的事。老项目里看到 ioutil 不用慌,能跑,只是新代码别再那么写。

并发

goroutine 加 WaitGroup,最朴素的并发模式:

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()
}

wg.Done() 用 defer 保证函数退出时一定执行,就算 panic 也能减一。两个坑新手最爱踩:忘了 Add(1),Wait() 提前返回;忘了 Done(),Wait() 死锁。