当前位置: 首页 新闻详细

SQL Server占用内存过高,什么原因导致的,用什么方法可以解决,数据库连接池内存泄漏问题的分析和解决方案

专业小程序设计开发——助力新电商新零售

电话+V:159999-78052,欢迎咨询sql数据库内存泄漏怎么解决呢,[小程序设计与开发],[小程序投流与推广],[小程序后台搭建],[小程序整套源码打包],[为个体及小微企业助力],[电商新零售模式],[小程序运营推广及维护]

一、SQLServer占用内存过高,什么原因导致的,用什么方法可以解决

经常使用MSSQL的朋友都会发现一个小小的网站在运行若干天后MSSQL就会把服务器上所有的内存都吃光,此时你不得不重新启动一下服务器或mssql来释放内存,有人认为是MSSQL有内存泄露问题,其实不然,微软给我们了明确说明:在您启动SQLServer之后,SQLServer内存使用量将会持续稳定上升,即使当服务器上活动很少时也不会下降。另外,任务管理器和性能监视器将显示计算机上可用的物理内存稳定下降,直到可用内存降到4至10MB为止。

仅仅出现这种状态不表示内存泄漏。此行为是正常的,并且是SQLServer缓冲池的预期行为。

默认情况下,SQLServer根据操作系统报告的物理内存加载动态增大和收缩其缓冲池(缓存)的大小。只要有足够的内存可用于防止内存页面交换(在4至10MB之间),SQLServer缓冲池就会继续增大。像在与SQLServer分配内存位于相同计算机上的其他进程一样,SQLServer缓冲区管理器将在需要的时候释放内存。SQLServer每秒可以释放和获取几兆字节的内存,从而使它可以快速适应内存分配变化。

更多信息

您可以通过服务器内存最小值和服务器内存最大值配置选项设置SQLServer数据库引擎使用的内存(缓冲池)量的上下限。在设置服务器内存最小值和服务器内存最大值选项之前,请查阅以下Microsoft知识库文章中标题为'内存'一节中的参考信息:319942HOWTO:DeterminePropersqlserver(WINDOWS平台上强大的数据库平台)ConfigurationSettings(确定正确的sqlserver(WINDOWS平台上强大的数据库平台)配置设置)

请注意,服务器内存最大值选项只限制SQLServer缓冲池的大小。服务器内存最大值选项不限制剩余的未保留内存区域,sqlserver(WINDOWS平台上强大的数据库平台)准备将该区域分配给其他组件,例如扩展存储过程、COM对象、以及非共享DLL、EXE和MAPI组件。由于前面的分配SQLServer专用字节超过服务器内存最大值配置是很正常的。有关此未保留内存区域中分配的其他信息,请单击下面的文章编号,以查看Microsoft知识库中相应的文章:316749PRB:在使用大量数据库时可能没有足够的虚拟内存

下面我们就来实战如何限制MSSQL内存使用:

第一步:打开企业管理器双击进入要修改的MSSQL.

第二步:在左侧MSSQL上点击右键,选择属性,弹出SQLServer属性(配置)对话框(最好打上SQLSP4补丁)

第三步:点击内存选项卡.在这里,你会看到MSSQL默认设置为使用最大内存,也就是你所有的内存,根据你的需要,设置它的最大值(一般为物理内存-128M)和最小值(一般为最大内存的1/4)吧.

二、sql编辑表前两百行就重启

数据库服务器负载过高,数据库连接超时,内存泄漏。

1、数据库服务器负载过高:如果数据库服务器负载过高,会导致SQL编辑器无法完成数据读取和处理任务,从而引起程序崩溃或者重启。为了避免这种情况发生,可以考虑优化查询语句,或者增加硬件配置以提高服务器性能。

2、数据库连接超时:如果SQL编辑器连接数据库的时间过长,会触发超时机制而导致程序重启。为了避免连接超时,可以尝试通过优化网络配置或者调整SQL编辑器的连接参数来缩短连接时间。

数据库连接池内存泄漏问题的分析和解决方案

2020-09-16 17:12·帅哥的职业修养

github地址

github.com/whx123/Java…

一、问题描述

上周五晚上主营出现部分设备掉线,经过查看日志发现是由于缓存系统出现长时间gc导致的。这里的gc日志的特点是:

  • 1.gc时间都在2s以上,部分节点甚至出现12s超长时间gc。
  • 2.同一个节点距离上次gc时间间隔为普遍为13~15天。
  • 然后紧急把剩余未gc的一个节点内存dump下来,使用mat工具打开发现,
    com.mysql.jdbc.NonRegisteringDriver对象占了堆内存的大部分空间。

    查看对象数量,发现
    com.mysql.jdbc.NonRegisteringDriver$
    ConnectionPhantomReference这个对象堆积了10140个

    初步判断长时间gc的问题应该是由于
    com.mysql.jdbc.NonRegisteringDriver$
    ConnectionPhantomReference这个对象大量堆积引起的

    二、问题分析

    目前正式环境使用数据库相关依赖如下:

    依赖版本

    mysql5.1.47

    hikari2.7.9

    Sharding-jdbc3.1.0

    根据以上描述,提出以下问题:

  • 1、com.mysql.jdbc.NonRegisteringDriver$ConnectionPhantomReference到底是个什么对象呢?
  • 2、这种对象为什么会大量堆积,JVM回收不过来了?
  • NonRegisteringDriver$ConnectionPhantomReference到底是个什么对象呢?

    简单来说,NonRegisteringDriver类有个虚引用集合connectionPhantomRefs用于存储所有的数据库连接,
    NonRegisteringDriver.trackConnection方法负责把新创建的连接放入connectionPhantomRefs集合。源码如下:

    1.publicclassNonRegisteringDriverimplementsjava.sql.Driver{2.protectedstaticfinalConcurrentHashMap<ConnectionPhantomReference,ConnectionPhantomReference>connectionPhantomRefs=newConcurrentHashMap<ConnectionPhantomReference,ConnectionPhantomReference>();3.protectedstaticfinalReferenceQueue<ConnectionImpl>refQueue=newReferenceQueue<ConnectionImpl>();4.5.....6.7.protectedstaticvoidtrackConnection(ConnectionnewConn){8.9.ConnectionPhantomReferencephantomRef=newConnectionPhantomReference((ConnectionImpl)newConn,refQueue);10.connectionPhantomRefs.put(phantomRef,phantomRef);11.}12.....13.}

    我们追踪创建数据库连接的过程源码,发现其中会调到
    com.mysql.jdbc.ConnectionImpl的构造函数,该方法会调用createNewIO方法创建一个新的数据库连接MysqlIO对象,然后调用我们上面提到的
    NonRegisteringDriver.trackConnection方法,把该对象放入
    NonRegisteringDriver.connectionPhantomRefs集合。源码如下:

    1.publicclassConnectionImplextendsConnectionPropertiesImplimplementsMySQLConnection{2.3.publicConnectionImpl(StringhostToConnectTo,intportToConnectTo,Propertiesinfo,StringdatabaseToConnectTo,Stringurl)throwsSQLException{4....5.createNewIO(false);6....7.NonRegisteringDriver.trackConnection(this);8....9.}10.}

    connectionPhantomRefs是一个虚引用集合,何为虚引用?为什么设计为虚引用队列

    虚引用队列也称为“幽灵引用”,它是最弱的一种引用关系。

    如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

    为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

    当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,将这个虚引用加入引用队列,在其关联的虚引用出队前,不会彻底销毁该对象。所以可以通过检查引用队列中是否有相应的虚引用来判断对象是否已经被回收了。

    connectionPhantomRefs这种对象为什么会大量堆积,JVM回收不过来了?

    这里结合项目中hikaricp数据配置和官方文档结合说明~

    我们先查阅hikaricp数据池的官网地址,看看部分属性介绍如下:

    maximumPoolSize

    Thispropertycontrolsthemaximumsizethatthepoolisallowedtoreach,includingbothidleandin-useconnections.Basicallythisvaluewilldeterminethemaximumnumberofactualconnectionstothedatabasebackend.Areasonablevalueforthisisbestdeterminedbyyourexecutionenvironment.Whenthepoolreachesthissize,andnoidleconnectionsareavailable,callstogetConnection()willblockforuptoconnectionTimeoutmillisecondsbeforetimingout.Pleasereadaboutpoolsizing.Default:10

    maximumPoolSize控制最大连接数,默认为10

    minimumIdle

    ThispropertycontrolstheminimumnumberofidleconnectionsthatHikariCPtriestomaintaininthepool.IftheidleconnectionsdipbelowthisvalueandtotalconnectionsinthepoolarelessthanmaximumPoolSize,HikariCPwillmakeabestefforttoaddadditionalconnectionsquicklyandefficiently.However,formaximumperformanceandresponsivenesstospikedemands,werecommendnotsettingthisvalueandinsteadallowingHikariCPtoactasafixedsizeconnectionpool.Default:sameasmaximumPoolSize

    minimumIdle控制最小连接数,默认等同于maximumPoolSize,10。

    ?idleTimeout

    Thispropertycontrolsthemaximumamountoftimethataconnectionisallowedtositidleinthepool.ThissettingonlyapplieswhenminimumIdleisdefinedtobelessthanmaximumPoolSize.IdleconnectionswillnotberetiredoncethepoolreachesminimumIdleconnections.Whetheraconnectionisretiredasidleornotissubjecttoamaximumvariationof+30seconds,andaveragevariationof+15seconds.Aconnectionwillneverberetiredasidlebeforethistimeout.Avalueof0meansthatidleconnectionsareneverremovedfromthepool.Theminimumallowedvalueis10000ms(10seconds).Default:600000(10minutes)

    连接空闲时间超过idleTimeout(默认10分钟)后,连接会被抛弃

    ?maxLifetime

    Thispropertycontrolsthemaximumlifetimeofaconnectioninthepool.Anin-useconnectionwillneverberetired,onlywhenitisclosedwillitthenberemoved.Onaconnection-by-connectionbasis,minornegativeattenuationisappliedtoavoidmass-extinctioninthepool.Westronglyrecommendsettingthisvalue,anditshouldbeseveralsecondsshorterthananydatabaseorinfrastructureimposedconnectiontimelimit.Avalueof0indicatesnomaximumlifetime(infinitelifetime),subjectofcoursetotheidleTimeoutsetting.Default:1800000(30minutes)

    连接生存时间超过maxLifetime(默认30分钟)后,连接会被抛弃.

    我们再回头看看项目的hikari配置

  • 配置了minimumIdle=10,maximumPoolSize=50,没有配置idleTimeout和maxLifetime。所以这两项会使用默认值idleTimeout=10分钟,maxLifetime=30分钟。
  • 也就是说假如数据库连接池已满,有50个连接,假如系统空闲,40个连接会在10分钟后(超过idleTimeout)被废弃;假如系统一直繁忙,50个连接会在30分钟后(超过maxLifetime)后被废弃。
  • 猜测问题产生的根源:

    每次新建一个数据库连接,都会把该连接放入connectionPhantomRefs集合中。数据连接在空闲时间超过idleTimeout或生存时间超过maxLifetime后会被废弃,在connectionPhantomRefs集合中等待回收。因为连接资源一般存活时间比较久,经过多次YoungGC,一般都能存活到老年代。如果这个数据库连接对象本身在老年代,connectionPhantomRefs中的元素就会一直堆积,直到下次fullgc。如果等到fullgc的时候connectionPhantomRefs集合的元素非常多,该次fullgc就会非常耗时。

    那么怎么解决呢?可以考虑优化minimumIdle、maximumPoolSize、idleTimeout、maxLifetime这些参数,下一小节我们分析一波

    三、问题验证线上模拟环境

    为了验证问题,我们需要模拟线上环境,调整maxLifetime等参数~压测思路如下

  • 1.缓存系统模拟线上的配置,使用压测系统一段时间内持续压缓存系统,使缓存系统短时间创建/废弃大量数据库连接,观察NonRegisteringDriver对象是否如期大量堆积,再手动调用System.gc()观察NonRegisteringDriver对象是否被清理。
  • 2.调整maxLifetime参数,观察相同的压测时间内NonRegisteringDriver对象是否还发生堆积。
  • 这里有以下注意点:

  • 1、要满足(gc间隔时间*新生代进入老年代前的存活次数<maxLifetime)这个条件,NonRegisteringDriver对象才满足进入老年代的条件。
  • 2、minimumIdle=10,maximumPoolSize=50(minimumIdle和maximumPoolSize和线上配置一致),idleTimeout设置10s,maxLifetime设100s(gc时间约20s,所以要大于20*3=60s)。这样预计在持续压测下每30s就会产生10个新连接(就算设置了maximumPoolSize=50,这种程序的压测10个连接足以应付)
  • 3、项目内存分配小一点,以及把新生代进入老年代前的存活次数调小一点,方便新生代的NonRegisteringDriver对象在较短时间能进入老年代,方便在较短时间观察到明显的对象增长。
  • 4、要监测缓存系统数据连接池的连接存活情况,以及系统gc情况。
  • 最终环境配置如下:

    模拟实验结果
  • 启用jvisualvm工具对缓存系统进行实时观察
  • 打开hikari相关debug日志观察连接池情况
  • 设置maxLifetime=100s,启动缓存系统

    确认hikari和jvm配置生效

    观察jvisualvm,发现产生20个NonRegisteringDriver对象

    观察hikari日志,确认有20个连接对象生成,以及产生总连接10个,空闲连接10个。

    初步判断一个数据库连接会生成两个NonRegisteringDriver对象。

    启动压测程序,压测1000s

    期间观察gc日志,gc时间间隔约20s,100s后发生5次gc

    观察hikari日志,确认有20个连接对象生成

    观察jvisualvm变成40个NonRegisteringDriver对象,符合预期。

    持续观察,1000s后理论上会产生220个对象(20+20*1000s/100s),查看jvisualvm如下

    产生了240个对象,基本和预期符合。

    实验结果分析

    再结合我们生产的问题,假设我们每天14个小时高峰期(12:00~凌晨2:00),期间连接数20,10个小时低峰期,期间连接数10,每次fullgc间隔14天,等到下次fullgc堆积的NonRegisteringDriver对象为(20*14+10*10)*2*14=10640,与问题dump里面NonRegisteringDriver对象的数量10140个基本吻合。

    至此问题根源已经得到完全确认!!!

    四、问题解决方案

    由上面分析可知,问题产生的废弃的数据库连接对象堆积,最终导致fullgc时间过长。所以我们可以从以下方面思考解决方案:

  • 1、减少废弃的数据连接对象的产生和堆积。
  • 2、优化fullgc时间.
  • 【调整hikari参数】

    我们可以考虑设置maxLifetime为一个较大的值,用于延长连接的生命周期,减少产生被废弃的数据库连接的频率,等到下次fullgc的时候需要清理的数据库连接对象会大大减少。

    Hikari推荐maxLifetime设置为比数据库的wait_timeout时间少30s到1min。如果你使用的是mysql数据库,可以使用showglobalvariableslike'%timeout%';查看wait_timeout,默认为8小时。

    下面开始验证,设置maxLifetime=1小时,其他条件不变。压测启动前观察jvisualvm,NonRegisteringDriver对象数量为20

    1000s,观察NonRegisteringDriver对象仍然为20

    NonRegisteringDriver对象没有发生堆积,问题得到解决。

    同时另外注意:minimumIdle和maximumPoolSize不要设置得太大,一般来说配置minimumIdle=10,maximumPoolSize=10~20即可。

    【使用G1回收器】

    G1回收器是目前java垃圾回收器的最新成果,是一款低延迟高吞吐的优秀回收器,用户可以自定义最大暂停时间目标,G1会尽可能在达到高吞吐量同时满足垃圾收集暂停时间目标。

    下面开始验证G1回收器的实用性,该验证过程需要一段较长时间的观察,同时借助链路追踪工具skywalking。最终观察了10天,结果图如下:使用G1回收器,部分jvm参数-Xms3G-Xmx3G-XX:+UseG1GC

    使用java8默认的ParallelGC回收器组合,部分jvm参数-Xms3G-Xmx3G

    以上图中四个内容,从左到右分别为

  • 1、堆内存,分为已使用和空闲内存。
  • 2、方法区内存,这个不需要关注
  • 3、younggc和fullgc时间
  • 4、程序启动以后younggc和fullgc次数
  • 我们可以看到使用ParallelGC回收器组合的服务消耗的内存速度较快,发生了6996次younggc且发生了一次fullgc,fullgc时间长达5s。另外一组使用G1回收器的服务消耗内存速度较为平稳,只发生3827次younggc且没有发生fullgc。由此可以看到G1回收器确实可以用来解决我们的数据库连接对象堆积问题。

    【建立巡查系统】

    这个我们目前还没有经过实践,但是根据上面分析结果判断,定期触发fullgc可以达到每次清理少量堆积的数据库连接的作用,避免过多数据库连接一直堆积。采用该方法需要对业务的内容和高低峰周期非常熟悉。实现思路参考如下:

  • 1、创建java程序,使用定时任务定期调用System.gc()。该方法的缺点是即使手动调用了System.gc(),jvm不一定会立刻开始回收工作,有可能会根据它本身的算法,自行选择最优时间才开始进行回收工作。
  • 2、创建shell脚本调用jmap-dump:live,file=dump_001.binPID,使用linux的crontab任务保证定时执行,执行完后再把dump_001.bin删掉即可。该方法能保证一定发生fullgc,缺点是功能过于单一零散,不好集中管理。
  • 五、总结

    我们这次问题产生的根源是数据库连接对象堆积,导致fullgc时间过长。解决思路可以从以下三点入手:

  • 1、调整hikari配置参数。例如把maxLifetime设置为较大的值(比数据库的wait_timeout少30s),minimumIdle和maximumPoolSize值不能设置太大,或者直接采用默认值即可。
  • 2、采用G1垃圾回收器。
  • 3、建立巡查系统,在业务低峰期主动触发fullgc。
  • 【WINDRISES MINIPROGRAM PROMOTION】尊享直接对接老板

    电话+V: 159999-78052

    专注于小程序推广配套流程服务方案。为企业及个人客户提供了高性价比的运营方案,解决小微企业和个体拓展客户的问题

    sql数据库内存泄漏怎么解决呢
    发布人:qq4767244 发布时间:2024-11-13