平时做项目的时候,经常听到团队里有人说‘这数据库怎么又吃内存了’。特别是用NoSQL的时候,比如MongoDB、Redis这类,看着操作简单,性能也不错,但资源占用问题其实挺让人头疼的。
内存占用:快是快,代价也不小
拿Redis来说,它把所有数据都放在内存里,读写速度自然飞快。可一旦数据量上来,内存消耗就很明显。比如你存了几百万条用户行为日志,可能不知不觉就占了十几个GB的内存。服务器要是配置跟不上,直接就开始swap,性能立马打对折。
MongoDB虽然数据存在硬盘,但它靠内存映射文件(mmapv1或wiredTiger)来加速访问,实际使用的内存量往往接近数据集大小。如果你的数据有50GB,服务器只配了32GB内存,那查询响应就会变得忽快忽慢,像卡顿的老视频。
CPU使用率波动大
NoSQL不像传统关系型数据库那样有固定的执行计划优化器,很多操作依赖脚本或聚合管道。比如用MongoDB做个复杂的$lookup联表查询,CPU瞬间就能冲到80%以上。高峰期一来,服务器风扇狂转,监控告警短信跟着就来了。
再比如Redis执行一个大的KEYS * 命令,虽然方便调试,但会阻塞主线程,CPU占用飙升的同时还影响其他请求响应。这种“方便一时爽,运维火葬场”的操作,在小团队里太常见了。
磁盘IO和存储膨胀
很多人以为NoSQL省空间,其实不一定。MongoDB默认的BSON格式比JSON还占地方,加上索引、副本集日志、oplog这些附加内容,实际磁盘占用可能是原始数据的1.5倍以上。
如果开了TTL索引自动删过期数据,看起来省事,但后台删除进程会持续产生磁盘IO压力。特别是在机械硬盘上跑,整台机器都会变慢。换成SSD好一些,但成本又上去了。
连接数与网络带宽
NoSQL通常支持高并发连接,但每个连接都会消耗一定内存和文件描述符。比如Redis默认最大客户端连接是10000,真到了这个量级,光是维护连接状态就得吃掉几百MB内存。
再加上现在很多应用用NoSQL当缓存+消息队列用,频繁地get/set或者发布订阅,网络吞吐量很容易拉满。办公室里测试环境和生产共用一台Redis,某天接口突然变慢,一查发现是另一个组在跑批量导入,把带宽占满了。
配置不当放大资源消耗
有个项目刚开始用MongoDB,谁都没调过参数,默认配置直接上线。结果每天凌晨备份时,内存使用暴涨,服务卡死几分钟。后来才发现是WiredTiger的缓存没限制,默认吃了几乎全部系统内存。
加了这几行配置后才稳住:
storage:
wiredTiger:
engineConfig:
<!-- 限制WiredTiger使用最多8GB内存 -->
cacheSizeGB: 8
类似的情况在Redis也常见。持久化开RDB+AOF双模式,fork子进程时内存翻倍,小内存机器直接OOM被系统kill掉。
监控才是关键
与其等出问题再救火,不如提前盯紧资源变化。用mongostat看MongoDB实时状态,redis-cli info查Redis内存和连接数,配合Prometheus + Grafana画个面板,哪个时段资源突增一目了然。
比如发现每周日晚上8点Redis内存快速上涨,查日志原来是定时任务往里面塞推送数据。这时候就可以考虑压缩数据结构,或者改用分片集群分流压力。