Java面试题冲刺第二十五天--实战编程2

目录
  • 面试题2:怎么理解负载均衡的?你处理负载均衡都有哪些途径?
    • 1、【协议层】http重定向
    • 2、【协议层】DNS轮询
    • 3、【协议层】CDN
    • 4、【协议层】反向代理负载均衡
    • 5、【网络层】IP负载均衡
  • 面试题3:你平时是怎样定位线上问题的?
  • 总结

面试题1:当你发现一条SQL很慢,你的处理思路是什么?

  • 发现Bug
  • 确定Bug不是自己造成的,如果无法确定,不要理会步骤1
  • 向组内宣传“程序里有一个未知Bug,错不在我”
  • 谁响应,谁对Bug负责
  • 没人响应,就要求特定人员配合调试
  • 如果不配合,就是特定人员对Bug负责
  • 如果特定人员配合,就相当于特定人员发现了一个Bug
  • 让特定人员看步骤1
  • 完美,无懈可击

我们是如何发现慢SQL的?除了慢查询日志和人为发现之外,一般出现慢查询时会有如下三个特征:

  • 数据库CPU负载高。一般是查询语句中有很多计算逻辑,或并发处理线程较多,导致数据库cpu负载。
  • IO过高导致服务器卡住,这个一般和全表查询没索引有关系,问题出在处理的数据量太大。
  • 查询语句正常,索引正常但是还是慢。如果表面上索引都配置了,但是查询慢,那得看看索引是否生效。

有些SQL虽然出现在慢查询日志中,但未必是其本身的性能问题,可能是因为锁等待,服务器压力高等等。

需要分析SQL语句真实的执行计划,而不是看重新执行一遍SQL时,看是不是变快了(查询缓存都不带考虑的?)。。而是使用Explain工具来逐步调优,了解 MySQL 在执行这条数据时的一些细节,比如是否进行了优化、是否使用了索引、索引选择器是否正确选择等等。基于 Explain 的返回结果我们就可以根据 MySQL 的执行细节进一步优化语法,使索引能被正确使用,实现性能需求。

关于索引的创建及优化原则,美团点评技术团队的几点总结,引用一下:

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整;

2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式,当然,好习惯要靠自己保持;

3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录;

4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = '2014-05-29'就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp('2014-05-29');

5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

当然,我们知道还有单表数据量过大、大字段检索、模糊匹配效率、时间维度统计效果差等MySQL硬性问题,俗称硬伤,那我们就得基于实际情况来进行分库分表、切换检索引擎(如ES、FastDFS)等方式来处理了,术业有专攻,总不能在一棵树上吊死对吧。

面试题2:怎么理解负载均衡的?你处理负载均衡都有哪些途径?

负载均衡(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的吞吐率限制,理论上可以无限增加实际服务器的数量。

优势:

1.可以根据用户IP来进行智能解析。DNS服务器可以在所有可用的A记录中寻找离用记最近的一台服务器。

2.动态DNS:在每次IP地址变更时,及时更新DNS服务器。当然,因为缓存,一定的延迟不可避免。

不足:

1.没有用户能直接看到DNS解析到了哪一台实际服务器,对运维人员的调试带来了不便。

2.策略的局限性。例如你无法将HTTP请求的上下文引入到调度策略中,而在前面介绍的基于HTTP重定向的负载均衡系统中,调度器工作在HTTP层面,它可以充分理解HTTP请求后根据站点的应用逻辑来设计调度策略,比如根据请求不同的URL来进行合理的过滤和转移。

3、【协议层】CDN

CDN(Content Delivery Network,内容分发网络)。通过发布机制将内容同步到大量的缓存节点,并在DNS服务器上进行扩展,找到里用户最近的缓存节点作为服务提供节点。

因为很难自建大量的缓存节点,所以通常使用CDN运营商的服务。目前国内的服务商很少,而且按流量计费,价格昂贵。

4、【协议层】反向代理负载均衡

在客户端明确的前提下,大量访问请求(QPS)涌入。我们后台通过Nginx代理了20个服务器,高QPS打进来后先打到Nginx中,通过Nginx的负载均衡来把请求分发给这20台服务器,减轻了单台服务器负担。这种客户端 → Nginx → 服务器 的模式称为反向代理,如下图:

N个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则均衡分发给了后端的业务处理服务器进行处理了。此时,请求的客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。

1.客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。

2.反向代理,它代理的是服务端,代服务端接收请求,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息。

反向代理的作用:

(1)保证内网的安全,通常将反向代理服务器配置为公网访问地址,代理的Web服务器是内网IP。

(2)负载均衡,通过反向代理服务器来优化每个单机服务实例的负载。

5、【网络层】IP负载均衡

1.客户端会向一个ip地址发出请求,这个ip地址是一个VIP(虚拟IP),这也是调度器向外公布的一个地址。

2.请求达到调度器,调度器会根据负载均衡算法从RealServer列表中选取一个负载不高的服务器,然后把请求报文的目标地址,也就是VIP和端口通过iptables进行NAT转换成选中的服务器的真实ip地址。最后,调度器会把其连接保存在一个hash表中,只要这个连接下次再发请求报文过来就会把其分发到上次选定的服务器中。

3.RealServer收到报文之后,会把响应返回给调度器。

4.调度器收到报文之后,会把源地址和源端口改为虚拟ip和端口,最后再返回给客户端。

特点

  • RealServer和调度器必须位于一个ip网络之中。
  • 调度器位于RealServer和客户端之间,处理进出的通信。
  • RIP通常是内部地址,仅用于集群之间通信。
  • RealServer的网关必须指向调度器。
  • 支持端口映射,RealServer没必要跟调度器一个端口。

限制

响应报文一般比较大,每一次都需要NAT转换的话,大流量的时候,会导致调度器成为一个瓶颈。

面试题3:你平时是怎样定位线上问题的?

本题回答摘自朱晔的《Java业务开发常见错误100例》

定位问题,首先要定位问题出在哪个层次上。比如,是 Java 应用程序自身的问题还是外部因素导致的问题。我们可以先查看程序是否有异常,异常信息一般比较具体,可以马上定位到大概的问题方向;如果是一些资源消耗型的问题可能不会有异常,我们可以通过指标监控配合显性问题点来定位。

一般情况下,程序的问题来自以下三个方面。

第一,程序发布后的 Bug,回滚后可以立即解决。这类问题的排查,可以回滚后再慢慢分析版本差异。

第二,外部因素,比如主机、中间件或数据库的问题。这类问题的排查方式,按照主机层面的问题、中间件或存储(统称组件)的问题分为两类。

主机层面的问题,可以使用工具排查:

  • CPU 相关问题:可以使用 top、vmstat、pidstat、ps 等工具排查;
  • 内存相关问题:可以使用 free、top、ps、vmstat、cachestat、sar 等工具排查;
  • IO 相关问题:可以使用 lsof、iostat、pidstat、sar、iotop、df、du 等工具排查;
  • 网络相关问题:可以使用 ifconfig、ip、nslookup、dig、ping、tcpdump、iptables 等工具排查。

组件的问题,可以从以下几个方面排查:

  • 排查组件所在主机是否有问题;
  • 排查组件进程基本情况,观察各种监控指标;
  • 查看组件的日志输出,特别是错误日志;
  • 进入组件控制台,使用一些命令查看其运作情况。

第三,因为系统资源不够造成系统假死的问题,通常需要先通过重启和扩容解决问题,之后再进行分析,不过最好能留一个节点作为现场。系统资源不够,一般体现在 CPU 使用高、内存泄漏或 OOM 的问题、IO 问题、网络相关问题这四个方面。

对于 CPU 使用高的问题,如果现场还在,具体的分析流程是:

  • 首先,在 Linux 服务器上运行top -Hp pid命令,来查看进程中哪个线程 CPU 使用高;
  • 然后,输入大写的 P 将线程按照 CPU 使用率排序,并把明显占用 CPU 的线程 ID 转换为 16 进制;
  • 最后,在 jstack 命令输出的线程栈中搜索这个线程 ID,定位出问题的线程当时的调用栈。

如果没有条件直接在服务器上运行 top 命令的话,我们可以用采样的方式定位问题:间隔固定秒数(比如 10 秒)运行一次 jstack 命令,采样几次后,对比采样得出哪些线程始终处于运行状态,分析出问题的线程。

如果现场没有了,我们可以通过排除法来分析。CPU 使用高,一般是由下面的因素引起的:

  • 突发压力。这类问题,我们可以通过应用之前的负载均衡的流量或日志量来确认,诸如 Nginx 等反向代理都会记录 URL,可以依靠代理的 Access Log 进行细化定位,也可以通过监控观察 JVM 线程数的情况。压力问题导致 CPU 使用高的情况下,如果程序的各资源使用没有明显不正常,之后可以通过压测 +Profiler(jvisualvm 就有这个功能)进一步定位热点方法;如果资源使用不正常,比如产生了几千个线程,就需要考虑调参。
  • GC。这种情况,我们可以通过 JVM 监控 GC 相关指标、GC Log 进行确认。如果确认是 GC 的压力,那么内存使用也很可能会不正常,需要按照内存问题分析流程做进一步分析。
  • 程序中死循环逻辑或不正常的处理流程。这类问题,我们可以结合应用日志分析。一般情况下,应用执行过程中都会产生一些日志,可以重点关注日志量异常部分。

对于内存泄露或 OOM 的问题,最简单的分析方式,就是堆转储后使用MAT分析。堆转储,包含了堆现场全貌和线程栈信息,一般观察支配树图、直方图就可以马上看到占用大量内存的对象,可以快速定位到内存相关问题。

需要注意的是,Java 进程对内存的使用不仅仅是堆区,还包括线程使用的内存(线程个数 * 每一个线程的线程栈)和元数据区。每一个内存区都可能产生 OOM,可以结合监控观察线程数、已加载类数量等指标分析。另外,我们需要注意看一下,JVM 参数的设置是否有明显不合理的地方,限制了资源使用。

问题来了,JVM 哪块内存区域不会发生内存溢出?

程序计数器:程序计数器是一块内存较小的区域,它用于存储线程的每个执行指令,每个线程都有自己的程序计数器,此区域不会有内存溢出的情况。

IO 相关的问题,除非是代码问题引起的资源不释放等问题,否则通常都不是由 Java 进程内部因素引发的。网络相关的问题,一般也是由外部因素引起的。

对于连通性问题,结合异常信息通常比较容易定位;对于性能或瞬断问题,可以先尝试使用 ping 等工具简单判断,如果不行再使用 tcpdump 或 Wireshark 来分析。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java协程编程之Loom项目实战记录

    目录 前提 Loom项目简单介绍 Virtual Thread使用 小结 前提 之前很长一段时间关注JDK协程库的开发进度,但是前一段时间比较忙很少去查看OpenJDK官网的内容.Java协程项目Loom(因为项目还在开发阶段,OpenJDK给出的官网https://openjdk.java.net/projects/loom中只有少量Loom项目相关的信息)已经在2018年之前立项,目前已经发布过基于JDK17编译和JDK18编译等早期版本,笔者在下载Loom早期版本的时候只找到JDK18编译

  • Java面试题冲刺第二十六天--实战编程

    目录 面试题1:你们是怎样保存用户密码等敏感数据的? 面试题2:怎么控制用户请求的幂等性的? 1.设置唯一索引:防止新增脏数据 2.token机制:防止页面重复提交 3.悲观锁 4.乐观锁 5.分布式锁 面试题3:你们是如何预防SQL注入问题的? 预防方式: 1.PreparedStatement(简单有效) 2.使用正则表达式过滤传入的参数 3.使用正则表达式过滤传入的URL 总结 面试题1:你们是怎样保存用户密码等敏感数据的? 本题回答参考朱晔的<Java业务开发常见错误100例> 我们知

  • Java多线程编程实战之模拟大量数据同步

    背景 最近对于 Java 多线程做了一段时间的学习,笔者一直认为,学习东西就是要应用到实际的业务需求中的.否则要么无法深入理解,要么硬生生地套用技术只是达到炫技的效果. 不过笔者仍旧认为自己对于多线程掌握不够熟练,不敢轻易应用到生产代码中.这就按照平时工作中遇到的实际问题,脑补了一个很可能存在的业务场景: 已知某公司管理着 1000 个微信服务号,每个服务号有 1w ~ 50w 粉丝不等.假设该公司每天都需要将所有微信服务号的粉丝数据通过调用微信 API 的方式更新到本地数据库. 需求分析 对此

  • java图形界面编程实战代码

    关于Java图形化界面设计,基础知识网上可搜,下面简单介绍一下重点概念,然后就由浅入深代码实例. 程序是为了方便用户使用的,Java引入图形化界面编程. 1.JFrame 是容器类 2.AWT 是抽象窗口组件工具包,是 Java 最早的用于编写图形节目应用程序的开发包. 3.Swing 是为了解决 AWT 存在的问题而新开发的包,它以 AWT 为基础的. 代码实例1: package com.zhouzhou; //练习网格布局 import java.awt.*; import javax.s

  • Java编程枚举类实战代码分享

    本文句句走心,希望老铁们用心阅读并实战,一定会有收获的. 摘要:本文主要讨论生产环境中枚举类的使用.首先会通过对枚举类概念进行简单的介绍,引入我们讨论的主题:然后就直接进入实战部分,本文只会介绍在实战中用的比较多,也比较常用的情况,所以希望老铁可以用心体会并实践,最终化为己有:最后会大致在对枚举的 API 做了一个简单的介绍.其余没有介绍的内容,基本上在我们的生产环境中极少用到,如果有兴趣的可以自己在深入研究. 枚举 概念:枚举类型是 Java 5 中新增特性的一部分,它是一种特殊的数据类型,它

  • Java面试题冲刺第二十五天--实战编程2

    目录 面试题2:怎么理解负载均衡的?你处理负载均衡都有哪些途径? 1.[协议层]http重定向 2.[协议层]DNS轮询 3.[协议层]CDN 4.[协议层]反向代理负载均衡 5.[网络层]IP负载均衡 面试题3:你平时是怎样定位线上问题的? 总结 面试题1:当你发现一条SQL很慢,你的处理思路是什么? 发现Bug 确定Bug不是自己造成的,如果无法确定,不要理会步骤1 向组内宣传"程序里有一个未知Bug,错不在我" 谁响应,谁对Bug负责 没人响应,就要求特定人员配合调试 如果不配合

  • Java面试题冲刺第二十五天--并发编程3

    目录 面试题1:你了解线程池么?简单介绍一下. 追问1:连接池 和 线程池是一个意思么?有什么区别? 不同点 面试题2:线程池中核心线程数量大小你是怎么设置的? 追问1:核心线程数量过大或过小会造成什么后果? 面试题3:线程池都有哪些状态呀? 追问1:什么条件下会进入TERMINATED状态 总结 面试题1:你了解线程池么?简单介绍一下. java提供的一个java.util.concurrent.Executor接口的实现用于创建线程池. 线程池是一种多线程处理形式,处理过程中将任务提交到线程

  • Java面试题冲刺第二十五天--并发编程1

    目录 面试题1:简单说下你对线程和进程的理解? 正经回答: 深入追问: 追问1:那进程和线程有哪些区别呢? 面试题2:守护线程和用户线程的区别? 正经回答: 面试题3:什么是线程死锁? 正经回答: 深入追问: 追问1:形成死锁的四个必要条件是什么? 追问2:我们该如何避免死锁? 追问3:死锁避免和死锁预防有啥不同? 总结 面试题1:简单说下你对线程和进程的理解? 正经回答: 进程 一个在内存中运行的应用程序.每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,

  • Java面试题冲刺第二十五天--并发编程2

    目录 面试题1:简单说下你对线程和进程的理解? 正经回答: 深入追问: 追问1:那进程和线程有哪些区别呢? 面试题2:守护线程和用户线程的区别? 正经回答: 面试题3:什么是线程死锁? 正经回答: 深入追问: 追问1:形成死锁的四个必要条件是什么? 追问2:我们该如何避免死锁? 追问3:死锁避免和死锁预防有啥不同? 总结 面试题1:简单说下你对线程和进程的理解? 正经回答: 进程 一个在内存中运行的应用程序.每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,

  • Java面试题冲刺第二十五天--JVM2

    目录 面试题1:简单说一下java的垃圾回收机制. 面试题2:JVM会在什么时候进行GC呢? 追问1:介绍一下不同代空间的垃圾回收机制 追问2:能说一下新生代空间的构成与执行逻辑么? 追问3:说一下发生OOM时,垃圾回收机制的执行流程. 面试题3:Full GC .Major GC和 Minor GC有什么不同 (1)Minor GC / Young GC (2)Old GC (3)Full GC (4)Major GC (5)Mixed GC 总结 面试题1:简单说一下java的垃圾回收机制.

  • Java面试题冲刺第二十四天--并发编程

    目录 面试题1:说一下你对ReentrantLock的理解? CAS: AQS: 追问1:你认为 ReentrantLock 相比 synchronized 都有哪些区别? 面试题2:解释一下公平锁和非公平锁? 面试题3:能详细说一下CAS具体实现原理么? 追问1:那CAS的缺陷有哪些呢? 1.ABA: 2.自旋消耗资源: 3.多变量共享一致性问题: 追问2:讲一下什么是ABA问题?怎么解决? 总结 面试题1:说一下你对ReentrantLock的理解? ReentrantLock是JDK1.5

  • Java面试题冲刺第二十六天--实战编程2

    目录 面试题2:怎么理解负载均衡的?你处理负载均衡都有哪些途径? 1.[协议层]http重定向 2.[协议层]DNS轮询 3.[协议层]CDN 4.[协议层]反向代理负载均衡 5.[网络层]IP负载均衡 面试题3:你平时是怎样定位线上问题的? 总结 面试题1:当你发现一条SQL很慢,你的处理思路是什么? 发现Bug 确定Bug不是自己造成的,如果无法确定,不要理会步骤1 向组内宣传"程序里有一个未知Bug,错不在我" 谁响应,谁对Bug负责 没人响应,就要求特定人员配合调试 如果不配合

  • Java面试题冲刺第十五天--设计模式

    目录 面试题1:面向对象程序设计(OOP)的六大原则分别有哪几个 面试题2:你说一下什么是设计模式 追问1:那你怎么理解高内聚和低耦合? 面试题3:设计模式有哪几种? 追问1:你比较熟悉哪种设计模式?说说原理. 追问2:那你说说适配器模式的原理吧 适配器模式优缺点 总结 面试题1:面向对象程序设计(OOP)的六大原则分别有哪几个 开闭原则(Open Close Principle)及"开放-封闭原则"单一职责原则(Single Responsibility Principle)里氏替换

  • Java面试题冲刺第二十九天--JVM3

    目录 面试题1:如何判断对象是否存活 1.引用计数算法 2.可达性分析算法 面试题2:哪些对象可以作为GC Roots? 面试题3:你了解的对象引用方式都有哪些? 1 强引用 2 软引用 3 弱引用 4 虚引用 总结 面试题1:如何判断对象是否存活 对于判断对象是否存活,主要是两种基本算法,引用计数和可达性分析,目前java主要采用的是可达性分析算法 1.引用计数算法 判断对象是否存活的方式如:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一:当引用失效时,计数器值就减一:任何

随机推荐