goland gorm分组查询统计_Golang踩坑分析之 Gorm

案例1:Golang Gorm踩坑

## 影响情况##

服务A 是定时调度系统,利用github.com/robfig/cron 来实现,发现服务运行3小时左右就不打印sql 日志,但是API 服务响应正常. 导致调度服务不执行、

##分析思路##

Golang 编写调度的服务遇到不调度的情况如何分析处理那?首先我们利用golang 自带的pprof来分析。在main.go中 增加`

go func() {

if err := http.ListenAndServe("0.0.0.0:12345", nil); err != nil {

log.Println(err)

}

}()`

然后再结合火焰图 去分析。可惜这次,pprof 并没有帮上我们的忙,因为调度服务很轻量,内存方面没超过1MB,profile cpu等也很正常、那么接下来怎么办?

由于是服务调度每次都会打印sql,那么我们就去查看mysql 的资源吧、

首先 通过show full process list; 如下

mysql> show full processlist;

+---------+-----------+------------------+---------+---------+------+----------+-----------------------+-----------+---------------+

| Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined |

+---------+-----------+------------------+---------+---------+------+----------+-----------------------+-----------+---------------+

| 2855999 | A | *:52898 | my | Sleep | 37 | | NULL | 0 | 0 |

| 2880711 | A | *:26280 | my | Sleep | 10 | | NULL | 0 | 0 |

| 2904663 | A | *:50519 | my | Sleep | 2544 | | NULL | 0 | 0 |

| 2905293 | A | *:6492 | my | Sleep | 567 | | NULL | 0 | 0 |

| 2905341 | A | *:2777 | my | Query | 0 | starting | show full processlist | 0 | 0 |

| 2905425 | A | *:4081 | my | Sleep | 57 | | NULL | 0 | 0 |

| 2905444 | A | *:15888 | my | Sleep | 92 | | NULL | 0 | 0 |

| 2905523 | A | *:13524 | my | Sleep | 10 | | NULL | 0 | 0 |

+---------+-----------+------------------+---------+---------+------+----------+-----------------------+-----------+———————+

测试环境服务 发现会有大量的sleep,那么我们再看下my.conf 配置。一般最大连接数就设置200左右。所所以1分钟调度一次,200分钟我们的服务就被把数据库连接数占满。那么接下来去分析下源码。

//以下是调度的问题代码

for rowsInner.Next() {

dataMap[supportInt] = true

break

}

我们发现 直接做了break、但是为什么会导致连接被占用 不能释放那?

func (rs *Rows) Next() bool {

var doClose, ok bool

withLock(rs.closemu.RLocker(), func() {

doClose, ok = rs.nextLocked()

})

if doClose {

rs.Close()

}

return ok

}

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

rs.closemu.RLock()

if rs.lasterr != nil && rs.lasterr != io.EOF {

rs.closemu.RUnlock()

return rs.lasterr

}

if rs.closed {

err := rs.lasterrOrErrLocked(errRowsClosed)

rs.closemu.RUnlock()

return err

}

rs.closemu.RUnlock()

if rs.lastcols == nil {

return errors.New("sql: Scan called without calling Next")

}

if len(dest) != len(rs.lastcols) {

return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))

}

for i, sv := range rs.lastcols {

err := convertAssignRows(dest[i], sv, rs)

if err != nil {

return fmt.Errorf(`sql: Scan error on column index %d, name %q: %v`, i, rs.rowsi.Columns()[i], err)

}

}

return nil

}

我们通过源码可以发现,Next()方法判断下一个元素有没有,但是我们没有用scan,因为scan 每次返回都会释放掉一行数据库的缓存,所以 我们 手动rows.Close(),这种情况也很难遇到,因为一般情况下,我们都是 scan到底,最后底层代码会 执行 d.close(rows.lasterr)、所以我们一般不需要手动close rows.

结论:每次返回的rows 都是新的实例,占用一个数据库连接. rows.next()直到false 就会关闭,否则就会出现资源没有被回收,但数据库设置的超时回收也会去回收 但是会降低服务Tps。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值