本文已获得原作者 _陈哈哈 授权并经过重新整理规划后发布。
本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识、集合容器、并发编程、JVM、Spring全家桶、MyBatis等ORMapping框架、MySQL数据库、Redis缓存、RabbitMQ消息队列、Linux操作技巧等。
我们是如何发现慢SQL的?除了慢查询日志和人为发现之外,一般出现慢查询时会有如下三个特征:
有些SQL虽然出现在慢查询日志中,但未必是其本身的性能问题,可能是因为锁等待,服务器压力高等等。
需要分析SQL语句真实的执行计划,而不是看重新执行一遍SQL时,看是不是变快了(查询缓存都不带考虑的?)。。而是使用Explain工具来逐步调优,了解 MySQL 在执行这条数据时的一些细节,比如是否进行了优化、是否使用了索引、索引选择器是否正确选择等等。基于 Explain 的返回结果我们就可以根据 MySQL 的执行细节进一步优化语法,使索引能被正确使用,实现性能需求。
关于索引的创建及优化原则,美团点评技术团队的几点总结,引用一下:
当然,我们知道还有单表数据量过大、大字段检索、模糊匹配效率、时间维度统计效果差等MySQL硬性问题,俗称硬伤,那我们就得基于实际情况来进行分库分表、切换检索引擎(如ES、FastDFS)等方式来处理了,术业有专攻,总不能在一棵树上吊死对吧。
负载均衡(Load Balance)是集群技术(Cluster)的一种应用。负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力。下面是一组普通的web架构,我理解做负载均衡的切入点一般在web层和数据层。

目前最常见的负载均衡应用是Web层负载均衡。根据实现的原理不同,常见的web负载均衡技术包括:DNS轮询、IP负载均衡、CDN、反向代理以及http重定向等等。其中IP负载均衡可以使用硬件设备或软件方式来实现。
对于数据层负载均衡,我们常用的分布式架构、多节点集群、消息中间件(如Mycat)等来控制集群负载均衡状况,同样是横向扩展,但侧重点在于对集群的管理和灾备(高可用)方面。因此,实际大多负载均衡的工作还是在Web层。

Web层负载均衡
任何的负载均衡技术都要想办法建立某种一对多的映射机制:一个请求的入口映射到多个处理请求的节点,从而实现分而治之(Divide and Conquer)。
1、【协议层】http重定向
当http代理(比如浏览器)向web服务器请求某个URL后,web服务器可以通过http响应头信息中的Location标记来返回一个新的URL。这意味着HTTP代理需要继续请求这个新的URL,完成自动跳转。

这种负载均衡方案的有点是比较简单,缺点是浏览器需要两次请求服务器才能完成一次访问,性能较差;同时,重定向服务器本身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;使用HTTP返回码302重定向,有可能使搜索引擎判断为SEO作弊,降低搜索排名。因此实践中很少使用这种负载均衡方案来部署。
2、【协议层】DNS轮询
DNS负责提供域名解析服务,当访问某个站点时,实际上首先需要通过该站点域名的DNS服务器来获取域名指向的IP地址,在这一过程中,DNS服务器完成了域名到IP地址的映射,同样,这样映射也可以是一对多的,这时候,DNS服务器便充当了负载均衡调度器,它就像http重定向转换策略一样,将用户的请求分散到多台服务器上,但是它俩的实现机制完全不同。
相比http重定向,基于DNS的负载均衡完全节省了所谓的主站点,或者说DNS服务器已经充当了主站点的职能。但不同的是,作为调度器,DNS服务器本身的性能几乎不用担心。因为DNS记录可以被用户浏览器或者互联网接入服务商的各级DNS服务器缓存,只有当缓存过期后才会重新向域名的DNS服务器请求解析。也说是DNS不存在http的吞吐率限制,理论上可以无限增加实际服务器的数量。
优势:
不足:
3、【协议层】CDN
CDN(Content Delivery Network,内容分发网络)。通过发布机制将内容同步到大量的缓存节点,并在DNS服务器上进行扩展,找到里用户最近的缓存节点作为服务提供节点。
因为很难自建大量的缓存节点,所以通常使用CDN运营商的服务。目前国内的服务商很少,而且按流量计费,价格昂贵。
4、【协议层】反向代理负载均衡
在客户端明确的前提下,大量访问请求(QPS)涌入。我们后台通过Nginx代理了20个服务器,高QPS打进来后先打到Nginx中,通过Nginx的负载均衡来把请求分发给这20台服务器,减轻了单台服务器负担。这种客户端 → Nginx → 服务器 的模式称为反向代理,如下图:

N个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则均衡分发给了后端的业务处理服务器进行处理了。此时,请求的客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。
反向代理的作用:
(1)保证内网的安全,通常将反向代理服务器配置为公网访问地址,代理的Web服务器是内网IP。
(2)负载均衡,通过反向代理服务器来优化每个单机服务实例的负载。
5、【网络层】IP负载均衡

特点
限制
响应报文一般比较大,每一次都需要NAT转换的话,大流量的时候,会导致调度器成为一个瓶颈。
本题回答摘自朱晔的《Java业务开发常见错误100例》
定位问题,首先要定位问题出在哪个层次上。比如,是 Java 应用程序自身的问题还是外部因素导致的问题。我们可以先查看程序是否有异常,异常信息一般比较具体,可以马上定位到大概的问题方向;如果是一些资源消耗型的问题可能不会有异常,我们可以通过指标监控配合显性问题点来定位。
一般情况下,程序的问题来自以下三个方面。
第一,程序发布后的 Bug,回滚后可以立即解决。这类问题的排查,可以回滚后再慢慢分析版本差异。
第二,外部因素,比如主机、中间件或数据库的问题。这类问题的排查方式,按照主机层面的问题、中间件或存储(统称组件)的问题分为两类。
主机层面的问题,可以使用工具排查:
组件的问题,可以从以下几个方面排查:
第三,因为系统资源不够造成系统假死的问题,通常需要先通过重启和扩容解决问题,之后再进行分析,不过最好能留一个节点作为现场。系统资源不够,一般体现在 CPU 使用高、内存泄漏或 OOM 的问题、IO 问题、网络相关问题这四个方面。
对于 CPU 使用高的问题,如果现场还在,具体的分析流程是:
如果没有条件直接在服务器上运行 top 命令的话,我们可以用采样的方式定位问题:间隔固定秒数(比如 10 秒)运行一次 jstack 命令,采样几次后,对比采样得出哪些线程始终处于运行状态,分析出问题的线程。
如果现场没有了,我们可以通过排除法来分析。CPU 使用高,一般是由下面的因素引起的:
对于内存泄露或 OOM 的问题,最简单的分析方式,就是堆转储后使用MAT分析。堆转储,包含了堆现场全貌和线程栈信息,一般观察支配树图、直方图就可以马上看到占用大量内存的对象,可以快速定位到内存相关问题。
需要注意的是,Java 进程对内存的使用不仅仅是堆区,还包括线程使用的内存(线程个数 * 每一个线程的线程栈)和元数据区。每一个内存区都可能产生 OOM,可以结合监控观察线程数、已加载类数量等指标分析。另外,我们需要注意看一下,JVM 参数的设置是否有明显不合理的地方,限制了资源使用。
问题来了,JVM 哪块内存区域不会发生内存溢出?
程序计数器:程序计数器是一块内存较小的区域,它用于存储线程的每个执行指令,每个线程都有自己的程序计数器,此区域不会有内存溢出的情况。
IO 相关的问题,除非是代码问题引起的资源不释放等问题,否则通常都不是由 Java 进程内部因素引发的。网络相关的问题,一般也是由外部因素引起的。
对于连通性问题,结合异常信息通常比较容易定位;对于性能或瞬断问题,可以先尝试使用 ping 等工具简单判断,如果不行再使用 tcpdump 或 Wireshark 来分析。