你在云上买了 8 核 16G 的实例,跑同一个压测脚本,今天能到 10000 QPS,明天只有 6000 QPS,配置没变、代码没变。这种"同样的钱买到不一样的算力"的现象,根源就是资源超分(Overcommit,也叫超量分配、超售)。它是云计算商业模式的基石——厂商不可能为每个客户预留足额的空闲算力,否则机器利用率低下、价格必然高得多。但对用户而言,超分意味着性能存在不确定性。本文讲清楚超分的计算方法、三类资源(CPU/内存/磁盘)各自的超分边界、以及如何在自己的 KVM 环境里量化与控制。
一、什么是超分:一个统计复用的游戏
超分指的是分配给虚拟机的资源总量,超过了宿主机实际拥有的物理资源总量。
举个具体例子:一台宿主机有 2 颗 CPU,每颗 16 核,开启超线程后总共有 2 × 16 × 2 = 64 个逻辑线程。如果在这台机器上创建了 10 台 8 vCPU 的虚拟机,总共分配了 80 个 vCPU,那么 CPU 超分比就是 80 / 64 = 1.25:1。
这种做法能成立是因为统计复用:绝大多数虚拟机大部分时间处于空闲状态,四台机器不太可能同时跑满。但如果真的大部分虚拟机的业务都跑起来了,矛盾就暴露了——所有实例都会一起变慢。
# 先确认宿主机的真实物理拓扑
lscpu
# 关键三行:
# Socket(s): 2 ← 物理 CPU 颗数
# Core(s) per socket: 16 ← 每颗物理核数
# Thread(s) per core: 2 ← 每个核的线程数(2 表示开启了超线程)
# CPU(s): 64 ← 逻辑线程总数 = 2 × 16 × 2
# 物理逻辑线程数(最可靠的计算方式)
echo $(( $(lscpu | awk -F: '/^Socket\(s\)/{print $2}') * \
$(lscpu | awk -F: '/^Core\(s\) per socket/{print $2}') * \
$(lscpu | awk -F: '/^Thread\(s\) per core/{print $2}') ))
# libvirt 视角的节点信息
virsh nodeinfo
# CPU model / CPU(s) / CPU frequency / CPU socket / Core per socket / Thread per core
virsh capabilities | head -30
# 宿主机实际可用内存(注意 MemTotal 会略小于物理容量)
free -h
grep MemTotal /proc/meminfo
dmidecode -t memory | grep -iE 'size|speed' | head
二、量化超分比:算出你到底超了多少
在自建 KVM 环境里,超分比是可以精确计算的。
# 1. 统计所有虚拟机分配的 vCPU 总数
total_vcpu=0
for vm in $(virsh list --all --name); do
c=$(virsh dominfo "$vm" 2>/dev/null | awk '/^CPU\(s\)/{print $2}')
echo "$vm vCPU=$c"
total_vcpu=$(( total_vcpu + c ))
done
echo "已分配 vCPU 总数: $total_vcpu"
# 2. 统计所有虚拟机配置的内存总量(单位 KiB)
total_mem=0
for vm in $(virsh list --all --name); do
m=$(virsh dominfo "$vm" 2>/dev/null | awk '/^Max memory/{print $3}')
total_mem=$(( total_mem + m ))
done
echo "已分配内存总数: $(( total_mem / 1024 / 1024 )) GB"
# 3. 统计所有虚拟机实际占用的内存(比配置值更有意义)
virsh domstats --balloon # 各虚拟机的 balloon 当前值
virsh domstats | grep balloon.current # 单位是 KiB
# 4. 磁盘:统计所有镜像文件的名义容量 vs 实际占用
for f in /var/lib/libvirt/images/*.qcow2; do
qemu-img info "$f" | awk -v F="$f" '/virtual size|disk size/ {print F, $0}'
done
# virtual size = 名义容量(sum 即为超额部分),disk size = 实际占用(精简置备)
得到分子后,按下表套公式即可:
| 资源 | 超分比公式 | 经验安全区间 | 超分失效时的表现 |
|---|---|---|---|
| CPU | Σ vCPU ÷ 物理逻辑线程数 | 通用业务 2:1 ~ 4:1;数据库与低延迟场景 ≤ 1:1 | steal time 上升,延迟抖动 |
| 内存 | Σ 虚拟机配置内存 ÷ 物理内存 | 建议 ≤ 1:1;配合 balloon+KSM 可到 1.2:1 | 宿主机 swap、OOM Killer |
| 磁盘(精简置备) | Σ 虚拟磁盘名义容量 ÷ 实际存储容量 | 建议 ≤ 1.5:1,并始终保留 20% 余量 | 存储写满,虚拟机 I/O 报错只读 |
三、CPU 超分:怎么知道自己被"抢"了
CPU 超分的影响通过 steal time(被 Hypervisor 偷走的 CPU 时间) 体现。它统计的是虚拟机的 vCPU 已经准备好运行、但宿主机把它调度给了别的虚拟机的时间。
# 方法 1:top 里的 %st 列
top -b -n 1 | head -12
# 关注 CPU 行的 st 值
# 方法 2:vmstat 的 st 列
vmstat 1 10
# 输出列含义:r b swpd free buff cache si so bi bo in cs us sy id wa st
# 最后一列 st 即 steal time
# 方法 3:sar(需安装 sysstat)
sar -P ALL 1 5
mpstat -P ALL 1 5
# 方法 4:压测单核算力,与已知基线对比
sysbench cpu --cpu-max-prime=20000 --threads=1 run
# 重复采样多次,观察 events per second 的波动幅度
for i in 1 2 3 4 5; do
sysbench cpu --cpu-max-prime=20000 --threads=1 run | \
awk '/events per second/{print $NF}'
done
判断阈值(实践参考):
%st长期低于 3% —— 正常,宿主机资源充足。%st持续在 5%-10% —— 宿主机已有明显争抢,对延迟敏感的业务需要关注。%st超过 10% —— 严重争抢,应迁移到更高规格实例或独享型机型。- 单核算力多次采样标准差超过均值的 15% —— 说明算力不稳定,不适合跑数据库。
对于自建 KVM 环境,如果某台虚拟机不允许算力波动,可以用 vCPU 绑定(pinning)把它独占到特定物理核心上。注意:一旦做了 pinning,这些核心就不能再被其他虚拟机超分使用,两者是互斥的。
# 查看当前 vCPU 与物理 CPU 的映射 virsh vcpuinfo vm01 # 绑定 vCPU 0/1 到物理 CPU 8/9(独占,其他虚拟机不可用) virsh vcpupin vm01 0 8 virsh vcpupin vm01 1 9 virsh vcpupin vm01 0 8 --config # NUMA 绑定(大规格虚拟机必须做,否则跨节点访问延迟翻倍) virsh numatune vm01 --nodeset 0 --mode strict numactl --hardware numastat -c qemu-kvm
四、内存超分:三种机制与它们的代价
内存超分比 CPU 超分危险得多,因为内存不像 CPU 那样可以"等一等",一旦虚拟机真的把分配到的内存都用上,宿主机要么开始 swap,要么触发 OOM Killer 杀进程。业界有三种手段缓解:
4.1 Ballooning(气球驱动)
虚拟机里安装 virtio_balloon 驱动后,宿主机可以"要回"虚拟机暂时不用的内存页。virsh setmem 就是走这条路。代价:虚拟机把内存交给宿主机后,如果自己突然需要,必须先申请回来,会产生明显延迟。
# 宿主机侧:查看虚拟机实际用量(比 dominfo 的配置值准确得多) virsh dommemstat vm01 # 关键字段: # actual 虚拟机当前可用内存(KiB) # swap_in/out 虚拟机内的换页量,大于 0 说明 guest 已内存吃紧 # major_fault 硬缺页,频繁发生说明内存压力大 # unused 虚拟机完全未使用的内存 # 调整虚拟机内存(需未超过 maxmemory) virsh setmem vm01 4G --live virsh setmaxmem vm01 16G --config # 虚拟机内确认 balloon 驱动已加载 lsmod | grep virtio_balloon dmesg | grep -i balloon
4.2 KSM(Kernel Samepage Merging,内核同页合并)
宿主机扫描所有虚拟机的内存页,内容完全相同的页合并为一个(写时复制)。运行大量同构虚拟机(比如同一个镜像起的 20 台 Docker 宿主)时节省效果显著。代价:扫描本身消耗 CPU,且存在旁路攻击的理论风险,很多生产环境默认关闭。
# 查看 KSM 是否运行(0=关 1=开 2=仅 NUMA 同域合并) cat /sys/kernel/mm/ksm/run # 查看合并效果 cat /sys/kernel/mm/ksm/pages_shared # 被合并的唯一页数量 cat /sys/kernel/mm/ksm/pages_sharing # 因此节省的页数量 cat /sys/kernel/mm/ksm/pages_unshared # 内容唯一、无法合并的页 cat /sys/kernel/mm/ksm/pages_volatile # 变化过快来不及合并的页 # 计算节省的内存(KiB → MB) echo "节省约: $(( $(cat /sys/kernel/mm/ksm/pages_sharing) * 4 / 1024 )) MB" # 开启 KSM 并调整扫描强度 echo 1 > /sys/kernel/mm/ksm/run echo 1000 > /sys/kernel/mm/ksm/pages_to_scan # 每次扫描页数,默认 100 echo 20 > /sys/kernel/mm/ksm/sleep_millisecs # 扫描间隔,默认 20ms # 生产环境建议用 ksmtuned 按负载自动调节 systemctl enable --now ksmtuned cat /etc/ksmtuned.conf
4.3 HugePages 与 swap 的取舍
使用静态大页(HugePages,通常 2MiB 或 1GiB)能显著减少 TLB miss、提升内存访问性能(数据库、DPDK 场景常用),但大页内存不能被 swap、也不能被 balloon 回收,一旦预留就从宿主机可用内存里永久扣除。因此在超分环境里,给虚拟机配置静态大页会大幅压缩超分空间。
# 查看大页配置 grep -i huge /proc/meminfo # HugePages_Total / HugePages_Free / Hugepagesize # 临时预留大页(不可回收!) echo 1024 > /proc/sys/vm/nr_hugepages # 宿主机 swap 情况(一旦开始使用,说明内存已真正超分到危险区) swapon --show cat /proc/sys/vm/swappiness vmstat 1 5 # 关注 si/so 两列
五、容器与 K8s 场景的资源超分
容器虽然不是完整虚拟机,但资源超分的逻辑完全一致,而且更隐蔽——因为 Docker/K8s 的 requests 与 limits 是两套东西。
- requests:调度依据,是"承诺"。节点上所有 Pod 的 requests 之和不能超过节点 Allocatable。超分时看这个值。
- limits:硬上限,由 CFS(完全公平调度器)配额执行,超过就被限流(throttling),不是被抢占。
- cpu-shares:相对权重,默认 1024。只有在 CPU 争抢时才起作用,不争抢时容器可以用满所有空闲 CPU。
# Docker 侧查看限制
docker stats --no-stream
docker inspect <容器> --format 'CPU配额: {{.HostConfig.NanoCpus}} 权重: {{.HostConfig.CpuShares}} 绑核: {{.HostConfig.CpusetCpus}} 内存: {{.HostConfig.Memory}}'
# 用 CFS 配额限制容器最多用 1.5 个核
docker run -d --cpus=1.5 --memory=2g --memory-swap=2g nginx
# K8s 侧查看节点的资源超分情况(Allocated resources 段)
kubectl describe node <节点名> | grep -A12 'Allocated resources'
kubectl get node <节点名> -o jsonpath='{.status.allocatable}'
# 统计所有 Pod 的 requests 总量
kubectl get pods -A -o json --field-selector=spec.nodeName=<节点名> | \
python -c "import sys,json; d=json.load(sys.stdin); \
print(sum(int(c['resources']['requests'].get('cpu','0').rstrip('m') or 0) for p in d['items'] for c in p['spec']['containers'] if c.get('resources',{}).get('requests')), 'millicores requested')"
# 发现 CFS 限流(容器明明没跑满却被卡的元凶)
# cgroup v1
cat /sys/fs/cgroup/cpu/cpu.stat # nr_throttled / throttled_time
# cgroup v2
cat /sys/fs/cgroup/cpu.stat # nr_throttled / throttled_usec
六、控制超分的实施清单
- 用
lscpu和free -h确认宿主机真实物理资源,得到超分比的分母。 - 用
virsh dominfo或kubectl describe node汇总已分配资源,得到分子。 - 业务分类:把数据库、中间件、低延迟服务划为"不可争抢",优先 ≤1:1 或做 CPU pinning。
- Web/API 等可容忍抖动的服务接受较高超分比(2:1 ~ 4:1)。
- 内存默认不超分,确需适度超分时启用 balloon 并在 guest 内安装 virtio_balloon 驱动。
- 评估 KSM:同构虚拟机多的收益明显,异构或安全敏感场景建议关闭。
- 磁盘精简置备务必保留 20% 以上余量,并监控实际使用率而非名义容量。
- 持续监控
%st:设置告警线 5%,超过即检查宿主机负载。 - 给宿主机自身预留资源:建议保留 4-8GB 内存和 1-2 个物理核心不被分配。
- 云上选型:对算力稳定性有要求时,选择独享型/专用宿主机而非共享型实例,并确认是否存在 CPU 积分(突发)机制的限制。
# 监控脚本:每小时采集一次宿主机超分与争抢数据
cat > /usr/local/bin/host-oversubscribe-report.sh <<'EOF'
#!/bin/bash
TOTAL=$(virsh nodeinfo | awk '/^CPU\(s\)/{print $2}')
for vm in $(virsh list --name); do
[ -z "$vm" ] && continue
USED=$(( USED + $(virsh dominfo "$vm" | awk '/^CPU\(s\)/{print $2}') ))
MEM=$(( MEM + $(virsh dominfo "$vm" | awk '/^Used memory/{print $3}') ))
done
echo "物理线程: $TOTAL 已分配 vCPU: $USED 超分比: $(echo "scale=2; $USED/$TOTAL" | bc):1"
echo "已用内存: $(( MEM / 1024 / 1024 )) GB"
echo "steal: $(vmstat 1 2 | tail -1 | awk '{print $NF}')%"
EOF
chmod +x /usr/local/bin/host-oversubscribe-report.sh
/usr/local/bin/host-oversubscribe-report.sh
小结
虚拟化服务器资源超分是云计算成本结构的必然产物,本身不是坏事——它让闲置算力被利用起来,也让价格降到大多数业务能接受的水平。真正需要理解的是:超分换来的是"平均可用"而非"始终可用"。用技术水平 5% 的 steal time 作为健康检查线,用 requests/limits 或 CPU pinning 明确划分"不可争抢"与"可争抢"两类业务,就能在成本与性能之间拿到可控的平衡。
避坑建议:
- 不要把
nproc等同于物理核数,它是逻辑线程数,且超线程带来的额外线程只有 0.3 倍左右的实际算力。 - 内存不建议超分,一旦宿主机开始 swap,性能下降是断崖式的,远不如 CPU 争抢温和。
- 数据库类业务要求超分比 ≤1:1 或做 vCPUpin,延迟长尾在争抢环境下会被急剧放大。
- 注意 CPU 突发积分机制(部分云厂商的突发性能实例),累积积分耗尽后会强制降频到基线性能。
- 磁盘精简置备是隐性超分,监控必须看
df实际使用而非qemu-img info的 virtual size。 - K8s 里常见问题不是超分而是 CFS 限流,limits 设太小会让容器在低负载下也被 throttle,表现为莫名的响应变慢。
- 给宿主机留资源,把资源 100% 分光会导致管理进程、监控 agent 无资源可用。
- 迁移实例前先做大页/ balloon 的兼容性检查,静态大页在线迁移的目标机必须有同等数量和大小的空闲大页。
延伸阅读:理解了超分机制,就能更好判断云服务器与物理服务器的取舍,以及在什么时机该做垂直扩容或水平扩容。
企业QQ咨询




