mysql

package module
v1.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2020 License: BSD-2-Clause Imports: 13 Imported by: 0

README

go-mysql:MySQL 客户端

go-mysql 封装了 database/sql 的接口,所有接口与标准库功能保持一致,修复了 Go 标准库设计和实现层面的 bug。

使用方法

如果使用 go-runner 启动服务,go-mysql 会被自动创建,无需操心。所有配置放在配置文件的 [mysql] 条目下。

配置需要填写以下信息:

[mysql]
dsn = "username:password@protocol(address)/dbname?param=value"

业务代码需要使用 MySQL 时,直接使用 New 方法即可。

import "git.altstory.com/altstory-framework/go-mysql"

func Foo(ctx context.Context, req *FooRequest) (res *FooResponse, err error) {
    // 这里省略各种参数检查……

    db := mysql.New(ctx)
    rows, err := db.Query("SELECT uid FROM foo WHERE status = ?", status)

    // 判断错误,使用 rows。具体代码省略……
}

SQL builder 和 ORM

原则上不推荐使用任何 ORM,比如 xormgorm 等,这些 ORM 副作用比较难以控制,且无法很好的根据 ctx 控制执行时间。

推荐使用 SQL builder 库来拼接 SQL,提升可控性并减少人工拼接的过程。推荐的库是 go-sqlbuilder

高级用法

主从分离

配置里可以设置 dsn_slave 来指定一个从库,所有的读流量会走到从库,而写流量走主库。当这个配置没有设置时,所有流量都会走 dsn 指定的主库。

[mysql]
dsn = "username:password@protocol(address)/dbname?param=value"
dsn_slave = "username:password@protocol(address)/dbname?param=value"

默认情况下,所有的 MySQL#Query/MySQL#QueryRow/Stmt#Query/Stmt#QueryRow 会走从库,所有的 Tx/MySQL#Exec/Stmt#Exec 会走主库。

如果需要强行走主库,可以使用以下代码来指定走主库。

row, err := mysql.UseMaster().QueryRow(sql, args)
在服务中使用多个 MySQL 连接

在某些场景下,仅使用一个 MySQL 并不足够,那么我们可以自行构建 Factory 来连接更多的 MySQL 服务。

首先在配置文件中写一个新的 MySQL 连接配置。

[mysql_another]
dsn = "username:password@protocol(address)/dbname?param=value"

然后实例化一个新的工厂。

// anotherMySQLFactory 的类型是 **Factory,是一个指针的指针。
var anotherMySQLFactory = mysql.Register("mysql_another")

接着,使用这个全局变量 anotherMySQLFactory 来创建新的 MySQL client,在业务中使用。

factory := *anotherMySQLFactory
mysql := factory.New(ctx)

// 使用 mysql client 进行各种操作……
使用 MySQL 多实例集群

为了能够方便的进行 MySQL 扩容,go-mysql 支持配置多实例,从而让未来的 MySQL 扩容变得相对简单。

例如,在上线时我们先仅设置一个数据库信息。

[mysql]
mod = 1

    [[mysql.instances]]
    dsn = "username:password@protocol(address1)/dbname?param=value"
    buckets = [0]

如果发现这个库扛不住,可以添加一个新的实例。这里需要注意,mod 直接设置成了 10,这是为了方便未来在扩容的时候保持实例数据的稳定。

[mysql]
mod = 10

    [[mysql.instances]]
    dsn = "username:password@protocol(address1)/dbname?param=value"
    buckets = [0, 1, 2, 3, 4]

    [[mysql.instances]]
    dsn = "username:password@protocol(address2)/dbname?param=value"
    buckets = [5, 6, 7, 8, 9]

又过了一段时间,我们发现 address2 有些问题,需要分出更多的实例,可以直接通过修改 buckets 来分摊 address2 流量。

[mysql]
mod = 10

    [[mysql.instances]]
    dsn = "username:password@protocol(address1)/dbname?param=value"
    buckets = [0, 1, 2, 3, 4]

    [[mysql.instances]]
    dsn = "username:password@protocol(address2)/dbname?param=value"
    buckets = [5, 6, 7]

    [[mysql.instances]]
    dsn = "username:password@protocol(address3)/dbname?param=value"
    buckets = [8, 9]

由于 MySQL 分流后,数据写入会分到不同的主库,如果我们需要再次合并这些数据库实例,则需要花费较长的时间合并数据到一个实例才行。这需要在运维的时候注意这个细节。

使用了集群配置之后,MySQL 的使用方法会发生变化,必须使用 mysql.WithIndex 来修饰 ctx 来选择实例。

// 假设我们拿到了一个数据库 hash id,需要将它转化成一个 int64 类型的数据并设置到 ctx 里面去。
// 如果 id 是 int64 类型,直接设置即可;
// 如果 id 是 string,推荐使用 hash/fnv 的 fnv.New64() 来获得一个 hash 来计算,这里做了个演示。
uuid := "xxxxxxxx-xxxxx-xxxxxxxx"
hash := fnv.New64()
io.WriteString(hash, uuid)
idx := hash.Sum64()

ctx = mysql.WithIndex(ctx, idx)
m := mysql.New(ctx)

Documentation

Index

Constants

View Source
const (
	// DefaultConnMaxLifetime 代表默认的连接的最大保持时间,当前设置为 1h 时间。
	DefaultConnMaxLifetime time.Duration = time.Hour

	// DefaultMaxIdleConns 代表默认的最大空闲连接数,当前设置为 10。
	DefaultMaxIdleConns = 10
)

Variables

This section is empty.

Functions

func WithIndex

func WithIndex(ctx context.Context, idx int64) context.Context

WithIndex 在 ctx 中设置 idx,用来选择使用哪个 MySQL 实例。

Types

type Config

type Config struct {
	DSN      string `config:"dsn"`       // DSN 是 MySQL 主库的连接字符串。
	DSNSlave string `config:"dsn_slave"` // DSNSlave 是从库的 MySQL 连接字符串,所有只读的 Query/QueryRow 都会走这个连接,默认与 DSN 相同。

	Mod       int64            `config:"mod"`       // Mod 是 hash 分桶的余数,比如设置为 10 就会将 hash%10 来计算命中哪一个实例,默认不分桶。
	Instances []ConfigInstance `config:"instances"` // Instances 是分桶后的数据库连接配置。

	ConnMaxLifetime time.Duration `config:"conn_max_life_time"` // ConnMaxLifetime 设置连接的最大保持时间,默认是 DefaultConnMaxLifetime。
	MaxIdleConns    int           `config:"max_idle_conns"`     // MaxIdleConns 设置最多保持多少个空闲连接,默认是 DefaultMaxIdleConns。
	MaxOpenConns    int           `config:"max_open_conns"`     // MaxOpenConns 设置最大同时连接数,默认是不限制。
}

Config 代表 MySQL 的配置。

type ConfigInstance

type ConfigInstance struct {
	DSN      string `config:"dsn"`       // DSN 是 MySQL 主库的连接字符串。
	DSNSlave string `config:"dsn_slave"` // DSNSlave 是从库的 MySQL 连接字符串,所有只读的 Query/QueryRow 都会走这个连接,默认与 DSN 相同。

	Buckets []int64 `config:"buckets"` // Buckets 表示这个实例对应的 bucket 号,可以是多个号,比如 [0, 1, 2]。
}

ConfigInstance 代表一组 MySQL 实例的连接字符串。

type Factory

type Factory struct {
	// contains filtered or unexported fields
}

Factory 代表一个用于创建 MySQL 连接的工厂。 创建 Factory 之后必须调用 `Factory#Conn` 方法建立连接, 否则后续无法通过 `Factory#New` 方法创建 MySQL 实例。

func NewFactory

func NewFactory(config *Config) *Factory

NewFactory 实例化一个工厂。 创建工厂后需要调用 `Factory#Conn` 才能真正建立连接,后续才能使用 `Factory#New`。

func Register

func Register(section string) **Factory

Register 将配置文件里 [section] 部分的配置用于初始化 MySQL。 需要注意,Register 函数依赖于 runner 的启动流程, 在 AddClient 周期结束前,返回的 Factory 并不可用。

func (*Factory) Close

func (f *Factory) Close() error

Close 关闭数据库连接,一般没有调用的必要。

func (*Factory) Conn

func (f *Factory) Conn(ctx context.Context) (err error)

Conn 建立 MySQL 连接。

func (*Factory) New

func (f *Factory) New(ctx context.Context) *MySQL

New 建立新的 MySQL 实例,供业务代码使用。

type MySQL

type MySQL struct {
	// contains filtered or unexported fields
}

MySQL 代表一个数据库的链接。

func New

func New(ctx context.Context) *MySQL

New 通过默认工厂创建一个 MySQL 实例。

func (*MySQL) BeginTx

func (mysql *MySQL) BeginTx(opts *sql.TxOptions) (tx *Tx, err error)

BeginTx 开始一个事务。

func (*MySQL) Exec

func (mysql *MySQL) Exec(query string, args ...interface{}) (result Result, err error)

Exec 执行一条修改语句并返回结果。

func (*MySQL) Ping

func (mysql *MySQL) Ping() (err error)

Ping 测试连接是否可用。

func (*MySQL) Prepare

func (mysql *MySQL) Prepare(query string) (stmt *Stmt, err error)

Prepare 准备一个 Stmt,方便绑定参数。

func (*MySQL) Query

func (mysql *MySQL) Query(query string, args ...interface{}) (rows *Rows, err error)

Query 查询一个带参数的查询,返回所有的结果。

func (*MySQL) QueryRow

func (mysql *MySQL) QueryRow(query string, args ...interface{}) (row *Row, err error)

QueryRow 查询一个带参数的查询,返回第一条结果。 如果查询出现错误,QueryRow 依然会保证返回一个合法的 row,但是调用 row.Scan() 会报错。

func (*MySQL) Stats

func (mysql *MySQL) Stats() sql.DBStats

Stats 返回数据库当前状态。

func (*MySQL) UseMaster

func (mysql *MySQL) UseMaster() *MySQL

UseMaster 返回一个 MySQL 实例,调用这个实例的所有方法都会调用主库。

type Result

type Result sql.Result

Result 代表执行一条数据库修改命令之后返回的结果。

type Row

type Row struct {
	// contains filtered or unexported fields
}

Row 代表一条查询结果。

func (*Row) Scan

func (r *Row) Scan(dest ...interface{}) error

Scan 将查询出来的数据设置到 dest 里面。

type Rows

type Rows struct {
	// contains filtered or unexported fields
}

Rows 代表一个查询结果。

func (*Rows) Close

func (rs *Rows) Close() error

Close 关闭 rs 来释放资源。

func (*Rows) ColumnTypes

func (rs *Rows) ColumnTypes() ([]*sql.ColumnType, error)

ColumnTypes 返回列类型信息。

func (*Rows) Columns

func (rs *Rows) Columns() ([]string, error)

Columns 返回所有列名。

func (*Rows) Err

func (rs *Rows) Err() error

Err 返回当前的错误。

func (*Rows) Next

func (rs *Rows) Next() bool

Next 查询下一条结果,如果已经没有更多结果或者出错,返回 false。

func (*Rows) NextResultSet

func (rs *Rows) NextResultSet() bool

NextResultSet 查询是否存在下一条记录,但是并不会真的返回下一条结果。 真正 Scan 之前,还得调用 Next 来实际获取这条结果。

func (*Rows) Scan

func (rs *Rows) Scan(dest ...interface{}) error

Scan 将查询出来的数据设置到 dest 里面。

type Stmt

type Stmt struct {
	// contains filtered or unexported fields
}

Stmt 代表一个准备好的语句,可以绑定参数并执行。

func (*Stmt) Close

func (s *Stmt) Close() error

Close 关闭这条语句并释放资源。

func (*Stmt) Exec

func (s *Stmt) Exec(args ...interface{}) (result Result, err error)

Exec 执行一条修改语句并返回结果。

func (*Stmt) Query

func (s *Stmt) Query(args ...interface{}) (rows *Rows, err error)

Query 查询一个带参数的查询,返回所有的结果。

func (*Stmt) QueryRow

func (s *Stmt) QueryRow(args ...interface{}) (row *Row, err error)

QueryRow 查询一个带参数的查询,返回第一条结果。 如果查询出现错误,QueryRow 依然会保证返回一个合法的 row,但是调用 row.Scan() 会报错。

type Tx

type Tx struct {
	// contains filtered or unexported fields
}

Tx 代表一个事务。

func (*Tx) Commit

func (tx *Tx) Commit() (err error)

Commit 提交事务。

func (*Tx) Exec

func (tx *Tx) Exec(query string, args ...interface{}) (result Result, err error)

Exec 执行一条修改语句并返回结果。

func (*Tx) Prepare

func (tx *Tx) Prepare(query string) (stmt *Stmt, err error)

Prepare 准备一个 Stmt,方便绑定参数。

func (*Tx) Query

func (tx *Tx) Query(query string, args ...interface{}) (rows *Rows, err error)

Query 查询一个带参数的查询,返回所有的结果。

func (*Tx) QueryRow

func (tx *Tx) QueryRow(query string, args ...interface{}) (row *Row, err error)

QueryRow 查询一个带参数的查询,返回第一条结果。 如果查询出现错误,QueryRow 依然会保证返回一个合法的 row,但是调用 row.Scan() 会报错。

func (*Tx) Rollback

func (tx *Tx) Rollback() error

Rollback 回滚事务。

func (*Tx) Stmt

func (tx *Tx) Stmt(stmt *Stmt) (txStmt *Stmt, err error)

Stmt 将一个指定的 stmt 纳入到事务 tx 的管理范围内,使其受到 commit 和 rollback 的控制。

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL