运维新人踩坑记录:Redis与MySQL生产故障排查&优化手册

文章目录

Redis篇

这里都是个人总结的经验,没有什么高深莫测的,只有平常碰到的问题的解决过程。

Redis作为高性能的键值数据库,其性能瓶颈与稳定性问题多集中于内存管理、日志配置及服务启停机制。以下结合实际告警与故障场景,从问题根源到优化落地进行完整拆解。

优化一、解决Redis碎片率过高

根据告警信息和 INFO memory输出,Redis 实例的内存碎片率过高。

bash 复制代码
# dev
127.0.0.1:12204> INFO memory
# Memory
used_memory:5714768           # 实际使用:5mb
used_memory_rss:12537856      # 操作系统实际分配:12mb
mem_fragmentation_ratio:2.30  # 碎片率

虽然不是啥大问题,毕竟我做了最大内存限制是8G,这点占用就是洒洒水啦。可我想做的是如果真的到了某阈值的时候,我的处理办法有哪些,所以才做了一个优化。

问题本质:Redis依赖内存分配器(默认jemalloc)管理内存,当频繁执行键的增删操作时,会产生大量内存空洞------已释放的内存块因大小不连续,无法被新的内存请求复用,导致操作系统分配的物理内存远大于Redis实际使用的内存,形成碎片。

优化过程:手动清理

bash 复制代码
# dev环境测试
# 尝试清理缓存:(定期在业务低峰执行)
127.0.0.1:12204> INFO memory 

# 动态设置为4GB,重启后失效。请务必也修改配置文件使其永久生效。
CONFIG SET maxmemory 4294967296

# 清理页面缓存
127.0.0.1:12204> echo 1 > /proc/sys/vm/drop_caches  # 清除页面缓存(对运行中的Redis服务无影响)

# 再次查询:
# 降至1.91

注意:echo 1 > /proc/sys/vm/drop_caches仅清理页缓存,若执行echo 2则清理目录项和inode缓存,可能影响文件系统性能,生产环境需谨慎。

或者开启自动清理功能:

bash 复制代码
vim /etc/redis/redis.conf
activedefrag yes

# 重启redis即可。

如果,我是说如果,你开启了之后出现了以下问题:

bash 复制代码
root@dev:~# journalctl -xeu redis-server.service 
10月 14 17:00:54 dev systemd[1]: redis-server.service: Failed with result 'exit-code'.
░░ Subject: Unit failed
░░ Defined-By: systemd
░░ Support: http://www.ubuntu.com/support
░░ 
░░ The unit redis-server.service has entered the 'failed' state with result 'exit-code'.
10月 14 17:00:54 dev systemd[1]: Failed to start Advanced key-value store.
░░ Subject: redis-server.service 单元已失败
░░ Defined-By: systemd
░░ Support: http://www.ubuntu.com/support
░░ 
░░ redis-server.service 单元已失败。
░░ 
░░ 结果为"failed"。

恭喜你,你的redis可能有点小问题:Redis 的自动碎片整理功能依赖于一个特定修改版的 Jemalloc 库。Redis 是通过系统包管理器apt安装的,使用了标准的 libc malloc或不完整功能的 Jemalloc,导致此功能无法启用 。

解决办法:源码安装,补全依赖,重新编译依赖库,重装。

警醒 : 安装核心服务尽量选取官方源码包或二进制包进行安装,以免出现不完整库的情况。

最好通过Prometheus+Grafana监控redis_memory_fragmentation_ratio指标,设置1.8为告警阈值,2.0为紧急阈值。

优化二、提高慢查询日志长度

简单来说,调高 slowlog-max-len就像给 Redis 的性能诊断工具换上一个"更大容量的黑匣子",让它能记录更长时间内的飞行数据,这对于保障生产环境的稳定性和可排查性非常关键。

设置完成后,通过 CONFIG GET slowlog-max-len命令确认修改是否成功,并使用 SLOWLOG LEN随时查看当前慢查询列表中的实际记录数量。

设置过程:

bash 复制代码
# 原始值128
127.0.0.1:12206> CONFIG GET slowlog-max-len
1) "slowlog-max-len"
2) "128"

# 修改为1000
127.0.0.1:12206> CONFIG SET slowlog-max-len 1000
OK
127.0.0.1:12206> CONFIG GET slowlog-max-len
1) "slowlog-max-len"
2) "1000"
127.0.0.1:12206> CONFIG REWRITE
OK

意义:

保留更多历史记录,避免重要慢查询丢失 :Redis 的慢查询日志是一个先进先出 (FIFO) 的队列。当新产生的慢查询数量达到列表最大长度时,最早的一条记录会被移出队列。在并发较高或问题排查周期较长时,较小的队列(如默认的128)可能导致早期的关键慢查询记录被快速挤出,从而无法追溯。设置为 1000 可以显著增加日志的留存时间,为您分析和排查间歇性或历史性的性能问题提供更充足的数据支持。

适合生产环境需求 :对于线上环境,将 slowlog-max-len设置为 1000 或更大 是一个常见的最佳实践 。因为 Redis 在记录慢查询时会对较长的命令进行截断处理,所以适当增加列表长度通常不会占用大量****内存,却可以极大提升问题诊断的便利性。

配合更严格的慢查询阈值 :在高流量、低延迟要求的场景(高OPS场景)下,通常会将慢查询阈值(slowlog-log-slower-than)设置为一个更小的值(例如 1 毫秒,即1000微秒),以捕获更多潜在的性能抖动。降低阈值会导致被记录的慢查询数量增加,此时调高 slowlog-max-len可以确保这些新增的记录有足够的空间被保存下来。

优化三、Redis宕机无法启动

事情经过是这样,某一天凌晨测试在测试服务,但是不知道为什么,redis突然就宕机了,后来给我的说法是上层的某个网络设备出了问题,导致那个设备坏了,但是redis崩了并没有给我解释,就告诉我快点处理,那就处理呗,没招。我一看日志:

bash 复制代码
10月 17 09:36:22 prod redis-server[1768297]: >>> 'logfile "/data/redis/redis-server.log"'
10月 17 09:36:22 prod redis-server[1768297]: Can't open the log file: Read-only file system
10月 17 09:36:22 prod systemd[1]: redis-server.service: Main process exited, code=exited, status=1/FAILURE
10月 17 09:36:22 prod systemd[1]: redis-server.service: Failed with result 'exit-code'.
10月 17 09:36:22 prod systemd[1]: Failed to start Advanced key-value store.
10月 17 09:36:22 prod systemd[1]: redis-server.service: Scheduled restart job, restart counter is at 2.
10月 17 09:36:22 prod systemd[1]: Stopped Advanced key-value store.
10月 17 09:36:22 prod systemd[1]: Starting Advanced key-value store...
10月 17 09:36:22 prod redis-server[1768304]: *** FATAL CONFIG FILE ERROR (Redis 6.0.16) ***
10月 17 09:36:22 prod redis-server[1768304]: Reading the configuration file, at line 260
10月 17 09:36:22 prod redis-server[1768304]: >>> 'logfile "/data/redis/redis-server.log"'
10月 17 09:36:22 prod redis-server[1768304]: Can't open the log file: Read-only file system
10月 17 09:36:22 prod systemd[1]: redis-server.service: Main process exited, code=exited, status=1/FAILURE
10月 17 09:36:22 prod systemd[1]: redis-server.service: Failed with result 'exit-code'.
10月 17 09:36:22 prod systemd[1]: Failed to start Advanced key-value store.
10月 17 09:36:23 prod systemd[1]: redis-server.service: Scheduled restart job, restart counter is at 3.
10月 17 09:36:23 prod systemd[1]: Stopped Advanced key-value store.
10月 17 09:36:23 prod systemd[1]: Starting Advanced key-value store...
10月 17 09:36:23 prod redis-server[1768517]: *** FATAL CONFIG FILE ERROR (Redis 6.0.16) ***
10月 17 09:36:23 prod redis-server[1768517]: Reading the configuration file, at line 260
10月 17 09:36:23 prod redis-server[1768517]: >>> 'logfile "/data/redis/redis-server.log"'
10月 17 09:36:23 prod redis-server[1768517]: Can't open the log file: Read-only file system
10月 17 09:36:23 prod systemd[1]: redis-server.service: Main process exited, code=exited, status=1/FAILURE
10月 17 09:36:23 prod systemd[1]: redis-server.service: Failed with result 'exit-code'.
10月 17 09:36:23 prod systemd[1]: Failed to start Advanced key-value store.
10月 17 09:36:23 prod systemd[1]: redis-server.service: Scheduled restart job, restart counter is at 4.
10月 17 09:36:23 prod systemd[1]: Stopped Advanced key-value store.
10月 17 09:36:23 prod systemd[1]: Starting Advanced key-value store...
10月 17 09:36:23 prod redis-server[1768739]: *** FATAL CONFIG FILE ERROR (Redis 6.0.16) ***
10月 17 09:36:23 prod redis-server[1768739]: Reading the configuration file, at line 260
10月 17 09:36:23 prod redis-server[1768739]: >>> 'logfile "/data/redis/redis-server.log"'
10月 17 09:36:23 prod redis-server[1768739]: Can't open the log file: Read-only file system
10月 17 09:36:23 prod systemd[1]: redis-server.service: Main process exited, code=exited, status=1/FAILURE
10月 17 09:36:23 prod systemd[1]: redis-server.service: Failed with result 'exit-code'.
10月 17 09:36:23 prod systemd[1]: Failed to start Advanced key-value store.
10月 17 09:36:24 prod systemd[1]: redis-server.service: Scheduled restart job, restart counter is at 5.
10月 17 09:36:24 prod systemd[1]: Stopped Advanced key-value store.
10月 17 09:36:24 prod systemd[1]: redis-server.service: Start request repeated too quickly.
10月 17 09:36:24 prod systemd[1]: redis-server.service: Failed with result 'exit-code'.
10月 17 09:36:24 prod systemd[1]: Failed to start Advanced key-value store.

在日志里面找到了核心问题:

bash 复制代码
10月 17 09:36:22 prod redis-server[1768297]: >>> 'logfile "/data/redis/redis-server.log"'
10月 17 09:36:22 prod redis-server[1768297]: Can't open the log file: Read-only file system
10月 17 09:36:22 prod systemd[1]: redis-server.service: Main process exited, code=exited, status=1/FAILURE

aof、rdb、log文件没有写入权限,说实话懵了一下下,之前为什么好用,现在为啥权限干没了???给我干一脸问号,我怀疑是之前的文件目录或许被动过了,也就是说更换了一个新的dir,但是并没有给权限,也没daemon-reload,但是这个宕机之后,再次启动的时候就用到了新修改的目录,所以出现的问题,但是仅限于我的猜测,不管咋样,总之问题就是文件没有权限呗,解决就完了:

  • 直接原因:Redis配置的日志目录/data/redis所在文件系统为只读模式,无写入权限;
  • 潜在原因:磁盘挂载参数错误(如/etc/fstab中配置了ro权限)、磁盘损坏触发保护模式、systemd服务配置限制了目录访问权限。
bash 复制代码
root@dev:~# vim /etc/systemd/system/redis.service 
[Service]
# 现有配置...
ReadWritePaths=-/var/lib/redis
ReadWritePaths=-/var/log/redis
ReadWritePaths=-/var/run/redis
ReadWritePaths=-/etc/redis
# 添加以下行,允许写入 /data/redis 目录
ReadWritePaths=-/data/redis

root@dev:~# systemctl daemon-reload
root@dev:~# systemctl restart redis 

这就解决了,属于是给我干笑了哈哈哈哈哈。

MySQL篇

优化一、MySQL的InnoDB缓冲池命中率低

Prometheus监控告警:MySQL 实例 prod-mysql InnoDB 缓冲池命中率 94.70%,低于 95% 阈值,建议增加innodb_buffer_pool_size。

**虽说这个告警是我设置的,但是一看到但是MySQL的问题还是心头一惊。**其实看到之后感觉也不是啥大问题,不过是缓冲池大小没被修改过,还是MySQL的默认值,上调一下就好了,对了,解释一下上调的意义:

上调MySQL的innodb_buffer_pool_size最核心的意义在于,通过分配更多内存来缓存InnoDB存储引擎的表数据和索引,显著减少数据库对磁盘的直接I/O访问,从而大幅提升数据的读取速度和处理性能,这是优化MySQL数据库性能最有效的手段之一。

调整原则:

  • 独立MySQL服务器:建议设置为物理内存的50%70%(如16GB内存服务器设为812GB);
  • 混合部署服务器:预留至少4GB内存给操作系统及其他服务,剩余内存的50%分配给缓冲池;
  • 多实例部署:按实例数据量比例分配内存,避免单实例占用过高导致系统OOM。

那还说啥了,改呗兄弟:

SQL 复制代码
# 查看原始值:
mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+
1 row in set (0.00 sec)

# 上调至8G:
# 热加载模式,无需重启redis

mysql> SET GLOBAL innodb_buffer_pool_size = 8589934592;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 8589934592 |
+-------------------------+------------+
1 row in set (0.01 sec)

mysql> 

# 写入配置文件
vim /etc/mysql/mysql.conf.d/mysqld.cnf 
# innodb上调-2025.10.16
innodb_buffer_pool_size = 8G

优化二、因apt自动更新导致的数据库重启

在平常不过的一天,自己刚部署了夜莺的监控,即将完善的时候,查看了一下MySQL的运行日志,突然发现生产数据库在早上竟然迷之重启了:

我一下子就懵了,赶紧查服务运行日志:

bash 复制代码
root@prod:/app/n9e/categraf# sudo journalctl -u mysql.service -n 10
11月 20 06:25:47 prod systemd[1]: Stopping MySQL Community Server...
11月 20 06:26:00 prod systemd[1]: mysql.service: Deactivated successfully.
11月 20 06:26:00 prod systemd[1]: Stopped MySQL Community Server.
11月 20 06:26:00 prod systemd[1]: mysql.service: Consumed 18h 6min 50.746s CPU time.
11月 20 06:26:16 prod systemd[1]: Starting MySQL Community Server...
11月 20 06:26:19 prod systemd[1]: Started MySQL Community Server.
root@prod:/app/n9e/categraf# journalctl -u mysql.service --since "06:25:00" --until "06:27:00"
11月 20 06:25:47 prod systemd[1]: Stopping MySQL Community Server...
11月 20 06:26:00 prod systemd[1]: mysql.service: Deactivated successfully.
11月 20 06:26:00 prod systemd[1]: Stopped MySQL Community Server.
11月 20 06:26:00 prod systemd[1]: mysql.service: Consumed 18h 6min 50.746s CPU time.
11月 20 06:26:16 prod systemd[1]: Starting MySQL Community Server...
11月 20 06:26:19 prod systemd[1]: Started MySQL Community Server.

只看到了数据库停止和数据库启动的操作,并且时间极短,那这该怎么去排查问题呢?

**首先,这个动作还是比较像人为的,发现问题后第一时间问了所有的后端,是不是他们动的(怀疑有人加班然后导入新的数据库了等等原因),**然后我就屁颠屁颠的去问了,问了一圈都没回应,我想应该不是了,那个时间还加班可真的太阴间了哈哈哈哈哈。

既然不是人为的,那就有可能是系统自动执行的程序导致的,我直接查看了数据库从停止到再次启动之间的系统日志

bash 复制代码
root@prod:/app/n9e/categraf# sudo journalctl --since "06:25:00" --until "06:27:00"
# 开始apt自动更新
11月 20 06:25:01 prod CRON[3454664]: (root) CMD (test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ))
11月 20 06:25:35 prod systemd[1]: Starting Daily apt upgrade and clean activities...
......
# 数据库被停止
11月 20 06:26:00 prod systemd[1]: mysql.service: Deactivated successfully.
11月 20 06:26:00 prod systemd[1]: Stopped MySQL Community Server.
11月 20 06:26:00 prod systemd[1]: mysql.service: Consumed 18h 6min 50.746s CPU time.
# 告警引擎发现了数据库宕机
11月 20 06:26:06 prod categraf[3797908]: 2025/11/20 06:26:06 mysql.go:219: E! failed to ping mysql: dial tcp 192.168.119.2:12106: connect: connectio>
......
# 数据库被启动
11月 20 06:26:16 prod systemd[1]: Starting MySQL Community Server...
11月 20 06:26:19 prod systemd[1]: Started MySQL Community Server.
......
# apt定时自动更新完成
11月 20 06:26:39 prod systemd[1]: apt-daily-upgrade.service: Deactivated successfully.
11月 20 06:26:39 prod systemd[1]: Finished Daily apt upgrade and clean activities.

当我把日志整理之后粘贴出来,其实就显而易见了:

  • 日常执行:apt 每日升级完整流程
  • 启动时间:06:25:35(systemd 发起「Daily apt upgrade and clean activities」)
  • 重启关联:apt 启动后 12 秒触发 MySQL 停止
  • 停止流程:06:25:47 系统发起停止指令 → 06:26:00 成功停止(「Deactivated successfully」)
  • 启动流程:06:26:16 系统发起启动指令 → 06:26:19 成功启动(「Started MySQL Community Server」)
  • 关键状态:重启全程无报错,仅监控工具(categraf、mysqld_exporter)因服务临时停止报连接失败
  • 完成时间:06:26:39(成功停止,状态为「Deactivated successfully」)

到此,已经基本可以确定是apt自动升级导致mysql版本升级导致的服务自动启停。

原数据库版本:8.0.43,现在数据库版本:8.0.44

为了确保问题排查的准确性,查询apt的更新日志:

bash 复制代码
cat /var/log/apt/history.log

# 数据库更新了
Start-Date: 2025-11-20  06:25:46
Commandline: /usr/bin/unattended-upgrade
Upgrade: mysql-server-8.0:amd64 (8.0.43-0ubuntu0.22.04.1, 8.0.44-0ubuntu0.22.04.1), mysql-client-8.0:amd64 (8.0.43-0ubuntu0.22.04.1, 8.0.44-0ubuntu0.22.04.1), mysql-server-core-8.0:amd64 (8.0.43-0ubuntu0.22.04.1, 8.0.44-0ubuntu0.22.04.1)
End-Date: 2025-11-20  06:26:19

Start-Date: 2025-11-20  06:26:29
Commandline: /usr/bin/unattended-upgrade
Upgrade: mysql-client-core-8.0:amd64 (8.0.43-0ubuntu0.22.04.1, 8.0.44-0ubuntu0.22.04.1)
End-Date: 2025-11-20  06:26:29

到此,排查结束。为了避免此类问题出现,导致服务宕机,使用apt-mark阻止服务被更新、重装或卸载,同时保持服务正常运行。

bash 复制代码
# 集群同步执行
root@prod:/# apt-mark hold mysql-server mysql-client 
mysql-server 设置为保留。
mysql-client 设置为保留。
root@dev:~# apt-mark hold redis-server 
redis-server 设置为保留。

# 解除锁定(需要更新时)
sudo apt-mark unhold mysql-server

总结

Redis与MySQL的运维优化核心在于"精准定位问题本质+构建长效保障机制"。无论是Redis的内存碎片、慢查询配置,还是MySQL的缓冲池优化、服务管控,都需结合技术原理与业务场景,既要解决当前问题,更要通过监控、配置加固与流程规范避免问题复发。本文档的优化方案均经过生产环境验证,可根据服务器配置与业务需求灵活调整。

(本文章会持续更新,作为本人实习阶段的收获。)

相关推荐
deng-c-f1 小时前
配置(9):在ubuntu上生成core文件
数据库·windows·ubuntu
野生技术架构师1 小时前
数据库连接池爆满如何排查
网络·数据库·oracle
百***81271 小时前
从 SQL 语句到数据库操作
数据库·sql·oracle
rchmin1 小时前
mysql中 char 和 varchar 的区别
数据库·mysql
SelectDB2 小时前
深入理解 Doris Variant:如何让 JSON 查询性能追平列存,还能承载万列索引字段?|Deep Dive
大数据·数据库·数据分析
百***3282 小时前
数据库(MySQL):使用命令从零开始在Navicat创建一个数据库及其数据表(一).创建基础表
数据库·mysql·oracle
snpgroupcn2 小时前
如何在SAP中实现数据验证自动化?5天缩短验证周期,提升转型效率的3大关键策略
运维·人工智能·自动化
optimistic_chen2 小时前
【Linux 系列】Linux 命令/快捷键详解
linux·运维·服务器·ubuntu·命令行·快捷键
i***66502 小时前
Spring Boot 整合 Redis 步骤详解
spring boot·redis·bootstrap