首页 / 资讯动态 / Ubuntu服务器内存磁盘性能优化运维技巧

Ubuntu服务器内存磁盘性能优化运维技巧

接手一台Ubuntu服务器,第一件事不是盲目调参,而是搞清楚内存和磁盘的真实状况。很多人上来就改sysctl,结果越改越糟。先用free -h看内存,别只看available那一列,重点看buff/cache占了多少。如果这个值很高但available很低,说明内存确实吃紧,应用在频繁触发OOM Killer。如果available充裕而buff/cache很大,这是Linux内核的正常行为,它在用空闲内存做文件缓存,不用干预。

内存水位线与Swap的博弈

Ubuntu默认的vm.swappiness值是60,这对服务器来说往往偏高。swappiness控制内核将匿名页交换到Swap的倾向,值越低越倾向于保留在物理内存。数据库服务器建议设为1或10,不要让内核过早地把热数据页换出去。修改命令:

sysctl vm.swappiness=10

永久生效需写入/etc/sysctl.conf或/etc/sysctl.d/下的配置文件。另一个常被忽略的参数是vm.vfs_cache_pressure,默认100,表示内核回收dentry和inode缓存的积极性。文件服务器或大量小文件读写的场景,建议调低到50,让内核更倾向于保留目录结构缓存,减少磁盘寻道。

sysctl vm.vfs_cache_pressure=50

真正危险的是OOM。Ubuntu的OOM Killer在内存耗尽时会杀进程,但它的决策逻辑有时很离谱,可能杀掉sshd或数据库主进程。生产环境务必设置vm.oom_kill_allocating_task=0,并配合vm.panic_on_oom=0。更精细的做法是用cgroup限制关键进程的内存,或者给数据库进程设置oom_score_adj为-500,降低被杀概率。

查看某个进程的oom_score:

cat /proc/$(pidof mysqld)/oom_score

调整oom_adj值:

echo -500 > /proc/$(pidof mysqld)/oom_score_adj
透明大页的陷阱

Ubuntu默认开启Transparent Huge Pages,这对某些工作负载是灾难。MongoDB、Redis、PostgreSQL的官方文档都明确建议关闭THP。THP会导致内存分配延迟增加,在数据库fork子进程时引发内存占用翻倍。检查是否开启:

cat /sys/kernel/mm/transparent_hugepage/enabled

输出always表示开启。关闭方法:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

永久关闭需修改/etc/default/grub,在内核启动参数加上transparent_hugepage=never,然后update-grub重启。对于Redis这类内存数据库,关闭THP后延迟抖动会明显减少。

磁盘I/O调度器的选择

Ubuntu 20.04之后默认使用mq-deadline调度器,18.04是cfq。NVMe固态盘用none调度器最合适,因为硬件队列足够深,内核调度反而增加开销。SATA固态盘建议用mq-deadline,机械盘用bfq能改善交互响应。查看当前磁盘调度器:

cat /sys/block/sda/queue/scheduler

方括号内的就是当前生效的调度器。临时切换:

echo mq-deadline > /sys/block/sda/queue/scheduler

永久设置需通过udev规则,在/etc/udev/rules.d/下创建规则文件,匹配磁盘型号或路径后指定调度器。另一个关键参数是磁盘的预读值,默认128KB对顺序读场景偏小。用blockdev查看和调整:

blockdev --getra /dev/sda
blockdev --setra 256 /dev/sda

数据库服务器的磁盘预读建议256到512,但别盲目调大,随机读场景下过大的预读会浪费I/O带宽。

文件系统挂载选项的实战优化

ext4文件系统挂载时加上noatime或relatime,减少访问时间戳的写入。noatime完全禁止更新访问时间,relatime仅在访问时间早于修改时间时才更新,后者是Ubuntu的默认行为,但显式指定更保险。数据库分区建议加上data=writeback,提高写入性能,代价是崩溃后可能丢失最近几秒的数据,但有数据库日志恢复机制兜底。

/etc/fstab示例:

UUID=xxx /data ext4 defaults,noatime,data=writeback 0 2

XFS文件系统在Ubuntu上对大量小文件的表现不如ext4,但大文件顺序读写优势明显。挂载XFS时建议加上inode64,让inode分配不受32位限制,适合海量文件场景。日志类应用(Kafka、Elasticsearch)强烈建议用XFS,并挂载时指定logbsize=256k,增大日志缓冲区。

I/O压力定位与fio基准测试

遇到磁盘性能瓶颈,先用iostat定位。iostat -x 1看每块盘的利用率、等待队列长度和平均服务时间。如果%util持续接近100%,且await远大于svctm,说明磁盘已经饱和。重点看r_await和w_await,NVMe盘的正常值在1ms以内,SATA固态盘5ms以内,机械盘10-20ms算正常。如果await飙升但%util不高,可能是磁盘固件问题或连接线松动。

更深入的分析用pidstat -d 1看哪个进程在大量读写,配合iotop实时观察。iotop -o只显示有I/O活动的进程,减少干扰。如果发现jbd2或kworker线程I/O很高,说明文件系统日志或内核工作队列在频繁刷盘,需要检查挂载选项和脏页参数。

做性能验证用fio,别用dd,dd的测试结果没有参考价值。一个典型的随机读测试:

fio --name=randread --ioengine=libaio --direct=1 --bs=4k --numjobs=4 --size=10G --runtime=60 --time_based --rw=randread --group_reporting

顺序写测试:

fio --name=seqwrite --ioengine=libaio --direct=1 --bs=1M --numjobs=1 --size=10G --runtime=60 --time_based --rw=write --group_reporting

测试前务必设置direct=1绕过页面缓存,否则测的是内存速度。numjobs根据CPU核数调整,一般4到8足够。结果关注IOPS和延迟百分位数,P99延迟比平均值更有意义。

脏页刷盘策略调优

Linux内核的脏页机制直接影响写入性能。vm.dirty_ratio默认20%,表示当脏页占总内存20%时,所有写入进程开始同步刷盘,这会导致瞬间I/O风暴。vm.dirty_background_ratio默认10%,达到这个阈值后台刷盘线程开始工作。对于大内存服务器,10%可能是几十GB,一次性刷盘压力巨大。建议用vm.dirty_bytes和vm.dirty_background_bytes替代百分比参数,精确控制。

sysctl vm.dirty_background_bytes=134217728
sysctl vm.dirty_bytes=268435456

以上设置后台刷盘阈值为128MB,强制刷盘阈值为256MB。这样即使有256GB内存,脏页也不会堆积到几十GB。数据库服务器建议设置得更保守,后台64MB,强制128MB,让写入压力均匀分布。

vm.dirty_expire_centisecs和vm.dirty_writeback_centisecs控制脏页的老化时间和回写间隔。默认3000(30秒)和500(5秒),对于SSD可以适当缩短,让数据更快落盘。

NUMA架构下的内存分配策略

多路CPU的服务器上,NUMA节点间的内存访问延迟差异显著。Ubuntu默认的内存分配策略是localalloc,进程优先从本地节点分配内存。但MySQL、PostgreSQL等数据库在分配大块共享内存时,可能跨节点分配,导致性能抖动。用numactl --hardware查看NUMA拓扑,用numastat观察各节点的内存分配情况。

对于数据库,建议用numactl --interleave=all启动,让内存均匀交错分配在所有节点上,避免单个节点内存耗尽而其他节点空闲。修改systemd服务文件:

ExecStart=/usr/bin/numactl --interleave=all /usr/sbin/mysqld

同时配合vm.zone_reclaim_mode=0,禁止内核在内存不足时回收本地内存,优先使用远程内存,避免不必要的页面回收开销。

LVM与磁盘条带化

如果使用LVM管理多块磁盘,务必开启条带化。线性卷只能顺序写入一块盘,条带化卷能将数据分散到多块盘并行读写。创建条带化卷:

lvcreate -L 500G -i 4 -I 64k -n striped_vol vg_data

-i指定磁盘数量,-I指定条带大小。条带大小根据I/O模式调整,数据库的16KB页面对应64KB条带比较合适,大文件存储用256KB或更大。条带化对随机读提升不明显,但对顺序读写和并行I/O提升巨大。

定期维护与监控

fstrim对SSD至关重要,它通知固态硬盘哪些块已经不再使用,让垃圾回收机制高效工作。Ubuntu默认每周执行一次fstrim,但可以通过systemctl status fstrim.timer确认。手动执行:

fstrim -v /

磁盘健康状态用smartctl定期检查,重点关注Reallocated_Sector_Ct和Wear_Leveling_Count。NVMe盘用nvme-cli工具查看剩余寿命和温度。内存方面,用edac-utils监控ECC错误,非ECC内存用memtester定期做压力测试,但生产环境慎用,memtester会占用大量内存。

最后,所有调优参数都要在测试环境验证,用sysbench或实际业务负载压测,对比调优前后的TPS和延迟分布。调优不是一锤子买卖,而是持续观测和迭代的过程。每次只改一个参数,观察效果后再改下一个,才能准确归因。