服务端面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 05月30日 19:40

MariaDB 性能调优应该先看慢查询还是参数?

MariaDB 性能调优不要一上来就改几十个参数。更可靠的顺序是:先确认慢在哪里,再看执行计划和索引,最后才调整 InnoDB、连接数、临时表、排序缓冲等配置。参数调优像放大器,SQL 和索引方向对了,它能放大性能;方向错了,它只会把问题藏得更深。追问慢查询应该怎么打开和分析?开启 slow_query_log,把 long_query_time 设置成符合业务的阈值。再按出现频率、扫描行数、总耗时排序,不要只盯单次最慢 SQL。最关键的参数是哪个?InnoDB 场景先看 innodb_buffer_pool_size,因为它影响数据页和索引页缓存命中率。专用数据库常按内存 60%-80% 估算,但要留空间给系统和连接线程。索引优化有什么取舍?高频 WHERE、JOIN、ORDER BY 列适合联合索引,但字段不是越多越好。索引会提升读,也会拖慢写入、占用磁盘。max_connections 越大越好吗?不是。它只允许更多连接进来,不代表数据库能同时跑更多重查询。连接过多会放大线程、内存和锁竞争。sortbuffersize 和 joinbuffersize 能随便调大吗?它们通常是会话级参数,并发一高总内存会放大。只有确认瓶颈并估算过峰值连接数后,才适合小步调整。写段配置slow_query_log=1long_query_time=1innodb_buffer_pool_size=8GEXPLAIN SELECT * FROM orders WHERE user_id=1001 ORDER BY created_at DESC;CREATE INDEX idx_orders_user_created ON orders(user_id, created_at);
服务端阅读 05月30日 19:40

MariaDB 如何做安全配置?哪些设置最容易被忽略?

MariaDB 安全配置不能只理解成“把 root 密码改复杂一点”。更稳妥的做法是从账号权限、网络入口、传输加密、文件导入导出、审计日志和备份存放几个层面一起收紧。很多事故不是数据库漏洞导致的,而是测试账号没删、远程访问开太大、备份文件裸放。追问最小权限应该怎么落地?应用不要连 root,也不要授予 ALL PRIVILEGES。按业务库创建专用账号,只授予需要的 SELECT、INSERT、UPDATE、DELETE 等权限。远程访问应该怎么限制?bind-address 不要直接监听公网地址,账号 host 也不要写成 %。需要临时排障时可以开白名单,但结束后要及时移除。SSL/TLS 一定要开启吗?跨主机连接建议开启 require-secure-transport,避免账号密码和查询内容明文暴露。上线前要确认客户端驱动和证书配置支持。哪些文件相关配置容易被忽略?local_infile 建议关闭,secure_file_priv 应限制导入导出目录。备份文件要加密并放在受控目录。审计和日常运维要看什么?关注失败登录、权限变更、异常来源 IP、大批量导出和高危 DDL。日志要轮转,否则磁盘打满也是事故。写段配置CREATE USER 'app_user'@'10.%' IDENTIFIED BY 'strong_password';GRANT SELECT, INSERT, UPDATE ON app_db.* TO 'app_user'@'10.%';bind-address=10.0.0.5local_infile=0require-secure-transport=ON
服务端阅读 05月30日 19:40

MariaDB 分区表有哪些类型?如何创建和维护?

MariaDB 分区表适合处理持续增长、访问模式明确的大表,比如日志、订单、流水。它不是把业务拆成多张表,而是在一张逻辑表下面按规则拆出多个物理分区;查询仍写同一张表名,优化器在条件命中分区键时可以做分区裁剪。分区能降低历史数据维护成本,也能减少部分扫描范围,但不能替代索引。追问MariaDB 分区表有哪些类型?常见类型有 RANGE、LIST、HASH 和 KEY。RANGE 适合按时间、ID 区间切分;LIST 适合地区、业务状态;HASH/KEY 主要用来均匀打散。RANGE、LIST、HASH、KEY 应该怎么选?查询经常带 created_at、biz_date 时优先 RANGE;固定枚举隔离用 LIST;没有明显范围条件但要拆散数据时考虑 HASH/KEY。创建分区表最容易踩什么坑?主键和唯一键通常必须包含分区键,否则可能建表失败或唯一性语义不符合预期。分区键不出现在查询条件里,收益也会很有限。历史分区怎么维护?RANGE 分区通常提前创建未来分区。删除历史数据时,DROP PARTITION 比大批量 DELETE 更快,但它是物理删除,归档前要先备份。怎么确认查询真的用到了分区?用 EXPLAIN PARTITIONS 看 partitions 字段,确认只访问目标分区,而不是全分区扫描。写段 SQLCREATE TABLE orders (id BIGINT, created_at DATE, PRIMARY KEY(id, created_at))PARTITION BY RANGE (TO_DAYS(created_at)) ( PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')), PARTITION pmax VALUES LESS THAN MAXVALUE);
服务端阅读 05月30日 15:53

什么是 Nginx?它为什么适合高并发?

Nginx 是一个高性能 Web 服务器,也常用作反向代理、负载均衡器、静态资源服务器和 API 网关。它适合高并发,核心原因是事件驱动和异步非阻塞 I/O:少量 worker 进程通过事件循环处理大量连接,不需要给每个连接都分配一个线程。简单说,Apache 更像“一个请求安排一个人盯着”,Nginx 更像“少量工作人员按事件通知处理”,长连接和静态资源场景下资源消耗更低。追问Nginx 主要解决什么问题?它常解决三件事:对外提供 HTTP 服务,把请求转发给后端,以及把流量分摊到多台服务器。实际项目里还会让它处理 HTTPS 终止、静态资源缓存、限流、压缩、访问日志和灰度路由。为什么说 Nginx 适合高并发?它不为每个连接创建独立线程,而是通过 worker 进程和事件循环处理连接状态变化。大量连接处于等待网络 I/O 时,Nginx 可以把 CPU 留给真正有事件的连接,所以并发连接数上来后仍然比较稳。Nginx 和 Apache 有什么区别?Nginx 更擅长高并发连接、反向代理和静态资源;Apache 模块生态成熟,动态内容处理能力强。现代架构里常见做法是 Nginx 放入口层,业务逻辑交给 Node.js、Java、Go、PHP-FPM 等后端服务。反向代理和负载均衡分别是什么?反向代理是客户端只访问 Nginx,由 Nginx 转发到真实后端;负载均衡是在多个后端实例之间分配请求。两者经常一起用:Nginx 既隐藏后端地址,也按权重、轮询、IP Hash 等策略分流。实际项目里怎么用?常见链路是“用户 → CDN/云负载均衡 → Nginx → 应用服务”。Nginx 负责入口层的 TLS、路由、缓存、限流和日志;后端只关注业务处理。排查问题时也通常先看 Nginx access.log、error.log 和 upstream 响应时间。写段配置upstream app { server 127.0.0.1:3000; server 127.0.0.1:3001;}server { listen 80; server_name example.com; location /static/ { root /var/www; expires 7d; } location /api/ { proxy_pass http://app; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}
服务端阅读 05月30日 12:39

Nginx 常见故障有哪些?如何快速排查?

Nginx 常见故障主要看三类:状态码、连接和性能。502 多半是后端不可用或 upstream 配错;504 是后端响应太慢;403 是权限、目录索引或访问控制问题;404 看 root、alias、tryfiles;413 调 clientmaxbodysize;并发打满就查 worker_connections、文件描述符和后端连接池。排查顺序:先确认现象,再看 error.log,再跑 nginx -t,最后查后端和系统资源。追问502 和 504 有什么区别?502 是 Nginx 连不上或拿不到有效后端响应;504 是连上了但后端超时。403 一定是文件权限问题吗?不一定。还可能是目录没有 index、allow/deny 命中、SELinux 拦截,或 root/alias 写错。响应慢怎么定位?access log 记录 $request_time 和 $upstream_response_time,区分慢在客户端、Nginx、网络还是后端。
服务端阅读 05月30日 12:39

Nginx 常见部署架构有哪些?如何选择?

Nginx 常见部署架构主要有单机、反向代理、负载均衡、多层代理、CDN 源站、高可用、缓存层和 API 网关。选择时先看业务规模:小站用单机;多应用统一入口用反向代理;高并发用负载均衡;怕单点故障用 Keepalived 或云负载均衡;全球访问配 CDN;读多写少加缓存。别一开始就堆复杂架构,出问题时没人知道流量走哪一层。追问反向代理和负载均衡有什么区别?反向代理强调统一入口和转发,负载均衡强调把流量分给多台后端。实际项目里两者通常一起用。高可用架构怎么做?常见做法是两台 Nginx 配 Keepalived 共享 VIP,主节点故障后 VIP 漂移到备节点。云上更常用 SLB/ELB。写段配置upstream backend { server 10.0.0.11:8080; server 10.0.0.12:8080; }location / { proxy_pass http://backend; }
服务端阅读 05月30日 12:39

什么是 Logstash?它的工作原理是什么?

Logstash 是 Elastic Stack 里的服务端数据处理管道,用来采集、解析、转换并转发日志或事件。它的核心模型很简单:Input 负责进数据,Filter 负责处理数据,Output 负责送到 Elasticsearch、Kafka、文件等目标。真正的价值不只是“搬运日志”,而是把杂乱文本变成可检索、可分析的结构化字段。追问Logstash 的三段式流程是什么?Input 从 File、Beats、Kafka、HTTP 等来源读取事件;Filter 用 grok、mutate、date、geoip 等插件加工字段;Output 把结果写到 ES、Kafka、Redis 或文件。Logstash 和 Filebeat 有什么区别?Filebeat 更轻,主要负责边缘采集和转发;Logstash 更重,适合复杂解析、字段清洗和多目标路由。它的主要瓶颈在哪里?常见瓶颈在 Grok 正则、输出端 bulk 写入和队列堆积。
服务端阅读 05月30日 12:39

Logstash Grok 过滤器是什么?如何解析日志?

Grok 是 Logstash 里把非结构化日志拆成字段的过滤器,本质是“命名正则模板”。它用 %{PATTERN:field} 把文本匹配成字段,比如把 Nginx 日志拆出 IP、状态码、请求路径。面试时要说清:Grok 适合解析固定格式文本;解析失败会打 _grokparsefailure;性能上要避免一上来就用 %{GREEDYDATA} 贪婪匹配。追问Grok 和普通正则有什么区别?Grok 是对正则的封装,内置了很多模式,如 IP、NUMBER、TIMESTAMP_ISO8601。解析失败怎么排查?先用 Grok Debugger 验证模式,再看事件里是否出现 _grokparsefailure。写段配置filter { grok { match => { "message" => "%{TIMESTAMP_ISO8601:time} %{LOGLEVEL:level} %{GREEDYDATA:msg}" } }}
服务端阅读 05月30日 12:39

Logstash 常用输入插件有哪些?File 和 Kafka 怎么配置?

Logstash 输入插件负责从数据源读取事件,常见的有 File、Beats、Kafka、HTTP、TCP/UDP、Syslog、JDBC、Redis。File 适合读取本机或挂载目录里的日志,Kafka 适合高吞吐、可回放的日志总线。生产里通常让 Filebeat 采集文件,再发给 Logstash;只有需要复杂本地读取规则时,才直接用 File input。追问File input 的 start_position 有什么坑?它只在文件第一次被 Logstash 发现时生效。读到哪里由 sincedb 记录,改成 beginning 不会自动重读旧文件。Kafka input 为什么要配 group_id?同组多个 Logstash 实例会分摊分区,不同组会各自消费一份。写段配置input { file { path => ["/var/log/app/*.log"] start_position => "end" } kafka { bootstrap_servers => "kafka:9092" topics => ["app-logs"] group_id => "logstash-app" }}
服务端阅读 05月30日 12:39

Logstash 常用输出插件有哪些?Elasticsearch 输出怎么配置?

Logstash 输出插件负责把处理后的事件送到目标系统。常见输出有 Elasticsearch、File、Kafka、Redis、HTTP、Stdout;生产里最常用的是 Elasticsearch,通常要配 hosts、index、认证、TLS、失败重试和条件路由。索引名建议按业务和日期拆分,比如 app-%{[service]}-%{+YYYY.MM.dd},不要所有日志都塞进一个大索引。追问Elasticsearch 输出必须配哪些参数?至少配 hosts 和 index。如果集群开启安全认证,还要配 user/password、ssl、cacert。Kafka、File、Stdout 分别适合什么场景?Kafka 适合解耦下游,File 适合落盘备份,Stdout 主要用于调试。实际项目里最容易踩什么坑?动态索引字段为空会生成脏索引,ES 认证或证书错误会导致持续重试。写段配置output { elasticsearch { hosts => ["https://es1:9200"] index => "app-%{+YYYY.MM.dd}" }}
服务端阅读 05月30日 12:39

Logstash 条件判断怎么写?常见操作符有哪些?

Logstash 条件判断写在 filter 和 output 里最常见,用来按字段、标签、正则或数值把事件分流。语法类似 if/else if/else:比较用 == != < > <= >=,逻辑用 and or not,正则用 =~ !~,包含判断用 in、not in。注意条件一般不能放在 input 插件内部,因为 input 阶段事件字段还没生成。追问in 判断字符串和数组有什么坑?"error" in [tags] 是判断标签数组是否包含 error;"err" in [message] 可能变成字符串包含判断。正则匹配适合放很多吗?不适合滥用。大量复杂正则会拖慢 pipeline,高频字段能用精确匹配就别用正则。解析失败怎么处理?利用 _grokparsefailure,再用条件把失败事件打标、落单独索引或输出到排查队列。写段配置filter { if [type] == "nginx" and [status] >= 500 { mutate { add_tag => ["server_error"] } }}
服务端阅读 05月30日 12:39

Logstash 集群如何部署?高可用方案怎么选?

Logstash 本身没有主从式集群,所谓 Logstash 集群通常是多实例横向部署:上游用 Beats 负载均衡、Kafka/Redis 缓冲,或 LB 分发流量;下游写 Elasticsearch。高可用重点不是“选主”,而是让输入可重放、实例可替换、配置一致、队列不丢数据。生产里优先推荐 Filebeat loadbalance + 多 Logstash;流量大或不能丢日志时,在前面加 Kafka,再让多个 Logstash consumer group 消费。追问Beats 直连和 Kafka 缓冲怎么选?Beats 直连简单、延迟低,适合可接受短暂重试的日志链路。Kafka 多一层运维成本,但能削峰、回放、隔离 Logstash 故障。持久化队列能替代 Kafka 吗?不能完全替代。persisted queue 只保护单个 Logstash 节点本地未处理事件,节点磁盘坏了仍可能丢。多节点配置怎么管理?配置放 Git,用 Ansible、K8s ConfigMap 或镜像发布同步。上线前跑 --config.test_and_exit。写段配置queue.type: persistedqueue.max_bytes: 1gbpipeline.workers: 4pipeline.batch.size: 500
服务端阅读 05月30日 10:11

ASCII 和 UTF-8、GB2312 有什么区别?

ASCII 是 7 位字符编码,只能表示 128 个字符,适合英文、数字、控制符等基础文本。它和其他编码最大的区别不是“好不好”,而是覆盖范围不同:ASCII 管英文基础字符,GB2312/Shift-JIS 管特定语言,UTF-8 管全球字符。追问ASCII 和 ISO-8859-1 有什么区别?ASCII 只有 0-127;ISO-8859-1 扩展到 256 个字符,前 128 个字符和 ASCII 完全一致。ASCII 和 GB2312、Shift-JIS 兼容吗?它们通常保留 ASCII 区间,所以英文部分可兼容。但中文、日文字符需要额外字节,不能按纯 ASCII 解析。ASCII 和 UTF-8 是什么关系?UTF-8 完全兼容 ASCII:ASCII 字符在 UTF-8 中仍然占 1 字节,编码值不变。现在还会直接选 ASCII 吗?纯英文协议、日志、嵌入式小文本可以用 ASCII;只要涉及中文、多语言、表情或国际化,优先选 UTF-8。
服务端阅读 05月30日 10:11

Gradle Wrapper 是什么?如何生成和使用?

Gradle Wrapper 是项目自带的一组脚本和配置,用来固定 Gradle 版本。开发者不用提前安装 Gradle,只要执行 ./gradlew build,Wrapper 就会按配置下载并运行指定版本,保证本地、CI、同事机器上的构建环境一致。它通常包含 gradlew、gradlew.bat、gradle/wrapper/gradle-wrapper.jar、gradle/wrapper/gradle-wrapper.properties,真正决定版本的是 distributionUrl。追问为什么不用本机安装的 gradle?本机版本可能不一致,构建结果就可能不同。Wrapper 把版本写进仓库,CI 也能复现。Wrapper 文件要提交到 Git 吗?要提交脚本、jar 和 properties。不要提交下载下来的 Gradle 分发包。bin 和 all 版本怎么选?多数项目用 bin,体积小、下载快;需要 IDE 查看源码或调试时再用 all。写段代码gradle wrapper --gradle-version 8.0./gradlew --version./gradlew clean build
服务端阅读 05月30日 10:11

Gradle 和 Maven 有什么区别?该怎么选?

Gradle 和 Maven 都是 Java 构建工具。简单选型:项目简单、团队追求稳定统一,选 Maven;项目大、模块多、构建慢,或做 Android/Kotlin,优先选 Gradle。Maven 的核心是“约定优于配置”,结构固定,上手容易,企业老项目和 CI 生态成熟;缺点是 XML 冗长,自定义构建逻辑不够顺手。Gradle 用 Groovy/Kotlin DSL,任务模型更灵活,增量构建、构建缓存、并行构建更强;缺点是自由度高,脚本容易失控。追问配置方式有什么区别?Maven 写 XML,声明式强;Gradle 写 DSL,更像代码,能表达复杂逻辑。构建速度谁更快?大型多模块项目通常 Gradle 更快,主要靠增量构建、缓存和 daemon。小项目差距不一定明显。老项目要不要从 Maven 迁到 Gradle?如果只是构建时间慢一点,不一定值得迁。只有当多模块构建、定制任务、Android/Kotlin 支持成为痛点,迁移收益才明显。面试里怎么回答选择标准?先说团队熟悉度和项目复杂度,再说构建性能;可维护性比炫技重要。
服务端阅读 05月30日 02:24

Promise.allSettled() 有什么作用?和 Promise.all 有什么区别?

Promise.allSettled() 会等待一组 Promise 全部结束,不管成功还是失败,最后返回每个任务的状态和结果;Promise.all() 则要求全部成功,只要一个 reject 就立刻 reject。面试里一句话区分:all 适合“缺一个都不行”,allSettled 适合“尽量都跑完,再分别处理结果”。例如批量请求、批量上传、页面多个独立模块加载,更适合 allSettled。追问allSettled 返回值长什么样?每一项都有 status。成功是 { status: 'fulfilled', value },失败是 { status: 'rejected', reason }。allSettled 会吞掉错误吗?不会吞,只是把错误包装进结果数组。你仍然要检查 rejected 项,否则失败会被业务层忽略。什么时候不能用 allSettled?任务之间有强依赖时不适合。比如用户信息失败后,后续请求必须停止,这时用 all 或串行 await 更清楚。和给每个 Promise 单独 catch 有什么区别?单独 catch 可以兼容更老环境,也能自定义返回结构;allSettled 是标准化写法,语义更明确。写段代码const results = await Promise.allSettled([fetchUser(), fetchPosts()]);const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value);const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
服务端阅读 05月30日 02:24

Promise 和回调函数有什么区别?为什么能解决回调地狱?

Promise 和回调函数都能处理异步,但抽象层次不同。回调是“异步完成后调用你给的函数”,容易出现多层嵌套、错误分散、控制流难追踪;Promise 把异步结果封装成一个有状态对象,可以链式调用、统一 catch、组合 all/race/any/allSettled。面试里可以说:Promise 不是让异步变同步,而是让异步流程更可组合、错误更集中、代码更容易维护。追问Promise 解决了回调地狱吗?解决了一部分。链式 then 可以拉平嵌套,async/await 又进一步接近同步写法。但如果业务拆分不好,Promise 链也会写得很乱。Promise 的错误处理强在哪里?回调通常要每层传 err;Promise 的错误会沿链传播,最后一个 catch 可以兜底处理。回调还有用吗?有。事件监听、流、Node 风格 API、低层库里仍常见回调。Promise 更适合“一次性成功或失败”的异步结果。Promise 有什么代价?它会引入微任务调度和对象创建,不适合滥用在纯同步逻辑里。性能敏感代码要避免无意义包装。写段代码readFile('a.txt') .then(parse) .then(save) .catch(handleError);
服务端阅读 05月30日 02:24

Promise.any() 有什么作用?和 Promise.race 有什么区别?

Promise.any() 的作用是:一组 Promise 里只要有一个成功,就立刻返回这个成功结果;只有全部失败时,才会 reject,并抛出 AggregateError。它适合“多个候选源,谁先成功用谁”的场景,比如多 CDN 拉资源、多个镜像接口兜底。面试里要强调:它忽略失败,只关心第一个成功;这和 Promise.race() 谁先 settled 就返回完全不同。追问Promise.any 和 Promise.race 最大区别是什么?race 看第一个完成,不管成功还是失败;any 看第一个成功,失败会被暂时忽略,除非全部失败。全部失败时会发生什么?会 reject 一个 AggregateError,里面的 errors 保存所有失败原因。不要只 catch 后打印 message,最好把 errors 也记录下来。它和 Promise.all 有什么区别?all 要全部成功才成功,一个失败就失败;any 只要一个成功就成功。一个适合“都要”,一个适合“有一个可用就行”。实际项目里怎么用?适合容灾兜底,不适合支付、写入、下单这类不能重复尝试的操作。并行请求多个源时也要考虑取消慢请求或控制成本。写段代码try { const data = await Promise.any([ fetch('/cdn-a/config.json'), fetch('/cdn-b/config.json') ]); console.log(await data.json());} catch (e) { console.error(e.errors);}
服务端阅读 05月30日 02:24

RPC 和 RESTful API 有什么区别?什么时候选 RPC?

RPC 和 RESTful API 的核心区别是抽象不同:RPC 像调用远程函数,关注方法、参数和返回值;REST 更像操作资源,关注 URL、HTTP 方法和状态码。内部微服务、低延迟、高吞吐、强类型契约、双向流式通信,通常选 RPC,比如 gRPC、Dubbo。对外开放接口、浏览器直接访问、需要易调试和缓存语义,REST 更合适。面试里不要说谁替代谁,关键是边界:内部效率优先选 RPC,对外通用性优先选 REST。追问RPC 为什么通常性能更好?很多 RPC 框架使用二进制序列化和长连接,协议开销更小,也更容易做连接复用、流式传输和代码生成。REST 的优势是什么?它基于 HTTP 语义,curl、Postman、浏览器都好调试;URL、状态码、缓存头也更适合开放平台和前后端协作。gRPC 能直接给浏览器用吗?原生 gRPC 对浏览器不友好,通常需要 gRPC-Web 或网关转换。面向公网用户时,很多团队会在外层提供 REST。项目里怎么组合两者?常见做法是外部 REST 网关,内部服务之间用 RPC。网关负责鉴权、限流、协议转换,内部服务专注高效调用。
服务端阅读 05月30日 02:24

RPC 常见序列化协议有哪些?各自怎么选?

RPC 常见序列化协议有 Protobuf、Thrift、JSON、Hessian、MessagePack 和 Avro。面试先给结论:内部高性能微服务优先 Protobuf 或 Thrift;需要可读、易调试、对外兼容用 JSON;Java 旧系统可能见到 Hessian;需要类 JSON 但更小的体积可考虑 MessagePack;大数据和日志链路常用 Avro。选择时看四件事:体积、速度、跨语言、schema 演进能力。追问Protobuf 为什么常用于 RPC?它是二进制格式,体积小、解析快,靠 .proto 定义字段和类型,跨语言代码生成成熟。缺点是调试不如 JSON 直观。Thrift 和 Protobuf 有什么区别?Thrift 更像一整套 RPC 方案,包含 IDL、协议和传输;Protobuf 更专注数据序列化,常和 gRPC 搭配使用。JSON 为什么还没被淘汰?因为它人类可读、生态通用、排查方便。对外 API、管理接口、低频调用里,调试成本往往比极致性能更重要。协议升级最容易出什么问题?字段删除、类型变更、枚举兼容最容易翻车。新增字段通常安全,变更字段语义要同时考虑新老客户端。写段代码message UserReq { int64 id = 1; string name = 2;}