模拟问题 : 直接向数据库写入消息

环境配置

MySQL 版本 mysql8.x

MySQL 最大连接数 : 10000

# 最大连接数查看命令
show global status like 'Max_used_connections';
# 最大连接数设置命令
set global max_connections = 10000;

MySQL 相关教程推荐 :

https://www.lesscode.work/courses/info/8755b7ac7f9278e5058f03a8983d3db7.html

MySQL 最大连接数设置教程 :

https://www.lesscode.work/sections/7b81447c1dd488f776aa3f764d3251c4.html

模拟并发写入数据

数据库配置

package configs

var DBConfig = map[string]map[string]string{
	"DB": {
		// 运行模式 [ dev : 开发模式会输出详细运行日志; 其他 : 只输出错误日志 ]
		"RunMode": "prodoct",
		// 数据库类型
		"DBType": "MySQL",
		// dev ( 开发环境 ) 对应的数据库地址
		"HostDev": "192.168.1.100",
		// 数据库主机地址
		"Host": "192.168.1.100",
		// 数据库端口号
		"Port": "3306",
		// 用户名
		"Username": "root",
		// 密码
		"Password": "root",
		// 连接被使用的最长时间,秒
		"MaxLifetime": "180",
		// 最大连接数
		"MaxOpenConns": "5000",
		// 最大空闲连接数
		"MaxIdleConns": "500",
		// 数据库名称
		"DatabaseName": "test",
		// 统一表前缀
		"TablePrefix": "",
		// 字符集
		"Charset": "utf8mb4",
	},
}

并发写入代码

package main

import (
	"fmt"
	"godemo/configs"
	"strconv"
	"sync"

	"github.com/cnlesscode/gotool/db"
)

type Messages struct {
	Id      int    `gorm:"column:id;primaryKey"`
	Message string `gorm:"column:message"`
}

var wg sync.WaitGroup

func main() {
	db.Start(configs.DBConfig)
	for i := 1; i <= 50000; i++ {
		wg.Add(1)
		go func(step int) {
			defer wg.Done()
			db := db.Init()
			st := Messages{
				Message: strconv.Itoa(step),
			}
			err := db.Create(&st).Error
			if err != nil {
				fmt.Printf("err: %v\n", err)
			}
		}(i)
	}
	wg.Wait()
	println("-- done ---")
}

问题总结

在上面的模拟代码运行过程中会产生很多错误:

err: invalid connection; invalid connection
err: invalid connection
[mysql] 2023/10/10 12:17:53 packets.go:37: read tcp 192.168.1.102:55977->192.168.1.100:3306: read: connection reset by peer
[mysql] 2023/10/10 12:17:53 packets.go:37: read tcp 192.168.1.102:52847->192.168.1.100:3306: read: connection reset by peer
[mysql] 2023/10/10 12:17:53 packets.go:37: read tcp 192.168.1.102:55723->192.168.1.100:3306: read: connection reset by peer
err: invalid connection
err: invalid connection

并发会造成 MySQL 服务产生巨大压力,从连接到写入环节失败率极高。

突出的问题 :

1. 5万条数据丢失 1200 余条;
2. 整个数据库服务阻塞,影响其他业务;

为了解决这些问题,下一节我们将使用 GraceMQ 来存储并发消息,请继续阅读。