🎁理解Database/Sql
database/sql包的存在意义?👉 提供一个统一的接口,让我们操作不一样的数据库
解释一下基本用法的代码:
![]()
❗使用该代码需要先通过
go get github.com/go-sql-driver/mysql
来获取依赖
❗代码地8行的解释:
db,err:=sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello")
其中user为mysql的的用户名,pssword为密码,hello指代的是具体数据库的名称。
❗users?user?
我们需要创建一个结构体例如
type User struct{ ID int Name string }
由上面的代码我们可以看到,我们创建了一个名为users的User类型的数组,通过for循环和rows指向的结果逐条塞进user当中。
⚠在这个例子当中,如果我们切换其他数据库,我们只需要把mysql更改为其他的数据源即可,体现出了我们database/sql包的灵活性
🎁设计原理
❗连接池:采用池化技术,如果遇到请求特别大的情况,会影响我们的程序性能,使用池化技术就能够优化我们的系统。主要应用于把比较昂贵、费事的资源整合到一个特定的池子里。大幅度提高服务性能。
⁉database/sql:具体实现sql执行的过程?
for i:=0 ;i<maxBadConnRetries;i++{
dc.err:=db.conn(ctx,strategy)
defer dc.db.putConn(dc,err,true)
if err!=nil{
err=dc.ci.Query(sql,args...)
}
isBadConn=errors.Is(err,driver.ErrBadConn)
if !isBadConn{
break
}
}
代码解释:
❗其中在for循环中我们默认的maxBadConnRetries是两次,也就是说会有两次连接,如果我们从连接池获取到一个链接的时候,我们也会有两种策略dc,err:=db.conn(ctx,stategy),一种策略是“尽量呼应”(连接池中已经有链接了,我们尽量去使用这个原来的链接),另外一种是“新建一个新的链接”。获取到连接之后。defer表示我们处理完这个连接之后,将连接放回我们的连接池,同时会进行检查。最后判断有没有返回任何错误,如果有,再进行尝试。
⁉它是如何预留接口的?:Driver连接接口
//Driver接口
type Driver interface{
Open(name string)(Conn,error)
}
//注册全局Driver
func Register(name string,driver driver.Driver){
driverMu.Lock()
defer driverMu.Unlock()
if driver==nil{
panic("sql:Register driver is nil")
}
if _,dup:=driver[name];dup{
panic("sql:Register called twice for driver "+name)
}
drivers[name]=driver
}
❗解释一下课堂中没有讲的panic:
在 Golang 中,当执行代码遇到异常情况时,可以使用 panic 函数产生异常,程序会终止运行并输出异常信息。
panic 函数的语法如下:
func panic(v interface{})
panic 函数接收一个 interface 类型的参数 v,可以是任何类型。当调用 panic 函数时,程序会终止运行,并打印出相应的错误信息。
一般情况下,panic 的使用场景有两种:
- 当发生不可预料的异常情况时,我们可以使用 panic 函数抛出异常,以便在统计和分析程序错误时提供更多的信息;
- 在程序开发过程中,我们可以使用 panic 函数模拟一些异常情况,以便更好的测试程序的稳定性和容错性。
在 Go 语言中,panic 的典型用法是处理不可恢复的运行时错误,例如数组越界、类型断言、空指针引用、除零等等。因为这些错误无法恢复,程序无法继续运行,此时可以调用 panic 函数来终止程序的执行,并输出相应的错误信息。
举个例子,如果我们要实现一个函数将一个数组的元素倒序,并且要求该数组不能为空,我们可以这样写:
func ReverseArray(arr []int) []int { if len(arr) == 0 { panic("数组不能为空") } for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 { arr[i], arr[j] = arr[j], arr[i] } return arr }
在上述代码中,我们首先判断了输入的数组是否为空,如果为空,则调用 panic 函数抛出异常。这样,在使用 ReverseArray 函数时,如果传入空数组,程序会直接终止,并输出异常信息。
注意,当调用 panic 函数时,程序会在当前函数中立即停止执行,但是该函数可能会被其它函数调用,甚至可能被其它 goroutine 调用。在这种情况下,panic 会一直向外层调用的函数传递,直到程序中止运行或者被 defer 函数捕获。
类似于其他编程语言中的 try-catch 语句,Go 语言也提供了 recover 函数用来捕获 panic,recover 函数的语法如下:
func recover() interface{}
该函数会返回传递给 panic 函数的值,并让程序继续执行。如果当前函数中没有遇到 panic,recover 函数会返回 nil。
举个例子:
func foo() { defer func() { if r := recover(); r != nil { fmt.Println("捕获到了panic的信息:", r) } }() panic("抛出了一个异常") fmt.Println("这句话不会执行到") }
在上述代码中,我们使用了 defer 函数在 panic 函数后面注册了一个用来捕获异常信息的匿名函数,当程序遇到 panic 函数时,该匿名函数会被调用,并输出相应的错误信息。由于使用了 recover 函数,程序不会立即停止运行,而是继续执行后面的代码,例如上述代码中的 fmt.Println 函数就不会被执行到。
需要注意的是,在使用 recover 函数时,必须在 defer 函数中调用 recover 函数,而不能在函数体中调用 recover 函数,否则 recover 函数不会被执行,程序也不会停止运行。因此,在使用 recover 函数时,一定要在 defer 函数中进行调用。