IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Go语言操作MySql相关学习 -> 正文阅读

[大数据]Go语言操作MySql相关学习

假期关于mysql相关的操作学习,总结记录,以飨读者

Mysql是业届常用的关系型数据库,本文以go语言学习如何操作mysql数据库。
在这里插入图片描述

主要包括以下几个部分:

  • Ping测试mysql
  • mysql连接池相关参数配置
  • CURD相关操作
  • mysql超时操作
  • mysql事务操作

完整代码参考我的github工程go语言操作mysql

零、环境准备及go工程创建

环境:centos、mysql8.0
首先保证mysql服务正常,可以使用sql相关命令进行连接测试,创建几条测试数据方便后续操作。

mysql -u你的名字 -p你的密码
 
 CREATE DATABASE sql_test;
 use sql_test;
 
 CREATE TABLE `user` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(20) DEFAULT '',
    `age` INT(11) DEFAULT '0',
    PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

 INSERT INTO user (name , age) VALUES 
('张三', '18'),
('李四', '28'),
('翠花', '38');

select * from user;

在工作目录建立go工程,同时使用go mod进行初始化

mkdir go-sql-database-example
# you can name the module according to your directory
go mod init github.com/wsqyouth/tech_note/go-sql-database-example

Go语言sql包提供了操作SQL的接口,但并不提供具体数据库驱动,因此我们须引入第三方的驱动包。

 go get -u github.com/go-sql-driver/mysql
 touch main.go

至此,我们拥有了测试数据、安装了第三方驱动包、同时创建了go工程,让我们开始吧!

一、Ping测试mysql


// file: main.go
package main

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

	// we have to import the driver, but don't use it in our code
	// so we use the `_` symbol
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	pingDemo()
}

func pingDemo() {
	// The `sql.Open` function opens a new `*sql.DB` instance. We specify the driver name
	// and the URI for our database.
	dsn := "你的名字:你的密码@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Fatalf("could not connect to database: %v", err)
	}

	// To verify the connection to our database instance, we can call the `Ping`
	// method. If no error is returned, we can assume a successful connection
	if err := db.Ping(); err != nil {
		log.Fatalf("unable to reach database: %v", err)
	}
	fmt.Println("database is reachable")
	defer db.Close()
}

我们运行该文件,可以发现测试ok:

在这里插入图片描述

二、mysql连接池相关参数配置

为了方便测试后续CURD各功能函数,本着高内聚、低耦合的原则,我们定义一个全局变量db,用来保存连接数据库的句柄。将上面的示例代码拆分为独立的函数initDB,在程序启动时完成初始化,其他功能函数就可以直接使用全局变量了。
初始化连接函数

// 2.0 定义初始化数据库函数
func initDB() (err error) {
	dsn := "你的账号:你的密码@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	db, err = sql.Open("mysql", dsn) //注意使用全局对象进行赋值
	if err != nil {
		log.Fatalf("could not connect to database: %v", err)
	}
	if err := db.Ping(); err != nil {
		log.Fatalf("unable to reach database: %v", err)
	}
	// Maximum Idle Connections
	db.SetMaxIdleConns(5)
	// Maximum Open Connections
	db.SetMaxOpenConns(10)
	// Idle Connection Timeout
	db.SetConnMaxIdleTime(1 * time.Second)
	// Connection Lifetime
	db.SetConnMaxLifetime(30 * time.Second)
	fmt.Println("init succ")
	return nil
}

关于数据库的连接配置参考,这里我直接借鉴国外的一篇博客Using an SQL Database in Go进行摘录:

  • Maximum Open Connections are the maximum number of parallel connections that can be made to the database at any time.
  • Maximum Idle Connections are the maximum number of connections that can be inactive at any time. A connection is idle, if no queries are being executed on it. This can happen if the number of queries being executed are less than the current pool of connections can handle.
  • Idle Connection Timeout is the maximum time for which any given connection can be idle. After this time had elapsed, the connection to the database will be closed.
    *** Connection Lifetime** is the maximum amount of time that a connection can be open (regardless of whether it’s idle or not).
    在这里插入图片描述
    这几个参数是可以灵活设置的,但是总体上我们要考虑:
  • 最好让一小部分连接处于空闲状态,以应对查询吞吐量的突然峰值
  • 应该根据我们的数据库服务器和应用程序服务器的网络容量来设置打开连接的最大数量,其中较小的将是限制因素。

三、mysql相关CURD操作

其实就是CURD的封装,记得我们使用了上文提及的全局变量句柄db,代码如下:

// 2.1 查询单条数据
func queryRowDemo(id int) {
	sqlStr := "select id,name,age from user where id = ? limit 1"
	// `QueryRow` always returns a single row from the database
	row := db.QueryRow(sqlStr, id)

	var u user
	if err := row.Scan(&u.id, &u.name, &u.age); err != nil {
		log.Fatalf("could not scan row: %v", err)
	}
	fmt.Printf("user:%+v\n", u)
}

// 2.2 查询多条数据
func queryMultiRowDemo() {
	sqlStr := "select id,name,age from user where id > ?"
	// `QueryRow` always returns a single row from the database
	rows, err := db.Query(sqlStr, 1)
	if err != nil {
		log.Fatalf("could not execute query: %v", err)
	}
	//循环读取结果集数据
	users := []user{}
	for rows.Next() {
		var u user
		// create an instance of `user` and write the result of the current row into it
		if err := rows.Scan(&u.id, &u.name, &u.age); err != nil {
			log.Fatalf("could not scan row: %v", err)
		}
		users = append(users, u)
	}
	// print the length, and all the birds
	fmt.Printf("len:%v,users:%+v\n", len(users), users)
}

// 插入数据
func insertRowDemo() {
	newUser := user{
		name: "王五",
		age:  23,
	}
	sqlStr := "insert into user(name,age) values (?,?)"
	// the `Exec` method returns a `Result` type instead of a `Row`
	// we follow the same argument pattern to add query params
	result, err := db.Exec(sqlStr, newUser.name, newUser.age)
	if err != nil {
		log.Fatalf("could not execute query: %v", err)
	}

	theID, err := result.LastInsertId() //新插入的id
	if err != nil {
		log.Fatalf("get LastInsertId falid. err:%v", err)
	}
	fmt.Printf("insert succ. the id is %d\n", theID)

}

// 更新数据
func updateRowDemo() {
	newUser := user{
		name: "王五",
		age:  28,
	}
	sqlStr := "update user set age=? where name = ?"
	result, err := db.Exec(sqlStr, newUser.age, newUser.name)
	if err != nil {
		log.Fatalf("update falid. err:%v", err)
	}

	// the `Result` type has special methods like `RowsAffected` which returns the
	// total number of affected rows reported by the database
	// In this case, it will tell us the number of rows that were inserted using
	// the above query
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		log.Fatalf("could not get affected rows: %v", err)
	}
	// we can log how many rows were update
	fmt.Println("update succ, affected rows:%v", rowsAffected)

}

// 删除数据
func deleteRowDemo() {
	newUser := user{
		name: "张三",
	}
	sqlStr := "delete from user where name = ?"
	result, err := db.Exec(sqlStr, newUser.name)
	if err != nil {
		log.Fatalf("update falid. err:%v", err)
	}

	// the `Result` type has special methods like `RowsAffected` which returns the
	// total number of affected rows reported by the database
	// In this case, it will tell us the number of rows that were inserted using
	// the above query
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		log.Fatalf("could not get affected rows: %v", err)
	}
	// we can log how many rows were delete
	fmt.Println("delete succ, affected rows:%v", rowsAffected)
}

四、超时相关功能

sql的慢查询也是一个需要考虑的问题,如果某个请求的查询比较耗时,就会阻塞其他请求拿到连接,因此我们需要在连接sql时考虑到如果超时则取消功能。这里我们使用context进行测试:

//3.0 测试超时
func timeOutDemo() {
	// create a parent context
	ctx := context.Background()
	// create a context from the parent context with a 300ms timeout
	ctx, _ = context.WithTimeout(ctx, 300*time.Millisecond)
	// The context variable is passed to the `QueryContext` method as
	// the first argument
	// the provided number of seconds. We can use this to simulate a
	// slow query
	_, err := db.QueryContext(ctx, "select sleep(1)")
	if err != nil {
		log.Fatalf("could not execute query: %v", err)
	}
}

我们设置超时时间为300ms,但是这条sql语句需要1s才能执行完成,运行该demo预期输出:
在这里插入图片描述

五、mysql事务操作

在这里插入图片描述

事务的操作也是需要考虑的,要么一起成功、要么一起失败,因此这里的demo示例如下:

//4.0 事务操作示例
func transactionDemo() {
	tx, err := db.Begin() //开启事务
	if err != nil {
		if tx != nil {
			tx.Rollback() //回滚
		}
		fmt.Printf("begin trans failed. err:%v\n", err)
		return
	}

	sqlStr := "Update user set age = 34 where id = ?"
	result, err := tx.Exec(sqlStr, 2)
	if err != nil {
		if tx != nil {
			tx.Rollback() //回滚
		}
		fmt.Printf("update trans failed. err:%v\n", err)
		return
	}
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		if tx != nil {
			tx.Rollback() //回滚
		}
		log.Fatalf("could not get affected rows: %v", err)
		return
	}
	fmt.Println(rowsAffected)

	if rowsAffected == 1 {
		fmt.Println("only one user affect. trans commit")
		tx.Commit() //提交事务
	} else {
		fmt.Println("affect num err. trans rollback")
		tx.Rollback()
	}

	fmt.Println("exec trans end")
}

六、展望

至此,Go语言操作mysql常见的操作就算测试完了,但是这只是入门,站在这里,你可能会考虑一下问题:

  • sql预处理和sql注入问题
  • sql连接池监控问题
  • 本文使用的是原生sql处理,是否有orm框架来解决新增字段扩展性问题
  • 事务、分布式事务又将如何开展

文章参考

本文工程重点参考文章,特此感谢
国外谷歌工程师的文章:该文章是go语言操作pgsql,但是工程风格及讲解一流,引人入胜
Using an SQL Database in Go
A Guide On SQL Database Transactions In Go
李文周博客-go操作mysql

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 23:15:00  更:2022-04-06 23:15:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:41:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码