在Go中访问数据库需要用到sql.DB接口:它可以创建语句(statement)和事务(transaction),执行查询,获取结果。
使用数据库时,除了database/sql包本身,还需要引入想使用的特定数据库驱动。
官方不提供实现,先下载第三方的实现,https://github.com/golang/go/wiki/SQLDrivers查看各种各样的实现版本。
图片.png

开发环境准备

通过docker搭建MySQL服务器,首先创建initsql目录, 在initsql目录下创建测试数据文件student.sql,内容如下

  1. SET NAMES utf8mb4;
  2. CREATE DATABASE `test` DEFAULT CHARSETER SET utf8mb4 COLLATE utf8mb4_bin;
  3. USE test;
  4. -- ----------------------------
  5. -- Table structure for student
  6. -- ----------------------------
  7. DROP TABLE IF EXISTS `student`;
  8. CREATE TABLE `student` (
  9. `id` int(11) NOT NULL AUTO_INCREMENT,
  10. `name` varchar(10) NOT NULL,
  11. `gender` tinyint(4) NOT NULL COMMENT '1男 2女',
  12. `number` char(8) NOT NULL,
  13. `department` varchar(20) NOT NULL,
  14. `major` varchar(32) NOT NULL,
  15. PRIMARY KEY (`id`),
  16. UNIQUE KEY `uk_number` (`number`)
  17. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='学生信息';
  18. -- ----------------------------
  19. -- Records of student
  20. -- ----------------------------
  21. BEGIN;
  22. INSERT INTO `student` VALUES (1, '沈京兵', 1, '20190001', '计算机学院', '计算机科学与工程');
  23. INSERT INTO `student` VALUES (2, '范剑', 2, '20190002', '计算机学院', '计算机科学与工程');
  24. INSERT INTO `student` VALUES (3, '范统', 1, '20190003', '管理学院', '信息管理与信息系统');
  25. INSERT INTO `student` VALUES (4, '史珍香', 2, '20190004', '管理学院', '信息管理与信息系统');
  26. INSERT INTO `student` VALUES (5, '曾桃燕', 1, '20190005', '经济学院', '金融');
  27. INSERT INTO `student` VALUES (6, '朱逸群', 2, '20190006', '法学院', '法学');
  28. INSERT INTO `student` VALUES (7, '秦寿生', 1, '20190007', '经济学院', '国际经济与贸易');
  29. INSERT INTO `student` VALUES (8, '杜子腾', 1, '20190008', '经济学院', '国际经济与贸易');
  30. INSERT INTO `student` VALUES (9, '杜琦燕', 2, '20190009', '外国语', '西班牙语');
  31. INSERT INTO `student` VALUES (10, '赵邀靖', 2, '20190010', '外国语', '阿拉伯语');
  32. COMMIT;

设置student.sql 可执行权限

  1. chmod a+x student.sql

在当期目录创建mysql 目录

  1. mkdir mysql

整体目录结构

  1. tree
  2. .
  3. ├── initsql
  4. └── student.sql
  5. └── mysql
  6. 2 directories, 1 file

启动docker

  1. docker run -d -p3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -v $PWD/mysql:/var/lib/mysql -v $PWD/initsql:/docker-entrypoint-initdb.d mysql:5.7

当导入一个包时,该包下的文件里所有init()函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行init()函数而已。这个时候就可以使用 import 引用该包。_

  1. "database/sql"
  2. _ "github.com/go-sql-driver/mysql"

面的mysql驱动中引入的就是mysql包中各个init()方法,你无法通过包名来调用包中的其他函数。导入时,驱动的初始化函数会调用sql.Register将自己注册在database/sql包的全局变量sql.drivers中,以便以后通过sql.Open访问。

  1. package main
  2. import (
  3. "database/sql"
  4. _ "github.com/go-sql-driver/mysql"
  5. "log"
  6. )
  7. func main() {
  8. db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test")
  9. if err != nil {
  10. log.Fatal(err)
  11. }
  12. // 验证链接
  13. if err := db.Ping(); err != nil {
  14. log.Fatal(err)
  15. }
  16. rows, err := db.Query("SELECT name,number FROM student LIMIT 10")
  17. var name, num string
  18. for rows.Next() {
  19. if err := rows.Scan(&name, &num); err == nil {
  20. log.Println(name, num)
  21. } else {
  22. log.Fatal(err)
  23. }
  24. }
  25. }

使用 sql.Open() 连接数据库,第一个参数是驱动名称,import 语句 _ "github.com/go-sql-driver/mysql" 包导入时会注册 mysql的驱动,第二个参数是数据库名称,sql.Open()中的数据库连接串格式为:"``用户名:密码@tcp(IP:端口)/数据库?charset=utf8``"

通常不会约束你查询必须用Query,只是Query会返回结果集,而Exec不会返回。所以如果你执行的是增删改操作一般用Exec会好一些。Exec返回的结果是ResultResult接口允许获取执行结果的元数据:

  1. type Result interface {
  2. // 用于返回自增ID,并不是所有的关系型数据库都有这个功能。
  3. LastInsertId() (int64, error)
  4. // 返回受影响的行数。
  5. RowsAffected() (int64, error)
  6. }

参数设置

SetMaxOpenConns(maxOpenConns)

  1. func (db *DB) SetMaxOpenConns(n int)

连接池最多同时打开的连接数。
这个maxOpenConns理应要设置得比mysql服务器的max_connections值要小。
一般设置为: 服务器cpu核心数 * 2 + 服务器有效磁盘数。参考这里
可用show variables like ‘max_connections’; 查看服务器当前设置的最大连接数。

SetMaxIdleConns(maxIdleConns)

  1. func (db *DB) SetMaxIdleConns(n int)

连接池里最大空闲连接数。必须要比maxOpenConns小;如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制

SetConnMaxIdleTime(maxIdleTime)

连接池里面的连接最大空闲时长。
当连接持续空闲时长达到maxIdleTime后,该连接就会被关闭并从连接池移除,哪怕当前空闲连接数已经小于SetMaxIdleConns(maxIdleConns)设置的值。
连接每次被使用后,持续空闲时长会被重置,从0开始从新计算;
用show processlist; 可用查看mysql服务器上的连接信息,Command表示连接的当前状态,Command为Sleep时表示休眠、空闲状态,Time表示此状态的已持续时长;

image.png

SetConnMaxLifetime(maxLifeTime)

连接池里面的连接最大存活时长。
maxLifeTime必须要比mysql服务器设置的wait_timeout小,否则会导致golang侧连接池依然保留已被mysql服务器关闭了的连接。

wait_timeout

mysql服务器的wait_timeout默认是8 hour,可通过show variables like ‘wait_timeout’查看。
image.png

参考

https://www.cnblogs.com/Dominic-Ji/articles/11660056.html
https://github.com/8treenet/gcache
https://blog.csdn.net/wangshubo1989/article/details/75257614
https://xuchao918.github.io/2019/06/13/Go%E6%93%8D%E4%BD%9CMySql%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E6%96%B9%E5%BC%8F/
https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout
https://blog.csdn.net/qq_39384184/article/details/103954821
https://learnku.com/go/t/49809
https://www.cnblogs.com/show58/p/12622409.html
https://juejin.cn/post/6844904087427776519