Golang Mysql笔记(一)--- 链接与链接池

database/sql

database/sql是golang的标准库之一,它提供了一系列接口方法,用于访问关系数据库。它并不会提供数据库特有的方法,那些特有的方法交给数据库驱动去实现。
database/sql库提供了一些type。这些类型对掌握它的用法很是重要。
DB 数据库对象。 sql.DB类型表明了数据库。和其余语言不同,它并是数据库链接。golang中的链接来自内部实现的链接池,链接的创建是惰性的,当你须要链接的时候,链接池会自动帮你建立。一般你不须要操做链接池。一切都有go来帮你完成。
Results 结果集。数据库查询的时候,都会有结果集。sql.Rows类型表示查询返回多行数据的结果集。sql.Row则表示单行查询结果的结果集。固然,对于插入更新和删除,返回的结果集类型为sql.Result。
Statements 语句。sql.Stmt类型表示sql查询语句,例如DDL,DML等相似的sql语句。能够把当成prepare语句构造查询,也能够直接使用sql.DB的函数对其操做。mysql

warming up

下面就开始咱们的sql数据库之旅,咱们使用mysql数据库为例子,驱动使用go-sql-driver/mysql。
对于其余语言,查询数据的时候须要建立一个链接,对于go而言则是须要建立一个数据库抽象对象。链接将会在查询须要的时候,由链接池建立并维护。使用sql.Open函数建立数据库对象。它的第一个参数是数据库驱动名,第二个参数是一个链接字串(符合DSN风格,能够是一个tcp链接,一个unix socket等)。git

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

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
    if err != nil{
        log.Fatal(err)
    }
    defer db.Close()
}

建立了数据库对象以后,在函数退出的时候,须要释放链接,即调用sql.Close方法。例子使用了defer语句设置释放链接。
接下来进行一些基本的数据库操做,首先咱们使用Exec方法执行一条sql,建立一个数据表:github

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
    if err != nil{
        log.Fatal(err)
    }
    defer db.Close()

    _, err = db.Exec("CREATE TABLE IF NOT EXISTS test.hello(world varchar(50))")
    if err != nil{
        log.Fatalln(err)
    }
}

此时能够看见,数据库生成了一个新的表。接下来再插入一些数据。golang

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")

    ...

    rs, err := db.Exec("INSERT INTO test.hello(world) VALUES ('hello world')")
    if err != nil{
        log.Fatalln(err)
    }
    rowCount, err := rs.RowsAffected()
    if err != nil{
        log.Fatalln(err)
    }
    log.Printf("inserted %d rows", rowCount)
}

一样使用Exec方法便可插入数据,返回的结果集对象是是一个sql.Result类型,它有一个LastInsertId方法,返回插入数据后的id。固然此例的数据表并无id字段,就返回一个0.
插入了一些数据,接下来再简单的查询一下数据:sql

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")

    ... 

    rows, err := db.Query("SELECT world FROM test.hello")
    if err != nil{
        log.Fatalln(err)
    }

    for rows.Next(){
        var s string
        err = rows.Scan(&s)
        if err !=nil{
            log.Fatalln(err)
        }
        log.Printf("found row containing %q", s)
    }
    rows.Close()
}

咱们使用了Query方法执行select查询语句,返回的是一个sql.Rows类型的结果集。迭代后者的Next方法,而后使用Scan方法给变量s赋值,以便取出结果。最后再把结果集关闭(释放链接)。
经过上面一个简单的例子,介绍了database/sql的基本数据查询操做。而对于开篇所说的几个结构类型还没有进行详细的介绍。下面咱们再针对database/sql库的类型和数据库交互作更深的探究。
sql.DB
正如上文所言,sql.DB是数据库的抽象,虽然一般它容易被误觉得是数据库链接。它提供了一些跟数据库交互的函数,同时管理维护一个数据库链接池,帮你处理了单调而重复的管理工做,而且在多个goroutines也是十分安全。
sql.DB表示是数据库抽象,所以你有几个数据库就须要为每个数据库建立一个sql.DB对象。由于它维护了一个链接池,所以不须要频繁的建立和销毁。它须要长时间保持,所以最好是设置成一个全局变量以便其余代码能够访问。
建立数据库对象须要引入标准库database/sql,同时还须要引入驱动go-sql-driver/mysql。使用_表示引入驱动的变量,这样作的目的是为了在你的代码中不至于和标注库的函数变量namespace冲突。
链接池
只用sql.Open函数建立链接池,但是此时只是初始化了链接池,并无建立任何链接。链接建立都是惰性的,只有当你真正使用到链接的时候,链接池才会建立链接。链接池很重要,它直接影响着你的程序行为。
链接池的工做原来却至关简单。当你的函数(例如Exec,Query)调用须要访问底层数据库的时候,函数首先会向链接池请求一个链接。若是链接池有空闲的链接,则返回给函数。不然链接池将会建立一个新的链接给函数。一旦链接给了函数,链接则归属于函数。函数执行完毕后,要不把链接所属权归还给链接池,要么传递给下一个须要链接的(Rows)对象,最后使用完链接的对象也会把链接释放回到链接池。
请求一个链接的函数有好几种,执行完毕处理链接的方式稍有差异,大体以下:数据库

  • db.Ping() 调用完毕后会立刻把链接返回给链接池。
  • db.Exec() 调用完毕后会立刻把链接返回给链接池,可是它返回的Result对象还保留这链接的引用,当后面的代码须要处理结果集的时候链接将会被重用。
  • db.Query() 调用完毕后会将链接传递给sql.Rows类型,固然后者迭代完毕或者显示的调用.Clonse()方法后,链接将会被释放回到链接池。
  • db.QueryRow()调用完毕后会将链接传递给sql.Row类型,当.Scan()方法调用以后把链接释放回到链接池。
  • db.Begin() 调用完毕后将链接传递给sql.Tx类型对象,当.Commit()或.Rollback()方法调用后释放链接。

由于每个链接都是惰性建立的,如何验证sql.Open调用以后,sql.DB对象可用呢?一般使用db.Ping()方法初始化:安全

db, err := sql.Open("driverName", "dataSourceName")
if err != nil{
    log.Fatalln(err)
}

defer db.Close()

err = db.Ping()
if err != nil{
   log.Fatalln(err)
}

调用了Ping以后,链接池必定会初始化一个数据库链接。固然,实际上对于失败的处理,应该定义一个符合本身须要的方式,如今为了演示,简单的使用log.Fatalln(err)表示了。
链接失败
关于链接池另一个知识点就是你没必要检查或者尝试处理链接失败的状况。当你进行数据库操做的时候,若是链接失败了,database/sql会帮你处理。实际上,当从链接池取出的链接断开的时候,database/sql会自动尝试重连10次。仍然没法重连的状况下会自动从链接池再获取一个或者新建另一个。比如去买鸡蛋,售货员会从箱子里掏出鸡蛋,若是发现是坏蛋则连续掏10次,仍然找不到合适的就会换一个箱子招,或者从别的库房再拿一个给你。
链接池配置
不管哪个版本的go都不会提供不少控制链接池的接口。知道1.2版本之后才有一些简单的配置。但是1.2版本的链接池有一个bug,请升级更高的版本。
配置链接池有两个的方法:并发

  • db.SetMaxOpenConns(n int) 设置打开数据库的最大链接数。包含正在使用的链接和链接池的链接。若是你的函数调用须要申请一个链接,而且链接池已经没有了链接或者链接数达到了最大链接数。此时的函数调用将会被block,直到有可用的链接才会返回。设置这个值能够避免并发过高致使链接mysql出现too many connections的错误。该函数的默认设置是0,表示无限制。
  • db.SetMaxIdleConns(n int) 设置链接池中的保持链接的最大链接数。默认也是0,表示链接池不会保持释放会链接池中的链接的链接状态:即当链接释放回到链接池的时候,链接将会被关闭。这会致使链接再链接池中频繁的关闭和建立。

对于链接池的使用依赖于你是如何配置链接池,若是使用不当会致使下面问题:socket

大量的链接空闲,致使额外的工做和延迟。
链接数据库的链接过多致使错误。
链接阻塞。
链接池有超过十个或者更多的死链接,限制就是10次重连。tcp

大多数时候,如何使用sql.DB对链接的影响大过链接池配置的影响。这些具体问题咱们会再使用sql.DB的时候逐一介绍。
掌握了database/sql关于数据库链接池管理内容,下一步则是使用这些链接,进行数据的交互操做啦。


转载自:连接:https://www.jianshu.com/p/340eb943be2e

相关文章
相关标签/搜索