Java性能微调之数据库性能

 大部分Java系统性能问题基本上是由于错误的数据库访问方式引起的,带来了大量额外日志和内存消耗,这些都会对JVM的垃圾回收造成冲击影响,本文主要针对这种错误的数据库访问方式进行分析和诊断。

Java性能诊断工具使用Java自带的Java Mission Control或JProfiler等工具,许多框架如Hibernate或Spring访问数据的方式都可以通过日志输出得到诊断。

首先我们需要确认需要提升性能的部位,通常会有以下几个方面:

  • 不够效率的数据库使用: 错误查询设计,;业务逻辑主要集中在SQL语句中,很少使用Java实现的业务逻辑;数据访问框架的不正确配置方式。
  • 坏的数据表结构设计:数据表的关系太多;太慢的存储视图;没有或错误的索引;过时的数据表统计。
  • 不适当的数据库配置: 内存, 磁盘, 表空间, 连接池配置等

为了追查这些热点部位,可以对照下面的数据库问题模式checklist进行逐步排查:

  • 太长的SQL语句: 一次性执行很多(> 500)不同的SQL语句
  • N+1 查询问题: 多次(>20)执行同样的查询语句:
  • 单条SQL语句很慢:: 执行某条SQL语句占据整个过程的80%以上响应时间
  • 数据驱动问题Data-Driven Issue:同样的请求因为不同输入参数执行不同的SQL语句。
  • 数据库负载太重:数据库响应时间占据整个请求响应时间的 60%以上。
  • Unprepared 语句:没有使用prepare执行SQL语句。
  • 资源枯竭:数据库连接时间超过语句执行时间。
  • 没有效率的连接池访问:连接池连接频繁获得,调用 getConnection次数大于执行正常SQL语句的50%以上。
  • 数据库服务器超负载: 来自大量太多的请求导致数据库服务器超载,而运行Java的应用服务器比如Tomcat等的负载很低。整个系统的负载都集中到了数据库服务器上。

下面我们通过案例来对上述checklist进行详细描述。

首先,我们对一个Web应用进行响应时间统计,比如一个典型的Web应用是通过三个环节:Nginx/Apache + Tomcat + Database。我们通过性能测试工具可以大概获得三个部分的分别响应时间是:

Nginx: 3.18ms 0.01%

Tomcat: 20.28s 33.49%

Database:40.27 66.51%

从以上数据可以看出,数据库的响应时间占据整个请求响应时间的66.51%,超过一半时间以上,这说明数据库超负载运行了。

通过跟踪数据库访问方式,也就是SQL语句执行情况,会发现同一个SQL因为不同参数执行很多次,也就是N+1性能问题,比如可能我们的Java代码有一个循环语句:

foreach (catIDs:catID) {
Cat cat= find_cat(catID);
// ...
}

其中find_cat是一个JDBC查询语句:

SELECT * FROM hat WHERE catID =

这样这个循环执行就变成如下SQL语句执行:

SELECT * FROM hat WHERE catID = 1
SELECT * FROM hat WHERE catID = 2
SELECT * FROM hat WHERE catID = 3
SELECT * FROM hat WHERE catID = 4
SELECT * FROM hat WHERE catID = 5
...

这种因为不同的catID参数值不同,但是SQL语句相同的情况执行N多次,不如一次性使用批查询更加快捷:

SELECT * FROM hat WHERE catID IN (1, 2, 3, 4, 5, ...)

这就是典型的数据库N+1性能问题。除了使用SQL批查询,也可以使用缓存减少每个对象从SQL语句构造消耗的时间,或者使用O/R映射框架如Hibernate的懒加载。

这里需要比较一下使用SQL的inner join查询好还是使用缓存机制好呢?使用join查询虽然能够快速获得性能提升,但是可扩展性很差,join涉及的库表必须放在一个数据库服务器中,将来如果访问量负载更大,就无法分库分表了,丧失了扩展性Scalable,NoSQL数据库与关系数据库的主要区别就在于NoSQL消灭了join。

下面再谈谈Perpare语句:Hibernate框架缺省都是使用prepare,但是我们自己的SQL语句有可能没有使用,一条SQL语句是需要被数据库引擎分析的,然后创建数据访问计划,这个计划是存储在数据库的缓存中,这样,下次同样SQL语句,虽然参数不同,但是SQL语句不再需要重新分析,这样能够提高性能。

我们再看看连接池配置大小的误配:通常默认的连接池大小是每个池10或20个连接,在没有尖峰大量访问情况下,一般这个参数不需要配置优化,但是在真正运行时刻可能会造成瓶颈。连接池情况可以通过JMX测量发现,每个应用服务器如tomcat都会有后台管理,显示其当前的各种运行数据,我们通过观察数据库连接池Active活跃数据量是否达到最大值来进行判断。

总之,Java性能调试有两个方向:一个是在微调思路上做细做深,但是这对于有大量代码的关键业务运行场合几乎是很难实现,没有一个探测仪器不会对生产现场的稳定性不产生影响,看病X光扫描还会影响健康呢,但是没有X光又不能确诊。第二个方向就是重构,从新的思路在架构上重新梳理,这些方式引入后,会从根本上改变之前架构上的性能隐患和Bug。新思路:读写分离,读操作加入缓存机制,写操作引入并发机制,并在整个架构上引入分区分库等横向扩展性。

本文分享自微信公众号 - Linyb极客之路(gh_c420b2cf6b47)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

盲式出轨,上流社会边缘人士,2018朋友圈流行词,哪个词说中了你?

11530
来自专栏java一日一条

华为加班到底有多恐怖?

“我先说一下我的吧。昨天晚上好不容易11点之前搞完上线回到家,刚开门媳妇就叫到:你TMD给我站到阳台去!”

1.3K20
来自专栏java一日一条

博君一笑

9520
来自专栏程序员的知识天地

这些拍案惊奇的智障桥段,分明是在蔑视我作为程序员的debug

作为在网络高速发展的时代背景下成长起来的一代人,网络文学几乎伴随着我们的整个青春。

12920
来自专栏程序员的知识天地

阿里员工揭秘:很多程序员离职,在小公司当领导,只动嘴不动手!

阿里巴巴是中国知名的互联网公司,每个人或多或少的都从淘宝上购买的物品,自从1998年成立到现在,里面人才济济,里面的程序员不仅工资非常的高,不少程序员年收入竟然...

16420
来自专栏java一日一条

面试中单例模式有几种写法

纠结单例模式有几种写法有用吗?有点用,面试中经常选择其中一种或几种写法作为话头,考查设计模式和coding style的同时,还很容易扩展到其他问题。这里讲解几...

12170
来自专栏java一日一条

我的编码习惯 - 参数校验和国际化规范

今天我们说说参数校验和国际化,这些代码没有什么技术含量,却大量充斥在业务代码上,很可能业务代码只有几行,参数校验代码却有十几行,非常影响代码阅读,所以很有必要把...

12310
来自专栏金融民工小曾

电商平台分账交易是怎么做的?

另一篇文章讲到了电商平台的“二清”模式,在实际中,很多互联网电商平台需要分账给上面的平台商户或者其他角色,如果从严格的“二清”界定上来讲部分是属于违规进行了“信...

28510
来自专栏java一日一条

编程,从来都不晚:来自日本的82岁APP开发者

82岁的若宮正子第一次工作时,还是使用算盘来进行计算——而如今,她是世界上年纪最大的iPhone应用开发者之一,也是使得智能手机走入老年人生活的先驱者。

14920
来自专栏java一日一条

华为、腾讯、阿里、网易员工下班时间大曝光,为什么赢不了他们

这年头,不加班都不好意思说自己是上班族的。但有一种行业的疯狂加班程度,已经逐渐成为加班领域的一颗新星——互联网行业从事者!

14430

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励