配置文件

我们推荐使用配置组件来管理数据库配置,并使用g对象管理模块中的g.DB("数据库分组名称")方法获取数据库操作对象,数据库对象将会自动读取配置组件中的相应配置项,并自动初始化该数据库操作的单例对象。数据库配置管理功能使用的是配置管理组件实现(配置组件采用接口化设计默认使用文件系统实现),同样支持多种数据格式如:toml/yaml/json/xml/ini/properties。默认并且推荐的配置文件数据格式为yaml

简单配置

一个简单的数据库配置如下:

database:
  default:
    type:  "mysql"
    link:  "root:12345678@tcp(127.0.0.1:3306)/test"
  user:
    type:  "mysql"
    link:  "root:12345678@tcp(127.0.0.1:3306)/user"

以上数据库配置示例中包含两个数据库分组defaultuser,在代码中可以通过g.DB()g.DB("user")获取对应的数据库连接对象。也可以将type数据库类型写到link配置项中,以上简化为:

database:
  default:
    link:  "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
  user:
    link:  "mysql:root:12345678@tcp(127.0.0.1:3306)/user"

不同数据类型对应的link如下:

类型link配置更多参数
mysqlmysql: 账号:密码@tcp(地址:端口)/数据库名称mysql
mariadbmariadb: 账号:密码@tcp(地址:端口)/数据库名称mysql
tidbmtidb: 账号:密码@tcp(地址:端口)/数据库名称mysql
pgsqlpgsql: user=账号 password=密码 host=地址 port=端口 dbname=数据库名称pq
mssqlmssql: user id=账号;password=密码;server=地址;port=端口;database=数据库名称;encrypt=disablego-mssqldb
sqlitesqlite: 文件绝对路径 (如: /var/lib/db.sqlite3)go-sqlite3
oracleoracle://账号:密码@地址:端口/数据库名称go-oci8

各数据库类型更详细的link参数信息请查看对应引擎官网,支持的数据库类型参考 数据库ORM 章节。

完整配置

完整的config.yaml数据库配置项的数据格式形如下:

database:
  分组名称:
    host:                  "地址"
    port:                  "端口"
    user:                  "账号"
    pass:                  "密码"
    name:                  "数据库名称"
    type:                  "数据库类型(mysql/pgsql/mssql/sqlite/oracle)"
    link:                  "(可选)自定义数据库链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效,但是type必须有值"         
    role:                  "(可选)数据库主从角色(master/slave),不使用应用层的主从机制请均设置为master"
    debug:                 "(可选)开启调试模式"
    prefix:                "(可选)表名前缀"
    dryRun:                "(可选)ORM空跑(只读不写)"
    charset:               "(可选)数据库编码(如: utf8/gbk/gb2312),一般设置为utf8"
    weight:                "(可选)负载均衡权重,用于负载均衡控制,不使用应用层的负载均衡机制请置空"
    timezone:              "(可选)时区配置,例如:local,Asia/Shanghai"
    maxIdle:               "(可选)连接池最大闲置的连接数"
    maxOpen:               "(可选)连接池最大打开的连接数"
    maxLifetime:           "(可选)连接对象可重复使用的时间长度"
    createdAt:             "(可选)自动创建时间字段名称"
    updatedAt:             "(可选)自动更新时间字段名称"
    deletedAt:             "(可选)软删除时间字段名称"
    timeMaintainDisabled:  "(可选)是否完全关闭时间更新特性,true时CreatedAt/UpdatedAt/DeletedAt都将失效"

完整的数据库配置项示例(YAML):

database:
  default:
    host:          "127.0.0.1"
    port:          "3306"
    user:          "root"
    pass:          "12345678"
    name:          "test"
    type:          "mysql"
    role:          "master"
    debug:         "true"
    dryrun:        0
    weight:        "100"
    prefix:        "gf_"
    charset:       "utf8"
    timezone:      "local"
    maxIdle:       "10"
    maxOpen:       "100"
    maxLifetime:   "30s"

使用该配置方式时,为保证数据库安全,默认底层不支持多行SQL语句执行。为了得到更多配置项控制,请参考推荐的简化配置,同时建议您务必了解清楚简化配置项中每个连接参数的功能作用。

集群模式

gdb的配置支持集群模式,数据库配置中每一项分组配置均可以是多个节点,支持负载均衡权重策略,例如:

database:
  default:
  - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
    role: "master"
  - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
    role: "slave"

  user:
  - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/user"
    role: "master"
  - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/user"
    role: "slave"
  - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/user"
    role: "slave"

以上数据库配置示例中包含两个数据库分组defaultuser,其中default分组包含一主一从,user分组包含一主两从。在代码中可以通过g.DB()g.DB("user")获取对应的数据库连接对象。

日志配置

gdb支持日志输出,内部使用的是glog.Logger对象实现日志管理,并且可以通过配置文件对日志对象进行配置。默认情况下gdb关闭了DEBUG日志输出,如果需要打开DEBUG信息需要将数据库的debug参数设置为true。以下是为一个配置文件示例:

database:
  logger:
    path:    "/var/log/gf-app/sql"
    level:   "all"
    stdout:  true
  default:
    link:    "mysql:root:12345678@tcp(127.0.0.1:3306)/user_center"
    debug:   true

其中database.logger即为gdb的日志配置,当该配置不存在时,将会使用日志组件的默认配置,具体请参考 日志组件-配置管理 章节。

需要注意哦:由于ORM底层都是采用安全的预处理执行方式,提交到底层的SQL与参数其实是分开的,因此日志中记录的完整SQL仅作参考方便人工阅读,并不是真正提交到底层的SQL语句。

原生配置(高阶,可选)

以下为数据库底层管理配置介绍,如果您对数据库的底层配置管理比较感兴趣,可继续阅读后续章节。

数据结构

gdb数据库管理模块的内部配置管理数据结构如下:

ConfigNode用于存储一个数据库节点信息;ConfigGroup用于管理多个数据库节点组成的配置分组(一般一个分组对应一个业务数据库集群);Config用于管理多个ConfigGroup配置分组。

配置管理特点:

  1. 支持多节点数据库集群管理;
  2. 每个节点可以单独配置连接属性;
  3. 采用单例模式管理数据库实例化对象;
  4. 支持对数据库集群分组管理,按照分组名称获取实例化的数据库操作对象;
  5. 支持多种关系型数据库管理,可通过ConfigNode.Type属性进行配置;
  6. 支持Master-Slave读写分离,可通过ConfigNode.Role属性进行配置;
  7. 支持客户端的负载均衡管理,可通过ConfigNode.Weight属性进行配置,值越大,优先级越高;
type Config      map[string]ConfigGroup // 数据库配置对象
type ConfigGroup []ConfigNode           // 数据库分组配置
// 数据库配置项(一个分组配置对应多个配置项)
type ConfigNode  struct {
    Host             string        // 地址
    Port             string        // 端口
    User             string        // 账号
    Pass             string        // 密码
    Name             string        // 数据库名称
    Type             string        // 数据库类型:mysql, sqlite, mssql, pgsql, oracle
	Link             string        // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)     Role             string        // (可选,默认为master)数据库的角色,用于主从操作分离,至少需要有一个master,参数值:master, slave
    Debug            bool          // (可选)开启调试模式
    Charset          string        // (可选,默认为 utf8)编码,默认为 utf8
    Prefix           string        // (可选)表名前缀
    Weight           int           // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
    MaxIdleConnCount int           // (可选)连接池最大闲置的连接数
    MaxOpenConnCount int           // (可选)连接池最大打开的连接数
    MaxConnLifetime  time.Duration // (可选,单位秒)连接对象可重复使用的时间长度
}

特别说明,gdb的配置管理最大的特点是,(同一进程中)所有的数据库集群信息都使用同一个配置管理模块进行统一维护,不同业务的数据库集群配置使用不同的分组名称进行配置和获取。

配置方法

这是原生调用gdb模块来配置管理数据库。如果开发者想要自行控制数据库配置管理可以参考以下方法。若无需要可忽略该章节。

接口文档: https://pkg.go.dev/github.com/gogf/gf/v2/database/gdb

// 添加一个数据库节点到指定的分组中
func AddConfigNode(group string, node ConfigNode)
// 添加一个配置分组到数据库配置管理中(同名覆盖)
func AddConfigGroup(group string, nodes ConfigGroup)

// 添加一个数据库节点到默认的分组中(默认为default,可修改)
func AddDefaultConfigNode(node ConfigNode)
// 添加一个配置分组到数据库配置管理中(默认分组为default,可修改)
func AddDefaultConfigGroup(nodes ConfigGroup)

// 设置默认的分组名称,获取默认数据库对象时将会自动读取该分组配置
func SetDefaultGroup(groupName string)

// 设置数据库配置为定义的配置信息,会将原有配置覆盖
func SetConfig(c Config)

默认分组表示,如果获取数据库对象时不指定配置分组名称,那么gdb默认读取的配置分组。例如:gdb.NewByGroup()可获取一个默认分组的数据库对象。简单的做法,我们可以通过gdb包的SetConfig配置管理方法进行自定义的数据库全局配置,例如:

gdb.SetConfig(gdb.Config {
    "default" : gdb.ConfigGroup {
        gdb.ConfigNode {
            Host     : "192.168.1.100",
            Port     : "3306",
            User     : "root",
            Pass     : "123456",
            Name     : "test",
            Type     : "mysql",
            Role     : "master",
            Weight   : 100,
        },
        gdb.ConfigNode {
            Host     : "192.168.1.101",
            Port     : "3306",
            User     : "root",
            Pass     : "123456",
            Name     : "test",
            Type     : "mysql",
            Role     : "slave",
            Weight   : 100,
        },
    },
    "user-center" : gdb.ConfigGroup {
        gdb.ConfigNode {
            Host     : "192.168.1.110",
            Port     : "3306",
            User     : "root",
            Pass     : "123456",
            Name     : "test",
            Type     : "mysql",
            Role     : "master",
            Weight   : 100,
        },
    },
})

随后,我们可以使用gdb.NewByGroup("数据库分组名称")来获取一个数据库操作对象。该对象用于后续的数据库一系列方法/链式操作。




Content Menu


  • No labels

39 Comments

  1. roy

    哈咯,关于日志我想咨询一下

    我这边有这样的需求,全量格式化json日志输出,并保留之前的输出方法api

    通过源码翻看,DB对象是有SetLogger的方法,但是是个glogstruct。这里是否能考虑改成interface呢?通过接口外放自定义输出能力。

  2. oracle的配置能不能增加一个比较详细的示例啊?

    1. [database]
      [[database.default]]
      host = "127.0.0.1"
      port = "1521"
      user = "user"
      pass = "pass"
      name = "orcl"
      type = "oracle"
      role = "master"
      charset = "utf-8"

      config.toml中使用上面的配置。例如tnsnames.ora如下

      ORCL =
        (DESCRIPTION =
          (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.137.4)(PORT = 1521))
          (CONNECT_DATA =
          (SERVICE_NAME = orclsid)
          )
        )

      其中name是配置在监听文件中的的监听的名字。例如数据库的sidorclsid,那么config.toml中的name就是orcl

      另外,部署环境时建议直接安装oracle客户端,不要按照网上的下载单独的SDK,虽然SDK比较小,但是太麻烦,直接装客户端比较简单。

  3. sqlite该在gf-cli工具中如何配置?目前使用的数据库配置如下:

    [database]
        link = "sqlite:D:\\demo.db"

    gf-cli工具已经按照文档打开go-sqlite注释并编译,但具体到使用上面工程配置时,使用gf den dao一直报配置错误。

    1. 工具配置参考章节:数据规范-gen dao

  4. 使用 db.model.one 去查询的时候,返回的错误对象中提示 NextBytes InitializeSecurityContext failed 8009030e 请问这个是什么问题呢?也没有返回数据 

    1. 找到问题啦,是数据库配置文件哪里,多加了一个type的字段

      如果这样配置,就会出问题:

      [database]
          [[database.default]]
              type = "mssql"
              link = "mssql:user id=sa;password=xxxxxxxx;server=xxxx.xxxx.xxxx.xxxx;port=1433;database=xxxxxxxx;encrypt=disable"

      这样就不会:

      [database]
          [[database.default]]
              link = "mssql:user id=sa;password=xxxxxxxx;server=xxxx.xxxx.xxxx.xxxx;port=1433;database=xxxxxxxx;encrypt=disable"

      可是教程上不是可以这么配置吗?
      难道是数据库不同的问题吗?
      这个数据库是 sqlserver

      1. ORM组件需要识别你配置的数据库类型,你可以通过在link字段中添加mssql的配置,也可以增加独立的type字段配置数据库类型。以下两种方式都是可以的:

        [database]
            [[database.default]]
                link = "mssql:user id=sa;password=xxxxxxxx;server=xxxx.xxxx.xxxx.xxxx;port=1433;database=xxxxxxxx;encrypt=disable"

        或者

        [database]
            [[database.default]]
                type = "mssql"
                link = "user id=sa;password=xxxxxxxx;server=xxxx.xxxx.xxxx.xxxx;port=1433;database=xxxxxxxx;encrypt=disable"
      2. sqlserver 怎么执行存储过程,可以给个案例吗

        1. 已经搞定了,不知道有没有其他写法


          rows, err1 := db.Query(ctx, `exec sp_test @ptest_ky`, sql.Named("ptest_ky", 2))
          if err1 != nil {
          fmt.Println(err1.Error())
          }
          fmt.Println(rows)
  5. 配置里能设置默认排序吗,不然每次都要单独去手写一次排序。

    dao.Xxx.OrderDesc("id")

  6. 请教一下 强哥!!!!

    postgres 数据库:

    连接 : link.links = fmt.Sprintf("pgsql:user=%s password=%s host=%s port=%s dbname=postgres sslmode=disable", l.Username, l.Password, l.HostAddr, l.Port)

    sslmode 如果不配置是不是默认开启,配置项是否为sslmode=disable?

    如上link   => db_master.GetAll("SELECT datname FROM pg_database")     

    访问其他计算机, 提示: pq: ��ɫ "Administrator" ������, SELECT DATNAME FROM PG_DATABASE

    如果不加sslmode=disable ,提示:pq: SSL is not enabled on the server, SELECT DATNAME FROM PG_DATABASE


  7. 你好 请教一下这个连接池最大闲置链接数是做什么用的。

  8. 郭强  v2.0.0-rc2 版本无法自动加载pgsql, issues : https://github.com/gogf/gf/issues/1585

    ---

    closed

  9. [[database.default]]
    link = "mysql:***:****@tcp(****:3306)/***"
    role = "master"
    debug = true
    [[database.default]]
      link = "mysql:***:****@tcp(****:3306)/***"
    role = "slave"
    debug = true

    配置了主从之后,dao要怎么写,实现读写分离呢
    1. 全自动的,程序不需要特殊调用,具体请参考:ORM链式操作-主从切换

  10. 支持数据库密码加密吗,安全测试人员要求对数据库密码不能明文存储

    1. 可以通过HOOK实现,该功能将在下个版本发布。

      1. 好的,目前临时采用g.Cfg().Get()取到link内容然后解密pwd,再调用gdb.SetConfig()设置数据库密码。

        请问link的解析代码在哪里看到,gf代码看到头大也没有找到,希望能够讲解下大概的实现思路。

        鄙人实现的太丑陋了。

        package utils
        
        import (
        	"strings"
        
        	"github.com/gogf/gf/database/gdb"
        	"github.com/gogf/gf/frame/g"
        )
        
        func Config_DB_GetConfig() (*gdb.ConfigNode, error) {
        	var config gdb.ConfigNode
        
        	//mysql:alid_dev_user:alid_dev_20210913@tcp(192.168.11.211:3306)/alid_dev_20210913
        	// get database config info from config file, about database link
        	link := g.Cfg().GetString("database.link")
        
        	// host and port
        	indexStart := strings.Index(link, "@tcp(")
        	indexEnd := strings.Index(link, ")/")
        	sHostAndPort := link[indexStart+len("@tcp(") : indexEnd]
        	arTmp := strings.Split(sHostAndPort, ":")
        	config.Host = arTmp[0]
        	config.Port = arTmp[1]
        
        	// database name
        	indexStart = strings.LastIndex(link, "/")
        	sTmp := link[indexStart+1:]
        	config.Name = sTmp
        
        	// user and password
        	indexStart = strings.Index(link, "@tcp(")
        	sublink := link[:indexStart]
        	arTmp = strings.Split(sublink, ":")
        	config.Type = arTmp[0]
        	config.User = arTmp[1]
        	config.Pass = arTmp[2]
        
        	g.Log().Infof("config: \n%#v", config)
        	return &config, nil
        }
        
        func decrypt_passwork(pwd string) string {
        	// wait for a moment
        	return ""
        }
        func Hook_DB_Config() error {
        	configNode, err := Config_DB_GetConfig()
        	if err != nil {
        		return err
        	}
        
        	// decrypt user passwor
        	configNode.Pass = decrypt_passwork(configNode.Pass)
        
        	gdb.SetConfig(gdb.Config{
        		"default": gdb.ConfigGroup{
        			*configNode,
        		},
        	})
        
        	return nil
        }
        
        
        1. 没这么复杂。根据你的业务场景,那你完全不需要使用link配置项,而是使用pass字段,参考以下代码解析即可。

          1. 这样挺简洁的 good

  11. hi 郭强  

    升级到v2  database 的driver是mssql 

    出现这个错误


    ../../go/pkg/mod/github.com/gogf/gf/contrib/drivers/mssql/v2@v2.0.0-20220319145450-d1f76f3834e1/mssql.go:226:18: d.DoSelect undefined (type *Driver has no field or method DoSelect)
    ../../go/pkg/mod/github.com/gogf/gf/contrib/drivers/mssql/v2@v2.0.0-20220319145450-d1f76f3834e1/mssql.go:292:20: d.DoSelect undefined (type *Driver has no field or method DoSelect)

    go.mod

    module demo4

    go 1.15

    require (
    github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.0-20220319145450-d1f76f3834e1
    github.com/gogf/gf/v2 v2.0.4
    )
    1.   回一下状况:    

      应该版本差异造成的. 目前看到git hub 上也有类似的issue. 

      目前 建议使用以下版本.目前可以过了. 留下记录  

      require (          github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.0-20220314113956-2eec1bc61acf      )

  12. ljs

    使用两个数据库,偶尔会报这个错误

    connectex: Only one usage of each socket address (protocol/network address/port) is normally permitted., SELECT * FROM ...

    下面是数据库配置

    database:
      logger:
        path:    "logs/sql"
        level:   "all"
        stdout:  false
        ctxKeys: ["RequestId"]



      default:
        type: "mysql"
        host: "127.0.0.1"
        port: "3306"
        user: "root"
        pass: "root"
        name: "axea"
        prefix: "a_"
        role: "master"
        debug:  true
        MaxIdleConnCount: 5
        MaxOpenConnCount: 20
        MaxConnLifetime: 30
       
      mall:
        type: "mysql"
        host: "127.0.0.1"
        port: "3306"
        user: "root"
        pass: "root"
        name: "b_axea"
        prefix: "a_"
        role: "master"
        debug:  true
        MaxIdleConnCount: 5
        MaxOpenConnCount: 20
        MaxConnLifetime: 30
       

    1. 你提个issue,把报错的堆栈贴上。

  13. 什么时候会考虑增加一个Scope属性,可能某个查询值会根据权限来决定where条件,如果都用where去判断感觉重复逻辑有点多。写个函数返回 *gdb.Model 感觉又不好看

    1. 请提issue记录,详细描述。

      1. 谢谢,之前用laravel和gorm都叫scope,这里叫handle一下子没找到

        1. 其实我也觉着挺奇怪的.其它语言框架成熟的ORM方法名抄一遍感觉更能快速上手.

  14. LZC

       [[database.mssql2008]]

            link        = "mssql:user id=sa;password=11122;server=192.168.10.19;port=1888;database=MES_KanbanPj;encrypt=disable"

        [[database.mssql2000]]

            link        = "mssql:user id=sa;password=11122;server=192.168.10.1;port=1433;database=MES;encrypt=disable"

    mssql2000版本的数据库无法正确连接,Invalid TDS stream: unknown token type returned: token(0), SELECT * FROM 班组人员清单 WHERE 姓名='李式'

  15. 升级到2.1之后数据库使用这种方式:

    gdb.SetConfig(gdb.Config{
    "casbin": gdb.ConfigGroup{
    gdb.ConfigNode{
    Host: "127.0.0.1",
    Port: "3306",
    User: "root",
    Pass: "123456",
    Name: "test",
    Type: "mysql",
    Role: "master",
    Weight: 100,
    },
    },
    })

    db, err := gdb.Instance("casbin")
    if err != nil {
    log.Fatalf("Database init failure:%s \n", err.Error())
    }

    fmt.Println(db.GetSchema())

    报了个这个错误: 2022/06/23 22:29:41 Database init failure:cannot find database driver for specified database type "mysql", did you misspell type name "mysql" or forget importing the database driver? possible reference: https://github.com/gogf/gf/tree/master/contrib/drivers
    1. 2.1后需要手动引入driver库  

      github.com/gogf/gf/contrib/drivers/mysql/v2 v2.1.0
  16. 大哥这里的说明没写清楚吧、还是我理解错了

    sqlite配置    sqlite: 文件绝对路径 (如: /var/lib/db.sqlite3)

    Db, err := gdb.New(gdb.ConfigNode{
    Link: "sqlite: ./data.sqlite",
    Type: "sqlite",
    })
    会找不到数据库文件、还不能自动创建数据库文件
    后来我找到原因了、是因为把整个link属性都当作路径了。。。
    这里我试了不写Type属性、会报没有写Type属性的错误
    正确的写法应该是
    Db, err := gdb.New(gdb.ConfigNode{
    Link: "./data.sqlite",
    Type: "sqlite",
    })

  17. oracle对应的Link,似乎现在不是oracle: 账号/密码@地址:端口/数据库名称
    默认的应该是oracle: //账号:密码@地址:端口/数据库名称
    驱动中的代码如下:

    Link: fmt.Sprintf("%s://%s:%s@%s:%s/%s",
    			TestDbType, TestDbUser, TestDbPass, TestDbIP, TestDbPort, TestDbName)
  18. 设置timezone: "local" 会出错。
    sql.Open failed for driver "mysql" by source "root:12345@tcp(127.0.0.1:3306)/test?charset=utf8&loc=local": unknown time zone local
    不设置连接时区,导致预想存储的数据和数据库数据相差一天。应该是timezone: "Local"

  19. gf自带的orm怎么实现多租户,分库?

  20. 2.1.3 集成clickhouse    执行 sql  会报错  Error: code: 115, message: Unknown setting charset

    1. 这是 orm 的bug 嘛?