JavaGuide 官方知识星球

这部分内容摘自 JavaGuide 下面几篇文章的重点:

JavaGuide

高性能基础:

CDN 工作原理详解

CDN 工作原理详解

负载均衡原理及算法详解

负载均衡原理及算法详解

数据库性能优化:

读写分离和分库分表详解

读写分离和分库分表详解

数据冷热分离详解

数据冷热分离详解

常见 SQL 优化手段总结

常见 SQL 优化手段总结

深度分页介绍及优化建议

深度分页介绍及优化建议

回答高性能问题的通用思路

回答高性能问题的通用思路

高性能面试题通常不会只考一个概念。面试官可能先问“系统接口很慢怎么优化”,再沿着入口流量、应用线程池、缓存、数据库和消息队列继续追问。

回答时可以沿着下面这条思路展开:

明确目标:要优化的是平均响应时间、P99、吞吐量、数据库压力,还是用户感知速度?

定位瓶颈:入口带宽、应用线程池、缓存命中率、慢 SQL、锁竞争、下游依赖和 MQ 积压,分别看哪些指标?

分层处理:CDN 和负载均衡解决入口问题,缓存和异步化降低应用压力,索引、读写分离和分库分表提升数据层能力。

说明代价:缓存会带来一致性问题,读写分离会带来主从延迟,分库分表会增加路由、事务和数据迁移成本。

验证效果:压测、灰度、监控、告警和回滚方案要一起准备。

题目没有给出业务量级时,可以先询问或主动做出合理假设,再说明方案。不要拿到“接口慢”就直接回答加缓存或分库分表。

高性能基础

高性能基础

⭐️什么是 CDN?为什么 CDN 能提升访问速度?

⭐️什么是 CDN?为什么 CDN 能提升访问速度?

CDN(Content Delivery Network,内容分发网络)会把静态资源缓存到离用户更近的边缘节点。用户请求资源时,会优先访问合适的 CDN 节点;缓存没有命中时,节点才会回源获取资源。

CDN 简易示意图

CDN 能提升访问速度,主要靠下面几点:

就近访问:用户不需要跨地域访问源站,网络时延更低。

边缘缓存:静态资源命中缓存后可以直接返回。

减少回源:大量重复请求被拦在边缘节点,源站压力随之下降。

网络优化:CDN 服务商通常会做跨地域、跨运营商链路调度。

图片、CSS、JavaScript、字体和下载文件通常适合 CDN 缓存。HTML 是否长期缓存要谨慎:HTML 可能引用新的静态资源,旧 HTML 长时间留在缓存中,容易继续请求已经被删除的旧文件。

CDN 回源是什么意思?

CDN 回源是什么意思?

CDN 节点没有缓存用户请求的资源,或者缓存已经过期时,会向源站请求资源,这个过程叫 回源。

CDN 缓存的完整生命周期

两条常见链路如下:

缓存命中:用户 → CDN 节点 → 用户。

缓存未命中:用户 → CDN 节点 → 源站 → CDN 节点 → 用户。

回源量过大时,需要检查缓存规则、资源 URL 和刷新预热策略。资源没有设置合理的 Cache-Control、静态文件名没有内容 hash、热点资源发布后没有预热,都可能导致频繁回源。

Cache-Control

⭐️HTML、JavaScript、CSS 和图片的缓存策略有什么区别?

⭐️HTML、JavaScript、CSS 和图片的缓存策略有什么区别?

推荐策略如下:

资源类型推荐缓存策略原因HTML不长期缓存,或者只做短时间缓存HTML 是入口文件,可能引用新的静态资源带内容 hash 的 JS/CSS长期缓存,并设置 immutable内容变化后文件名也会变化图片根据更新频率设置较长缓存图片通常比 HTML 更新得少sitemap、robots不缓存或者只做短时间缓存搜索引擎需要尽快获取最新地址和抓取规则

资源类型推荐缓存策略原因

资源类型

推荐缓存策略

原因

HTML不长期缓存,或者只做短时间缓存HTML 是入口文件,可能引用新的静态资源

HTML

不长期缓存,或者只做短时间缓存

HTML 是入口文件,可能引用新的静态资源

带内容 hash 的 JS/CSS长期缓存,并设置 immutable内容变化后文件名也会变化

带内容 hash 的 JS/CSS

长期缓存,并设置 immutable

immutable

内容变化后文件名也会变化

图片根据更新频率设置较长缓存图片通常比 HTML 更新得少

图片

根据更新频率设置较长缓存

图片通常比 HTML 更新得少

sitemap、robots不缓存或者只做短时间缓存搜索引擎需要尽快获取最新地址和抓取规则

sitemap、robots

不缓存或者只做短时间缓存

搜索引擎需要尽快获取最新地址和抓取规则

CDN 缓存的刷新和预热

静态站点部署时还有一个常见问题:部署脚本删除了旧 /assets/,但 CDN 或浏览器里仍然保留着旧 HTML,旧 HTML 再去请求旧 hash 文件就会返回 404,页面可能白屏。因此,部署时可以暂时保留旧静态资源,HTML 走短缓存,带内容 hash 的静态资源走长期缓存。

/assets/

什么是负载均衡?

什么是负载均衡?

负载均衡会把请求分发到多个后端节点,避免流量集中到单台机器。它可以提升系统的吞吐能力、可用性和横向扩展能力。

多服务实例负载均衡

常见实现方式分为两类:

服务端负载均衡:客户端先访问负载均衡器,再由负载均衡器转发到后端服务。Nginx、LVS、云负载均衡和 API 网关都属于这一类。

客户端负载均衡:客户端获得服务实例列表后,在本地选择目标实例。微服务内部调用经常采用这种方式。

基于 Nginx 的服务端负载均衡

负载均衡还要配合健康检查、慢节点剔除、节点预热和故障转移。只把请求平均分出去,但仍然向故障节点转发,系统依然不可用。

⭐️常见的负载均衡算法有哪些?

⭐️常见的负载均衡算法有哪些?

算法思路适用场景随机随机选择一个节点节点性能和请求成本接近轮询按顺序分发请求节点配置接近加权轮询配置更高的节点分配更多请求后端机器配置不同最小连接优先选择连接数较少的节点请求耗时差异较大最快响应时间优先选择响应更快的节点更关注请求延迟一致性哈希同类请求尽量落到同一节点缓存、会话和分片路由

算法思路适用场景

算法

思路

适用场景

随机随机选择一个节点节点性能和请求成本接近

随机

随机选择一个节点

节点性能和请求成本接近

轮询按顺序分发请求节点配置接近

轮询

按顺序分发请求

节点配置接近

加权轮询配置更高的节点分配更多请求后端机器配置不同

加权轮询

配置更高的节点分配更多请求

后端机器配置不同

最小连接优先选择连接数较少的节点请求耗时差异较大

最小连接

优先选择连接数较少的节点

请求耗时差异较大

最快响应时间优先选择响应更快的节点更关注请求延迟

最快响应时间

优先选择响应更快的节点

更关注请求延迟

一致性哈希同类请求尽量落到同一节点缓存、会话和分片路由

一致性哈希

同类请求尽量落到同一节点

缓存、会话和分片路由

一致性哈希在扩缩容时只需要迁移部分映射,适合缓存节点和分片路由。不过,选择算法时不能只看分布是否均匀,还要处理节点健康、热点请求、慢节点和会话粘滞等问题。

Dubbo 加权轮询负载均衡算法

数据库性能优化

数据库性能优化

⭐️什么是读写分离?它解决了什么问题?

⭐️什么是读写分离?它解决了什么问题?

读写分离把写请求交给主库,把部分读请求分散到从库,主要用于缓解 读多写少场景下的主库读压力。

读写分离示意图

典型流程如下:

主库处理写请求。

主库通过 binlog 将数据变更复制到从库。

应用或数据库代理把允许读旧数据的查询路由到从库。

读写分离会引入主从延迟。刚写完就要读取最新结果的请求,可以在一段时间内读主库,或者让业务接受短暂的最终一致性。从库也不是免费的读能力:报表、全表扫描和慢 SQL 同样会占用从库资源,并可能影响复制进度。

MySQL 主从复制的基本原理是什么?

MySQL 主从复制的基本原理是什么?

MySQL 主从复制依赖 binlog。主库记录数据变更,从库接收并重放这些变更。

MySQL 主从复制

可以按 3 个阶段理解:

主库提交事务,并把变更写入 binlog。

从库 I/O receiver 线程接收 binlog,写入 relay log。

从库 applier 线程读取 relay log,把变更应用到本地数据。

主从延迟可能出现在网络传输、从库写 relay log 和从库重放事务的任意阶段。

⭐️什么情况下会出现主从延迟?

⭐️什么情况下会出现主从延迟?

常见原因包括:

从库机器性能比主库差。

从库承担了过多或过重的读请求。

主库产生了大事务,从库重放耗时很长。

主从之间网络延迟较高或者网络发生抖动。

从库复制线程的并行度不足。

从库磁盘 I/O 或 CPU 已经达到瓶颈。

处理时应先确认延迟发生在接收阶段还是重放阶段,再针对性地提升从库规格、治理慢 SQL 和大事务、分散查询、调整并行复制参数。核心写后读链路仍然可以路由到主库,不能假设半同步复制会让从库立即可读。

什么是分库分表?

什么是分库分表?

分库分表把数据拆到多个库或多张表中,用于解决单库单表容量过大、读写压力过高的问题。

常见拆分方式如下:

垂直分库:按业务拆库,例如用户库、订单库、商品库。

水平分库:把同一张逻辑表的数据按规则分散到多个库。

垂直分表:按字段拆表,把大字段或低频字段拆出去。

水平分表:把同一张逻辑表的记录按行拆成多张表。

水平分库和水平分表经常一起使用:先按分片键路由到某个库,再访问这个库中的具体分表。

⭐️什么时候需要分库分表?

⭐️什么时候需要分库分表?

分库分表应该放在常规优化之后考虑。典型场景包括:

单表数据量持续增长,核心查询和写入已经明显变慢。

单库容量、连接数或写入能力接近瓶颈。

通过索引、SQL 优化、缓存和读写分离仍然无法满足容量或吞吐要求。

业务存在稳定的分片维度,例如用户 ID、租户 ID 或订单 ID。

不能只根据“单表达到某个固定行数”决定是否拆分。表结构、索引数量、记录大小、硬件配置、查询方式和延迟目标都会影响实际容量。

分库分表会带来哪些问题?

分库分表会带来哪些问题?

常见问题包括:

跨库 Join、聚合、排序和分页变复杂。

分布式事务和数据一致性处理变复杂。

全局唯一 ID 需要单独设计。

扩容和数据迁移成本高。

分片键选错可能造成数据倾斜和热点。

原有唯一约束可能无法跨分片直接保证。

ShardingSphere、MyCat 或自研中间层可以处理路由、归并和分布式主键,但不会消除业务复杂度。设计阶段仍然需要说明跨分片查询、数据迁移和故障恢复如何完成。

ShardingSphere 提供的功能

⭐️分片键应该如何选择?

⭐️分片键应该如何选择?

分片键应尽量满足下面几个要求:

覆盖主要查询:核心查询能带上分片键,避免扫描多个分片。

分布足够离散:数据和请求不要集中到少数分片。

长期保持稳定:分片键变化会触发跨分片迁移。

方便后续扩展:扩容时能够控制迁移范围和路由变化。

订单系统可以把用户 ID、商家 ID 或订单 ID 作为候选,但最终选择取决于主要查询链路。按用户 ID 分片方便查询用户订单,按订单 ID 分片方便按订单查询,两者对应的跨分片场景不同。

SQL 优化有哪些常见手段?

SQL 优化有哪些常见手段?

常见手段包括:

只查询需要的字段,避免滥用 SELECT *。

SELECT *

为高频查询条件和排序条件设计合适索引。

使用覆盖索引减少回表。

避免在索引列上使用不必要的函数和表达式。

使用批量操作减少网络往返。

减少不必要的大表 Join、排序和临时表。

根据业务修改深度分页方式。

通过慢查询日志和执行计划定位瓶颈。

回答 SQL 优化问题时,可以先说如何定位,再谈优化动作:先通过监控和慢查询日志找到目标 SQL,然后使用 EXPLAIN 或 EXPLAIN ANALYZE 查看访问方式、索引、扫描行数、排序和回表情况,最后修改索引、SQL 或表结构,并用真实执行数据验证结果。

EXPLAIN
EXPLAIN ANALYZE

尽量避免多表 Join

⭐️哪些情况可能导致索引没有发挥作用?

⭐️哪些情况可能导致索引没有发挥作用?

常见情况包括:

对索引列使用函数或表达式。

查询条件发生隐式类型转换。

使用前导模糊匹配,例如 LIKE '%abc'。

LIKE '%abc'

联合索引没有满足最左前缀原则。

范围查询之后的联合索引列无法继续用于有序匹配。

查询条件选择性太差,优化器判断全表扫描成本更低。

统计信息不准确,优化器选择了不合适的执行计划。

“索引失效”只是一个笼统说法。有些情况下索引仍然被使用,但只能扫描大量索引项;有些情况下优化器主动放弃了索引。最终应结合 EXPLAIN、真实扫描行数和耗时判断。

EXPLAIN

什么是深度分页?为什么会慢?

什么是深度分页?为什么会慢?

深度分页指页码非常靠后,例如:

SELECT * FROM orders ORDER BY id LIMIT 1000000, 10;
SELECT * FROM orders ORDER BY id LIMIT 1000000, 10;

数据库需要先找到并跳过前面的 1000000 条记录,再返回 10 条。查询如果还涉及回表、排序或临时表,偏移量越大,浪费的工作越多。

深度分页问题

⭐️深度分页如何优化?

⭐️深度分页如何优化?

方案思路适用场景游标分页使用上一页最后一条记录的排序键继续查信息流、只需要上一页/下一页延迟关联先在覆盖索引上分页,再关联原表大偏移且仍需传统页码覆盖索引查询字段全部位于索引中,避免回表返回字段较少限制页数限制最大可翻页范围搜索、日志等无需无限翻页的场景

方案思路适用场景

方案

思路

适用场景

游标分页使用上一页最后一条记录的排序键继续查信息流、只需要上一页/下一页

游标分页

使用上一页最后一条记录的排序键继续查

信息流、只需要上一页/下一页

延迟关联先在覆盖索引上分页,再关联原表大偏移且仍需传统页码

延迟关联

先在覆盖索引上分页,再关联原表

大偏移且仍需传统页码

覆盖索引查询字段全部位于索引中,避免回表返回字段较少

覆盖索引

查询字段全部位于索引中,避免回表

返回字段较少

限制页数限制最大可翻页范围搜索、日志等无需无限翻页的场景

限制页数

限制最大可翻页范围

搜索、日志等无需无限翻页的场景

游标分页应使用稳定且唯一的排序键。例如按 created_at 倒序时,可以同时带上 id,避免时间相同的记录在翻页时重复或遗漏。

created_at
id
SELECT id, created_at, amount
FROM orders
WHERE (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 20;
SELECT id, created_at, amount
FROM orders
WHERE (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 20;

如果产品必须支持任意跳页,优化空间会小很多;移动端信息流改成“加载下一页”后,游标分页通常更合适。

什么是数据冷热分离?

什么是数据冷热分离?

数据冷热分离会把高频访问的热数据和低频访问的冷数据分开存储。热数据留在主业务库或高性能存储中,冷数据迁移到成本更低、查询频率更低的存储中。

典型场景包括:

订单系统把近期订单留在在线库,历史订单转入归档库。

日志系统保留近期可检索日志,历史日志转入对象存储。

内容系统把热门内容和历史低频内容分层存储。

冷热分离中的冷数据迁移

冷热分离更关注数据生命周期和存储成本,分库分表更关注容量和并发扩展。两者解决的问题不同。

⭐️冷热分离迁移时如何保证一致性?

⭐️冷热分离迁移时如何保证一致性?

可以采用分批迁移、校验和补偿的方式:

按时间或 ID 范围分批迁移,避免大事务长时间占用资源。

记录迁移范围和任务状态,任务失败后能够重试。

迁移后校验记录数量、主键范围和关键字段摘要。

查询层根据时间、状态或路由表决定访问热库还是冷库。

观察一段时间后再删除热库中的冷数据,删除前保留回滚能力。

通过定期对账任务发现并修复不一致数据。

迁移期间如果数据仍然会更新,还需要明确写入规则:冻结冷数据更新、同步变更日志,或者在读取时合并新旧存储中的结果。

系统设计综合题

系统设计综合题

⭐️如何设计一个高性能订单系统?

⭐️如何设计一个高性能订单系统?

可以沿着订单请求的完整链路回答:

明确目标:先确认峰值 QPS、订单量、响应时间和库存一致性要求。

入口层:静态资源交给 CDN,API 请求通过负载均衡进入服务集群。

应用层:核心写接口做好限流和幂等,热点读请求使用缓存。

数据库层:先优化索引和 SQL;读压力高时做读写分离,单库单表达到瓶颈后再考虑分库分表。

查询层:历史订单可以做冷热分离,列表查询优先采用游标分页。

异步层:订单创建后的通知、积分和部分风控动作通过 MQ 异步处理。

稳定性:监控 QPS、P99、错误率、慢 SQL、连接池、缓存命中率和消息积压。

方案必须说明一致性取舍。例如,库存扣减和订单状态属于核心链路,不能仅依靠异步消息“最终会成功”;通知和积分通常可以异步处理,并通过重试和补偿保证最终完成。

高性能优化的核心思路是什么?

高性能优化的核心思路是什么?

高性能优化可以从 6 个方向展开:

减少访问距离:CDN、就近接入。

分散流量压力:负载均衡、服务水平扩容。

减少重复计算和读取:缓存、预计算、批处理。

降低数据层压力:索引、SQL 优化、读写分离、冷热分离和分库分表。

削峰和异步化:消息队列、任务队列。

持续测量和验证:监控、压测、灰度和容量评估。

优化前先定位瓶颈,修改后再比较吞吐、延迟、错误率和资源使用率。没有指标的“优化”很难判断是否有效,也可能只是把压力从一个组件转移到了另一个组件。

写在最后

写在最后

感谢你能看到这里,也希望这篇文章对你有点用。

JavaGuide 坚持更新 6 年多,近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助,非常欢迎点个免费的 Star 支持下(完全自愿,觉得有收获再点就好):GitHub | Gitee。

GitHub

Gitee

如果你想要付费支持/面试辅导(比如简历优化、一对一提问、高频考点突击资料等)的话,欢迎了解我的知识星球。已经坚持维护六年,内容持续更新,虽白菜价(0.4元/天)但质量很高,主打一个良心!

知识星球

JavaGuide 公众号