mycommon/mymysql/mysql.go

204 lines
4.1 KiB
Go

package mymysql
import (
"database/sql"
"fmt"
"git.makemake.in/kzkzzzz/mycommon/myconf"
driverMysql "github.com/go-sql-driver/mysql"
"github.com/google/uuid"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"sync"
"time"
)
const (
DefaultInstance = "mysql"
)
type MysqlDb struct {
*gorm.DB
SqlDB *sql.DB
gormConfig *gorm.Config
disablePing bool
}
type Conf struct {
Dsn string
MaxOpenConn int // 最大连接数
MaxIdleConn int // 最大空闲连接数
MaxIdleTime string // 空闲时间
MaxLifeTime string // 连接最大有效时间
Debug bool
LogSqlSlowTimeMs int
LogDisableColor bool
}
var (
instanceMap = &sync.Map{}
)
func DB(name ...string) *MysqlDb {
var instanceName string
if len(name) > 0 {
instanceName = name[0]
} else {
instanceName = DefaultInstance
}
v, ok := instanceMap.Load(instanceName)
if !ok {
panic(fmt.Errorf("mysql instance [%s] not init", instanceName))
}
return v.(*MysqlDb)
}
// InitDb 初始化全局默认db
func InitDb(config *myconf.Config, opts ...Opt) {
client, err := NewDb(DefaultInstance, config, opts...)
if err != nil {
panic(err)
}
instanceMap.Store(DefaultInstance, client)
}
// InitDbInstance 初始化全局的db
func InitDbInstance(instanceName string, config *myconf.Config, opts ...Opt) {
client, err := NewDb(instanceName, config, opts...)
if err != nil {
panic(err)
}
instanceMap.Store(instanceName, client)
}
func NewDb(instanceName string, config *myconf.Config, opts ...Opt) (*MysqlDb, error) {
cf := &Conf{Debug: true}
err := config.UnmarshalKey(instanceName, &cf)
if err != nil {
return nil, err
}
db, err := NewDbFromConf(cf, opts...)
if err != nil {
return nil, err
}
instanceMap.Store(instanceName, db)
return db, nil
}
func NewDbFromConf(cf *Conf, opts ...Opt) (*MysqlDb, error) {
parseDsn, err := driverMysql.ParseDSN(cf.Dsn)
if err != nil {
return nil, fmt.Errorf("mysql parse dsn error: %s", err)
}
db := &MysqlDb{}
for _, opt := range opts {
opt(db)
}
if db.gormConfig == nil {
db.gormConfig = &gorm.Config{
SkipDefaultTransaction: true,
}
lCfg := logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: logger.Warn,
IgnoreRecordNotFoundError: false,
Colorful: true,
}
if cf.LogSqlSlowTimeMs > 0 {
lCfg.SlowThreshold = time.Duration(cf.LogSqlSlowTimeMs) * time.Millisecond
}
if cf.LogDisableColor {
lCfg.Colorful = false
}
l := newGormLogger(lCfg)
if cf.Debug {
db.gormConfig.Logger = l.LogMode(logger.Info)
} else {
db.gormConfig.Logger = l
}
}
gormDB, err := gorm.Open(mysql.Open(cf.Dsn), db.gormConfig)
if err != nil {
return nil, err
}
sqlDB, err := gormDB.DB()
if err != nil {
return nil, err
}
if db.disablePing == false {
err = sqlDB.Ping()
if err != nil {
return nil, err
}
}
if cf.MaxOpenConn <= 0 {
cf.MaxOpenConn = 1024
}
if cf.MaxIdleConn <= 0 {
// 默认最大空闲数等于最大连接数
cf.MaxIdleConn = cf.MaxOpenConn
}
if cf.MaxIdleTime == "" {
cf.MaxIdleTime = "10m"
}
sqlDB.SetMaxOpenConns(cf.MaxOpenConn)
sqlDB.SetMaxIdleConns(cf.MaxIdleConn)
if dv, err := time.ParseDuration(cf.MaxIdleTime); err != nil {
return nil, fmt.Errorf("parse MaxIdleTime err: %s", err)
} else {
sqlDB.SetConnMaxIdleTime(dv)
}
// max life time默认暂不设置, 使用idle time控制即可
if cf.MaxLifeTime != "" {
if dv, err := time.ParseDuration(cf.MaxLifeTime); err != nil {
return nil, fmt.Errorf("parse MaxLifeTime err: %s", err)
} else {
sqlDB.SetConnMaxLifetime(dv)
}
}
db.DB = gormDB
db.SqlDB = sqlDB
instanceMap.Store(uuid.New().String(), db)
log.Printf("connect db success [addr:%s - db:%s]", parseDsn.Addr, parseDsn.DBName)
return db, nil
}
func CloseAll() {
instanceMap.Range(func(k, v any) bool {
db, err := (v.(*MysqlDb)).DB.DB()
if err != nil {
db.Close()
}
return true
})
}
func newGormLogger(cfg logger.Config) logger.Interface {
return logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), cfg)
}