木子雷

木子雷 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织 leishen66.gitee.io/ 编辑
编辑

Web后端码仔,记录生活,分享技术!

公众号 搜索:木子雷
CSDN:https://blog.csdn.net/feichit...
掘 金: https://juejin.im/user/5c67b8...
GitHub:https://github.com/leishen6
个人网站:https://leishen66.gitee.io/

个人动态

木子雷 发布了文章 · 10月18日

数据库连接池连接耗尽,导致tomcat请求无响应,呈现出假死状态

最困难的事情就是认识自己!

个人网站 ,欢迎访问!

前言:

最近,测试部门的同事找到我,说他们测试时,没一会就发现服务接口请求一直无响应,Tomcat跟死掉了一样,也没有返回任何的错误响应,说让我赶紧排查下;听完后,我瞬间激灵了下,妹的,最近老是出问题,领导都要给我开批评大会了。哈哈,开玩笑的,像我这么英俊的人,领导怎么会忍心批评我呢,哼,我把这个问题马上解决掉,都不会让领导知道的!

简单说下程序部署情况:tomcat + oracle

排查过程:

排查时,可以使用命令进行排查,也可以使用可视化监控工具;例如使用使用JDK自带的 jvisualvm.exe 监控工具。

命令排查过程:

1、请求服务无响应,首先看看tomcat是否是真的挂掉了:

命令: ps -ef | grep tomcat

通过上面的命令查看tomcat运行着;执行结果如下:

注意: 如果此服务器中运行着多个tomcat,需要查看下图中画框的地方运行的tomcat地址是否正确;

通过命令查看发现,tomcat正常运行着,那么这就是处于假死状态,下面接着排查。

2、查看http请求是否到达了tomcat:

通过查看 tomcat 的 logs 目录下的 localhost_access_log 日志文件中 请求记录;

命令: tail -100f localhost_access_log

通过上面的命令查看实时的日志,执行完上面的查看日志的命令后,然后再次请求下程序,在日志中并没有发现请求记录,说明tomcat处于一种假死状态,下面接着排查。

3、查看tomcat的JVM的GC情况:

查看GC情况,是否由于频繁的GC,长时间的GC,导致程序出现长时间的卡顿,最终导致请求来不及处理,进入队列中进行等待,调用方长时间得不到响应,造成tomcat假死状态;

命令:jstat -gc pid time count

例如: jstat -gc 71129 1000 5 监控 71129 这个进程JVM的GC情况,每隔1000ms 输出一次,共输出5次;

命令执行结果参数解析:

通过上面命令查看GC情况,发现垃圾回收也不频繁,并且进行GC的时间也不长,应该不是GC的原因。

4、查看tomcat的JVM的堆情况:

查看堆内存的情况,是否存在堆内存溢出 导致tomcat假死,无法为新请求分配堆内存资源;

命令 : jmap -heap pid

例子: jmap -heap 71129 71129是正在运行tomcat的进程号

通过命令执行结果得知,堆内存中可使用内存还很大,不会出现内存溢出的问题,所以也不是堆内存过小导致的tomcat假死。

5、查看tomcat的 JVM线程情况:

①、使用 jstack 命令导出当前JVM的线程dump快照,然后看看dump中线程都在干什么?

命令:jstack pid >> jvmThreadDump.log

例子:jstack 71129 >> jvmThreadDump.log

生成 71129 进程的 JVM的线程快照,并将快照内容重定向到 jvmThreadDump.log 文件中;

注意:生成的 jvmThreadDump.log 在你当前执行命令的目录下。

②、接着使用命令 more 查看 jvmThreadDump.log 内容;

命令 : more jvmThreadDump.log

如果的dump文件太大的话,需要使用more 命令一点点看;执行完more 命令的话,再按 enter 回车键 一点点展示文件内容;

③、通过查看线程快照文件,发现很多线程的状态是 WAITING 等待状态;

并且使用命令查看线程状态为 WAITING 的线程占总线程的比例:

注意:tomcatDump.log 为生成的线程快照文件名称,记得改为自己设置的名称

count=`cat tomcatDump.log | grep java.lang.Thread.State | wc -l`; wait=`cat tomcatDump.log | grep WAITING | wc -l`;  a=`echo | awk "{print $wait/$count*100}"`; echo "$a%"

执行命令,得到结果 : 91.9786% ,发现九成多的线程处于等待状态;

至此,找到了tomcat假死的原因,但是还需进一步确定 什么原因导致的大量线程一直等待?

通过查看调用的服务接口代码得知,此接口业务逻辑中自己没设置任何的锁,所以应该不是自己写的代码的问题,但是此接口中涉及到了很多 JDBC数据库操作,那是不是数据库连接池中的连接不够用了呢?因为数据库连接属于竞争资源,如果连接池中的连接已经耗尽了,那么接下来的进行 JDBC的线程就需要进行wait 等待连接。

6、查看与数据库建立的TCP连接情况:

在上面发现,大量线程处于等待状态,而通过分析得知,可能是由于数据库连接池中的连接耗尽导致的,所以可以通过命令查看下,部署服务代码的服务器与数据库所在服务器建立的TCP连接数是否已经达到了配置的数据库连接池的最大连接数;

命令:netstat -pan | grep 1521 | wc -l

因为本文中使用的数据库是Oracle,所以 grep 搜索匹配的端口号是 1521;

如果是mysql数据库则将端口号改为3306 即可, netstat -pan | grep 3306 | wc -l ;

如果设置了自定义的数据库端口号,则改为自定义的端口号即可;

通过命令查询到 已经使用的数据库的连接数为 6 个,那接着看下设置的数据库连接池最大连接数;

数据源配置如下:

<Resource name="jdbc/testdemo"
      type="javax.sql.DataSource"
      factory="com.alibaba.druid.pool.DruidDataSourceFactory"
      url="jdbc:oracle:thin:@192.168.3.125:1521:ora11g"
      driverClassName="oracle.jdbc.driver.OracleDriver"
      username="root"
      password="root"
      auth="Container"
      initialSize="2"
      maxActive="6"
      minIdle="3"
      maxWait="30000"
      timeBetweenEvictionRunsMillis="30000"
      minEvictableIdleTimeMillis="600000"
      maxEvictableIdleTimeMillis="900000"
      poolPreparedStatements="true"
      maxOpenPreparedStatements="20"
      validationQuery="select 1 from dual"
      testOnBorrow="false"
      testOnReturn="false"
      testWhileIdle="true"
      filters="wall,stat,log4j2"
      />

通过查看数据源发现,连接池配置的最大连接数是 maxActive="6" ;发现目前程序中使用的连接数已达到最大值,那么后面再进行 JDBC 操作的线程将进入 等待状态 ,等待获取连接;

至此,tomcat假死的排查过程就结束了,并且原因也找到了,就是数据库连接池中的连接耗尽了;所以,在后面测试中,需要在数据源中将最大连接数设置的大一些,并且也再进一步查看下代码,看看是否存在数据库连接使用完后没有进行关闭的问题。

除了数据库连接池连接耗尽会导致tomcat假死外,还有一些其它的情况也会导致发生,例如: redis 连接池连接耗尽,或者是redis连接使用完不释放,最终导致redis连接耗尽。

除了使用上面的命令进行问题排查外,也可以直接使用可视化监控工具进行排查,更加简便、直观。

可视化监控工具排查

使用 JDK 自带的 jvisualvm.exe 工具进行 JMX远程 可视化监控tomcat;

jvisualvm.exe 位于 $JAVA_HOME/bin 目录下;

1、使用JMX实现远程监控步骤:

下面使用 JMX实现远程监控的内容参考自:jvisualvm远程监控tomcat

①、在 Tomcat 的 bin 目录下的 startup.sh 文件中的 倒数第一行上 加上如下内容:

export CATALINA_OPTS="$CATALINA_OPTS
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=192.168.1.130
-Dcom.sun.management.jmxremote.port=7003
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"

注意:如果直接复制上面的内容,然后放到 startup.sh 文件中的话,可能由于存在很多空格导致tomcat启动失败;请复制下面的内容,然后进行修改:

export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.1.130 -Dcom.sun.management.jmxremote.port=7003 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

上面内容参数解析:

-Dcom.sun.management.jmxremote 启用JMX远程监控
-Djava.rmi.server.hostname=192.168.1.130  这是连接你的tomcat所在的服务器地址
-Dcom.sun.management.jmxremote.port=7003  jmx连接端口
-Dcom.sun.management.jmxremote.ssl=false  是否ssl加密
-Dcom.sun.management.jmxremote.authenticate=false  远程连接需要密码认证

在 startup.sh 文件中添加上上面的内容后,需要将tomcat重启下才会生效;

②、将 jvisualvm.exe 打开,界面如下:

③、在远程上右击,添加主机,输入服务器的ip:就是在 startup.sh 文件中添加内容中的hostname

④、在远程主机上右击,添加 JMX连接 ,手动在ip地址后面加上设置的jmx连接端口7003,然后点击确定即可:

⑤、通过上面的步骤,就已经完成了远程监控连接了,然后自己双击就能进行监控界面了:

2、查看监视内容:

通过查看监视画面得知,CPU、GC、堆Heap的情况都没有问题,那接着查看下线程的情况:

点击上面图片中的 线程Dump 按钮,生成线程的快照,快照文件内容部分如下:

通过查看快照文件内容发现,很多线程的状态的都是 WAITING 等待状态;

接下来的分析排查过程就如上面的 命令排查过程 一样了。

总结:

上面的两种排查方式,本人比较推荐还是使用第一种 命令排查 ,因为很多的情况是不会让你修改配置文件进行远程监控的,即便使用监控工具看起来更加直观、简便;所以,平时需要记一些常用的排查命令,以备不时之需。

由于本人水平有限,如有问题,敬请提出;

❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=
查看原文

赞 0 收藏 0 评论 0

木子雷 发布了文章 · 10月6日

线上服务平均响应时间太长,怎么排查?

最困难的事情就是认识自己!

个人网站 ,欢迎访问!

前言:

最近线上环境某个接口服务响应时间偏长,导致用户体验超差,那平时该怎么快速的排查这类问题呢?

①、为代码添加上详细的打印日志; 不建议 ,一是线上环境,没法随便的重新部署更换了详细日志的代码,二是 添加详细的日志输出,那就意味这会生成大量的日志文件,这些日志文件会占据大量服务器磁盘空间。

②、搭建一个模拟了线上环境的测试环境进行复盘排查;额,出现了这种问题哪有那么多的时间让你进行环境复盘排查,所以此方案也是 不建议的

③、线上诊断神器 Arthas ,这个工具是阿里开源的,专门用于线上环境问题排查的,这个工具提供了很多 的 命令 用来排查问题; 当出现上面的响应时间偏长的问题,就可以使用 Arthas 提供的 trace 命令进行排查,使用这个工具的 trace 命令可以统计到方法中整个调用链路上的所有性能开销和追踪调用链路,查找其中耗时比较长的方法再具体排查即可。

文章接下来将从两方面展开:

①、搭建模拟线上服务接口响应时间偏长的环境;SpringBoot 服务接口 + JMeter 模拟服务接口调用;

②、使用诊断神器 Arthas 提供的命令 trace 命令进行响应时间偏长的问题排查;

模拟线上环境:

1、SpringBoot 项目搭建,并且编写好服务接口;

注意:服务接口代码为了简便,只写了 一些大循环的代码 来模拟较长的耗时;除此之外,实际上还包含很多多其它常见的情况,例如:

①、服务接口方法中存在很多的 JDBC 操作 ,并且由于数据库中数据量太大,导致很多的 JDBC 查询非常耗时,并且此时可能由于还没有创建合适的索引,导致查询耗时更加的长,最终导致服务接口响应时间偏长;

②、此服务接口中调用了 其它的服务接口 ,由于内部调用的其它服务接口出现问题等,导致此其它服务接口执行耗时比较长,进而导致服务接口响应时间偏长;

服务接口代码如下:

test1、test2方法如下:

2、JMeter 模拟用户调用的测试脚本配置:

3、服务接口 SpringBoot 代码 和 JMeter 测试脚本的所在项目位置:

服务接口代码准备好后,使用IDEA开发工具将其导出为 Jar 包 。

服务接口代码和JMeter脚本获取地址: https://github.com/leishen6/s...

为了模拟最为真实的线上环境,需将准备好的 服务接口 Jar 包放到 服务器中,然后使用命令 java -jar *.jar 运行起 Jar 包; 然后使用 JMeter 进行接口的调用,在 聚合报告 中发现平均响应时间偏长;如图:

如果有用户反映某功能响应时间太长了,别着急,根据下面的方法进行排查,绝对方便又快速的找到问题原因。

Arthas 问题排查:

1、首先需要下载阿里开源的Arthas 的诊断工具 Jar 包,下载地址:arthas-boot.jar ;然后将 Jar 包放到 部署服务接口项目的服务器中

2、然后使用 ps 命令,查询出当前运行服务接口的程序进程号;例如:本文章模拟的服务接口程序 Jar 包名称为 springboot_arthas-1.0.0.jar ,所以命令为: ps -ef | grep springboot_arthas-1.0.0

3、然后运行Arthas 诊断工具,命令:java -jar arthas-boot.jar ;开始运行的界面如图:

此时诊断工具还没有运行完,需要手动选择要诊断/监控的java 进程,并且此工具也会列出全部的java进程号,你只需要输入 它们最前的序号 [1] 即可;如图:

4、运行完后,可以使用 trace命令 监控服务接口方法中调用的其它方法的耗时;

trace 命令能主动搜索 class-patternmethod-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。

具体命令格式: trace [全限定类名] [类中的方法名]

例如:监控本服务接口;

com.lyl.controller.TestController : 全限定类名,process:TestController 类中的方法;

具体命令: trace com.lyl.controller.TestController process

5、trace 命令执行结果展示,如图:

通过trace 命令监控统计的调用链路各个方法的执行耗时,可以发现调用的 com.lyl.util.StringUtil 类中的 test2() 方法执行耗时比较大;所以需要特别去查看这个方法的代码是否存在问题;如果这个代码中还存在许多的方法调用链路,则需要再次使用 trace 命令进行监控调用链路的耗时,找出具体可能存在问题的方法。

Arthas 阿里开源的诊断工具还提供了很多的命令供使用,大家可以去查看学习,地址:命令列表

注意:

①、使用Arthas 诊断的程序代码,在打包时 不能混淆 ,否则在使用trace 命令会报 类或方法找不到

②、在使用trace命令监控统计时,需要JMeter测试脚本正在运行调用服务接口,如果没有调用,则统计不到内部调用链路的耗时情况;

由于本人水平有限,如有问题,敬请提出;

❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=
查看原文

赞 0 收藏 0 评论 0

木子雷 发布了文章 · 9月24日

JMeter压测时跑一会聚合报告就不动了

最困难的事情就是认识自己!
个人网站 ,欢迎访问!

前言:

最近,使用 JMeter 对项目进行压力测试;起初, JMeter 设置的并发线程数只是10个,然后在进行压力测试时,发现跑了一会后 JMeter 聚合报告 中的中数字全部不动了(像卡死了一样);

上面简单描述了本文要说的问题;下面将主要从两方面来进行聊。

①、具体问题描述

②、具体的排查思路

问题详述:

项目部署情况: 提供服务接口的项目 + Tomcat + Oracle

提供服务接口的项目:接口中包含很多的数据库查询、更新、新增操作;

部署项目的应用服务器: Tomcat

连接的关系数据库: Oracle

数据源配置 : 在Tomcat中的 context.xml 中配置数据源,连接池使用的是 Druid;

根据上面的部署情况, 然后使用 JMeter 进行压测,就出现了文中一开头说的问题,如下图:

问题排查:

下面会简单的描述下此问题的排查过程,让大家在面对这种问题时,可以有一些思路。

鉴于本人水平有限,如有问题敬请提出。

1、查看 JMeter 结果树:

首先看下 JMeter 结果树 中是否存在错误的日志,如果存在,然后看看是什么问题,具体进行解决;

但是,本文遇到这个问题时,发现结果树中并没有输出错误日志,说明请求接口时未出现问题;

2、服务端排查:

因为 JMeter 结果树中未展示出错误的内容,说明此时程序是正常运行着;

然后查看服务端日志,发现没有出现任何 异常 日志;

看到这,就发现怪异之处了,服务端也没有报任何错误,那问题到底出在哪里呢?

别着急,还得去看 JMeter 的聚合报告去,通过聚合报告可以发现一些问题,就是聚合报告中 请求接口的平均响应时间太长了,高达 2 秒多,这个不正常呀,但是代码逻辑确保是没有任何问题的;

噢,对了,接口中存在很多数据库操作,是不是数据库出现了问题,导致数据库操作比较耗时呢;通过查看数据库服务发现是正常的;那是不是连接数不够用了呢? 嗯嗯,那来看下具体数据源的配置信息:

通过查看 数据源 的配置信息发现,Druid 连接池没有配置 最大连接数 以及最小连接数等 ;要知道 druid默认的最大连接数是 8 ,然后咱再去使用命令查询下当前数据库的连接数: netstat -pan | grep 1521 | wc -l ,发现连接数是 8,说明当前压测时,连接数已经到达了最大值;由于连接不够用,导致程序中进行 JDBC 数据库操作时需要等待可用连接,所以说非常耗时,响应时间就慢了,进而导致大量到达 Tomcat的请求得不到及时的处理,最终导致 JMeter 像卡死一样。

解决方式:在数据源配置中添加上 最大连接数等 ;完整数据源配置如下:

最大连接数设置为了 maxActive="300" ; 最小连接数设置为了 minIdle="50" 。

<Resource name="jdbc/testdemo"
      type="javax.sql.DataSource"
      factory="com.alibaba.druid.pool.DruidDataSourceFactory"
      url="jdbc:oracle:thin:@192.168.3.125:1521:ora11g"
      driverClassName="oracle.jdbc.driver.OracleDriver"
      username="root"
      password="root"
      auth="Container"
      initialSize="30"
      maxActive="300"
      minIdle="50"
      maxWait="30000"
      timeBetweenEvictionRunsMillis="30000"
      minEvictableIdleTimeMillis="600000"
      maxEvictableIdleTimeMillis="900000"
      poolPreparedStatements="true"
      maxOpenPreparedStatements="20"
      validationQuery="select 1 from dual"
      testOnBorrow="false"
      testOnReturn="false"
      testWhileIdle="true"
      filters="wall,stat,log4j2"
      />

总结:

在项目进行压测前,一定记得需要结合当前机器的配置,以及制定的压测计划对一些参数进行优化;下面简单的说几方面,如果有不同见解,大家可以评论补充;例如:

①、如果 JMeter 设置的并发数比较大的话,需要将 Tomcat 的 连接器参数 进行优化一下,否则可能会出现请求处理超时,没有更多的线程去处理连接请求;

②、还有需要对 Tomcat 进行 JVM 参数调试,根据当前机器的硬件配置进行合理的 JVM 参数设置;

③、最后,特别重要的是 连接池 的配置,例如 :数据库连接池、redis连接池等,进行合理的大小配置;

❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=
查看原文

赞 0 收藏 0 评论 0

木子雷 发布了文章 · 8月29日

索引该怎么创建?

最困难的事情就是认识自己!
个人网站 ,欢迎访问!

前言:

想要彻底明白索引该怎么创建,以及怎样创建出最合理的索引,首先需要对一些知识要有所了解;

本文将从以下几方面来进行阐述:

  • 索引的相关知识(包括索引数据结构等);
  • 索引创建的准则/依据;
  • 学会查看sql执行计划,以及哪些sql执行时会导致索引失效;

索引基本知识:

1、索引的数据结构:

索引的数据机构是 B+Tree,B+Tree 是一个多路平衡查找树。
1.1、至于为什么索引使用此数据结构呢?

最主要一个原因就是:使用B+Tree这种数据结构的索引,在进行sql查询时只需要较少的几次磁盘IO 即可,所以会大大提升查询效率;具体原因可参考:【深入学习MySQL】MySQL的索引结构为什么使用B+树?

1.2、索引 B+Tree 结构的特性:

①、B+Tree 只有叶子节点会存储真实的数据,非叶子节点只会存储索引字段值;

②、B+Tree的叶子节点之间使用 双向链表 链接,所以更加适合范围查询和排序;

2、索引的类型:

在平时创建的索引中,可以将索引大体分为两类:

①、聚簇索引(主键索引) ②、非聚簇索引(二级索引)

二级索引根据索引中的字段个数可以分为:

①、单字段索引 ②、联合索引 / 复合索引(多个字段组成的索引)

3、不同类型索引在磁盘中的B+Tree的存储结构:

3.1、聚簇索引:(主键索引)
聚簇索引:当表中创建了主键,默认就会生成主键索引;

聚簇索引的B+Tree索引结构中,非叶子节点中存储的是 ID主键值 ,存储在叶子节点中的真实数据是具体的 行记录 ; 结构图如下:

3.2、单字段索引:(二级索引)
单字段索引:手动创建的索引,由 一个字段 组成的索引;

单字段索引的B+Tree索引结构中,非叶子节点中存储的是 索引字段值,存储在叶子节点中的数据部分是 主键值 。结构图如下:

3.3、联合索引:(二级索引)
联合索引:手动创建的索引,由 多个字段 组成的索引;

联合索引的B+Tree索引结构中,非叶子节点中存储的是 多个索引字段值,存储在叶子节点中的数据部分是 主键值 。结构图如下:

4、回表:

什么是回表?

回表 是发生在 二级索引 中的;在使用二级索引查询数据时,如果 select 投影列 中拥有 索引字段和主键 之外的字段时,此时就需要 回表;使用在二级索引树中查询到的 主键值 去主键索引树中查询具体的行记录,然后在具体行记录中取得最终的 select 投影列 数据。

4.1、举例说明:

数据表:t_user

字 段:id(主键)、name、age、address、sex

索 引:index( name, age ) 测试的这个索引是 联合索引,单字段索引也是一样的原理

Sql语句:select name, age, address, sex from t_user where name=’黎明’ and age=18

上面的sql语句执行时,就会发生回表;具体执行步骤如下:

①、执行时,首先会使用到 联合索引 index( name, age ),在此索引树中查询时,最终在叶子节点中查询到数据,发现此时最终只能得到 name、age、id 三个字段的数据,发现 address、sex 这两个字段的数据没有得到,所以只能再根据查询到主键值去主键索引树中查询;

②、拿着 id主键值 去主键索引树中查询具体的行记录,然后在行记录中取出select投影列需要的字段的数据,最后返回。

4.2、扩展:
根据上面举的例子可以知道什么时候会发生回表,但是发生回表就代表着 查询效率 会比较低下的,因为需要走两边索引树(二级索引树 + 主键索引树);所以一般情况下需要避免回表的发生;怎么避免发生呢?这又涉及到了 覆盖索引 这个知识点。下面就来介绍下 覆盖索引的知识

5、覆盖索引:

覆盖索引:大白话就是 将select 的投影列字段全部放入到 索引中;
5.1、举例说明:

数据表:t_user

字 段:id(主键)、name、age、address、sex

索 引:index( name, age )

Sql语句:select name, age, address, sex from t_user where name=’黎明’ and age=18

这个sql执行时,会发生回表;

发生回表的原因通过上面的解释应该也清楚了些,所以如果 将索引 index( name, age ) 改为 index( name, age, address, sex ) 后,在执行上面的sql 就 不会发生回表了,并且提高了查询效率, 这就是 覆盖索引。

但是,索引的创建及维护也是需要耗费代价的,并且这种代价也是随着索引中索引字段的个数增加而增加的,所以覆盖索引需要根据实际情况使用。

6、最左前缀原则:

说到索引的最左前缀原则,就必须说下 索引长度 这个概念了;

索引长度指的是 索引字段列的 前缀长度索引;

例如:name字段的长度设置为100,但是在以name设置索引时设置的索引长度为20;意思就是创建的索引中存储的索引key键值就是name字段值的前20长度的内容。

6.1、索引的最左前缀原则的两种情况:

①、索引字段的最左前缀原则;

②、联合索引的最左前缀原则;

6.2、索引字段的最左前缀原则:

根据 索引长度 应该就可以明白了 索引字段的最左前缀原则了;但还是举例说明下:

经常说的导致索引失效的情况之一: 模糊查询时 将 % 放在了索引字段的前面;导致失效的最终原因就是 不满足索引的最左前缀原则;

数据表:t_user

字 段:id(主键)、name、age、address、sex

索 引:index( name )

Sql语句:select name, age from t_user where name like ’ %黎明 ’

上面这个 sql 语句执行时,并不会走 index(name) 索引查询,因为什么呢?不满足索引的最左前缀原则。

6.2、联合索引的最左前缀原则:
根据联合索引的B+Tree 存储结构可以知道其最左前缀原则是什么。

不太清楚也别着急,下面将会举例说明:

数据表:t_user

字 段:id(主键)、name、age、address、sex

索 引:index( name, age )

Sql语句:select id from t_user where name=’黎明’ and age=26

测试数据 如图:

根据上面的测试数据及联合索引可以得到 B+Tree存储结构图:

上面sql执行时索引树的搜索步骤:

①、首先是加载索引树的根节点(磁盘块1),然后匹配根节点中的第一个索引字段的值,查询name值是“黎明”的数据,得到指向其子节点地址的指针p1;

②、然后根据指针p1 找到子节点所在的磁盘块,然后将磁盘块加载到内存中,然后接着查询name值是“黎明”的数据,得到指向其子节点(叶子节点)地址的指针p3;

③、然后根据p3指针找到name值是“黎明”的 所在叶子节点的磁盘块,然后将磁盘块中数据加载到内存中,然后再比较 age 为26的数据,最终找到name值是“黎明”并且age为26 的 id主键值,由于select 投影列 只有 id 主键值,所以不需要回表,直接返回结果集了。

注意:在联合索引中,当索引中前一个索引字段值相同时,后面紧挨着的索引字段的值是有序的,所以索引天然适合进行排序,无需自己再进行排序了,提升了查询效率。

根据 上面的索引树搜索过程,知道一开始是以索引中的第一个索引字段进行搜索的,最后在叶子节点中依次进行的索引字段值的匹配,这就是联合索引的最左前缀原则;

所以如果sql语句中的where 条件中没有使用到联合索引的第一个索引字段,则整个索引就失效了。

索引创建的准则:

1、考虑创建索引的表的读写情况:

对于 写操作比较多 的表,创建索引时,应该尽量保证 联合索引 尽可能的 窄(窄: 索引字段个数尽可能少), 如果索引字段比较多的话,写数据时的索引维护比较麻烦。

所以说,能创建单字段索引就不创建联合索引。

2、避免冗余索引:

合理创建索引,避免冗余索引;

联合索引index(a, b, c)相当于 index(a)、index(a,b)、index(a,b,c)这三个索引,所以如果有 index(a, b, c)索引了,那么 index(a)、index(a,b)这两个索引就不用存在了,属于重复索引了。

3、对多表关联查询使用的 连接键 字段创建索引:

针对 多表关联 查询时,需要将 连接键 字段创建索引 ; 例如:user 、role 两张表;

(一)、 select * from user a, role b where a.name = b.name【普通连接查询】

需要在 role 表中创建 name 连接键的索引,user 不必创建 name 字段索引;

(二)、select * from user a left join role b on a.name = b.name【左外连接查询】

需要在 role 表中创建 name 连接键的索引,user 不必创建 name 字段索引;

4、明确字段的区分度:

提前估算出表中每个字段存储的值的区分度,尽量在 区分度高的字段创建索引 ,区分度低的字段创建索引用处不大,并且还会多出索引维护的消耗。

区分度:字段值的不同的比例;例如性别字段只会有两种值,男或女,属于区分度低的字段。

5、保证索引的唯一性:

创建索引时,尽可能保证索引的唯一性,唯一性指的什么呢?

在查看SQL执行计划时,执行计划中 possible_keys 中展示的就是优化器采样估算后可能会使用到的索引,只有这里展示一个索引,并且与实际使用的索引一致时,这才是唯一性;如果此处展示了多个索引名称的话,说明没有保证索引的唯一性。

为什么要保证唯一性呢?

因为如果在执行一个SQL时,优化器是通过采样分析判断发现有两个索引都可以时,那么就会面临抉择,到底最终使用哪个索引呢;如果只有一个索引符合的话,优化器直接选择即可。所以如果可以使用的索引存在多个的话,那么优化器还需要通过 额外的运算 得到最终使用的索引的,那么这就会降低查询效率的。

6、创建联合索引时,索引字段怎么排序:

在联合索引中,怎么对索引字段进行排序? 大白话就是 在联合索引中,哪些索引字段 放在最前面?

①、where 条件中长使用的字段 放到前面。

②、区分度高的字段放在前面,因为这样可以减少扫描 B+tree 中的叶块。

《高性能MySQL》 一书中提到的一个经验法则:将选择性最高( 区分度高 )的列放到索引最前列。

7、SQL中,有等值查询和范围查询时,联合索引怎么创建?

当然是将 等值查询的字段 放在联合索引的前面, 范围查询 的放到联合索引的后面;

为什么这么设计呢?

因为依据索引的查询规则,在联合索引中,首先都是根据第一个索引字段查询符合要求的数据,然后再从符合要求的数据中筛选第二个索引字段符合的数据;如果第一个索引字段值不符合,则直接结束查询了,不会再去进行范围查询了;但是如果将范围查询字段放在联合索引的第一位的话,那么很大可能会查询到符合范围的数据,然后再从数据中筛选等值查询的字段值,如果等值查询的值不存在,那么前面范围查询所做的工作就白白浪费了;并且降低了查询效率。

8、单表索引数量建议:

建议:单张表中索引的数量不超过5个;单个索引中的索引字段个数不超过5个;

导致索引失效的SQL:

明明已经创建好了索引,但是SQL执行时通过执行计划发现没有走索引查询,为什么?

1、查看SQL的执行计划:

创建好索引后,一般会查看下SQL的执行计划,看看SQL执行时是否走了索引查询;

Mysql 数据库查看执行计划 参考:使用explain分析SQL执行计划

Oracle 数据库查看执行计划 参考:Oracle通过执行计划查看查询语句是否使用索引

2、导致SQL执行时没走索引的原因:

2.1、优化器采样估算后不走索引:
可能数据库的优化器在进行采样估算时,发现走全表扫描更加合适;例如:表中数据量很小时,全表扫描和索引查询可能一样快,所以此时可能会直接进行全部扫描查询了。
2.2、SQL语句问题导致索引失效:(简单写几个常用到的)
  • 不满足索引的最左前缀原则;例如:like模糊查询时,%放在前面;
  • 使用了反向查询,例如:not in 、!= 、<> 等,导致索引失效;
  • 在索引字段上进行了 函数运算 或者 算数运算;
  • 隐式转换导致索引失效:隐式类型转换、隐式字符集转换;其实底层都是使用函数进行的隐式转换,所以导致的索引失效;
  • 对索引字段做 判 null 操作,会导致索引失效;

❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=
查看原文

赞 1 收藏 1 评论 0

木子雷 发布了文章 · 8月25日

Tomcat优化,你值得拥有!

最困难的事情就是认识自己!
个人网站,欢迎访问!

前言:

Tomcat作为Web应用的服务器,目前绝大多数公司都是用其作为应用服务器的;应用服务器的执行效率会影响系统执行,这里会讲Tomcat怎样进行配置能提高处理性能;除此之外也必然会提到对应的JVM参数的优化的一些经验。

本文为 转载文章 ,原文地址:系统优化怎么做-Tomcat优化

Tomcat的运行模式:

运行模式分3种模式:

  • bio:默认的模式,效率比较低
  • nio:优化时使用的模式
  • apr:对系统配置有一些比较高的要求

确认Tomcat运行模式:

查找配置文件 server.xml , 在tomcat下的路径:conf 目录下;

Executor 为自定义配置 Tomcat 线程池:

<Executor name="tomcatThreadPool" 
namePrefix="catalina-exec-" 
maxThreads="1024" 
minSpareThreads="512" 
prestartminSpareThreads="true" />

关键配置:

maxThreads:
最大线程数,默认是200
minSpareThread:
最小活跃线程数,默认是25
maxQueueSize:
最大的等待队列个数,超过则请求拒绝默认值是Integer.MAX_VALUE ,一般不改变。在某些紧急状态修复问题需要调整
连接器(Connector):
Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。

nio 配置:

配置文件 server.xml
<!-- 运行模式为 nio -->
<Connector port="14081" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
<!-- 连接器中连接处理 使用上面自定义的 线程池中的线程 -->
executor="tomcatThreadPool" 
URIEncoding="UTF-8" 
compression="on"   
useBodyEncodingForURI="true" 
enableLookups="false" 
redirectPort="14443" />

影响性能的配置:

protocol:
org.apache.coyote.http11.Http11Protocol - 阻塞式的Java连接器
org.apache.coyote.http11.Http11NioProtocol - 不阻塞Java连接器
org.apache.coyote.http11.Http11AprProtocol - APR / native 连接器
选择不阻塞Java连接器
enableLookups:
若是你想request.getRemoteHost()的调用履行,以便返回的长途客户端的实际主机名的DNS查询,则设置为true。设置为false时跳过DNS查找,并返回字符串的IP地址(从而提高性能)。 默认场景下,禁用DNS查找
compression:
设置成on,开启压缩

禁用AJP链接器:

使用Nginx+tomcat的架构,用不着AJP协议,所以把AJP连接器禁用
server.xml注释掉以下配置:

  <Connector port="8019" protocol="AJP/1.3" redirectPort="8443" />

优化 JVM:

优化位置:/bin/catalina.sh

修改 JAVA_OPTS 参数,这里需要参照 机器配置 ,对JVM进行参数优化 。

JDK1.7:
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:PermSize=1024m -XX:MaxPermSize=1024m -XX:+DisableExplicitGC"
JDK1.8:
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:+DisableExplicitGC"

注意:1.8 中已经没有 永久代了,所以也就没有 没有PermSize、MaxPermSize ;Java8 中将永久代改为了 元空间 了,JAVA8里对metaspace可以在小范围自动扩展永生代避免溢出。

参数说明:

  • -Djava.awt.headless
没有设备、键盘或鼠标的模式。
  • -Dfile.encoding
设置字符集
  • -server
jvm的server工作模式,对应的有client工作模式。使用“java -version”可以查看当前工作模式
  • -Xms1024m
初始Heap大小,使用的最小内存
  • -Xmx1024m
Java heap最大值,使用的最大内存
经验: 设置Xms大小等于Xmx大小
  • -XX:NewSize=512m
表示新生代初始内存的大小,应该小于 -Xms的值
  • -XX:MaxNewSize=1024M
表示新生代可被分配的内存的最大上限,应该小于 -Xmx的值
  • -XX:PermSize=1024m
设定内存的永久保存区域,内存的永久保存区域,VM 存放Class 和 Meta 信息,JVM在运行期间不会清除该区域; 一般情况下,此参数值使用默认即可,默认大小就够用了

程序加载很多class情况下,超出PermSize情况下:
JDK1.7会抛出java.lang.OutOfMemoryError: PermGen space异常
JDK1.8下会抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常

  • -XX:MaxPermSize=1024m
设定最大内存的永久保存区域
经验: 设置PermSize大小等于MaxPermSize大小
  • -XX:+DisableExplicitGC
自动将System.gc()调用转换成一个空操作,即应用中调用System.gc()会变成一个空操作,避免程序员在代码里进行System.gc()这种危险操作。System.gc() 除非是到了万不得也的情况下使用,都交给JVM吧

其他参数优化:

  • X:SurvivorRatio=2
年轻代中Eden区与Survivor区的大小比值
  • -XX:ReservedCodeCacheSize=256m
保留代码占用的内存容量,无大的影响
  • -Xss1024k
单个线程堆栈大小值,减少这个值可以生成更多线程,操作系统对于一个进程内的线程数是有限制的,经验值在3000-5000左右
  • -XX:+CMSParallelRemarkEnabled
CMS 垃圾回收算法,对响应时间的重要性需求 大于 对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用
  • -XX:+UseCMSCompactAtFullCollection
在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少。
  • -XX:+UseCMSInitiatingOccupancyOnly
在FULL GC的时候, 对年老代的压缩。CMS是不会移动内存的, 因此这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。
  • -XX:CMSInitiatingOccupancyFraction=60
使用cms作为垃圾回收, 使用60%后开始CMS收集
  • -XX:+UseGCOverheadLimit
用来限制使用内存,如果不做控制,可能会报出
java.lang.OutOfMemoryError: GC overhead limit exceeded
  • -XX:+UseConcMarkSweepGC
使用CMS内存收集
  • -XX:+UseParNewGC
设置年轻代为并行收集
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=/x/dump_tomcat.hprof
JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。
  • -Xloggc:/xx/gc_tomcat.log
gc的日志,如果该日志中出现频繁的Full GC就是有相关的系统问题,如果很少,说明暂时还算正常
  • -XX:+PrintGCDateStamps
输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDetails
输出GC的日志格式
  • -Dnetworkaddress.cache.ttl=60
  • -Dsun.net.inetaddr.ttl=60
设置DNS缓存时间
  • -DautoStartup=false
  • -Dsun.net.client.defaultConnectTimeout=60000
连接建立超时时间
  • -Dsun.net.client.defaultReadTimeout=60000
内容获取超时设置
  • -Djmagick.systemclassloader=no
是否生成缩略图的一个框架的配置
  • -Djava.security.egd=file:/dev/./urandom

最佳实践:

export JAVA_OPTS="-server -showversion -Xms2000m -Xmx2000m -Xmn500m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=2 -XX:ReservedCodeCacheSize=256m -Xss1024k -Djava.awt.headless=true -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseGCOverheadLimit -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat_path/logs/dump_tomcat.hprof -Xloggc:/tomcat_path/logs/gc_tomcat.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCDetails -Dnetworkaddress.cache.ttl=60 -Dsun.net.inetaddr.ttl=60 -DautoStartup=false -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"

常见 JVM 异常:

  1. java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出:

    JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。

    解决方法:

    • 首先检查代码,是否存在创建了大量无用对象,且其被引用着,无法被GC回收 的代码;
    • 手动设置 JVM Heap(堆)的大小;
  2. java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出:

    jdk1.8 抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常
    PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的 APP 会载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。

    解决方法: 手动设置 MaxPermSize 大小;

  3. java.lang.StackOverflowError —- 栈溢出:
​ 栈溢出了,JVM 依然是采用栈式的虚拟机。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。

解决方法: 代码中递归也不要递归的层次过多;

❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=
查看原文

赞 0 收藏 0 评论 0

木子雷 赞了文章 · 8月21日

系统优化怎么做-SQL优化

前言

数据库很重要!很重要!很重要! 重要的事情说三遍。所以单独用一篇来讲述SQL怎么优化。这里提前说到一点,不建议在业务代码里写很多复杂业务SQL,基本尽可能的减少 join,子查询 等,也就说尽量在应用层来解决问题,降低产生低效SQL的概率,数据库只是完成数据存储及最简单查询的组件。

SQL优化

主要4个方向,以下4个方向尽可能达到了,SQL的执行效率就提高了。

  1. 避免全表扫描
  2. SQL中尽可能不使用临时表
  3. 减小查询中间结果集大小
  4. 尽可能命中索引

发现慢SQL

DBA开启MySQL的慢查询日志,对每日数据库慢查询进行监控。慢查询后每日汇总提供开发进行处理。DBA给出指导意见。

分析执行计划

主要看对SQL的执行过程中

    explain [extended] select … from … where …

得到结果是

+—-+————-+——-+——-+————-+——-+——-+——————-+———+———+——-+-+——-+——-+—-+———-+—————+———-+———+———+——-+——+——-+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows | Extra |
+—-+————-+——-+——-+————-+——-+——-+——————-+———+———+——-+-+——-+——-+—-+———-+—————+———-+———+———+——-+——+——-+

其中 table 表示是哪个表的数据。

  1. type比较重要。表示链接的类型。链接类型由好到坏的,依次是 system > const > eq_ref > ref >
    fulltext > ref_or_null > index_merge > unique_subquery >
    index_subquery > range > index > ALL 一般情况,至少要达到 range 级别,最好是 ref
    级别。否则可能会有性能问题。
  2. possible_keys 是指可以应用到该表的索引,如果为NULL则没有。
  3. key 是指用到的索引。
  4. key_len 是索引的长度,在不影响查询精度的情况下,值越小越好。
  5. ref 是指索引的那一列被使用了。一般会是个常数。
  6. rows 是指有多少行。
  7. extra 是指额外的信息。也是比较重要的。
如果值为 distinct ,说明mysql 找到了域行联合匹配的行,就不再查找了。
如果值为 not exits : mysql优化了 left join ,一旦找到了 left join 匹配的行,便不再进行搜索了。
如果值为 rang checked for each : 没有找到理想的索引。
如果为 using filesort ,则需要改进sql了。这说明 mysql执行 需要 文件排序。这是比较影响效率的。
如果为 using temporary , 这是使用了 临时表。 这种情况也比较影响效率,sql需要改进。或者从应用层进行改进。
如果为 where used 说明使用了where语句。如果 type为 all 或者 index ,一般会出现这样的结果。这样的问题,一般是查询需要改进。

SQL优化实例

分页查询

第一种分页写法

select * 
  from t
 where thread_id = 771025
   and deleted = 0
 order by gmt_create asc limit 0, 15;

原理:

一次性根据过滤条件取出所有字段进行排序返回。

数据访问开销=索引IO + 索引全部记录结果对应的表数据IO

缺点:

该种写法越翻到后面执行效率越差,时间越长,尤其表数据量很大的时候。适用场景:当中间结果集很小(10000行以下)或者查询条件复杂(指涉及多个不同查询字段或者多表连接)时适用。

第二种分页写法:

select t.* from (
     select id from t
     where 
     thread_id = 771025 
     and deleted = 0 order by gmt_create asc limit 0, 15) a, t 
 where a.id = t.id;

前提:

假设t表主键是id列,且有覆盖索引secondary key:(thread_id, deleted, gmt_create)

原理:

先根据过滤条件利用覆盖索引取出主键id进行排序,再进行join操作取出其他字段。

数据访问开销=索引IO+索引分页后结果对应的表数据IO

优点:

每次翻页消耗的资源和时间都基本相同,就像翻第一页一样

适用场景:

当查询和排序字段(即where子句和order by子句涉及的字段)有对应覆盖索引时,且中间结果集很大的情况时适用

批量SQL

减少和数据库交互次数
 INSERT ... ON DUPLICATE KEY UPDATE
 REPLACE INTO
 INSERT IGNORE
 INSERT INTO VALUES()
  • 对同一个表的多次alter操作必须合并为一次操作。
  • mysql对表的修改绝大部分操作都需要锁表并重建表,而锁表则会对线上业务造成影响。为减少这种影响,必须把对表的多次alter操作合并为一次操作。例如,要给表t增加一个字段b,同时给已有的字段aa建立索引,
    通常的做法分为两步:
alter table t add column b varchar(10);

然后增加索引:

alter table t add index idx_aa(aa);

正确的做法是:

alter table t add column b varchar(10),add index idx_aa(aa);

总结

数据库是有状态的服务,变更复杂而且速度慢,如果把业务逻辑放到数据库中,将会限制业务的快速发展。建议把业务逻辑提前,放到前端或中间逻辑层,而把数据库作为存储层,实现逻辑与存储的分离。

思考题

  1. 万一经过SQL优化后,还是达不到要求,还有什么手段能进行优化呢?
  2. 在一个既有系统前期业务快速迭代,导致系统很多业务已经写了很多低效的SQL,导致系统运行缓慢,领导需要快速解决运行缓慢的问题,有哪些手段可以用呢?
查看原文

赞 7 收藏 4 评论 0

木子雷 赞了文章 · 8月21日

系统优化怎么做-数据库优化

前言

目前大部分公司的数据库都是MySQL,虽然现在NoSQL数据库比如mongo, hbase越来越流行了,但传统的MySQL依然是业界用得最多。本文是以MySQL为例。

数据库

数据库是唯一在应用系统中的单点资源,对于数据库的资源的使用要特别小心。有如下几点注意点

  1. 数据库作为数据存储的地方,不应该把宝贵的资源用于数据的转换或统计操作,SQL中不使用一些字符转换等操作。
  2. 数据库连接资源宝贵,外围系统按需分配使用
  3. 数据库不怕高qps的小查询,但害怕慢查询,因此请消灭慢查询。
  4. 索引不是越多越好,维护索引资源也耗费数据库运算资源,数据库运算能力宝贵程度大于存储
  5. 如果是主从架构,主机器与从机器的网络带宽及稳定性要保证 不在数据库中存储图片、文件等大数据
  6. 禁止在线上做数据库压力测试
  7. 禁止从测试、开发环境直连线上数据库
  8. 不在业务高峰期批量更新、查询数据库
  9. 不在MySQL数据库中存放业务逻辑,写储存过程及触发器等
  10. 禁止在主库上执行后台管理和统计报表类的功能查询,都放到从库

硬件

  • 磁盘

MySQL每秒钟都在进行大量、复杂的查询操作,对磁盘的读写量可想而知。所以,通常认为磁盘I/O是制约MySQL性能的最大因素之一,推荐使用RAID-0+1磁盘阵列。

  • CPU

推荐使用至少4U以上的服务器来专门做数据库服务器,基本上是越多越好

  • 内存

服务器内存建议不要小于4GB。基本上是越大越好

系统配置

MySQL配置在my.conf,影响新能的几个关键配置属性

  1. 使用INNODB存储引擎 5.5以后的默认引擘,支持事务,行级锁,更好的恢复性,高并发下性能更好,对多核,大内存,ssd等硬件支持更好。
  2. 表字符集使用utf8mb4
    使用utf8mb4字符集,如果是汉字,占3个字节,但ASCII码字符还是1个字节;统一,不会有转换产生乱码风险,并能解决符号表情乱码问题;
  3. max_connections 最大连接(用户)数
  4. innodb_log_file_size
    在高写入负载尤其是大数据集的情况下很重要。这个值越大则性能相对越高,但是要注意到可能会增加恢复时间。设置为64-512MB,根据服务器大小而异
  5. Innodb_buffer_pool_pages_data 分配出去, 正在被使用页的数量
  6. Innodb_buffer_pool_pages_total 缓冲区总共的页面数 Innodb_page_size
    编译的InnoDB页大小(默认16KB)

调优参考计算方法:

val = Innodb_buffer_pool_pages_data / Innodb_buffer_pool_pages_total * 100%
  1. val > 95% 则考虑增大 innodb_buffer_pool_size, 建议使用物理内存的75%
  2. val < 95% 则考虑减小 innodb_buffer_pool_size,
  3. 建议设置为:
  Innodb_buffer_pool_pages_data * Innodb_page_size * 1.05 / (1024*1024*1024)

数据库表结构

表结构的设计目标除了满足业务以外,尽量减少代码实现上的联表查询操作,因此在设计上可以适当有一些冗余字段的设计,减少数据库IO次数。

现在很流行的ElasticSearch等大数据存储宽表的概念也是这种思想的体现
  1. 尽量避免使用分区表 MySQL的分区表实际性能不是很好。 拆分大字段和访问频率低的字段,分离冷热数据
  2. 采用合理的分库分表策略,推荐使用HASH进行分表,表名后缀使用十进制数,下标从0开始首次分表尽量多的分,避免二次分表,二次分表的难度和成本较高
  3. 单表字段数控制在20个以内
  4. 一条完整的建表语句中应包含必要的字段、主键、合理的索引(综合代码中所有的条件语句创建合理的索引,主键必须要有

索引设计

索引是一把双刃剑,它可以提高查询效率但也会降低插入和更新的速度并占用磁盘空间。

  1. 单张表中索引数量不超过5个
  2. 单个索引中的字段数不超过5个
  3. 对字符串使用前缀索引,前缀索引长度不超过10个字符;如果有一个CHAR(200)列,如果在前10个字符内,多数值是惟一的,那么就不要对整个列进行索引。对前10个字符进行索引能够节省大量索引空间,也可能会使查询更快
  4. 表必须有主键,不使用UUID、MD5、HASH作为主键,尽量不选择字符串列作为主键;主键建议选择自增id
  5. 创建复合索引时区分度较大的字段放在最前面
  6. 不在低区分度的字段上创建索引,如“性别”
  7. 避免冗余或重复索引
  8. 合理创建联合索引(避免冗余),index(a、b、c) 相当于index(a)、index(a、b)、index(a、、b、c)
  9. 索引不是越多越好,按实际需要进行创建
  10. 每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能
  11. 不在索引列进行数学运算和函数运算;
  12. 尽量不要使用外键 外键用来保护参照完整性,可在业务端实现,对父表和子表的操作会相互影响,降低可用性
  13. 不使用%前导的查询,如like“%xxx”,不使用反向查询,如not in / not like 无法使用索引,导致全表扫描 1. 全表扫描导致buffer pool利用降低

字段设计

  1. 尽可能不要使用TEXT、BLOB类型。删除这种值会在数据表中留下很大的"空洞",可以考虑把BLOB或TEXT列分离到单独的表中
  2. 用DECIMAL代替FLOAT和DOUBLE存储精确浮点数。浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的数据范围;浮点数的缺点是会引起精度问题
  3. 将字符转化为数字
  4. 使用TINYINT来代替ENUM类型
  5. 字段长度尽量按实际需要进行分配,不要随意分配一个很大的容量 VARCHAR(N),N表示的是字符数不是字节数,比如VARCHAR(255),可以最大可存储255个汉字,需要根据实际的宽度来选择N。VARCHAR(N),N尽可能小,因为6. 6.MySQL一个表中所有的VARCHAR字段最大长度是65535个字节,进行排序和创建临时表一类的内存操作时,会使用N的长度申请内存;
  6. 如果可能, 所有字段均定义为not null
  7. 使用UNSIGNED存储非负整数 同样的字节数,存储的数值范围更大。如tinyint有符号为-128-127,无符号为0-255
  8. 使用TIMESTAMP存储时间. 因为TIMESTAMP使用4字节,DATETIME使用8个字节,同时TIMESTAMP具有自动赋值以及自动更新的特性.
  9. 使用INT UNSIGNED存储IPV4
  10. 使用VARBINARY存储大小写敏感的变长字符串
  11. 禁止在数据库中存储明文密码

思考题

  1. 数据库有哪些高可用措施?
  2. 如果数据库挂了,怎么保证核心业务是可用的?
查看原文

赞 7 收藏 5 评论 2

木子雷 关注了用户 · 8月21日

昌松 @changsong

系统优化,系统架构,线上问题处理

关注 14

木子雷 赞了文章 · 8月21日

系统优化怎么做-Tomcat优化

前言

Tomcat作为Web应用的服务器,目前绝大多数公司都是用其作为应用服务器的,应用服务器的执行效率会影响系统执行,这里会讲Tomcat怎样进行配置能提高处理性能。另外必须提到对应的JVM参数的优化的一些经验。

Tomcat运行模式

分3种模式: bio,nio,apr 一般使用nio模式
bio效率低,apr对系统配置有一些比较高的要求

确认Tomcat的运行模式

配置文件 server.xml

<Executor name="tomcatThreadPool" 
namePrefix="catalina-exec-" 
maxThreads="1024" 
minSpareThreads="512" 
prestartminSpareThreads="true" />

关键配置

maxThreads

最大线程数, 默认是200

minSpareThread

最小活跃线程数, 默认是25

maxQueueSize

最大的等待队列个数,超过则请求拒绝默认值是Integer.MAX_VALUE ,一般不改变。在某些紧急状态修复问题需要调整

连接器(Connector)

Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。

nio配置- server.xml

<Connector port="14081" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
executor="tomcatThreadPool" 
URIEncoding="UTF-8" 
compression="on"   
useBodyEncodingForURI="true" 
enableLookups="false" 
redirectPort="14443" />

影响性能配置

protocol

org.apache.coyote.http11.Http11Protocol - 阻塞式的Java连接器
org.apache.coyote.http11.Http11NioProtocol - 不阻塞Java连接器
org.apache.coyote.http11.Http11AprProtocol - APR / native 连接器
选择不阻塞Java连接器

enableLookups

若是你想request.getRemoteHost()的调用履行,以便返回的长途客户端的实际主机名的DNS查询,则设置为true。设置为false时跳过DNS查找,并返回字符串的IP地址(从而提高性能)。默认场景下,禁用DNS查找

compression

设置成on,开启压缩

禁用AJP链接器

使用Nginx+tomcat的架构,用不着AJP协议,所以把AJP连接器禁用
server.xml注释掉以下配置

  <Connector port="8019" protocol="AJP/1.3" redirectPort="8443" />

优化JVM

/bin/catalina.sh
修改JAVA_OPTS参数,这里需要参照机器配置,对JVM进行参数优化

JDK1.7

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:PermSize=1024m -XX:MaxPermSize=1024m -XX:+DisableExplicitGC"

JDK1.8

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:+DisableExplicitGC"

1.8 版本中已经没有PermSize、MaxPermSize

JAVA8里对metaspace可以在小范围自动扩展永生代避免溢出。

参数说明

  • -Djava.awt.headless
没有设备、键盘或鼠标的模式。
  • -Dfile.encoding
设置字符集
  • -server
jvm的server工作模式,对应的有client工作模式。使用“java -version”可以查看当前工作模式
  • -Xms1024m
初始Heap大小,使用的最小内存
  • -Xmx1024m
Java heap最大值,使用的最大内存
经验: 设置Xms大小等于Xmx大小
  • -XX:NewSize=512m
表示新生代初始内存的大小,应该小于 -Xms的值
  • -XX:MaxNewSize=1024M
表示新生代可被分配的内存的最大上限,应该小于 -Xmx的值
  • -XX:PermSize=1024m
设定内存的永久保存区域,内存的永久保存区域,VM 存放Class 和 Meta 信息,JVM在运行期间不会清除该区域
程序加载很多class情况下,超出PermSize情况下
JDK1.7会抛出java.lang.OutOfMemoryError: PermGen space异常
JDK1.8下会抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常
  • -XX:MaxPermSize=1024m
设定最大内存的永久保存区域
经验: 设置PermSize大小等于MaxPermSize大小
  • -XX:+DisableExplicitGC
自动将System.gc()调用转换成一个空操作,即应用中调用System.gc()会变成一个空操作,避免程序员在代码里进行System.gc()这种危险操作。System.gc() 除非是到了万不得也的情况下使用,都交给JVM吧

其他优化参数

  • XX:SurvivorRatio=2
年轻代中Eden区与Survivor区的大小比值
  • -XX:ReservedCodeCacheSize=256m
保留代码占用的内存容量,无大的影响
  • -Xss1024k
单个线程堆栈大小值,减少这个值可以生成更多线程,操作系统对于一个进程内的线程数是有限制的,经验值在3000-5000左右
  • -XX:+CMSParallelRemarkEnabled
CMS 垃圾回收算法,对响应时间的重要性需求 大于 对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用
  • -XX:+UseCMSCompactAtFullCollection
在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少。
  • -XX:+UseCMSInitiatingOccupancyOnly
在FULL GC的时候, 对年老代的压缩。CMS是不会移动内存的, 因此这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。
  • -XX:CMSInitiatingOccupancyFraction=60
使用cms作为垃圾回收, 使用60%后开始CMS收集
  • -XX:+UseGCOverheadLimit
用来限制使用内存,如果不做控制,可能会报出
java.lang.OutOfMemoryError: GC overhead limit exceeded
  • -XX:+UseConcMarkSweepGC
使用CMS内存收集
  • -XX:+UseParNewGC
设置年轻代为并行收集
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=/x/dump_tomcat.hprof
JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。
  • -Xloggc:/xx/gc_tomcat.log
gc的日志,如果该日志中出现频繁的Full GC就是有相关的系统问题,如果很少,说明暂时还算正常
  • -XX:+PrintGCDateStamps
输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDetails
输出GC的日志格式
  • -Dnetworkaddress.cache.ttl=60
  • -Dsun.net.inetaddr.ttl=60
设置DNS缓存时间
  • -DautoStartup=false
  • -Dsun.net.client.defaultConnectTimeout=60000
连接建立超时时间
  • -Dsun.net.client.defaultReadTimeout=60000
内容获取超时设置
  • -Djmagick.systemclassloader=no
是否生成缩略图的一个框架的配置
  • -Djava.security.egd=file:/dev/./urandom

最佳实践

export JAVA_OPTS="-server -showversion -Xms2000m -Xmx2000m -Xmn500m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=2 -XX:ReservedCodeCacheSize=256m -Xss1024k -Djava.awt.headless=true -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseGCOverheadLimit -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat_path/logs/dump_tomcat.hprof -Xloggc:/tomcat_path/logs/gc_tomcat.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCDetails -Dnetworkaddress.cache.ttl=60 -Dsun.net.inetaddr.ttl=60 -DautoStartup=false -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"

常见JVM异常

  • java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出

JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。

解决方法:手动设置 JVM Heap(堆)的大小。

  • java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出。

jdk1.8 抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常
PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的 APP 会载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。

解决方法: 手动设置 MaxPermSize 大小

  • java.lang.StackOverflowError —- 栈溢出

栈溢出了,JVM 依然是采用栈式的虚拟机。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。

解决方法: 代码中递归也不要递归的层次过多

思考题

线上应用系统出现问题,怎么快速定位系统哪块资源问题?

查看原文

赞 99 收藏 80 评论 2

木子雷 赞了文章 · 8月21日

系统优化怎么做-Tomcat优化

前言

Tomcat作为Web应用的服务器,目前绝大多数公司都是用其作为应用服务器的,应用服务器的执行效率会影响系统执行,这里会讲Tomcat怎样进行配置能提高处理性能。另外必须提到对应的JVM参数的优化的一些经验。

Tomcat运行模式

分3种模式: bio,nio,apr 一般使用nio模式
bio效率低,apr对系统配置有一些比较高的要求

确认Tomcat的运行模式

配置文件 server.xml

<Executor name="tomcatThreadPool" 
namePrefix="catalina-exec-" 
maxThreads="1024" 
minSpareThreads="512" 
prestartminSpareThreads="true" />

关键配置

maxThreads

最大线程数, 默认是200

minSpareThread

最小活跃线程数, 默认是25

maxQueueSize

最大的等待队列个数,超过则请求拒绝默认值是Integer.MAX_VALUE ,一般不改变。在某些紧急状态修复问题需要调整

连接器(Connector)

Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。

nio配置- server.xml

<Connector port="14081" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
executor="tomcatThreadPool" 
URIEncoding="UTF-8" 
compression="on"   
useBodyEncodingForURI="true" 
enableLookups="false" 
redirectPort="14443" />

影响性能配置

protocol

org.apache.coyote.http11.Http11Protocol - 阻塞式的Java连接器
org.apache.coyote.http11.Http11NioProtocol - 不阻塞Java连接器
org.apache.coyote.http11.Http11AprProtocol - APR / native 连接器
选择不阻塞Java连接器

enableLookups

若是你想request.getRemoteHost()的调用履行,以便返回的长途客户端的实际主机名的DNS查询,则设置为true。设置为false时跳过DNS查找,并返回字符串的IP地址(从而提高性能)。默认场景下,禁用DNS查找

compression

设置成on,开启压缩

禁用AJP链接器

使用Nginx+tomcat的架构,用不着AJP协议,所以把AJP连接器禁用
server.xml注释掉以下配置

  <Connector port="8019" protocol="AJP/1.3" redirectPort="8443" />

优化JVM

/bin/catalina.sh
修改JAVA_OPTS参数,这里需要参照机器配置,对JVM进行参数优化

JDK1.7

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:PermSize=1024m -XX:MaxPermSize=1024m -XX:+DisableExplicitGC"

JDK1.8

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:+DisableExplicitGC"

1.8 版本中已经没有PermSize、MaxPermSize

JAVA8里对metaspace可以在小范围自动扩展永生代避免溢出。

参数说明

  • -Djava.awt.headless
没有设备、键盘或鼠标的模式。
  • -Dfile.encoding
设置字符集
  • -server
jvm的server工作模式,对应的有client工作模式。使用“java -version”可以查看当前工作模式
  • -Xms1024m
初始Heap大小,使用的最小内存
  • -Xmx1024m
Java heap最大值,使用的最大内存
经验: 设置Xms大小等于Xmx大小
  • -XX:NewSize=512m
表示新生代初始内存的大小,应该小于 -Xms的值
  • -XX:MaxNewSize=1024M
表示新生代可被分配的内存的最大上限,应该小于 -Xmx的值
  • -XX:PermSize=1024m
设定内存的永久保存区域,内存的永久保存区域,VM 存放Class 和 Meta 信息,JVM在运行期间不会清除该区域
程序加载很多class情况下,超出PermSize情况下
JDK1.7会抛出java.lang.OutOfMemoryError: PermGen space异常
JDK1.8下会抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常
  • -XX:MaxPermSize=1024m
设定最大内存的永久保存区域
经验: 设置PermSize大小等于MaxPermSize大小
  • -XX:+DisableExplicitGC
自动将System.gc()调用转换成一个空操作,即应用中调用System.gc()会变成一个空操作,避免程序员在代码里进行System.gc()这种危险操作。System.gc() 除非是到了万不得也的情况下使用,都交给JVM吧

其他优化参数

  • XX:SurvivorRatio=2
年轻代中Eden区与Survivor区的大小比值
  • -XX:ReservedCodeCacheSize=256m
保留代码占用的内存容量,无大的影响
  • -Xss1024k
单个线程堆栈大小值,减少这个值可以生成更多线程,操作系统对于一个进程内的线程数是有限制的,经验值在3000-5000左右
  • -XX:+CMSParallelRemarkEnabled
CMS 垃圾回收算法,对响应时间的重要性需求 大于 对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用
  • -XX:+UseCMSCompactAtFullCollection
在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少。
  • -XX:+UseCMSInitiatingOccupancyOnly
在FULL GC的时候, 对年老代的压缩。CMS是不会移动内存的, 因此这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。
  • -XX:CMSInitiatingOccupancyFraction=60
使用cms作为垃圾回收, 使用60%后开始CMS收集
  • -XX:+UseGCOverheadLimit
用来限制使用内存,如果不做控制,可能会报出
java.lang.OutOfMemoryError: GC overhead limit exceeded
  • -XX:+UseConcMarkSweepGC
使用CMS内存收集
  • -XX:+UseParNewGC
设置年轻代为并行收集
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=/x/dump_tomcat.hprof
JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。
  • -Xloggc:/xx/gc_tomcat.log
gc的日志,如果该日志中出现频繁的Full GC就是有相关的系统问题,如果很少,说明暂时还算正常
  • -XX:+PrintGCDateStamps
输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDetails
输出GC的日志格式
  • -Dnetworkaddress.cache.ttl=60
  • -Dsun.net.inetaddr.ttl=60
设置DNS缓存时间
  • -DautoStartup=false
  • -Dsun.net.client.defaultConnectTimeout=60000
连接建立超时时间
  • -Dsun.net.client.defaultReadTimeout=60000
内容获取超时设置
  • -Djmagick.systemclassloader=no
是否生成缩略图的一个框架的配置
  • -Djava.security.egd=file:/dev/./urandom

最佳实践

export JAVA_OPTS="-server -showversion -Xms2000m -Xmx2000m -Xmn500m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=2 -XX:ReservedCodeCacheSize=256m -Xss1024k -Djava.awt.headless=true -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseGCOverheadLimit -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat_path/logs/dump_tomcat.hprof -Xloggc:/tomcat_path/logs/gc_tomcat.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCDetails -Dnetworkaddress.cache.ttl=60 -Dsun.net.inetaddr.ttl=60 -DautoStartup=false -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"

常见JVM异常

  • java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出

JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。

解决方法:手动设置 JVM Heap(堆)的大小。

  • java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出。

jdk1.8 抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常
PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的 APP 会载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。

解决方法: 手动设置 MaxPermSize 大小

  • java.lang.StackOverflowError —- 栈溢出

栈溢出了,JVM 依然是采用栈式的虚拟机。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。

解决方法: 代码中递归也不要递归的层次过多

思考题

线上应用系统出现问题,怎么快速定位系统哪块资源问题?

查看原文

赞 99 收藏 80 评论 2

认证与成就

  • 获得 15 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 3月19日
个人主页被 1.3k 人浏览