-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Closed
Description
发现fullgc频繁,执行dump操作,发现近45%的内存占用都是存储的sql语句(堆的设置是2G)。跟踪引用关系,找到了JdbcSqlStat类,接着找到JdbcDataSourceStat类,发现如下代码发现是在这持有了sql语句的引用:
public JdbcSqlStat createSqlStat(String sql) {
lock.writeLock().lock();
try {
JdbcSqlStat sqlStat = sqlStatMap.get(sql);
if (sqlStat == null) {
sqlStat = new JdbcSqlStat(sql);
sqlStat.setDbType(this.dbType);
sqlStat.setName(this.name);
sqlStatMap.put(sql, sqlStat); //sqlStatMap是一个LinkedHashMap
}
return sqlStat;
} finally {
lock.writeLock().unlock();
}
}
进一步排查代码,暂时只发现三个方法对sqlStatMap对象存在移除操作:
1. com.alibaba.druid.stat.JdbcDataSourceStat#setMaxSqlSize
2. com.alibaba.druid.stat.JdbcDataSourceStat#reset
3. com.alibaba.druid.stat.JdbcDataSourceStat#getSqlStatMapAndReset
第二个reset方法,发现是通过前端指定的url来触发执行(我们这里的情况是没有调用这个url的)
第三个getSqlStatMapAndReset,发现是在通过注册了一个ServletContextListener来添加了一个定时任务,没隔300second来触发(因为我们这里的应用不是一个web所以也没有触发这个操作)
第一个setMaxSqlSize方法调用关系太多,也就没有一一梳理,不知道这个方法的调用策略是怎样的呢?
Activity
fuyi-wang commentedon Nov 7, 2017
楼主这个问题最终是如何解决的啊
rocky-peng commentedon Nov 8, 2017
@fuyi-wang 我们当初开启了这个功能,但没有使用。找到原因后,就把这个功能给关闭了。线上就正常了。
如果需要使用这个功能的话,可以参考:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
fuyi-wang commentedon Dec 8, 2017
@rocky-peng 我遇到的问题和你的一样,但又想使用 SQL监控的功能,我再查查看有没有其他的解决方案,多谢哈
cqwzboy commentedon Apr 24, 2019
楼主,我是JdbcDataSourceStat类中属性
ConcurrentMap<Long, JdbcConnectionStat.Entry> connections = new ConcurrentHashMap
内存占用很大,跟你情况差不多,这个map占用内存超过总的一半,最终把StatFilter禁用掉才恢复正常的。
raydl007 commentedon Jun 15, 2019
一样的情况,springboot项目,运行一天就oom了
rocky-peng commentedon Feb 27, 2020
@xuexuegege 试试自己写工厂方法new出datasurce,new的时候应该有参数设置

xuexuegege commentedon Mar 10, 2020
看了下,springboot集成druid是自动配置的,看了一下源码发现
com.alibaba.druid.spring.boot.autoconfigure.stat类中
这里默认开启了,所以需要手动设置为false就行了
也就是
xuexuegege commentedon May 13, 2020
其实有的业务需要监控,但是这里根据情况可以打开SQL合并,
这里SQL合并源码有点复杂,大致意思是简化sql语句,
比如:
select id from user where name = “xuemeng” -> select id from user where name = ?
update user set name = 'xuemeng' where id = 1 -> update user set name = ? where id = ?
然后因为这个SQL监控的LinkedHashMap<String, JdbcSqlStat> sqlStatMap 是以SQL语句作为键的,所以这里如果有很多类似结构的SQL语句执行,可以把SQL合并打开,就会产生很多相同的sql语句,而他的内部逻辑是如果有这个语句就不put了:
这样就会大大减少这么map的大小。
当然如果有业务需求不能合并sql或者合并了sql也没有太大效果(结构重复的sql语句不多),也可以不设置sql合并而是设置druid.stat.sql.MaxSize该参数是控制这个map最大容量的
因为这里重写了弃老规则,所以put的时候会判断下是否大于最大,是的话就会弃老
rocky-peng commentedon May 13, 2020
@xuexuegege nice
hurukawatinami commentedon Dec 8, 2020
nice, got it
zfy68 commentedon Sep 1, 2021
顶一个
zfy68 commentedon Sep 1, 2021
DruidDataSource dataSourceRDB = new DruidDataSource(); // 注掉下面的方法就可以了 dataSourceRDB.setFilters("stat")
FamousCodee commentedon Mar 7, 2022
timeBetweenLogStatsMillis 设置这个属性可以解决
AiClvGu commentedon Sep 8, 2023
mark一下
lizongbo commentedon Nov 19, 2023
可以利用启动将缓存的条数调小,比如只保存3条
-Ddruid.stat.sql.MaxSize=3
或者开启druid.stat.mergeSql=true