MySQL介绍以及Go操作MySQL | 青训营笔记

61 阅读4分钟

MySQL介绍以及Go操作MySQL

1. MySQL介绍

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面 MySQL 是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

2. MySQL安装

2.1. 下载

MySQL官网

2.2. 安装

MySQL安装教程

2.3. 配置环境变量

将MySQL安装目录下的bin目录添加到环境变量中

2.4. 测试


# 查看MySQL版本
mysql --version

# 登录MySQL
mysql -u root -p

# 退出MySQL
exit

3. MySQL的索引

3.1. 索引的概念

索引是帮助MySQL高效获取数据的数据结构,索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同的存储引擎具有不同的索引类型和实现,索引的类型也决定了它们如何实现以及它们可以提供的功能。

3.2. 索引的优点

  • 大大减少了服务器需要扫描的数据行数
  • 帮助服务器避免排序和临时表
  • 将随机I/O变为顺序I/O

3.3. 索引的缺点

  • 创建和维护索引需要时间成本,这种成本随着数据量的增加而增加
  • 索引需要占物理空间,除了数据表占用的存储空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大
  • 当对表中的数据进行增加、删除和修改的时候,索引也需要动态的维护,这样就降低了数据的维护速度

3.4. 索引的分类

  • 普通索引
  • 唯一索引
  • 主键索引
  • 组合索引
  • 全文索引

Gorm操作

2. 连接 MySQL

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // DSN:Data Source Name
    dsn := "root:123456@tcp(
    // 连接数据库
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("dsn %s invalid, err:%v\n", dsn, err)
        return
    }
    err = db.Ping()
    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", dsn, err)
        return
    }
    fmt.Println("连接数据库成功!")
}

3. 查询单条数据

func queryRowDemo() {
    sqlStr := "select id, name, age from user where id=?"
    var u user
    // 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放
    err := db.QueryRow(sqlStr, 1).Scan(&u.id, &u.name, &u.age)
    if err != nil {
        fmt.Printf("scan failed, err:%v\n", err)
        return
    }
    fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}

注意: QueryRow 之后必须调用 Scan 方法,因为该方法会释放数据库链接,如果不调用,会导致链接不会释放,从而导致数据库连接池被占用完毕,后续无法

优化: 使用 sync.Pool 进行优化

// db.go
package db

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "sync"
)

var (
    db  *sql.DB
    once sync.Once
)

func InitDB() (err error) {
    once.Do(func() {
        // DSN:Data Source Name
        dsn := "root:123456@tcp(

        db, err = sql.Open("mysql", dsn)
        if err != nil {
            fmt.Printf("open %s failed, err:%v\n", dsn, err)
            return
        }
        err = db.Ping()
        if err != nil {
            fmt.Printf("open %s failed, err:%v\n", dsn, err)
            return
        }
    })
    return
}
```

var db *sql.DB

## 4. 查询多条数据

```go
func queryMultiRowDemo() {
    sqlStr := "select id, name, age from user where id > ?"
    rows, err := db.Query(sqlStr, 0)
    if err != nil {
        fmt.Printf("query failed, err:%v\n", err)
        return
    }
    // 非常重要:关闭rows释放持有的数据库链接
    defer rows.Close()
    // 循环读取结果集中的数据
    for rows.Next() {
        var u user
        err := rows.Scan(&u.id, &u.name, &u.age)
        if err != nil {
            fmt.Printf("scan failed, err:%v\n", err)
            return
        }
        fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
    }
}

5. 插入数据

func insertRowDemo() {
    sqlStr := "insert into user(name, age) values (?,?)"
    ret, err := db.Exec(sqlStr, "小王子", 18)
    if err != nil {
        fmt.Printf("insert failed, err:%v\n", err)
        return
    }
    theID, err := ret.LastInsertId() // 新插入数据的id
    if err != nil {
        fmt.Printf("get lastinsert ID failed, err:%v\n", err)
        return
    }
    fmt.Printf("insert success, the id is %d.\n", theID)
}

6. 更新数据

func updateRowDemo() {
    sqlStr := "update user set age=? where id = ?"
    ret, err := db.Exec(sqlStr, 39, 1)
    if err != nil {
        fmt.Printf("update failed, err:%v\n", err)
        return
    }
    n, err := ret.RowsAffected() // 操作影响的行数
    if err != nil {
        fmt.Printf("get RowsAffected failed, err:%v\n", err)
        return
    }
    fmt.Printf("update success, affected rows:%d\n", n)
}

注意:

  1. 更新操作,Exec()返回的是结果对象,RowsAffected()返回的是影响的行数
  2. 插入操作,Exec()返回的是结果对象,LastInsertId()返回的是插入的 id
  3. 删除操作,Exec()返回的是结果对象,RowsAffected()返回的是影响的行数
  4. 查询操作,Query()返回的是行集对象,Next()用于循环每一行,Scan()用于读取每一列

7. 删除数据

func deleteRowDemo() {
    sqlStr := "delete from user where id = ?"
    ret, err := db.Exec(sqlStr, 1)
    if err != nil {
        fmt.Printf("delete failed, err:%v\n", err)
        return
    }
    n, err := ret.RowsAffected() // 操作影响的行数
    if err != nil {
        fmt.Printf("get RowsAffected failed, err:%v\n", err)
        return
    }
    fmt.Printf("delete success, affected rows:%d\n", n)
}

8. 更新注意事项

  1. sql 注入问题
func sqlInjectDemo(name string) {
    // 自己拼接 sql 语句的字符串
    sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name)
    fmt.Printf("SQL:%s\n", sqlStr)
    var u user
    err := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age)
    if err != nil {
        fmt.Printf("exec failed, err:%v\n", err)
        return
    }
    fmt.Printf("user:%#v\n", u)
}
  1. 预处理方式解决 sql 注入问题
func prepareQueryDemo(name string) {
    sqlStr := "select id, name, age from user where name=?"
    stmt, err := db.Prepare(sqlStr)
    if err != nil {
        fmt.Printf("prepare failed, err:%v\n", err)
        return
    }
    defer stmt.Close()
    rows, err := stmt.Query(name)
    if err != nil {
        fmt.Printf("query failed, err:%v\n", err)
        return
    }
    defer rows.Close()
    for rows.Next() {
        var u user
        err := rows.Scan(&u.id, &u.name, &u.age)
        if err != nil {
            fmt.Printf("scan failed, err:%v\n", err)
            return
        }
        fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
    }
}