# README
- 实现gorm.Print接口,加入sql耗时、慢查监控
- github.com/lun-zhang/gorm 框架
- 把非事务的读用从库,非事务的写和事务操作用主库,
- 另外新增WithContext(context.Context)函数,从而非破坏性地加入xray跟踪
- mysql.type包 实现database/sql/driver的Value和Scan方法, 在写入数据库时候将结构转成json格式的[]byte,取出来再解析回去
- time.Time包 实现了时间类型转成json的时候会转成timestamp(int),从json解析的时候,会把timestamp(int)转成time.Time
- time.Duration包 实现json.UnmarshalJSON接口,从json格式的[]byte解出时候,把"5m"解析成5分钟
两行即可增加sql监控
//import "zlutils/mysql"
mysql.InitDefaultMetric(projectName)
//dbConn, err := gorm.Open("mysql", "root:123@/counter?charset=utf8&parseTime=True&loc=Local")
dbConn.LogMode(true).SetLogger(&mysql.Logger{})
dbConn无论是由github.com/jinzhu/gorm创建还是github.com/lun-zhang/gorm创建,只要使用了Print(...interface{})接口打印sql,都可用以上两行完加入监控
效果如下:
sum(rate(task_wall_mysql_total[5m])) by (query)
sum(rate(task_wall_mysql_latency_millisecond_sum[5m])) by (query) / sum(rate(task_wall_mysql_latency_millisecond_count[5m])) by (query)
WHERE IN(?,...,?)的语句会被getSampleQuery函数替换成WHERE IN(?),免得参数个数不同变成不同的线(由于getSampleQuery函数写的很简陋,导致VALUES(?,...,?)替换成了VALUES(?)等)
我实现的Print方法除了sql监控外,还加入了slow sql警告日志:执行耗时超过200ms的sql会打印warning日志
mysql.MetricCounter和mysql.MetricLatency允许你定义自己喜欢的metric名字以及sql处理方式
当然你完全也可以通过实现自己的Print方法来加入监控
更简单:使用New/NewMasterAndSlave创建dbConn
- 定义数据库连接配置
//import "zlutils/mysql"
config := mysql.Config{
Url: "root:123@/counter?charset=utf8&parseTime=True&loc=Local",
//最大连接数,防止连接过多拖垮数据库,影响其他服务,
// 取值小于等于0时表示不限制连接数
MaxOpenConns: 10,
//最大空闲连接数,不用的连接会放到空闲连接池,方便下次需要连接时取用
// 太小可能导致新连接频繁创建和销毁,从而耗尽端口
// 取值小于等于0表示不保留空闲连接,
// 大于或者等于MaxOpenConns效果相同(见sql.DB.SetMaxIdleConns方法源码)
MaxIdleConns: 10,
}
配置一般写在consul:
{
"url":"root:123@/counter?charset=utf8&parseTime=True&loc=Local",
"max_open_conns":5,
"max_idle_conns":5
}
创建dbConn:
//import "zlutils/mysql"
//mysql.InitDefaultMetric(projectName)
dbConn := mysql.New(config)
如果想要读写分离,则提供主库和从库的配置:
//import "zlutils/mysql"
configMS := mysql.ConfigMasterAndSlave{
Master: mysql.Config{
Url: "root:123@tcp(counter.xxx.com:3306)/counter?charset=utf8&parseTime=True&loc=Local",
MaxOpenConns: 4,
MaxIdleConns: 3,
},
Slave: mysql.Config{
Url: "root:123@tcp(counter_reader.xxx.com:3306)/counter?charset=utf8&parseTime=True&loc=Local",
MaxOpenConns: 2,
MaxIdleConns: 1,
},
}
dbConn := mysql.NewMasterAndSlave(configMS)
那么在执行sql时候,非事务的读语句会使用从库,非事务的写和事务操作使用主库
拓传数据存储,还在每次json.Unmarshal/Marshal?
有些不会用于WHERE条件,只用来存取的字段,转成json格式的[]byte数组存储到数据库中是较为方便的做法,database/sql/driver提供了2个接口
// Scanner is an interface used by Scan.
type Scanner interface {
Scan(src interface{}) error
}
// Valuer is the interface providing the Value method.
//
// Types implementing Valuer interface are able to convert
// themselves to a driver Value.
type Valuer interface {
// Value returns a driver Value.
Value() (Value, error)
}
Scan将数据库类型转成你自定义的类型,Value相反,将自定义类型转成数据库类型
因此实现这两个方法实现你自定义的类型与json格式的[]byte互转
例如有个接口要存取用户联系方式,不需要筛选:
那么自定义的Contact类型实现接口(详细代码见type_test.go:TestContact)
//import "zlutils/mysql"
type Contact struct {
Phone string `json:"phone"` //电话号码
Email string `json:"email"` //邮箱
}
func (j Contact) Value() (driver.Value, error) {
return mysql.Value(j)
}
func (j *Contact) Scan(src interface{}) error {
return mysql.Scan(j, src)
}
写入数据库:
dbConn.Create(&C{
Contact:Contact{
Phone:"123",
Email:"[email protected]",
},
})
读取数据库:
var cs []C
dbConn.Find(&cs)
表结构:
CREATE TABLE `c` (
`contact` TEXT DEFAULT NULL
);
写入数据库后的样子:
mysql> select * from c;
+-----------------------------------+
| contact |
+-----------------------------------+
| {"phone":"123","email":"[email protected]"} |
+-----------------------------------+
# Functions
No description provided by the author
NOTE 只能用于初始化,失败则fatal.
No description provided by the author
获取gorm列,忽略omits指定的列,主要用于解决Save不存在会插入、Updates不会更新更新零值的问题.
No description provided by the author
No description provided by the author
No description provided by the author
# Variables
No description provided by the author
No description provided by the author
# Structs
No description provided by the author
No description provided by the author
虽然这个Logger也可以打印github.com/jinzhu/gorm的日志,但是没有ctx所以无法加入trace_id, 因此github.com/lun-zhang/gorm v1.13.1直接打印了日志,有tract_id.