2026-08-12约 12 分钟
BBR 系列算法是 Google 基于带宽-延迟模型设计的拥塞控制方案。BBRv1 在高丢包场景下存在 bufferbloat 问题,BBRv2 引入了丢包信号但收敛速度不理想。BBRv3 重新设计了 pacing rate 计算和瓶颈队列追踪机制,在 RTT 100ms+、丢包 1%-5% 的长肥管道场景下吞吐量相比 BBRv2 提升约 40%。
实际调优中,除了选择正确的算法,还需要关注以下几个内核参数的配合:
# sysctl 网络调优关键参数
net.ipv4.tcp_no_metrics_save = 1 # 不缓存路由度量,避免旧连接影响新连接
net.ipv4.tcp_moderate_rcvbuf = 1 # 启用自动接收缓冲区调节
net.ipv4.tcp_slow_start_after_idle = 0 # 空闲后不重置拥塞窗口
net.ipv4.tcp_fastopen = 3 # 启用客户端+服务端 TFO
其中 tcp_slow_start_after_idle 对长连接服务(如数据库连接池、gRPC 长连接)影响最大——关闭后可以避免每次空闲恢复时从初始拥塞窗口重新慢启动。
TCPBBRLinux内核性能调优
2026-08-05约 15 分钟
日志解析是后端基础设施中的高频操作。传统方案使用 BufReader 逐行读取后正则匹配,在 GB 级日志文件上 I/O 和内存拷贝成为瓶颈。本文展示如何利用 Rust 的 mmap + memchr 实现零拷贝的 SIMD 加速行扫描,实测解析速度达到每秒 2.3GB。
核心思路是将文件通过 mmap 映射到地址空间,省去用户态的 read/write 拷贝,再用 memchr 的 SIMD 实现(SSE2/AVX2)快速定位换行符:
use memmap2::MmapOptions;
use std::fs::File;
fn stream_lines(path: &str) -> anyhow::Result<()> {
let file = File::open(path)?;
let mmap = unsafe { MmapOptions::new().map(&file)? };
for chunk in mmap.chunks(4096) {
let mut start = 0;
while let Some(pos) = memchr::memchr(b'\n', &chunk[start..]) {
let line = &chunk[start..start + pos];
process_line(line);
start += pos + 1;
}
}
Ok(())
}
在AMD EPYC 7763 上的 benchmark 显示,此方案比 BufReader + Lines 快 8.2 倍,内存分配次数减少 99.7%。
Rust零拷贝性能SIMD
2026-07-28约 18 分钟
Go 的并发标记清除 GC 在大多数场景下表现优秀,但在高 QPS 低延迟的交易系统中,GC STW(Stop-The-World)导致的毛刺仍然不可忽视。本文记录了我们在一个日均 8000 万次请求的微服务上,将 GC P99 从 12ms 优化到 800μs 的完整过程。
关键优化手段:
1. 减少堆上指针数量——将高频路径上的 map[string]interface{} 替换为结构化 schema,GC 扫描对象数从 420 万降到 38 万。
2. GOGC 动态调整——在低峰期设 GOGC=200 节省内存,高峰期切到 GOGC=50 减少单次 GC 耗时。
3. offheap 分配——热路径上的 buffer pool 改用 mmap 管理,完全绕过 GC。
// GOGC 动态调整示例
import "runtime/debug"
func adjustGC(qps float64) {
if qps > 100000 {
debug.SetGCPercent(50) // 高峰:频繁小GC
} else {
debug.SetGCPercent(200) // 低峰:减少CPU开销
}
}
GoGC延迟优化性能
2026-07-20约 8 分钟
很多开发者将 TIME_WAIT 视为性能问题的万恶之源,盲目设置 net.ipv4.tcp_tw_reuse=1。实际上 TIME_WAIT 是 TCP 协议保证可靠性的核心机制——它确保最后一个 ACK 到达对端,并防止旧连接的延迟数据包干扰新连接。
TIME_WAIT 真正需要关注的场景:
• 短连接高并发(如 HTTP/1.1 未开启 keep-alive)——解决方案是启用连接池或升级到 HTTP/2
• 主动关闭方大量出现 TIME_WAIT ——考虑使用长连接、调整 tcp_max_tw_buckets
对于被动关闭方(server 端),应该关注的是 FIN_WAIT2 超时而非 TIME_WAIT:
# 合理的内核参数配置
net.ipv4.tcp_fin_timeout = 15 # FIN_WAIT2 超时
net.ipv4.tcp_tw_reuse = 1 # 安全复用 TIME_WAIT(仅客户端)
net.ipv4.tcp_max_tw_buckets = 180000 # TIME_WAIT 数量上限
TCP网络编程Linux
2026-07-12约 10 分钟
标准库的 HashMap 提供 O(1) 的插入和查找,但不支持 LRU 淘汰策略。在缓存系统、数据库 Buffer Pool 等场景下,我们需要 O(1) 的访问 + O(1) 的淘汰。经典方案是 HashMap + Doubly Linked List 的组合。
Rust 实现的关键在于自引用结构的处理——Rust 的所有权系统不允许一个 struct 同时拥有引用和被引用:
use std::collections::HashMap;
use std::ptr::NonNull;
struct Entry<K, V> {
key: K,
value: V,
prev: Option<NonNull<Entry<K, V>>>,
next: Option<NonNull<Entry<K, V>>>,
}
pub struct LruCache<K, V> {
map: HashMap<K, Box<Entry<K, V>>>,
head: Option<NonNull<Entry<K, V>>>,
tail: Option<NonNull<Entry<K, V>>>,
capacity: usize,
}
本文详细讲解了 unsafe 指针操作的正确姿势,以及如何用 Box::into_raw / Box::from_raw 安全地管理链表节点生命周期。
Rust数据结构LRUunsafe
2026-07-05约 14 分钟
epoll 是 Linux 上高性能网络编程的基石。它的两种触发模式——水平触发(LT)和边缘触发(ET)——在实际项目中有着截然不同的适用场景。
LT 模式下,只要 fd 处于可读/可写状态,每次 epoll_wait 都会通知。编程模型简单,但可能导致不必要的系统调用。ET 模式仅在状态变化时通知一次,性能更高但要求开发者必须一次性读完所有数据(用 EAGAIN 循环)。
实际选型建议:
• 用 LT:连接数多但活跃度低的场景(如 IM 长连接网关),编程简单不易出 bug
• 用 ET:连接少但吞吐要求极高的场景(如 RPC server),需要配合 O_NONBLOCK 和完善的错误处理
// ET 模式下必须循环读取直到 EAGAIN
loop {
match fd.read(&mut buf) {
Ok(n) if n > 0 => process(&buf[..n]),
Ok(_) => break, // EOF
Err(e) if e.kind() == ErrorKind::WouldBlock => break,
Err(e) => return Err(e.into()),
}
}
epoll网络编程LinuxRust