Golang 关于 Mysql连接池的实现

前言

随着业务体量上去势必会遇到数据库的性能问题,从合理的 sql 语句、字段索引、分库分表、读写分离等,在加上缓存技术,基本上就可以解决几乎所有业务,再差提升硬件即可,而 Go 自身就已实现连接池, 以解决连接带来的时延从而提升性能(空间换时间),于是想了解提升下。

MYSQL 参数说明

连接池 : 通过在内存中维护一组数据库连接,以便在需要时快速分配和释放连接。这样可以避免频繁地打开和关闭数据库连接,从而提高系统的效率和性能。

故它是以长连接的形式存在内存中,那势必存在 忙碌 / 空闲 两种状态,在空闲状态下需要考虑超时的情况,除去本身客户端的超时限制,那服务端也会存在超时限制;同样连接池是池,所有会有很多连接,在配置对应数量同样会受制于服务端的限制。

所以我们在配置连接池前,必须了解相关 mysql 的相关参数配置以及作用,否则会弄巧成拙。

字段 简述 补充说明
max_connection 同时处理最大连接数 这个参数的默认值通常是 100,但可以根据服务器的配置和性能需求进行调整。如果服务器达到了 max_connections 的限制,新的连接请求将被拒绝,直到当前的连接数降到小于 max_connections 为止。因此,如果你的服务器经常出现“Too many connections”错误,你可以通过增加 max_connections 的值来解决这个问题。但是,也要记住,增加 max_connections 可能会增加服务器的资源消耗,因此要根据实际情况进行调整。
extra_max_connections 超过最大连接数后最多接受连接数 它指定了服务器可以同时处理的最大连接数。如果服务器繁忙,并且已经达到了 max_connections 的限制,那么服务器将不再接受新的连接请求,直到有空闲的连接出现。extra_max_connections 参数允许服务器在特殊情况下,接受更多的连接请求,以便在紧急情况下提供更多的灵活性。例如,如果你将 extra_max_connections 设置为 10,那么在 max_connections 达到限制之后,服务器仍然可以接受 10 个额外的连接请求。需要注意的是,使用 extra_max_connections 这个参数可能会影响服务器的性能,因此应该在必要的情况下使用。
wait_timeout 非交互闲置后最大等待时间 如果在这段时间内连接没有发送任何请求,那么 MySQL 就会断开这个连接。这个参数的默认值通常是 8 小时,但是可以通过修改配置文件来更改它的值。这个参数的目的是为了避免连接泄漏,也就是说,如果一个连接长期不活动,那么 MySQL 就会断开它,以便节约资源。当然,如果你的应用需要长时间的连接,那么你可以适当地调大这个参数的值

wait_timeout 描述中 非交互 是指代码这类运行,而对应交互式则是 interactive_timeout 控制, 指 navicat 这类连接数据库的客户端。
这里提是因为当我们修改对应值若只是修改前者,在用客户端打印系统参数时会发现没有变动(其实已生效),如果强迫症可以都进行修改。

相关语句

-- 查询语句
SHOW VARIABLES LIKE '%max_connections';
SHOW VARIABLES LIKE 'wait_timeout%' ;
SHOW VARIABLES LIKE '%interactive_timeout%';

-- 设置全局变量
SET GLOBAL max_connections = 100;
SET GLOBAL wait_timeout = 10;
SET GLOBAL interactive_timeout = 10;

Go 连接池说明

连接池参数配置

=// 全局 db 对象
var DB *sql.DB

// ConnectionPool 连接池的学习
func ConnectionPool() {
    
    
	// 连接 db
	db, err := sql.Open("mysql", "root:123456@/db?charset=utf8")
	if err != nil {
    
    
		panic(err)
	}
	//defer db.Close()
	db.SetMaxOpenConns(100)                //   设置连接数总数, 需要根据实际业务来测算, 应小于 mysql.max_connection (应该远远小于), 后续根据指标进行调整
	db.SetMaxIdleConns(50)                 //  设置最大空闲连接数, 该数值应该小于等于 SetMaxOpenConns 设置的值
	db.SetConnMaxLifetime(0)               // 设置连接最大生命周期, 默认为 0(不限制), 我不建议设置该值, 只有当 mysql 服务器出现问题, 会导致连接报错, 恢复后可以自动恢复正常, 而我们配置了时间也不能卡住出问题的时间, 配置小还不如使用 SetConnMaxIdleTime 来解决
	db.SetConnMaxIdleTime(4 * time.Second) // 设置空闲状态最大生命周期, 该值应小于 mysql.wait_timeout 的值, 以避免被服务端断开连接, 产生报错影响业务, 一般可以配置 1天。

	// 赋值 db 对象
	DB = db
}

相关服务端的连接超时报错示例
在这里插入图片描述

database/sql 中关于连接池的指标和参数说明

关于 DBStats 的数据指标

字段 说明
MaxOpenConnections 最大连接数, MaxOpenConnections,由 SetMaxOpenConns 设置
OpenConnections 活跃的总连接数, 该值不会超过 MaxOpenConnections 值
InUse 正在使用的连接数,若执行 close 或 scan(自带 close)就会归还连接,否则会一直占用直到连接超时
Idle 当前空闲的连接数
WaitCount 当前等待数据库连接的总数
WaitDuration 等待数据库连接的时间
MaxIdleClosed 空闲连接数超限被关闭总数,受 SetMaxIdleConns 限制
MaxIdleTimeClosed 因空闲连接超时被关闭总数,受 SetMaxIdleTime 限制
MaxLifetimeClosed 因连接超时被关闭总数,受 SetMaxLiftTime 限制

调用方式

func getDbStats(w http.ResponseWriter, r *http.Request) {
    
    
	info := fmt.Sprintf("最大连接数:%d,  当前总连接数;%d,  已使用: %d, 空闲数量:%d \n", DB.Stats().MaxOpenConnections, DB.Stats().OpenConnections, DB.Stats().InUse, DB.Stats().Idle)
	info2 := fmt.Sprintf("数量指标 :) \n等待连接数量;%d,  等待创建新连接时长(秒): %f, 空闲超限关闭数量:%d, 空闲超时关闭数量:%d, 连接超时关闭数量:%d \n",
		DB.Stats().WaitCount,
		DB.Stats().WaitDuration.Seconds(),
		DB.Stats().MaxIdleClosed,
		DB.Stats().MaxIdleTimeClosed,
		DB.Stats().MaxLifetimeClosed,
	)

	_, _ = fmt.Fprintln(w, info+info2)
}

返回示例
在这里插入图片描述

代码实现

mdb.go

/*
Package study_package
*/
package study_package

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

// 全局 db 对象
var DB *sql.DB

// ConnectionPool 连接池的学习
func ConnectionPool() {
    
    
	fmt.Println("mysql 连接池实践 -- ")

	// 连接 db
	db, err := sql.Open("mysql", "root:12345678@/spider?charset=utf8")
	if err != nil {
    
    
		panic(err)
	}
	//defer db.Close()
	db.SetMaxOpenConns(100)                    //  设置连接数总数
	db.SetMaxIdleConns(50)                     //  设置最大空闲连接数, 该数值应该小于等于 SetMaxOpenConns 设置的值
	db.SetConnMaxLifetime(0)                   // 设置连接最大生命周期, 默认为 0(不限制), 我不建议设置该值, 只有当 mysql 服务器出现问题, 会导致连接报错, 恢复后可以自动恢复正常, 而我们配置了时间也不能卡住出问题的时间, 配置小还不如使用 SetConnMaxIdleTime 来解决
	db.SetConnMaxIdleTime(86400 * time.Second) // 设置空闲状态最大生命周期, 该值应小于 mysql.wait_timeout 的值, 以避免被服务端断开连接, 产生报错影响业务。

	// 创建连接池
	DB = db
}

web.go

// Copyright 2023 The wangkai. ALL rights reserved.

package study_package

import (
	"fmt"
	"net/http"
)


// Pool 学习搭建 数据库连接池相关概念
func Pool(w http.ResponseWriter, r *http.Request) {
    
    
	var count int
	// 对应数据库查询
	row := DB.QueryRow("SELECT COUNT(1) count FROM `xz_house`")
	//  遍历数据
	err := row.Scan(&count)
	if err != nil {
    
    
		fmt.Println(err)
	}

	_, _ = fmt.Fprintln(w, count)
}

// getDbStats 获取 DB 状态和指标
func getDbStats(w http.ResponseWriter, r *http.Request) {
    
    
	info := fmt.Sprintf("最大连接数:%d,  当前总连接数;%d,  已使用: %d, 空闲数量:%d \n", DB.Stats().MaxOpenConnections, DB.Stats().OpenConnections, DB.Stats().InUse, DB.Stats().Idle)
	info2 := fmt.Sprintf("数量指标 :) \n等待连接数量;%d,  等待创建新连接时长(秒): %f, 空闲超限关闭数量:%d, 空闲超时关闭数量:%d, 连接超时关闭数量:%d \n",
		DB.Stats().WaitCount,
		DB.Stats().WaitDuration.Seconds(),
		DB.Stats().MaxIdleClosed,
		DB.Stats().MaxIdleTimeClosed,
		DB.Stats().MaxLifetimeClosed,
	)

	_, _ = fmt.Fprintln(w, info+info2)
}

web_test.go

package study_package

import (
	"net/http"
	"testing"
)

func TestPool(t *testing.T) {
    
    
	type args struct {
    
    
		w http.ResponseWriter
		r *http.Request
	}
	tests := []struct {
    
    
		name string
		args args
	}{
    
    
		{
    
    "base", args{
    
    
			w: nil,
			r: nil,
		}},
	}
	for _, tt := range tests {
    
    
		t.Run(tt.name, func(t *testing.T) {
    
    
			// 创建连接池
			ConnectionPool()
			http.HandleFunc("/pool", Pool)
			http.HandleFunc("/getDbStats", getDbStats)
			// 创建 Listen Socket, 监听 9090 端口
			err := http.ListenAndServe(":9090", nil)
			if err == nil {
    
    
				log.Fatal(" ListenAndServe:", err)
			}
		})
	}
}

总结

掌握 Go 本身的功能相关参数配置以及了解 mysql 的配置,才能更加熟练的使用 Go 的连接池的功能,从而保障服务的健壮性。

当然不能仅仅关注代码层次,还需要了解硬件的指标,例如内存、I/O读写、CPU 等,不过那个时候相比是有专业的运维来介入支持,毕竟我们日常的业务和个人业务如前言所说,都能妥善解决。

猜你喜欢

转载自blog.csdn.net/weixin_43832080/article/details/128901991