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) }