go mysql slave_Go 操作 Mysql(二)

查询数据方法回顾整理

上一篇博客中,主要是快速过了一遍 demo 代码和 DB 类型对象中方法的使用

在整理查询数据方法的时候,使用了 Query() 方法,其实 sqlx 还提供了 QueryRow() 方法,查询单行记录,以及 Queryx() 和 QueryRowx() 方法,将查询的结果保存到结构体

所以我们通过 DB 查询数据的方法一共就有三对:

Query()  和  QueryRow()            分别返回 sql.Rows 和 sql.Row 类型

Queryx() 和 QueryRowx()             分别返回 sql.Rows 和 sql.Row 类型,支持将查询记录保存到结构体

Get()  和  Select()  将查询记录保存到结构体 和 结构体切片中

Query() 方法

func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

使用场景:查询字段较少的情况下使用,比如 select uid, username from userinfo; 这样的语句,如果是 select * from userinfo,就使用 Get() 或 Select() 好了

Query() 返回的结果集是 sql.Rows 类型,它有一个 Next() 方法,可以迭代数据库的游标,进而获取下一条记录,结果集使用完毕之后需要调用 rows.Close() 手动关闭连接

其实通过 for 循环迭代数据的时候,当迭代到最后一行记录时,会发出一个 io.EOF(与读文件类似),引发一个错误,同时 Go 会自动调用 rows.Close() 方法释放连接,然后返回 false,此时循环结束退出

通常情况下,会正常迭代完数据然后退出循环,可是如果因为循环语句中的其它错误导致退出了循环,此时 rows.Next() 处理结果集的过程并没有完成,归属于 rows 的数据库连接不会释放回到连接池,因此十分有必要正确的处理 rows 的连接,如果没有关闭 rows 连接,将导致大量的连接并且不会被其它方法重用,就像溢出了一样,最终导致数据库无法使用(提示数据库有过多的连接)

rows.Next循环迭代的时候,因为触发了io.EOF而退出循环。为了检查是否是迭代正常退出还是异常退出,需要检查rows.Err

package main

import (

"fmt"

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

"github.com/jmoiron/sqlx"

)

var (

userName string = "root"

password string = "seemmo"

ipAddrees string = "10.10.4.80"

port int = 3306

dbName string = "golang_db"

charset string = "utf8"

)

func connectMysql() *sqlx.DB {

dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", userName, password, ipAddrees, port, dbName, charset)

Db, err := sqlx.Open("mysql", dsn)

if err != nil {

fmt.Printf("mysql connect failed, detail is [%v]", err.Error())

}

return Db

}

func queryData(Db *sqlx.DB) {

rows, err := Db.Query("select uid, username, create_time from userinfo")

if err != nil {

fmt.Printf("query data failed, error is [%v]", err.Error())

return

}

for rows.Next() {

var uid int

var userName, createTime string

err := rows.Scan(&uid, &userName, &createTime)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

fmt.Println(uid, userName, createTime)

}

err = rows.Close()

if err != nil {

fmt.Println(err.Error())

}

}

func main (){

var Db *sqlx.DB = connectMysql()

defer Db.Close()

queryData(Db)

}

//运行结果:

//1 johny 2019-07-08 10:43:21

//2 anson 2019-07-08 10:52:46

QueryRow() 方法

func (db *DB) QueryRow(query string, args ...interface{}) *Row

Query() 方法是查询多行结果集的(sqlx.Rows),QueryRow() 方法用来查询单行结果集(sqlx.Row),不需要通过 Next() 方法迭代

QueryRow() 方法的返回值与 Query() 不同,它要么返回一个 sqlx.Row 类型,要么返回一个 error 类型,如果是发生了 error,则会延迟到 Scan() 方法调用结束后返回,如果没有错误,则 Scan 正常执行,只有当查询结果为空的时候,会触发一个 sqlx.ErrNoRows 错误,你可以先调用 Scan() 方法再检查错误(也可以先检查错误再调用 Scan() 方法)

在没有过滤条件的情况下,默认返回第一条数据,不用调用 Close() 方法释放连接(因为只有一条记录)

package main

import (

"fmt"

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

"github.com/jmoiron/sqlx"

"time"

)

var (

userName string = "root"

password string = "seemmo"

ipAddrees string = "10.10.4.80"

port int = 3306

dbName string = "golang_db"

charset string = "utf8"

)

func connectMysql() *sqlx.DB {

dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", userName, password, ipAddrees, port, dbName, charset)

Db, err := sqlx.Open("mysql", dsn)

if err != nil {

fmt.Printf("mysql connect failed, detail is [%v]", err.Error())

}

return Db

}

func queryRow(Db *sqlx.DB) {

row := Db.QueryRow("select uid, username, create_time from userinfo")

var uid int

var userName, createTime string

err := row.Scan(&uid, &userName, &createTime)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

fmt.Println(uid, userName, createTime)

}

func main (){

var Db *sqlx.DB = connectMysql()

defer Db.Close()

queryRow(Db)

}

运行结果:

1 johny 2019-07-08 10:43:21

查询方法补充

Queryx() 和 QueryRowx(),不仅支持 Scan() 方法,同时可将数据与结构体进行转换

1)Queryx()

func (db *DB) Queryx(query string, args ...interface{}) (*Rows, error)

代码示例

func queryx(Db *sqlx.DB) {

//定义结构体保存数据

type userinfo struct {

Uid int `db:"uid"`

UserName string `db:"username"`

CreateTime string `db:"create_time"`

}

var userData userinfo

rows, err := Db.Queryx("select uid, username, create_time from userinfo")

if err != nil {

fmt.Printf("query data failed, error is [%v]", err.Error())

return

}

var userDataSlice []userinfo

for rows.Next() {

err := rows.StructScan(&userData)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

userDataSlice = append(userDataSlice, userData)

}

fmt.Println(userDataSlice)

err = rows.Close()

if err != nil {

fmt.Println(err.Error())

}

}

运行结果:

[{1 johny 2019-07-08 14:05:40} {2 anson 2019-07-08 16:33:19}]

2)QueryRowx()

func (db *DB) QueryRowx(query string, args ...interface{}) *Row

代码示例

func queryRowx(Db *sqlx.DB) {

//定义结构体保存数据

type userinfo struct {

Uid int `db:"uid"`

UserName string `db:"username"`

CreateTime string `db:"create_time"`

}

var userData *userinfo = new(userinfo)

row := Db.QueryRowx("select uid, username, create_time from userinfo where uid = 1")

err := row.StructScan(userData)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

}

fmt.Println(userData.Uid, userData.UserName, userData.CreateTime)

}

运行结果:

1 johny 2019-07-08 14:05:40

说了这么多,Query(),QueryRow() 不如 Get(),Select() 方法简洁

空值处理

Scan() 方法处理数据库中的 null

1)使用标准库中的数据类型

数据库中有一个特殊的类型,null 空值,可是 null 不能通过 scan 直接给变量赋值,也不能将 null 赋值给 nil,对于 null 必须指定特殊的类型,这些类型定义在 sqlx 扩展库中,例如 sql.NullFloat64,sql.NullString,sql.NullBool,sql.NullInt64,如果在扩展库中找不到匹配的值,可以尝试在驱动中寻找,下面的 demo,当数据表中 create_time 字段为 null 时,如果直接这样查询,会提示错误:

sql: Scan error on column index 2, name "create_time": unsupported Scan, storing driver.Value type into type *string

所以需要将代码改为:

func queryRow(Db *sqlx.DB) {

row := Db.QueryRow("select uid, username, create_time from userinfo")

var uid int

var userName string

var createTime sql.NullString

err := row.Scan(&uid, &userName, &createTime)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

fmt.Println(uid, userName, createTime)

}

运行结果:

1 johny { false}

上面的运行结果中 {  false},其实是 空字符串  与 string 类型的判断结果

在查询数据之前,查询结果有两种情况,null 与 非null,所以是需要验证的,如果值为 null,则会输出 NullString 的默认值,否则输出查询的值,demo 如下:

func queryRow(Db *sqlx.DB) {

row := Db.QueryRow("select uid, username, create_time from userinfo")

var uid int

var userName string

var createTime sql.NullString

err := row.Scan(&uid, &userName, &createTime)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

fmt.Printf("%d %s\n", uid, userName)

fmt.Printf("createTime.String: '%v'\n", createTime.String)

fmt.Printf("createTime.Valid: %v\n", createTime.Valid)

}

运行结果:

//null值的情况

1 johny

createTime.String: ''

createTime.Valid: false

//值存在的情况

1 johny

createTime.String: '2019-07-08 12:53:18'

createTime.Valid: true

2)使用 []byte 接收数据

如果我们不关心查询的字段数据是不是 null 的时候,只是想把它当做空字符串处理就行,可以定义 []byte 接收数据,这样处理后,如果有值就获取值([]byte),如果没有则获取的为空字符串,demo 如下:

func queryRow(Db *sqlx.DB) {

row := Db.QueryRow("select uid, username, create_time from userinfo")

var uid []byte

var userName []byte

var createTime []byte

err := row.Scan(&uid, &userName, &createTime)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

fmt.Printf("%s %s\n", uid, userName)

fmt.Printf("createTime.String: '%s'\n", createTime)

}

运行结果:

//有值的情况

1 johny

createTime.String: '2019-07-08 12:53:18'

//null值的情况

1 johny

createTime.String: ''

自动匹配字段数据

竟然所有的数据都能通过 []byte 进行接收,而字段名都是 string 类型,那么可以就可以把查询的数据放到 map 中保存,然后根据 key 进行取值,这样就方便多了

demo:

package main

import (

"fmt"

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

"github.com/jmoiron/sqlx"

)

var (

userName string = "root"

password string = "seemmo"

ipAddrees string = "10.10.4.80"

port int = 3306

dbName string = "golang_db"

charset string = "utf8"

)

func connectMysql() *sqlx.DB {

dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", userName, password, ipAddrees, port, dbName, charset)

Db, err := sqlx.Open("mysql", dsn)

if err != nil {

fmt.Printf("mysql connect failed, detail is [%v]", err.Error())

}

return Db

}

func queryData(Db *sqlx.DB) {

rows, err := Db.Query("select uid, username, create_time from userinfo")

if err != nil {

fmt.Printf("query data failed, error is [%v]", err.Error())

return

}

cols, err := rows.Columns()

if err != nil {

fmt.Errorf("get rows columns failed, error is [%v]", err.Error())

}

var vals = make([][]byte, len(cols)) //用来存放查询数据

var scanSlice = make([]interface{}, len(cols)) //用来当做参数,Scan 接收接口类型的参数

将 []byte 放入接口

for i := range cols {

scanSlice[i] = &vals[i]

}

var sliceMapData = make([]map[string]string, 0)

for rows.Next() {

err := rows.Scan(scanSlice...)

if err != nil {

fmt.Printf("scan data failed, error is [%v]", err.Error())

return

}

var mapData = make(map[string]string)

//这里遍历的是 字节切片

for i, value := range vals {

mapData[cols[i]] = string(value)

}

fmt.Println(mapData)

sliceMapData = append(sliceMapData, mapData)

}

fmt.Println(sliceMapData)

err = rows.Close()

if err != nil {

fmt.Println(err.Error())

}

}

func main (){

var Db *sqlx.DB = connectMysql()

defer Db.Close()

queryData(Db)

}

运行结果:

map[create_time:2019-07-08 14:05:40 uid:1 username:johny]

map[create_time: uid:2 username:anson]

[map[create_time:2019-07-08 14:05:40 uid:1 username:johny] map[create_time: uid:2 username:anson]]

查询的是全部字段的数据,使用 rows.Columns() 方法可以获取到字段数据的切片([]string)

然后创建一个切片 vals,用来存放所取出来的数据结果

接下来又定义一个切片 scanSlice,在 Scan() 中使用,因为Scan() 方法接收的数据是接口类型,将数据库的查询结果复制给到它

vals 则得到了 scanSlice 复制给它的值,因为是 byte 切片,因此在循环一次,将其转换成 string,最后添加到 map 类型中

参考链接:https://www.cnblogs.com/zhaof/p/8509164.html

ending ~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL 主从复制是一种非常重要且常用的数据同步方式,对于数据库管理员来说,保证主从同步的正常运行对于数据库的可靠性和稳定性都是非常重要的。当我们在使用 MySQL 主从同步时,如果发现 slave_io_running:no 的情况,那么意味着 MySQL 从服务器的 IO 线程停止了工作。 IO 线程是 MySQL 主从复制中非常重要的一个组件,它的作用是从主服务器上读取进制数据,并将其传输到从服务器上。如果 IO 线程出现问题,那么意味着从服务器无法从主服务器获取新的数据,从而导致从服务器无法同步主服务器的数据。 在出现 slave_io_running:no 的情况时,我们需要立即查找 IO 线程出现问题的原因,并进行解决。以下是常见的 IO 线程故障原因及对应解决方案: 1. 网络问题。如果主服务器和从服务器之间的网络出现了问题,那么 IO 线程就无法正常工作。我们可以通过检查网络连接以及网络质量来解决这个问题。 2. DNS 解析错误。如果主服务器和从服务器之间使用的是主机名进行通信,那么可能出现 DNS 解析错误的情况。我们需要检查主机名是否正确,以及 DNS 解析是否正常。 3. 权限问题。如果从服务器的用户权限不正确,那么也可能会导致 IO 线程无法正常工作。我们需要检查从服务器的用户权限是否足够,以及是否正确配置了主从服务器之间的连接信息。 4. 数据库配置问题。如果从服务器的数据库配置不正确,那么也可能会导致 IO 线程无法正常工作。我们需要检查从服务器的数据库配置是否正确,并根据需要进行更改。 总的来说,当出现 slave_io_running:no 的情况时,我们需要立即对问题进行排查和解决,以便尽快恢复 MySQL 主从同步的正常运行。在排查问题过程中,我们需要充分了解 MySQL 主从复制的工作原理,熟悉常见的故障原因,并加强对 MySQL 数据库的日常维护和管理工作,以提高数据库的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值