面试剧本
首次交锋【自我介绍】
面试官: 你好,请先做个简短的自我介绍。
你:
好的,面试官你好,我嘛,毕业之后也是一直做java的,到现在差不多5年多的时间。
这么多年来,我开发过大大小小的项目大概十几个,各种类型的都有,有电商啊,医疗啊,物联网项目啊,AI项目啊,OA、ERP、SASS等等,C端和B端的都有。 这些项目有单体的,也有微服务的。基本都是前后端分离的多。技术栈主要是: vue2/3, springboot,springcloud , springcloud alibaba,nacos、网关gateway ,MQ,redis,openfeign远程调用,Dubbo等RPC框架 。还有像es , xxl-job , websocket , netty ,seata ,redisson,等等,这些都用过。
近年来,随着AI越来越火,AI应用开发也是非常熟悉,也在多个项目中集成了AI模块。
我这几年做的项目都是互联网项目,涉及电商,医疗,物联网这些行业。不管是PC,H5,APP,小程序都开发过。前端这块我也是一点都没问题的。之前都独立负责过多个前后端分离的项目,都是我一个人做的。
在项目组里也是个主开/架构师/全栈工程师,参与公司核心服务,核心功能模块的开发;
当然从0~1做项目也是没有问题的。包括前期的需求分析,功能设计,数据库,表结构设计,接口文档编写等。
平时我这边也要负责一些管理工作,如需求对接,任务拆分,排期,日例会,代码审查等等
还有带新人的经验,我们组的新人基本上都要从我手里过一遍。
最近在上家公司做的是一个电商项目,我简单来说说这个项目。开始从项目背景、功能模块设计、核心业务、自己负责的模块开始着手来说。所有模块中的技术栈都要去扩展
次轮交锋【项目介绍】
面试官:介绍下你最近做的项目,主要负责的什么内容?
你:【项目背景】
我最近的这个项目是一个综合类电商平台,注册会员差不多二百多万,DAU(每日活跃用户数) 在 10 万左右。大促期间(如双11、618),瞬时流量可达日常的 20 倍以上,尤其是秒杀活动,经常出现 2000+ QPS 的峰值请求。
我们系统采用微服务架构,c端的话,按业务域拆分为:商品服务、搜索服务、库存服务、营销中心、订单服务、购物车服务、会员服务、支付服务、任务调度服务等等,通过openfeign 实现远程调用,网关层使用 Spring Cloud Gateway 统一入口。
c端的下单/购物车/营销/秒杀/支付的核心链路,这块也是我来负责的,这个当时要求三个月要上线的,然后它是有一些性能要求的,项目的注册会员,因为这个是有政府背景嘛,所以推广起来非常快,半年不到的时间,会员从0一直干到200多万,我们一些核心的接口在高峰值时qps要达到3000+,所以需要做一些性能优化的,这块主要也是由我来负责的。
还有,项目上线后的遇到的一些问题,如响应速度慢,CPU飙升,线程死锁等,我也要去负责。
在日常中,我还需要负责部分管理方面的工作,包括需求排期,例会检查进度,代码评审、带新人之类的。
我们每个员工都是有绩效考核的,公司有规定,像我们后端这块,连续三个月绩效考核分数低于0.8,可能就要被约谈,甚至淘汰了。
面试官:能具体说说你开发的功能模块吗?
我重点参与并主导了以下几个核心模块的开发与优化:
| 模块 | 我的角色 | 核心挑战 |
|---|---|---|
| 秒杀系统 | 主导重构 | 高并发、防超卖、系统稳定性 |
| 订单中心 | 核心开发 | 状态机、超时关闭、幂等性 |
| 购物车服务 | 设计与实现 | 多端同步、性能优化 |
| 库存服务 | 深度参与 | 扣减准确性、分布式事务 |
接下来,我想以秒杀系统为例,详细说明我是如何设计和落地这个高并发场景的。
按照事先准备的功能模块进行应答。
三次交锋【问答环节】
首次交锋坑点
坑点1:如何0~1开发
面试官:当你拿到一个需求,是如何开展工作的
你:
最重要的就是搞清楚需求,什么角色,在哪些场景,有哪些具体的操作,等等。
然后就是要搞清楚这些需求,对应的表有哪些,哪些是业务主表,表之间的关系,是一对一,一对多还是多对多,和业务相关的字段有哪些,特别是一些状态相关的字段,一定要搞清楚。一些复杂的业务最好把sql先写清楚。评审通过再开发,这样最保险。
编码实现的话比较简单,主要是注重代码的可读性,代码质量,一些核心业务代码要加注释,测试用例驱动的编码风格,不要各种if 嵌套,可读性会很差,后期代码维护起来费劲。
坑点2:项目中的困难
面试官:项目过程中有遇到过什么困难吗?
你:
技术上还好,基本上不会有什么太大的问题。很多时候还是沟通上的一些问题。
特别是有个项目,我们是外包进场,那个甲方领导特别强势。经常提新的需求,在他眼里就是放个按钮,然后出来他想要的数据。但是他不了解背后的代码逻辑,数据之间的关系。就很难搞。
面试官:那你是怎么解决这个问题的
你:
我是这么理解的,我觉得这个问题最关键的是,搞清楚甲方的真正需求,很多时候,你会发现,并不是他描述的需求不合理,只是他没法正确的表达出来。
我们需要去参考一些竞品,争取把客户真正的需求挖掘出来,再和客户去确认。要是不合理也得和客户解释清楚,如果合理的话,就得把原型图画出来,和客户确认清楚,当然,工期的延长,还有费用这块也得是事先说清楚的。
面试官:那你是如何进行需求分析?
你:
需求分析的话我们一般先确定一个系统边界,系统边界外的就是我们的角色,如果角色比较复杂的话,我们还需要给它的组织架构搞清楚,系统边界内的就是我们的用例,我们会对它进行微服务的划分,功能点的拆分。我们还会开需求分析会。组内的成员都需要进行需求反讲,在我给他们讲解需求后,他们需要将自己的理解说出来
像之前的项目需求分析差不多花了一个月左右的时间,每天还会有例会,他们需要上去讲自己理解的东西,我对他们进行点评,会出来一些脑图,微服务的功能划分图。对于一些核心的业务我们会出一些序列图,来体现各个服务之间的调用关系。还会出一些流程图,差不多就是这样
坑点2:数据库设计+索引
面试官:做过数据库设计吗?你会考虑哪些因素
你:
初期的时候,因为我们是拆分成微服务的,所以说我们对我们的数据库也做了一个垂直拆分,这样子做到真正的业务隔离,我们数据也做到隔离。
然后在我们设计表的时候会考虑两方面:一个是三范式,一个是冗余,我们的核心业务,所对应的主表,我们尽量遵守三范式,这样在后期扩展的时候,可扩展性会更好一点。
我们一些涉及到查询的表,数据量非常的大,特别是C端的,他查询的性能有一定的要求,那我们这个时候可以允许他有一定的冗余,比如你用三范式来查的时候,有五表联查、六表联查,但是这时候我们让他设计有一定的冗余,最后我两表联查,三表联查就可以。
还有,就是我们在做表设计的时候需要考虑索引的问题,你可以预见性的有些表的数据后期会非常的大,那么你在创建表的时候就需要考虑他的索引。
当然还有一些审计字段,包括我们的主表需要有一些预留字段,以及逻辑删除、乐观锁之类的、这些都是需要去考虑的。
面试官:哪些字段适合建索引呢?
你:
索引的话,像一些字段,经常来做查询条件的、最好是非空的、唯一的、最好是数值类型、有序的,那么这种字段就适合建立索引,像雪花算法就是比较好的选择
当然如果他允许为空的话,我们可以给它设计一些默认值,如果他不是唯一的,我们有一个叫散列系数,我去查这一列,distinct(去重)一下,再除以总数,如果大于80%,也就是说大于80%是不一样的,这个时候也是可以建立索引的。
面试官:索引在哪些情况下会失效呢?
你:
模糊查询,左边加%
查询的字段有null值也会失效
查询条件有函数运算会失效
还有排序的时候,如果你有多个字段,一个升序,一个降序也会失效
联合索引,不满足最左匹配也会失效
还有一些其他的情况吧,我们开发的时候,有个规范手册的,里面都有规定,代码审查的时候得注意这些
次轮交锋坑点
坑点1:关于库存超卖的问题
面试官:库存是怎么防止超卖的
你:
首先,我们的库存是这样设计的,第一:我们原始是有仓储物流系统的,这个系统是对接很多业务系统的,我们电商系统只是其中一部分,会给我们配额 第二:我们自己的电商系统,是有库存的,还有库存锁定,下单之后只能锁定,真正发货了才会减库存 ,第三,由于高并发,我们会将库存缓存到redis中
我们设计的时候,redis中库存和数据库库存就是不一致的。
项目启动的时候,会将数据库库存缓存到redis
详情页的时候,看到的就是redis中的库存,并且页面是一秒刷一次的,所以看到的库存和redis也是不一致的,但是不要紧,对于用户来讲,只关心,有没有库存
下单前判断,包括下单成功,进行库存预减操作,我们操作的都是redis库存
支付成功之后,数据库的库存锁定字段进行锁定
后期就对接物流系统,仓储系统中的商品真正出库时,会给我们电商系统发消息,当我们listen到的时候才会真正的将电商系统的库存,和库存锁定字段进行扣减
整个过程是不会超卖的
redis中,比如,获取库存,判断库存是否充足,扣减库存,这一块我们采用lua脚本,保证原子性,不会超卖
数据库基于乐观锁version, 在更新时正常 update ware set stock = stock -5,version = version+1 where stock-5≥0 and version = version
我们还做了唯一索引防重复下单。一旦重复下单就会报错。
有时候可能会出现代码以外的异常,比如redis出现了超卖,按照我们的逻辑,redis中的库存应该永远小于等于数据库的库存,所以我们采用阿里的cacal,监测mysql的binlog , 一旦发现数据库库存为0,就将redis立刻变成0,这样就做了一个技术兜底,防止超卖。
坑点2:库存一致性的问题
面试官:那你们数据的库存和redis的库存是如何保持一致性的
类似问题:
如果数据库库存补货了,redis库存如何操作?
你:
我们库存的设计,数据库和redis就是不一致的,所以在补货的业务场景下,数据库和redis是同时进行加的操作。
当然我们还会进行对账和告警操作,同时还可以进行人工干预,具体如下:
- 每日凌晨对账任务
- 差异>10条则告警
- 提供“同步库存”人工工具
面试官:那如果你们redis服务器宕机了,你们的库存如何恢复?
你:
关于库存的操作,不管是redis,还是数据库,我们都会异步去记录到一个日志表里,真出问题了可以基于这个日志表操作。
而且我们也有canal去监测biglog日志,做技术兜底,确保库存不会出问题。
面试官:那我就需要让redis和数据库保持缓存一致呢,你们会怎么做
你:
看具体情况吧
如果实时性要求不是太高,比如我们首页的三级分类,如果后台数据更新了,我们会让缓存失效,这样用户下次加载的时候会加载最新的,当然因为数据库的操作是耗时的,所以数据库操作的那段时间你抓的数据就是有问题的,但是这种场景短暂的这种脏数据是允许的。
如果不允许脏读的话,我们可以采用延迟双删,可以保证数据肯定是没问题的,但是性能会比较差
如果还需要考虑性能的话,我们就可以采用canal等这些技术,他是检测数据库的binlog,实时性能还是比较好的
坑点3:接口性能优化
面试官:你这个性能优化是怎么做的,能详细描述一下吗?
你:
接口性能调优,我们一般考虑的思路是,从下往上
最下层肯定是硬件:
数据库,正常有横向扩展和纵向扩展,纵向就是堆配置,横向的话,一般我们采用的是俩主多从,俩个innodb,多个myisam
还有就是分库分表(水平分库分表 shardingsphere)
再往上就是考虑数据库的吞吐量,不能太大,一般采用接口合并,比如每800ms合并一千个请求,变成批量处理,但是这里麻烦的是,你合并完了还得将响应结果进行拆分和请求对象匹配上
再往上就是sql的性能,像我们一些核心接口,做自测的时候,正常就是mapper,service ,controller这样从下往上测,mapper其实测的就是sql的性能
再往上就相当于应用层,就是我们编码的内容,可以采用redis,mq,线程池,异步编排,es等等这些中间件,提升性能,当然这样也会带来技术复杂度的增加
最后要再优化,可能就要对需求进行优化了,像我之前对接的短信微服务,节假日要给会员发消息,会发生消息堆积,我们技术层面也去解决了,采用的滚动的时间片算法,每秒处理5千个短信,但是还会发生堆积,最终还是在业务层进行了改造,根据不同会员等级在不同时段发送,最终才解决了问题
面试官:你结合一个具体的业务讲讲你的优化思路
你:
比如订单微服务,下单接口tps需要达到三四百
我是这样设计的,下单前首先要进行校验,比如说库存啊,活动的时效性,购买商品数量的限制等等,
这些数据的原始数据都是在表里的,但是并发比较大,你不能让请求全部打到数据库,扛不住,所以我们通过缓存的方式进行优化,服务启动或者按照实际的业务需求设计定时器,将数据预热到redis中。
校验通过,才能进行下单,当然下单肯定也是异步下单,通过mq的方式。因为异步嘛,前台结果是通过轮询的方式到redis中查看下单结果,我们下单成功,会将订单状态更新到redis中。
但是mq只是起到一个削峰的作用,想要提升并发度的话,还要结合线程池加上异步编排,提升消息处理的速度
订单最终还是要入库的,所以数据库那块还要做接口合并,降低数据库的负载
我们项目中有一个monitor服务,做服务监控的,里面集成了skywalking,我们会去链路追一下,服务的调用,包括各个服务中持久层,他的一个请求结果和耗时都可以看到,我们根据具体的情况再去做优化。
面试官:有sql优化的经验吗?讲讲的,项目中是如何sql调优的
你:
嗯,这个在项目还是挺常见的,当然如果直说sql优化的话,我们会从这几方面考虑,比如
建表的时候、使用索引、sql语句的编写、主从复制,读写分离,还有一个是如果量比较大的话,可以考虑分库分表。具体参考下面sql优化相关资料来进行回答
面试官:你这个接口合并降低数据库负载,能具体讲讲你的方案吗?
你:
额,是这样子的,我将多个相似请求合并为单次批量操作,减少数据库访问频率。例如,一万次查询合并为单次批量查询后,数据库只需响应一次操作。如用户信息查询、商品列表显示等条件固定的场景。登录、注册等高频请求可通过合并降低数据库压力。当让还有要尽量避免那种N+1查询。
坑点4: 并发编程+线程池
面试官:项目中用过线程池吗?哪些场景,如何使用的?
你:
这个互联网的项目肯定经常用的。
我们项目中的详情页,下单啊,都会用到,比如下单,他要去抓会员中心的信息,还有商品中心,像spu,sku,还有商家信息,还有营销信息优惠券等等。这时候就可以采用线程池提升请求的rt时间。当然了,这些异步任务的调用如果存在先后关系,可以结合异步编排来做
我们项目中关于线程池的使用一般会做业务隔离。有一些通用的线程池。都是自定义线程池。
一些核心的接口,通过配置不同的前缀用来做业务隔离,会去配置configuration,还有configurationProperties。
面试官:你讲一下线程池有哪些核心参数,他的运行流程是怎样的?
你:
有核心线程数,最大线程数
还有线程存活时间:他指的是核心线程以外的线程,一直空闲,超过线程的存活时间会被销毁
存活时间单位
还有阻塞队列,线程工厂,拒绝策略
线程池一般用自定义线程池,其他的最大线程数是Integer.Max_value,不适合实际应用,容易OOM
调用 execute() 时,线程数量小于 corePoolSize时,会立刻创建线程对象
如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入阻塞队列
队列满了会继续创建线程直至数量到最大线程数
如果还有异步任务过来,就执行拒绝策略
除核心线程数以外,其他的线程对象,空闲时间超过线程存活时间,该线程对象会被销毁
面试官:线程池的核心线程数配置多大合适呢?
你:
主要看你的异步任务是cpu密集型还是io密集型
CPU密集型:运算比较多的话 CPU 核数+1
IO密集型:就是文件操作比较多的,像对数据库的操作,一般是CPU核心数*2
坑点5:接口幂等
面试官:你是如何防止重复下单的
类似问题:
- 你是如何防止重复支付的
- rabbitmq你是如何防止消息重复消费的
你:
参考网址:
https://blog.csdn.net/lichunericli/article/details/141141257 10wqps高并发,如何防止重复下单? https://blog.csdn.net/panci_/article/details/143588512?share_token=e8b03ced-22ca-4781-801a-9fe886b53839 接口幂等性
RabbitMq
mq如何选型的
从几种mq的优缺点上来答,最后回到自己的项目中使用到哪种?哪些地方使用到了?
rabbitmq如何防止消息重复消费
消费者的确认机制,消息幂等性两方面去答
rabbitmq如何防止消息丢失
mq的可靠性:生产者、broker消息代理、消费者三方面去回答
rabbitmq如何防止消息积压
兜底方案,存储方案等方面去回答
rabbitmq如何保证消息的顺序
rabbitmq集群
Redis
Sql优化
JVM优化
如何设计一个高并发系统
并发编程(线程池)
分布式锁
**面试官:**redisson底层实现原理。
类似问题:如果让你来实现一个分布式锁,你该怎么实现
你:
redisson分布式锁的实现原理是这样的,有以下几点要考虑:
第一:首先,基于redis 的setNx这个方法,这个方法的特点是,要设置的数据只有redis中不存在,才能设置成功,才能返回1,否则就返回0,不成功;用这个特点实现互斥的效果,所以,你大并发来了,只有一个线程能设置成功,才能获得锁资源。
第二点:为了防止死锁,我们需要去设置过期时间
第三点:设置了过期时间会存在一个问题,就是锁过期时间到了,业务还没执行完,那就锁不住了。我们需要设置【看门狗】机制给我们业务自动续期,比如:redisson默认过期时间是40S, 我们设置的看门狗每隔20S去检查下业务代码有没有执行完,如果没有执行完,自动续期40S.
第四点:就是刚刚第二点的问题,锁是自动过期的,但是我们业务执行完了,还会执行释放锁的代码,就会去释放别人的锁,因为你的锁自动释放后别人是有机会获得锁的。那这个就有问题了,所以再获取锁的时候,在当前线程生成一个uuid存入到redis,这样在释放的时候需要去判断uuid, 是当前线程生成的uuid才可以释放
第五点:就是
你加锁并且设置过期时间 和 判断uuid并且删除 这俩步都得具备原子性。加锁的时候springboot对redis的操作就提供一个方法就是具备原子性的,判断uuid,释放锁这个步骤得写成lua脚本
我们项目中用的一般是在redisson基础上进一步封装的redissonClient , 他的过期时间和自动续期次数都可以自定义。
事务
分布式事务控制解决方案从入门到应用,微服务分布式系统开发教程
Mysql+Redis夺命30问,全部都答对金九银十拿offer就稳了!
微服务生态
https://www.bilibili.com/video/BV1gu4y1N7gL/?spm_id_from=333.337.search-card.all.click&vd_source=724a3df3269224133d5f7c7de76c979b 微服务面试教程,一口气刷完面试必问的23个java微服务面试核心知识点,让你面试少走99%的弯路!
线上问题如何排查,如何解决的
57.CPU飙升,使用Arthas,3秒定位问题代码_哔哩哔哩_bilibili
你们这个系统上线了吗?部署了多少个服务?

我们公司服务全部是上云的,有专门的运维人员负责,之前看过公司的云平台:
以上是一个非常大的公司的服务器参考值,一般公司服务器没那么多,就那么几台服务器,但是数据库、redis、OSS等都是有的
订单号及分库分表
订单号怎么创建的?
- 使用雪花算法
- 使用Redis的increment进行自增长
分库分表 参考文档: 订单模块中分库分表实现思路及方案
AI应用
AI五件套:角色定制+记忆存储+Function Calling+MCP+RAG知识库
Agent:基于大模型训练自己的智能体
模型选型:千问、deepseek
AI框架:springAI、langchain4j
参考视频:springAI场景及相关原理篇
本地部署:
- 服务器与硬件参考配置
| 模型规模 | 推荐GPU | GPU数量 | 显存要求 | CPU | 内存 | 存储 | 网络 |
|---|---|---|---|---|---|---|---|
| 7B-14B (推理) | RTX 3090/4090, A10G | 1-2 | 24GB+ | 8核+ | 32GB+ | 1TB SSD+ | 千兆以太网 |
| 30B-70B (推理/轻量训练) | A100 40GB/80GB, H100 | 2-4 | 80GB+ (总计) | 16核+ | 64GB+ | 2TB NVMe SSD+ | 10GbE / InfiniBand |
| >100B (训练) | A100/H100 SXM | 8-64+ | 数TB | 高核心数 (如AMD EPYC) | 512GB+ | 高速分布式存储 | 高速InfiniBand (200/400 Gbps) |
关键硬件说明:
- GPU:是核心,NVIDIA的A100/H100是数据中心主流,消费级的3090/4090因其高显存性价比也被广泛用于本地部署。
- 显存 (VRAM):这是最关键的限制因素。模型参数、优化器状态、激活值都需要显存。通常需要模型大小的2-3倍显存才能流畅运行。
- CPU & 内存:负责数据预处理、模型加载和系统管理。内存应至少是显存的2-3倍。
- 存储:模型文件巨大(7B模型约15GB,70B模型约140GB),需要高速SSD或NVMe存储。
- 网络:在多节点集群中,节点间通信是瓶颈。InfiniBand提供极低延迟和高带宽,远超普通以太网。
部署工具与框架参考
- 推理服务框架:
- Ollama:极其流行,命令行工具,支持一键下载和运行多种开源模型(如Llama, DeepSeek),非常适合本地部署和快速测试。
- vLLM:专为高吞吐量和低延迟推理设计,支持PagedAttention等优化技术。
- Text Generation Inference (TGI):Hugging Face推出的推理服务器,功能强大。
- DeepSpeed:微软开发,支持高效的推理和训练,尤其擅长模型并行。
- 模型管理与交互:
- DS本地部署大师:如文中所述,专为非技术人员设计的图形化工具,简化了DeepSeek等模型的部署流程。
- ChatBox AI:提供美观的图形界面,可以连接Ollama等后端服务。
- LM Studio:类似Ollama的桌面应用,提供用户友好的界面。
- 集群管理:
- Kubernetes (K8s):容器编排的事实标准,可以管理GPU资源和部署模型服务。
- Slurm:高性能计算(HPC)领域常用的作业调度系统。
- Ray:一个用于构建和运行分布式应用的通用框架,也常用于AI工作负载。
- 实际案例参考
- 个人开发者:使用一台配备RTX 3090(24GB显存)的台式机,通过Ollama部署
deepseek-r1:7b模型,实现本地化、私密的AI问答,避免云端服务的排队和隐私问题。 - 企业级部署:建立一个由4台服务器组成的集群,每台配备8块A100 80GB GPU,通过InfiniBand连接,使用DeepSpeed或vLLM部署一个70B参数的定制化大模型,为内部员工提供智能知识库问答服务。
- 去中心化训练:如
Psyche Network,个人开发者贡献自己的3090显卡算力,通过点对点网络和DisTrO优化器,共同训练一个40B参数的模型,突破单机算力限制。
- 个人开发者:使用一台配备RTX 3090(24GB显存)的台式机,通过Ollama部署
支付
QPS、TPS、RT、PV、UV
QPS(Queries Per Second)每秒查询
每秒查询数率,系统每秒能够处理的查询请求次数,即一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。
有两种计算公式:
QPS = req/sec = 请求数/秒
QPS = 总请求数 / ( 进程总数 * 请求时间 )
TPS(Transactions Per Second)每秒事务
每秒事务数,即每秒系统能够处理的事务次数。
TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
TPS与QPS区别
一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。而在这个TPS中,为了处理第一次请求可能会引发后续多次对服务端的访问才能完成这次工作,每次访问都算一个QPS。所以,一个TPS可能包含多个QPS
对于一个页面的一次访问,形成一个Tps;但一次页面请求,可能产生多次对服务器的请求,服务器对这些请求,就可计入“Qps”之中。如:访问一个页面会请求服务器3次,一次放,产生一个“T”,产生3个“Q”
PV(Page View)页面访问量
页面被浏览的次数,每次用户访问或者刷新页面都会被计算在内。
用户对同一页面的多次刷新,访问量累计。与 PV 相关的还有 RV,即重复访问者数量(repeat visitors)。
计算公式:
日PV=QPS*60*60*24 //即QPS乘以一天的秒数
峰值QPS=(日PV*80%)/(60*60*24*20%)//通用公式每天80%的访问集中在20%的时间里,这20%时间叫做峰值时间
RT(Response-time)响应时间
执行一个请求从开始到最后收到响应数据所花费的总体时间,即从客户端发起请求到收到服务器响应结果的时间。它的数值大小直接反应了系统的快慢。
用户平均请求等待时间(Time per requests)
计算公式:用户平局请求等待时间 = 总时间 / (总请求数 / 并发用户数)
服务器平均请求等待时间(Time per requests: across all concurrent requests)
计算公式:服务器平均等待时间 = 总时间 / 总请求数 = 用户平均请求等待时间 / 并发用户数
QPS和RT的关系?
1.对于大部分web系统,响应时间一般由CPU执行时间,线程等待时间(IO等待,sleep, wait)时间组成。QPS和RT成反比关系
2.在实际的测试环境中,QPS和RT并不是非常直接的反比关系
并发数(The number of concurrent connections)
并发请求数/连接数,是指系统同时能处理的请求数量,这个也是反应了系统的负载能力。
计算公式:并发量 = QPS * 平均响应时间,并发请求数 = 并发用户数 * 单个用户平均请求数
并发用户数(The number of concurrent users, Concurrent Level)
指的是某个时刻同时在线的用户数。一个用户可能同时会产生多个会话,也即多个请求连接数。
RPS(Requests Per Second )吞吐量
吞吐量是指单位时间内系统能处理的请求数量,单位是 reqs/s,体现系统处理请求的能力。
吞吐率是基于并发用户数的。这句话代表了两个含义:a、吞吐率和并发用户数相关;b、不同的并发用户数下,吞吐率一般是不同的
某个并发用户数下单位时间内能处理的最大的请求数,称之为最大吞吐率。
系统的吞吐量(承压能力)与request对CPU的消耗、外部接口、IO等等紧密关联。单个request 对CPU消耗越高,外部系统接口、IO速度越慢,系统吞吐能力越低,反之越高。系统吞吐量几个重要参数:QPS(TPS)、并发数、响应时间。
计算公式:吞吐率 =并发请求数/总请求处理时长
比如:在并发用户数为1000时,一共有5000个请求,请求了5分钟,那么每秒钟,服务器可以处理5000/5*60 = 16个请求呢。这就是服务器的吞吐率
实际举例
我们通过一个实例来把上面几个概念串起来理解。如果每天 80% 的访问集中在 20% 的时间里,这 20% 时间就叫做峰值时间。
公式:( 总PV数 * 80% ) / ( 每天秒数 * 20% ) = 峰值时间每秒请求数(QPS)
机器:峰值时间每秒QPS / 单台机器的QPS = 需要的机器
1、每天300w PV 的在单台机器上,这台机器需要多少QPS?
( 3000000 * 0.8 ) / (86400 * 0.2 ) = 139 (QPS)
2、如果一台机器的QPS是58,需要几台机器来支持?
139 / 58 = 3
3、服务器计算
服务器数量 = ceil( 每天总PV / 单台服务器每天总PV )
最佳线程数
1、单线程QPS公式:QPS=1000ms/RT
对同一个系统而言,支持的线程数越多,QPS越高。假设一个RT是80ms,则可以很容易的计算出QPS,QPS = 1000/80 = 12.5
多线程场景,如果把服务端的线程数提升到2,那么整个系统的QPS则为 2*(1000/80) = 25, 可见QPS随着线程的增加而线性增长,那QPS上不去就加线程呗,听起来很有道理,公司也说的通,但是往往现实并非如此。
2、最佳线程数量
刚好消耗完服务器的瓶颈资源的临界线程数,公式如下
最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间)* cpu数量
特性:
在达到最佳线程数的时候,线程数量继续递增,则QPS不变,而响应时间变长,持续递增线程数量,则QPS开始下降。
每个系统都有其最佳线程数量,但是不同状态下,最佳线程数量是会变化的。
瓶颈资源可以是CPU,可以是内存,可以是锁资源,IO资源:超过最佳线程数-导致资源的竞争,超过最佳线程数-响应时间递增。
UV(Unique Visitor)独立访客访问数
统计 1 天内访问某站点的用户数(以 cookie 为依据),一台电脑终端为一个访客。
可以理解成访问某网站的电脑的数量。网站判断来访电脑的身份是通过来访电脑的 cookies 实现的。如果更换了 IP 后但不清除 cookies,再访问相同网站,该网站的统计中 UV 数是不变的。如果用户不保存 cookies 访问、清除了 cookies 或者更换设备访问,计数会加 1。00:00-24:00 内相同的客户端多次访问只计为 1 个访客。
IP(Internet Protocol)独立 IP 数
指 1 天内多少个独立的 IP 浏览了页面,即统计不同的 IP 浏览用户数量。
同一 IP 不管访问了几个页面,独立 IP 数均为 1;不同的 IP 浏览页面,计数会加 1。IP 是基于用户广域网 IP 地址来区分不同的访问者的,所以,多个用户(多个局域网 IP)在同一个路由器(同一个广域网 IP)内上网,可能被记录为一个独立 IP 访问者。如果用户不断更换 IP,则有可能被多次统计。
| 指标 | 试运营阶段 | 低版配置 | 成熟中型电商 | 健康参考值 |
|---|---|---|---|---|
| 注册用户数 | 数千 | 数万 | 数十万至百万 | - |
| 月活跃用户(MAU) | 2000 | 5万 | 50万 | - |
| 日活跃用户(DAU) | 10%-20% | 10%-20% | 10%-20% | |
| 日PV值 | 2000 | 5万 | 50万 | PV/UV ≥ 2-3 最佳 |
| 日UV值 | 1000 | 3万 | 30万 |
运营建议
- 注册转化:若UV远高于注册用户数,需优化注册流程(如简化表单、提供游客转注册激励)。
- 流量质量:通过PV/UV比值分析用户粘性,低比值可能需优化页面内容或导航设计。
- 服务器配置:根据PV/UV规模选择服务器(如低版配置需4核CPU、8G内存、10M带宽)。
典型场景参考值
| 系统类型 | 常规值 | 高并发门槛 |
|---|---|---|
| 小型网站 | QPS<500 | QPS>2000 |
| 中型电商 | PS 300-800 | TPS>1500 |
| 金融支付系统 | TPS 500-1200 | TPS>3000 |
| 大型社交平台 | QPS>5000 | QPS>20000 |
行业标杆案例
支付宝2022双十一:峰值TPS达58.3万笔/秒
12306售票系统:春运期间QPS突破150万次/秒
微博热点事件:突发QPS可达300万次/秒
影响因素
系统架构设计:微服务化程度直接影响横向扩展能力
数据库优化:读写分离策略可提升30%-50%吞吐量
缓存机制:合理使用Redis集群可减少70%数据库访问
网络带宽:万兆网络相比千兆网络吞吐量提升8-10倍
优化实践方案
负载均衡:采用Nginx+LVS组合,实测可承载10万级QPS
分库分表:订单表按用户ID哈希分128个库,提升TPS处理能力
异步处理:将支付成功通知改为消息队列处理,系统吞吐量提升4倍
熔断机制:配置Hystrix熔断策略,防止雪崩效应
tips:其他模块大家可以参考着去整理
在公司怎么代码检测/评审的?
- 我会在我的项目中集成:静态代码分析工具 SonarQube,引入我们的Maven依赖中
- 代码规范遵循阿里的Java开发规范,代码要进行格式化
- 进行单元测试(JUnit/TestNG)及统计测试覆盖率(JaCoCo)
- 使用GitLab MR/Gerrit进行代码审查(Code Review), 重点审查:逻辑正确性、设计合理性、性能考虑、安全风险(SQL注入、XSS攻击等),通常要求至少1-2名同事批准后才能合并
- 安全扫描,集成Snyk 扫描第三方依赖中的已知漏洞,使用 SonarQube Security Hotspots 检测代码中的安全缺陷
- 流水线部署时,每次提交代码(或合并请求)时自动触发构建、代码分析、单元测试和覆盖率检查。如果检测失败,阻止合并或部署。
在公司怎么跟测试和运维怎么对接的啊?
跟测试对接
需求阶段: 提前明确边界和技术难点,提供接口文档,如使Swagger/OpenAPI 或 YAPI 等生成的文档,便于测试编写用例。 接口变更时及时同步。
开发完成后提测: 提测流程(Test Submission):完成功能开发 + 自测后,填写提测单(在 Jira、禅道、钉钉等工具中)。 明确提测版本(Git Tag/分支)、部署环境、测试注意事项。 部署测试环境: 配合运维或使用 CI/CD 流水线将代码部署到 测试环境(Test Environment)。 确保配置、数据库、依赖服务(如 Redis、MQ)已正确就位。
测试执行阶段协作 Bug 跟踪与修复: 测试提交 Bug 后,通过 Jira/Bugzilla 等工具认领并修复。 修复后及时通知测试人员回归验证。 对争议问题组织三方(开发、测试、产品)沟通。 配合接口/自动化测试: 协助测试人员调试接口问题。 提供测试数据构造脚本或 Mock 服务(如使用 WireMock)。
自动化测试协作 单元测试:开发负责编写,测试可审查覆盖率。 接口自动化:与测试共同维护接口测试用例(如用 Postman + Newman 或 RestAssured)。 UI 自动化:提供稳定的页面标识(如 data-testid),便于测试定位元素。
与运维团队的对接
环境与部署 环境管理: 明确开发、测试、预发、生产等环境的访问方式和配置差异。 使用 配置中心(如 Nacos、Apollo)实现环境隔离。 部署流程: 通过 CI/CD 流水线(Jenkins/GitLab CI)自动打包并部署到各环境。 运维负责审批生产环境发布(如 Jenkins 的“人工确认”节点)。 发布文档: 提交发布说明,包括:版本号、变更内容、回滚方案、影响范围。
监控与日志 日志规范: 使用统一日志框架(如 Logback + SLF4J),输出结构化日志(JSON 格式)。 关键操作打日志,便于运维排查。 接入监控系统: 集成 Prometheus(普罗米修斯) + Grafana 暴露 JVM、接口 QPS、响应时间等指标。 接入 ELK 或 SkyWalking 实现日志收集与链路追踪。 异常自动上报 Sentry 或 日志告警系统。
上线与应急响应 上线支持: 上线期间在岗,配合运维观察系统状态。 出现问题快速定位,必要时执行回滚。 故障排查: 运维发现服务异常(如 CPU 飙升、OOM)后通知开发。 开发通过日志、堆栈、监控数据定位问题,提供修复方案。 应急预案: 共同制定服务降级、熔断、限流策略(如使用 Sentinel)。 定期参与演练。
性能与容量规划 压测支持: 配合运维和测试进行性能测试(JMeter/LoadRunner)。 优化慢接口、数据库查询、缓存使用等。 资源申请: 根据业务增长预估 JVM 参数、服务器资源,提前与运维沟通扩容。