Linux虚拟机中MySQL数据库安装配置与故障排查
Linux、虚拟机、MySQL、数据库这四个词凑在一起基本就是每个后端新手和计算机专业学生的必修课。我前后在 VMware 和 VirtualBox 里装过不下二十遍 MySQL从 CentOS 6 时代用 rpm 包硬怼到后来 Ubuntu 上一行 apt 搞定再到为了折腾特定版本去解压通用二进制包该踩的坑基本踩了个遍。这篇东西就是把这套流程完整拆开讲一遍虚拟机的资源怎么分、Linux 装哪个发行版更省事、MySQL 三条安装路线各自的适用场景、初始化配置里哪几个参数必须改、跑起来之后遇到报错该按什么顺序排查。不管你是要搭一套课程设计环境还是给测试团队准备一套隔离的数据库照着走一遍基本能少走半天弯路。1. 先把虚拟机这层地基打牢很多人装 MySQL 失败问题根本不在 MySQL而是在虚拟机和系统这一层就没弄利索。数据库是个对内存、磁盘 IO、时间同步都很敏感的服务你在宿主机上随手给的资源、随手选的网络模式会在后面变成一堆莫名其妙的现象服务起不来、连不上、查询慢、时间对不上导致连接被拒。所以这一节先把地基讲清楚。1.1 资源配置怎么给才不卡虚拟机的资源分配有个基本原则MySQL 的缓冲池要能装下你的热数据而缓冲池是靠内存喂出来的。默认配置下 InnoDB 的缓冲池只有 128MB这个值在现代数据库里基本等于没有稍微大点的表查询就会疯狂读磁盘。我的一般建议是这样用途场景CPU内存磁盘说明只跑通安装、学 SQL 语法2 核2 GB20 GB勉强够跑大点的导入会卡课程设计 / 毕设 / 小项目2 核4 GB40 GB最舒服的入门配置模拟生产、压测、主从4 核8 GB100 GB 起磁盘选 SSD 类型磁盘类型一定要在创建虚拟机时选对。VMware 里如果勾了「将虚拟磁盘存储为单个文件」读写性能会好一些但迁移时文件很大拆成多个 2GB 文件的话方便拷贝代价是略微的 IO 损耗。这个取舍看你是本地学习还是要频繁搬家。提示虚拟磁盘别用「动态扩展」还只给 20GB 然后指望它能撑住一个几百兆的 SQL 导入。动态扩展盘在写入时会边扩容边写数据导入大文件时那种一顿一顿的卡顿一半来自这里。另一个容易被忽略的点是交换分区。Linux 安装时如果自动分区swap 通常给得不大。MySQL 一旦缓冲池设太大、宿主机内存又紧张触发 swap 后性能会断崖式下跌。我的习惯是把 innodb_buffer_pool_size 控制在虚拟机内存的 50% 到 60%给系统留出余量别把内存榨干。1.2 网络模式与静态IP决定后面能不能连上虚拟机网络模式就那么几种但对数据库来说意义完全不同NAT 模式虚拟机借宿主机的网络出去宿主机访问虚拟机没问题但同一个局域网里的其他机器访问不到。适合一个人闷头学习。桥接模式虚拟机会在局域网里拿一个独立 IP和你宿主机平级。团队协作、手机连、同事连你数据库都得用这个。仅主机模式只有宿主机和虚拟机互通。做些纯隔离的测试用。我吃过最大的亏是虚拟机默认用 DHCP某天重启后 IP 从 192.168.1.105 变成了 192.168.1.118前一天配好的连接串全部失效客户端连不上我盯着防火墙规则查了半小时。所以系统装完的第一件事就是把网卡改成静态 IP。以 Ubuntu 的 netplan 为例配置文件在/etc/netplan/下面network: version: 2 ethernets: ens33: dhcp4: no addresses: - 192.168.1.150/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: [192.168.1.1, 223.5.5.5]改完执行netplan apply然后用ip addr确认地址生效。CentOS / Rocky 系则是改/etc/sysconfig/network-scripts/ifcfg-ens33把BOOTPROTO改成static补上IPADDR、NETMASK、GATEWAY、DNS1几项再systemctl restart network新版用 NetworkManager 的话是nmcli connection reload。注意ifcfg 文件里的网卡名一定要和ip addr输出的名字对得上。VMware 里常见的是 ens33、ens160VirtualBox 里是 enp0s3写错了 NetworkManager 会直接忽略这一份配置你还以为是没生效。1.3 系统装完先做三件事第一件事是更新系统。这一步看着废话但很多依赖库的版本问题都是老镜像导致的# Debian / Ubuntu 系 apt update apt upgrade -y # RHEL / Rocky / CentOS 系 dnf update -y第二件事是关掉或调通 SELinux。SELinux 是个好东西但它在 MySQL 换数据目录、换端口的时候会毫不留情地拦住你报错信息还特别含糊。学习环境我一般先临时关掉确认问题getenforce # 看当前状态 Enforcing / Permissive / Disabled setenforce 0 # 临时切成宽容模式重启失效真要长期关改/etc/selinux/config把SELINUXenforcing改成disabled然后重启。生产环境别这么干正确做法是用semanage fcontext给新目录打上正确的安全上下文比如mysqld_db_t。第三件事是时间同步。这个坑非常隐蔽MySQL 8.0 默认启用 SSL 连接如果虚拟机时间和宿主机差太多证书校验会直接失败客户端报一个跟时间八竿子打不着的连接错误。装上虚拟机增强工具并开启时间同步# VMware 环境 apt install -y open-vm-tools open-vm-tools-desktop # 用 systemd 统一管理时间同步 timedatectl set-ntp true timedatectl statustimedatectl status输出的System clock synchronized: yes就说明同步正常。如果显示 no检查一下虚拟机设置里「同步客户机时间与主机时间」这个选项有没有勾上。2. MySQL 安装路线怎么选三种方案的取舍环境就绪之后正式进入安装环节。Linux 上装 MySQL 大体三条路发行版包管理器、官方仓库、通用二进制包。新手最容易犯的错是「看哪篇教程短就抄哪篇」结果教程用的是 Ubuntu自己装的是 Rocky命令全不对。2.1 三种安装方式横向对比对比项发行版自带源官方 Yum/APT 仓库通用二进制包上手难度最低低中高版本可控性差往往是发行版锁定的老版本好可选 5.7 / 8.0 / 8.4最好指定到小版本目录结构分散符合发行版规范分散符合发行版规范全部收敛在 basedir/datadir升级便利性跟着系统走换仓库版本即可手动替换目录风险高适合谁只想快点跑起来大多数人的最优解需要多版本共存或特殊参数有个坑要提前说CentOS 7 / 8 和 Rocky Linux 的自带源里yum install mysql-server装出来的其实是 MariaDB这不是 MySQL 官方版本。虽然语法高度兼容但某些函数、JSON 处理、窗口函数的行为有差异课程设计里跑出不一样的结果就尴尬了。想装正版 MySQL必须显式写mysql-community-server或者先加官方仓库。Ubuntu 这边相对友好apt install mysql-server装的就是 Oracle 官方维护的 MySQL 8.0 包不需要额外加仓库。2.2 路线一官方Yum仓库安装Rocky/CentOS系先说结论这是 RHEL 系里最省心的做法。核心操作就是往/etc/yum.repos.d/里丢一个仓库描述文件。手动写仓库文件比下载 rpm 包再 rpm -ivh 更可控因为你能自己决定启不启用、指向哪个版本# /etc/yum.repos.d/mysql-community.repo [mysql80-community] nameMySQL 8.0 Community Server baseurlhttps://repo.mysql.com/yum/mysql-8.0-community/el/8/$basearch/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022这里el/8要跟你系统的大版本对应Rocky 8 / CentOS 8 用 el/8Rocky 9 用 el/9。$basearch是个变量yum 会自动替换成 x86_64 或 aarch64所以同一份配置在 ARM 机器上也能用这点比自己写死路径要聪明。导入 GPG 公钥这一步别偷懒rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 dnf clean all dnf makecache dnf install -y mysql-community-serverdnf clean all加makecache是我每次改完仓库必做的一步。缓存里的元数据不刷新会出现「明明仓库里有的包yum 说找不到」这种莫名其妙的状况。装完之后服务名是mysqldsystemctl start mysqld systemctl enable mysqld systemctl status mysqldenable这一条很多人会漏。少了它虚拟机重启之后数据库不会自己起来你又得排查一遍「为什么连不上」。2.3 路线二APT安装Ubuntu/Debian系Ubuntu 上的流程短得多但有两个细节和 RHEL 系完全不同。apt update apt install -y mysql-server systemctl status mysql第一Ubuntu 下服务名是mysql而不是mysqld写脚本的时候别搞混。第二Ubuntu 从 20.04 开始的默认安装里root 用户用的是auth_socket认证插件——意思是系统里 root 账号直接sudo mysql就能进不验密码但用密码从 TCP 连 root 会被拒。这就是为什么很多人明明把密码改对了用客户端连还是报Access denied。想让 root 走密码认证ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY YourStrongPass#2024; FLUSH PRIVILEGES;注意MySQL 8.0 的默认认证插件是caching_sha2_password而 5.7 时代是mysql_native_password。一些老版本的客户端库连 8.0 会直接报「Authentication plugin cannot be loaded」。解决办法要么升级客户端要么建账号时显式指定IDENTIFIED WITH mysql_native_password BY ...。另外 MySQL 8.4 起已经不再默认支持 mysql_native_password需要额外启用选版本时要留意。2.4 路线三通用二进制包不受发行版限制需要指定某个精确版本、或者想让 MySQL 的所有文件都待在一个目录里方便打包迁移的时候通用二进制包是唯一选择。整套流程的关键在于解压、建专用用户、建数据目录、初始化。# 1. 建 mysql 专用用户不带登录 shell groupadd mysql useradd -r -g mysql -s /bin/false mysql # 2. 解压到 /usr/local 并做个软链接方便以后换版本 tar -xvf mysql-8.0.36-linux-glibc2.17-x86_64.tar.xz -C /usr/local/ ln -s /usr/local/mysql-8.0.36-linux-glibc2.17-x86_64 /usr/local/mysql # 3. 建数据和日志目录注意属主 mkdir -p /data/mysql/{data,logs,tmp} chown -R mysql:mysql /data/mysql那个软链接是个小心机以后升级到 8.0.37只要解压新目录、把软链接指过去配置文件里的basedir/usr/local/mysql一个字都不用改。这种细节平时看不出来等到一年后再回来看这套目录你就知道省了多少事。接下来是初始化这一步决定了数据目录里生成什么/usr/local/mysql/bin/mysqld \ --initialize \ --usermysql \ --basedir/usr/local/mysql \ --datadir/data/mysql/data \ --lc-messages-dir/usr/local/mysql/share--initialize生成的是一个随机临时密码的 root 账号密码会打印在日志里。如果想让 root 一开始就没有密码只在完全隔离的学习环境里这么干把参数换成--initialize-insecure。我强烈建议别图这个方便养成用好密码的习惯。初始化完成后用绝对路径启动服务或者配置 systemd 单元文件# /usr/lib/systemd/system/mysqld.service [Unit] DescriptionMySQL Server Afternetwork.target [Service] Usermysql Groupmysql ExecStart/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf LimitNOFILE65535 [Install] WantedBymulti-user.targetLimitNOFILE65535这一行迟早会救你一次。MySQL 在高并发下会打开大量文件描述符系统默认的 1024 很快就不够用报错信息是Too many open files看着不像是配置问题。3. 初始化与配置把默认值改成能用的值装完只是能启动跑得好不好用全看配置。这一节把 my.cnf 里真正影响体验的参数拎出来讲。3.1 首次启动与临时密码Yum 仓库安装的 MySQL 8.0第一次启动时会自动初始化并生成临时密码位置在/var/log/mysqld.loggrep temporary password /var/log/mysqld.log拿到密码后立刻登录并改掉因为在改密码之前任何 SQL 都执行不了mysql -uroot -pALTER USER rootlocalhost IDENTIFIED BY NewPass#2024;如果这里报密码策略相关的错比如Your password does not satisfy the current policy requirements说明validate_password组件卡着。发行版安装包通常默认启用它策略等级是 MEDIUM要求密码至少 8 位且包含大小写字母、数字、特殊字符。查看当前策略SHOW VARIABLES LIKE validate_password%;想临时放宽学习环境可以生产别动SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 8;提示MySQL 8.0 里这些是组件变量写法是validate_password.policy而不是 5.7 时代的validate_password_policy。同一台机器上两套教程混着看很容易被这个下划线搞晕。改完密码之后可以顺手跑一遍mysql_secure_installation它会引导你删掉匿名账号、禁止 root 远程登录、删掉 test 库。这几个默认项在测试环境里无所谓但如果你打算把虚拟机 IP 暴露给同事连那就值得花两分钟跑一遍。3.2 my.cnf关键参数逐条拆解配置文件的位置各发行版不一样Ubuntu 是/etc/mysql/mysql.conf.d/mysqld.cnfRHEL 系是/etc/my.cnf。别猜用命令问它mysqld --verbose --help | grep -A1 Default options下面这份是我在生产型配置里常用的骨架逐条解释[mysqld] # 基础路径 basedir /usr/local/mysql datadir /data/mysql/data socket /data/mysql/mysql.sock port 3306 pid-file /data/mysql/mysqld.pid log-error /data/mysql/logs/error.log # 字符集 character-set-server utf8mb4 collation-server utf8mb4_unicode_ci skip-character-set-client-handshake TRUE # 存储引擎与内存 default-storage-engine INNODB innodb_buffer_pool_size 2G innodb_buffer_pool_instances 2 innodb_redo_log_capacity 512M innodb_flush_log_at_trx_commit 2 innodb_flush_method O_DIRECT # 连接 max_connections 500 max_connect_errors 1000 wait_timeout 28800 interactive_timeout 28800 # 日志 slow_query_log 1 long_query_time 2 log_queries_not_using_indexes 0 # 其他 lower_case_table_names 1 explicit_defaults_for_timestamp 1 time_zone 08:00几个参数值得单独展开说。innodb_buffer_pool_size 2G这是全表最重要的一个参数。它决定 InnoDB 能在内存里缓存多少数据和索引。虚拟机给 4GB 内存的话2G 是个稳妥的选择。设太小查询全走磁盘设太大系统本身没内存用会被 OOM Killer 干掉 mysqld 进程然后你看到的现象是「数据库莫名其妙自己停了」。innodb_flush_log_at_trx_commit 2这个参数的默认值是 1意思是每次事务提交都把日志刷到磁盘最安全但最慢。设成 2 表示每秒刷一次宕机最多丢一秒的数据性能能提升好几倍。本地学习和测试环境设 2 完全够用生产环境要按业务能不能容忍丢一秒数据来决定。lower_case_table_names 1这个参数有个致命特性——在 MySQL 8.0 里它只能在初始化数据目录的时候设定服务起来之后再改会直接报错拒绝启动。默认值在 Linux 上是 0也就是表名区分大小写。这会导致你在 Windows 上开发时写的SELECT * FROM User拿到 Linux 上就报「表不存在」。很多人被这个坑到深夜最后发现原因是大小写。注意如果你已经初始化完才发现要改这个参数唯一办法是备份数据、删掉 datadir、加上参数重新初始化、再导入数据。所以务必在第一次初始化前就把 my.cnf 定稿。innodb_redo_log_capacityMySQL 8.0.30 起innodb_log_file_size和innodb_log_file_files_in_group被合并成了这一个参数。写老教程里的参数在 8.0.30 之后虽然还能启动但会被忽略并给出警告。time_zone 08:00不设的话MySQL 用的是系统时区。虚拟机上时间一旦不对NOW()返回的时间会让你在排查业务问题时怀疑人生。显式写死是个好习惯。3.3 字符集乱码问题的根子MySQL 的字符集不是一个变量而是一整串变量分服务端和客户端两层。查询一下SHOW VARIABLES LIKE character%; SHOW VARIABLES LIKE collation%;理想状态下character_set_server、character_set_database、character_set_client、character_set_connection、character_set_results应该全是utf8mb4。为什么强调 utf8mb4 而不是 utf8因为 MySQL 里的utf8是个历史遗留名字它最多只存 3 个字节存不下 emoji 和一些生僻汉字。utf8mb4才是真正的完整 UTF-8。这两者的选择建议是无脑选 utf8mb4别犹豫。配置里那个skip-character-set-client-handshake TRUE是关键的一笔。它的作用是让服务端忽略客户端上报的字符集强制统一。否则客户端驱动如果上报的是 latin1服务端就会按 latin1 来解读你发过来的字节存进去就是乱码。排序规则上utf8mb4_unicode_ci和utf8mb4_0900_ai_ci都可用。8.0 默认的utf8mb4_0900_ai_ci基于 Unicode 9.0比较规则更准确utf8mb4_unicode_ci兼容性更好一些。同一套系统里统一用哪一个都行最怕的是建库用一种、建表用另一种做 JOIN 的时候会报排序规则冲突。3.4 账号、权限与远程访问数据库装好了接下来一定要建业务账号别一直用 root 连。原因很实际root 权限太大一个手滑的DROP DATABASE就没了而且从权限模型上讲业务代码不需要 DDL 权限。CREATE DATABASE appdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER appuser192.168.1.% IDENTIFIED BY AppPass#2024; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO appuser192.168.1.%; FLUSH PRIVILEGES;几个细节主机部分别轻易写%。%表示任何来源都能连虽然方便但也意味着只要网络能通、密码被猜到数据库就暴露了。测试环境里按网段限制成192.168.1.%是比较务实的做法。生产环境应该精确到具体 IP或者只允许应用服务器连。权限按最小必要给。开发阶段可能还需要CREATE、DROP、INDEX、ALTER那就追加但别图痛快直接GRANT ALL ON *.*。远程访问要同时打开三道门缺一道都连不上账号主机限制appuser%或者具体网段。服务端监听地址my.cnf 里bind-address默认是127.0.0.1只能本机连。要远程连就改成0.0.0.0或者具体的网卡 IP。注意改完要重启服务。防火墙放行# firewalld firewall-cmd --permanent --add-port3306/tcp firewall-cmd --reload # ufw ufw allow from 192.168.1.0/24 to any port 3306验证一下端口是不是真的在监听ss -lntp | grep 3306如果输出的地址是127.0.0.1:3306那就还是本机模式要是0.0.0.0:3306或*:3306才说明远程可达。这一步比在客户端那边反复试密码有用得多。用 MySQL Workbench、DBeaver 这类图形客户端连虚拟机的时候填的主机地址是虚拟机的 IP端口 3306。连不上先看这三道门别急着怀疑密码。4. 踩坑实录常见故障与排查思路这一节的每一条都是我自己或者身边同事真金白银花时间换来的。故障排查最怕的是没有顺序四处乱试最后虽然好了但不知道为啥好的。所以下面每类问题我都给一个固定的排查顺序。4.1 起不来服务启动失败排查顺序systemctl start mysqld报Job for mysqld.service failed别慌按这个顺序走第一步看日志。这是唯一正确的起点journalctl -u mysqld -n 100 --no-pager tail -200 /data/mysql/logs/error.logMySQL 的错误日志写得相当详细绝大多数问题它都直说了。第二步看数据目录属主。权限不对是最常见的原因报错通常是Cant create/write to file或者Operating system error number 13ls -ld /data/mysql/data # 应该显示 mysql mysql chown -R mysql:mysql /data/mysql第三步看 SELinux。如果改完属主还是起不来临时setenforce 0再试一次。起来了就说明是 SELinux 上下文的问题这时候别一关了之用下面的方式给目录打标签semanage fcontext -a -t mysqld_db_t /data/mysql/data(/.*)? restorecon -Rv /data/mysql/data第四步看端口占用。如果错误日志里有Address already in use说明 3306 被别的进程占了可能是系统里已经装过一个 MariaDBss -lntp | grep 3306第五步看磁盘。No space left on device——虚拟机磁盘写满了MySQL 直接拒绝启动。虚拟机的动态磁盘很容易在你导入几个大 SQL 文件之后悄悄撑满。4.2 连不上客户端连接报错的四种可能连接被拒分两种报错文案不同含义也完全不同报错信息含义排查方向Cant connect to MySQL server (111)网络层不通防火墙、bind-address、服务是否在跑Access denied for user认证或权限不对密码、账号主机部分、权限表Unknown MySQL server host域名解析失败用 IP 连检查 hostsSSL connection error证书或时间问题虚拟机时间同步第一种情况最好排查就是那三道门的问题按 3.4 节的顺序过一遍。可以先用 telnet 或 nc 测一下端口通不通nc -zv 192.168.1.150 3306第二种情况有个特别容易忽略的地方MySQL 的账号是「用户名 主机」的组合键appuserlocalhost和appuser%是两个完全不同的账号。你从虚拟机外面连进来匹配的是%那条记录如果你只在 localhost 上建了账号远程连当然被拒。查一下就知道SELECT user, host, plugin FROM mysql.user;第四种情况就是前面提过的时间同步问题。虚拟机挂起再恢复、宿主机休眠过、时区设置有问题都可能触发。检查date输出和宿主机对一下。4.3 乱码与导入导出数据迁移时的编码坑从 Windows 上导出的 SQL 文件拿到 Linux 上导入中文变问号或者变成一堆方块这个场景太常见了。原因通常不在数据库而在文件本身的编码。排查顺序是这样的先确认文件编码。Windows 上用记事本或者某些工具导出的文件可能是 GBK 或 GB18030file -i backup.sql输出如果带charsetiso-8859-1基本就是 GBK 被误判可以用 iconv 转iconv -f GBK -t UTF-8 backup.sql -o backup_utf8.sql再确认导入时指定的字符集。mysql -uroot -p --default-character-setutf8mb4 appdb backup_utf8.sql最后确认表本身的字符集。SHOW CREATE TABLE appdb.user_table\G如果建表时用的是 latin1那不管导入过程多正确存进去还是乱。顺带说一个相关的场景从 Windows 拷过来的 zip 压缩包在 Linux 里解压之后文件名全是乱码。这是因为 zip 格式的历史遗留问题——它不强制规定文件名编码Windows 用 GBKLinux 用 UTF-8两边对不上。可以这样处理unzip -O GBK package.zip -d ./target如果系统自带的 unzip 不支持-O参数用 7z 或 bsdtar 也行它们对编码的猜测更聪明。数据库转储文件经常和这些压缩包一起出现所以顺手记一下没坏处。4.4 虚拟机特有的坑有几个问题只在虚拟机里出现物理机上碰不到值得单独拎出来。虚拟机快照与数据一致性。VMware 和 VirtualBox 都有快照功能很多人把它当备份用随手一点就打个快照。问题是如果 MySQL 正在运行内存里还有未刷盘的脏页和未提交的事务快照出来的磁盘状态是不一致的恢复之后 InnoDB 可能要做崩溃恢复运气不好就是数据损坏。正确做法是打快照前先停库或者至少执行一次干净关闭-- 临时加读锁保证一致性仅 MyISAM 场景意义大InnoDB 用下面的方式 FLUSH TABLES WITH READ LOCK;更稳妥的办法是打快照前systemctl stop mysqld打完再起来。磁盘级的快照永远比不上逻辑备份可靠这一点要记牢。虚拟机克隆导致的网络配置冲突。从一台装好的虚拟机克隆出第二台第二台起来后网络直接不通。原因是克隆保留了原来的 MAC 地址和网卡配置两台机器抢同一个 IP。解决方式是克隆后删掉/etc/udev/rules.d/70-persistent-net.rules老系统修改网卡配置里的 MAC 地址和 IP或者干脆在克隆时选「重新生成 MAC 地址」。内存过载。宿主机同时开两三个虚拟机每个都配了 4GB宿主机自己就崩了。这时候虚拟机会被强制挂起或者 OOM Killer 直接杀掉 mysqld。养成看free -h的习惯available那一列才是真正能用的内存。磁盘 IO 瓶颈。虚拟磁盘默认走的是宿主机文件系统写入延迟比物理机高。如果发现大批量导入特别慢可以在虚拟机设置里打开磁盘写入缓存注意这会牺牲一部分断电安全性或者直接换到 SSD 上放虚拟磁盘文件。5. 跑起来之后备份、同步与日常维护数据库装上能用只是开始。真正让它稳定服务靠的是备份、同步和日常巡检这几件事。5.1 备份策略与mysqldump实操学习环境里最容易出事的是「我改了个配置表没了」。所以备份得是习惯而不是补救。mysqldump是最基础也最可靠的工具。一份完整的逻辑备份命令长这样mysqldump -uroot -p \ --single-transaction \ --routines \ --triggers \ --events \ --set-gtid-purgedOFF \ --default-character-setutf8mb4 \ --databases appdb /backup/appdb_$(date %F).sql逐条解释为什么这么写--single-transaction是关键。它利用 InnoDB 的 MVCC 机制在不锁表的情况下拿到一致性快照。少了它备份期间有写操作进来备份出来的数据可能前后不一致。--routines --triggers --events三个参数保证存储过程、触发器、定时事件也被导出。默认情况下 mysqldump 是不导这些的很多人恢复之后发现触发器全没了就是这个原因。--set-gtid-purgedOFF在你没开 GTID 的时候会报错或者写一堆无用的 SET 语句进去加上它省事。--default-character-setutf8mb4避免导出时把中文转坏。恢复很简单mysql -uroot -p --default-character-setutf8mb4 /backup/appdb_2024-06-01.sql注意mysqldump 恢复前要先建库除非导出时用了--databases参数它会带上 CREATE DATABASE 语句。用mysqldump appdb x.sql这种写法导出的文件里没有建库语句直接导入会报「No database selected」。备份文件别放在虚拟机同一个磁盘上。虚拟机磁盘一崩数据库和备份一起没了。写个脚本同步到宿主机或者其他存储上用scp或者挂共享目录都行。5.2 主从同步的环境搭建如果你想两台虚拟机之间做数据同步MySQL 原生的主从复制是最直接的选择。它靠的是二进制日志。主库上的配置my.cnf 加这几行然后重启[mysqld] server-id 1 log-bin mysql-bin binlog_format ROW gtid_mode ON enforce_gtid_consistency ON expire_logs_days 7binlog_format ROW是现代 MySQL 的推荐值。它记录的是每行数据的变化而不是 SQL 语句本身避免了NOW()、RAND()这类函数在从库上执行结果不一致的问题。从库上server-id必须是不同的值其他类似但不需要开log-bin除非要做级联复制。然后建一个专供复制用的账号CREATE USER repl192.168.1.% IDENTIFIED BY ReplPass#2024; GRANT REPLICATION SLAVE ON *.* TO repl192.168.1.%; FLUSH PRIVILEGES;先把主库的数据全量灌到从库用 5.1 的 mysqldump加上--master-data2或--source-data2参数然后在从库上指向主库CHANGE REPLICATION SOURCE TO SOURCE_HOST 192.168.1.150, SOURCE_PORT 3306, SOURCE_USER repl, SOURCE_PASSWORD ReplPass#2024, SOURCE_AUTO_POSITION 1; START REPLICA; SHOW REPLICA STATUS\G看Replica_IO_Running和Replica_SQL_Running两个字段是不是都是 Yes。这里要提一下术语变更MySQL 从 8.0.22 开始把CHANGE MASTER TO改成了CHANGE REPLICATION SOURCE TOSHOW SLAVE STATUS改成了SHOW REPLICA STATUS。老教程里的命令还能用但会报 deprecation 警告看着心烦。写自动化脚本时建议直接用新语法。排查复制中断看Last_IO_Error和Last_SQL_Error两个字段就够了。IO 线程报错通常是网络或账号问题SQL 线程报错通常是主从数据不一致比如从库上有人手动写入了数据导致主键冲突。后者用SET GLOBAL SQL_SLAVE_SKIP_COUNTER 1跳过或者直接STOP REPLICA; ... START REPLICA;定位。5.3 日常巡检命令清单最后给一份我平时往虚拟机里一扔就能快速看状态的命令清单建议做成一个 shell 脚本#!/bin/bash echo 系统时间 date echo 内存 free -h echo 磁盘 df -h /data echo 端口监听 ss -lntp | grep 3306 echo 服务状态 systemctl is-active mysqld echo 连接数 mysql -uroot -p$MYSQL_PWD -e SHOW STATUS LIKE Threads_connected; echo 慢查询数量 mysql -uroot -p$MYSQL_PWD -e SHOW GLOBAL STATUS LIKE Slow_queries;数据库里几个常用的巡检 SQL-- 当前连接和线程状态 SHOW PROCESSLIST; -- InnoDB 引擎状态看是否有死锁、长事务 SHOW ENGINE INNODB STATUS\G -- 表空间大小排行 SELECT table_schema, ROUND(SUM(data_length index_length) / 1024 / 1024, 2) AS size_mb FROM information_schema.tables GROUP BY table_schema ORDER BY size_mb DESC; -- 表锁等待情况 SELECT * FROM performance_schema.data_lock_waits;SHOW ENGINE INNODB STATUS这个命令的输出很长但里面有一段TRANSACTIONS特别有用。如果有事务跑了几个小时不提交长事务会把 undo log 撑大还会阻塞 DDL 操作是很多「数据库突然变慢」问题的元凶。养成定期看一眼的习惯比出问题再查要省事得多。虚拟机里的 MySQL 还有个特别实际的建议给虚拟机本身的快照起个文件命名规范比如按「日期-变更内容」命名例如「20240601-升级MySQL8.0.36前」。等你半个月后想回滚面对一堆「快照1、快照2、快照3」那才是真的抓瞎。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →