前阵子帮朋友在一台全新的Ubuntu 22.04服务器上部署MySQL,顺手翻了不少教程,结果发现一个很普遍的问题:网上的教程大量停留在MySQL 5.7时代,很多命令和配置在8.0上要么失效,要么有隐藏的坑。最典型的就是root账号的默认认证方式,Ubuntu的apt源安装的MySQL 8.0默认用的是auth_socket,而不是caching_sha2_password,导致很多人“设了密码”之后依然无法远程登录、甚至本地登录都报错。这篇文章我就把这套流程完整走一遍,从零开始,覆盖安装、安全初始化、root认证修复、远程访问、防火墙、基础调优,一直到xtrabackup备份和GTID主从同步的思路,最后再补一个Docker Compose部署的对比,希望能帮你少踩几个坑。
文章适用于两类读者:一类是第一次在Ubuntu 22.04上装MySQL、需要一份能直接照着敲的新手,另一类是想在测试环境快速验证备份恢复、主从复制方案的运维,虽然不会深入到生产级参数调优,但关键节点的思路和坑都会讲到。
1. 安装前的环境准备:为什么我推荐apt源安装而不是二进制包
1.1 Ubuntu 22.04自带的MySQL版本与选型
Ubuntu 22.04(Jammy)的官方软件源里默认带的是MySQL 8.0.x版本。国内很多云厂商的镜像源也都同步了这个版本。我查了一下当前源里的具体版本号,一般在8.0.3x左右,具体以apt-cache policy mysql-server显示为准。
选型上我强烈建议直接用apt源安装,理由很简单:
- 依赖自动处理:MySQL Server在Ubuntu上有大量依赖包(libaio、libmecab、libjson-c等),用apt安装会自动把这些依赖一并装上,二进制包方式还得手动处理一堆运行时库。
- 服务管理集成:apt安装后会自动注册为systemd服务,
systemctl start/stop/restart mysql就能管理,省去自己写启动脚本的麻烦。 - 升级路径清晰:后续MySQL发布小版本更新,
apt upgrade就能平滑升级,二进制包方式升级要重新解压覆盖,容易出权限错乱。
你可能想问,那为什么不直接用Docker?这个我放在文章最后专门对比,先按原生安装走。
1.2 先更新软件源并检查系统基础组件
在开始安装之前,先把系统更新到最新状态,这一步不是可有可无的。我遇到过几次因为libaio版本过旧导致后续MySQL启动失败的情况,根源就是系统没有先整体更新。
sudo apt update sudo apt upgrade -y中间如果有内核更新,建议重启一下服务器,让新内核生效。不重启的话,后续MySQL装好后虽然能跑,但某些系统调优参数可能受旧内核限制,影响性能表现,尤其是vm.swappiness这类参数。
顺手把必要的基础工具装上,MySQL安装过程会用到:
sudo apt install -y net-tools curl wget gnupg lsb-release其中gnupg和lsb-release在后面添加Percona仓库(用于安装xtrabackup)时会用到,先装好不亏。
1.3 检查端口占用:3306被占用的处理
在正式安装之前,先确认一下3306端口是否被占用。有些云服务器镜像会预装别的数据库(比如MariaDB),而MariaDB和MySQL的包在Ubuntu里是冲突的,直接apt install mysql-server可能会报错。
sudo ss -lntp | grep 3306如果已经占了,先确认是什么进程:
sudo lsof -i:3306如果是MariaDB,先停掉并卸载:
sudo systemctl stop mariadb sudo apt remove --purge mariadb-server mariadb-client -y sudo apt autoremove -y这一步一定要做干净,否则后面装MySQL时,mysql_install_db会因为数据目录里残留的旧库文件直接报错。
2. 核心安装流程:从apt install到安全初始化脚本
2.1 安装mysql-server
在确保环境干净之后,执行安装命令:
sudo apt install mysql-server -y安装过程Ubuntu会自动启动MySQL服务。安装完成后,检查状态:
sudo systemctl status mysql sudo systemctl enable mysqlenable这一步是把MySQL设为开机自启,默认情况下apt安装后其实会自动启用,但手动执行一次更保险,尤其是你装过其他数据库、可能改过systemd配置的情况下。
验证MySQL是否真的能正常工作:
sudo mysqladmin ping如果返回mysqld is alive,说明服务正常。
2.2 安全初始化脚本:mysql_secure_installation逐项讲解
装好后,MySQL默认的安全配置很松,这就是为什么要跑一遍安全初始化脚本。执行:
sudo mysql_secure_installation这里有一个跟5.7不一样的地方:8.0版本安装完成后,root账号默认是auth_socket认证(后面详细讲),所以这个脚本会以sudo身份让你直接进入MySQL来配置,而不是要你先输入root密码。脚本会依次问你以下几个问题:
- 是否设置密码验证组件(VALIDATE PASSWORD COMPONENT):建议选
Y,但密码策略先选LOW(只校验长度),避免后续开发调试时因密码规则太严格而烦躁。生产环境再按需调成MEDIUM或STRONG。 - 设置root密码:注意这里的root是MySQL的root,不是操作系统的root。
- 删除匿名用户(Remove anonymous users):选
Y。匿名用户是安全大忌,尤其是MySQL默认监听将通过后续配置开放到局域网时。 - 禁止root远程登录(Disallow root login remotely):选
Y。这个建议选Y,后续我们会单独创建一个管理账号用于远程连接,root只保留本机登录权限。 - 删除test数据库并访问(Remove test database and access to it):选
Y。test库有权限漏洞,留着没用。 - 重新加载权限表(Reload privilege tables now):选
Y,让所有改动立即生效。
每一步的含义我简单解释一下:MySQL的权限系统是分层的,mysql.user表存储全局权限,mysql.db存储数据库级权限,匿名用户如果存在,会在连接时被MySQL优先匹配,导致你新建的普通用户反而拿不到预期权限,所以必须清理干净。
2.3 安装后的目录结构速览
这一步是为了让你后续排查问题时能快速找到对应文件:
- 数据目录:
/var/lib/mysql,所有数据库文件、binlog文件都在这里 - 配置文件:
/etc/mysql/mysql.conf.d/mysqld.cnf,这是最主要的配置入口 - 错误日志:
/var/log/mysql/error.log - systemd服务文件:
/lib/systemd/system/mysql.service
有个脚本化安装的细节:如果你需要批量部署多台机器,mysql_secure_installation没法直接传参数,但可以通过debconf预置回答。不过8.0版本这个脚本内部用的是mysqladmin和SQL语句执行,预置比较麻烦,我的做法是安装后用一段SQL在多台机器上统一执行,后面统一修改root认证部分会讲到。
3. root账号认证方式的坑:8.0的auth_socket改成密码登录
3.1 为什么默认root登录不需要密码
这里必须花点篇幅说清楚。Ubuntu源安装的MySQL 8.0,root账号默认使用auth_socket插件认证。这个插件的认证逻辑是这样的:只要当前操作系统用户是root(或者sudo切换成root),就能直接通过socket连接MySQL,不需要输密码。
设计初衷是安全——root账号只在系统本机使用,避免暴力破解和远程探测。但实际使用中很多人不适应:明明设置了密码,却始终无法用密码登录;改配置想远程连,发现root根本连不上;甚至用Navicat等客户端本地连接也失败。
查看当前root的认证方式:
SELECT user, host, plugin FROM mysql.user WHERE user = 'root';正常情况下会看到root的plugin是auth_socket。
3.2 修改认证方式的具体SQL
将root改为caching_sha2_password密码认证,执行以下SQL即可:
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '你的强密码'; FLUSH PRIVILEGES;需要注意:8.0默认情况下mysql_native_password插件虽然还存在,但官方已经标记为废弃,新客户端(包括MySQL Shell、最新版本的Navicat)都能正常支持caching_sha2_password,没必要继续用旧的。
改完后,退出再重新登录验证:
mysql -u root -p如果密码能正常登录,说明修改成功。
3.3 我这边的踩坑记录:一次权限表误操作
网上不少人教“直接UPDATE mysql.user SET plugin='mysql_native_password'”,这种方法不是说完全不行,但风险很大。我曾经在一次操作中直接UPDATE整个user表,把user和host字段也顺带改了,导致MySQL重启后拒绝所有连接,最后只能通过--skip-grant-tables方式进入数据库修复权限表。那次之后我养成了一个习惯:修改认证方式一律用ALTER USER,不动user表本身。
还有一个隐藏问题:如果你之前执行过ALTER USER把root改成了密码认证,然后又用mysql_secure_installation重新跑了一遍,脚本会要求先输root密码才能继续。所以顺序上我的建议是:先跑安全初始化脚本,再修改认证方式。
4. 配置远程连接:bind-address、用户授权与防火墙
4.1 修改监听地址
MySQL默认只监听127.0.0.1,也就是只能本机连接。要允许其他机器通过局域网访问,需要修改配置文件:
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf找到这一行:
bind-address = 127.0.0.1改成:
bind-address = 0.0.0.0重启MySQL使配置生效:
sudo systemctl restart mysql改完后检查监听状态:
sudo ss -lntp | grep mysql如果显示0.0.0.0:3306或具体网卡IP,说明监听已经开放。
这里有一个值得注意的点:如果你的服务器有多个网卡(比如内网A、公网B),不要直接设0.0.0.0,最好指定具体的内网IP,避免公网端口暴露。比如:
bind-address = 192.168.1.1004.2 创建专用账号并授权
远程连接不推荐用root,原因不言自明——root权限太大,一旦被爆破,整个数据库就完了。正确的做法是创建专用业务账号:
CREATE USER 'devops'@'192.168.1.%' IDENTIFIED BY 'StrongPassw0rd!'; GRANT ALL PRIVILEGES ON *.* TO 'devops'@'192.168.1.%'; FLUSH PRIVILEGES;'devops'@'192.168.1.%'表示仅允许192.168.1网段的机器连接,比'%'(允许所有IP)安全得多。如果确实需要允许所有IP访问(开发环境常见),可以使用'devops'@'%',但生产环境强烈不建议。
单独给业务应用建账号时,最好遵循最小权限原则:
CREATE USER 'app_user'@'%' IDENTIFIED BY 'AppPassw0rd!'; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app_user'@'%'; FLUSH PRIVILEGES;能看到区别吗?业务账号只给了某个数据库的增删改查权限,连DROP、CREATE都没给,这样即使账号被拖库,影响面也有限。
4.3 防火墙配置:ufw和云安全组
Ubuntu 22.04默认使用ufw防火墙。如果你启用过ufw(检查sudo ufw status),需要放行3306端口:
sudo ufw allow from 192.168.1.0/24 to any port 3306 proto tcp sudo ufw reload只放行指定网段访问,而不是sudo ufw allow 3306这种全开放方式,这是我装过N台机器后总结的经验:全开放意味着公网也能访问,加上云服务器控制台还有一层安全组,两层的规则如果都太宽松,那就等于裸奔。
云服务器(阿里云、腾讯云、AWS等)还需要在控制台的安全组/防火墙规则里也放行对应端口,这个别漏了,很多人改了服务器里的ufw,却忘了安全组那层,结果还是连不上。
4.4 远程连接的测试
在另一台机器上测试:
mysql -h 192.168.1.100 -u devops -p如果提示Access denied,优先检查账号的host范围是否覆盖了你的来源IP:
SELECT user, host FROM mysql.user;如果提示Can't connect to MySQL server(10060/2003),优先检查防火墙和安全组,其次是bind-address配置。
5. 基础性能调优:一套适用于16G内存主机的my.cnf配置
5.1 关键参数解释
MySQL装完默认配置其实是非常保守的,它要考虑在最低配机器上也能跑起来。如果你的是16G内存的主机,不改配置直接上业务,用不了多久就会遇到性能瓶颈。我最常用的一套基础调优配置如下:
[mysqld] # 缓冲池大小,建议设为物理内存的50%-70% innodb_buffer_pool_size = 8G innodb_buffer_pool_instances = 8 # 日志相关 innodb_redo_log_capacity = 1G max_binlog_size = 512M binlog_expire_logs_seconds = 2592000 # 连接数 max_connections = 500 max_connect_errors = 1000 # 临时表 tmp_table_size = 64M max_heap_table_size = 64M # 慢查询日志 slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2 # 排序和join缓冲 sort_buffer_size = 4M join_buffer_size = 4M各参数的作用简单说明一下:
innodb_buffer_pool_size是最核心的参数,InnoDB引擎的索引和行数据在内存中的缓存池大小。8G缓冲池意味着热数据基本都在内存里,磁盘的随机读次数会大幅下降。但设置太大会导致内存不足,触发Swap,反而更慢,所以50%-70%是经验值。
innodb_redo_log_capacity在8.0.30之后取代了旧的innodb_log_file_size,Redo Log大小直接影响崩溃恢复的速度和写入吞吐量,1G是比较合适的起步值。
binlog_expire_logs_seconds取代了废弃的expire_logs_days,单位是秒,2592000就是30天,避免binlog把磁盘塞满。
tmp_table_size和max_heap_table_size需要一起设置,否则内存临时表超限后会转成磁盘临时表,复杂查询会慢得离谱。
5.2 应用配置并验证
修改配置文件后重启:
sudo systemctl restart mysql验证参数是否生效:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections';这里有个细节:innodb_buffer_pool_size修改后,MySQL冷启动时会有初始化和预热过程,第一次重启会慢几秒,属于正常现象,不用惊慌。
5.3 一个调优的反面教训:连接数和线程池
有一次我图省事,把max_connections直接设成5000,想着反正内存大。结果连续两个星期出现连接数飙升、CPU耗尽的情况。排查下来发现,一个业务连接池配置不合理,每个Tomcat实例默认建了200个连接,几个实例一叠加,瞬间把数据库连接榨干。后面把连接数调回500,同时优化了业务的连接池配置,系统才稳定下来。所以说:调优不是调一个参数就完事,要先看业务背压的方向,再动手改。
6. 备份与扩容:xtrabackup备份主库和GTID主从同步思路
如果你只是学习安装配置,到第5节就可以收官了。但既然标题里有“部署”,部署就意味着要考虑备份和扩展,这也是最近后台私信里问得最多的话题:怎么备份MySQL主库?怎么部署从库?GTID同步到底怎么玩?我把关键流程列一遍,细节每个都可以单独写一篇,这里先给完整路线。
6.1 安装XtraBackup
XtraBackup是Percona开源的物理备份工具,相比mysqldump逻辑备份,它的优势是备份速度快、支持热备、恢复时能保留InnoDB表空间和binlog位点信息,非常适合做主从搭建和日常备份。
Ubuntu 22.04上通过Percona官方仓库安装:
wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb sudo percona-release setup ps80 sudo apt update sudo apt install -y percona-xtrabackup-80确认版本:
xtrabackup --version如果输出里有version 8.0.x,就装好了。
6.2 全量备份与恢复验证
备份命令:
sudo xtrabackup --backup \ --target-dir=/data/backup/mysql_full_$(date +%Y%m%d) \ --user=backup_user \ --password='BackupPassw0rd!' \ --parallel=4记得提前创建备份专用账号,并给予相应权限:
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'BackupPassw0rd!'; GRANT BACKUP_ADMIN, RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'backup_user'@'localhost'; FLUSH PRIVILEGES;备份完成后,目录里会包含一份xtrabackup_checkpoints文件,记录了备份的LSM(Log Sequence Number)信息,做主从同步时这个信息非常关键。
恢复的时候不要直接restore到正在运行的数据目录,而是先prepare再copy-back:
sudo xtrabackup --prepare --target-dir=/data/backup/mysql_full_20250101 sudo xtrabackup --copy-back --target-dir=/data/backup/mysql_full_20250101prepare阶段做了两件事:回放Redo Log,把数据文件推到一致状态;清理未提交的Undo Log事务。不做prepare直接copy-back,MySQL启动时会报数据文件不一致错误。
6.3 GTID主从同步的配置要点
GTID(Global Transaction Identifier)是MySQL 5.6之后引入的全局事务标识符,相比传统的基于binlog文件名+位置的同步方式,GTID的优点是主从切换时不需要手动找偏移量,故障转移复杂度大幅降低。
主库开启GTID需要修改配置文件:
[mysqld] server-id = 1 gtid_mode = ON enforce_gtid_consistency = ON log_bin = /var/log/mysql/mysql-bin从库配置:
[mysqld] server-id = 2 gtid_mode = ON enforce_gtid_consistency = ON注意:gtid_mode=ON不能直接设置,MySQL要求按照OFF -> OFF_PERMISSIVE -> ON_PERMISSIVE -> ON的顺序递增设置。在线上环境直接改回ON会报错,必须一步步来:
SET GLOBAL gtid_mode = OFF_PERMISSIVE; SET GLOBAL gtid_mode = ON_PERMISSIVE; SET GLOBAL gtid_mode = ON;6.4 部署从库的流程简述
有了全量备份和GTID,从库搭建就简单了,核心思路是“主库做一次全量备份 -> 恢复到从库 -> 从库通过GTID自动追平主库”。
具体步骤:
- 在主库上创建复制账号:
CREATE USER 'repl'@'%' IDENTIFIED BY 'ReplPassw0rd!'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;对主库使用xtrabackup做一次全量备份(步骤见6.2),拿到备份目录和其中的
xtrabackup_binlog_info文件(记录GTID位置)。把备份目录拷贝到从库,执行prepare和copy-back,注意从库的数据目录要先清空。
在从库上执行:
CHANGE MASTER TO MASTER_HOST='192.168.1.100', MASTER_USER='repl', MASTER_PASSWORD='ReplPassw0rd!', MASTER_AUTO_POSITION=1; START SLAVE;MASTER_AUTO_POSITION=1是GTID自动定位的关键,意味着从库会从主库拉取从全量备份时间点之后的所有事务。
- 查看同步状态:
SHOW SLAVE STATUS\G重点关注两个字段:Slave_IO_Running: Yes和Slave_SQL_Running: Yes。如果两个都是YES,说明同步正常。
我遇到比较多的问题是Slave_SQL_Running: No,大都是由主从数据不一致(比如有人在从库手动写了数据)、事务冲突引起的。处理思路是先跳过出错事务(临时手段),然后对比数据,必要时重建从库。想在GTID模式下安全跳过,用以下SQL:
STOP SLAVE; SET GLOBAL sql_slave_skip_counter = 1; START SLAVE;但这只是缓兵之计,最终还是要从主库重新同步一次才干净。
7. 卸载与重建:Ubuntu 22.04上正确处理MySQL残留
7.1 为什么卸载不干净会导致重装失败
这节是意外塞进来的,起因是一个粉丝反馈“按教程装MySQL,装一半报错说数据目录已存在”,查了半天发现是他之前卸载过一次MySQL,但/var/lib/mysql目录没有清干净。MySQL安装包会检测数据目录,如果目录存在且非空,就会跳过初始化流程,导致安装完成后服务无法启动。
这是Ubuntu系列Linux上最典型的重装坑,专门写一节讲清楚。
7.2 完整卸载流程
完整的卸载命令如下:
sudo systemctl stop mysql sudo apt remove --purge mysql-server mysql-client mysql-common -y sudo apt autoremove -y然后清理残留目录:
sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql sudo rm -rf /var/log/mysql清理依赖包(可选但推荐):
sudo apt purge '*mysql*' -y sudo apt autoremove -y全部清完后检查:
dpkg -l | grep mysql如果没有输出,说明卸载干净了。这时候重新执行第二章的安装步骤就不会有任何干扰。
7.3 保留数据目录的部分卸载
有一种场景是你只想重装MySQL服务端,但保留数据库文件(比如你想换个版本验证兼容性,又怕数据丢失),那就可以不清/var/lib/mysql,只做apt remove --purge mysql-server,但装新版本之前最好先备份数据目录:
sudo cp -a /var/lib/mysql /var/lib/mysql.bak重装后如果数据目录里的版本和新装版本不一致(比如从8.0降到5.7),MySQL会拒绝启动并提示“data directory was initialized by a different version”。这种情况基本只能恢复备份或升级回原版本,没有其他好办法。
8. 补充对比:Docker Compose部署MySQL的适用场景
8.1 什么时候用Docker、什么时候用原生安装
最近Docker Compose部署MySQL的热度很高,我也在实际项目里用过。简单对比一下:
| 维度 | 原生apt安装 | Docker Compose部署 |
|---|---|---|
| 环境隔离 | 直接使用宿主机资源 | 容器隔离,端口映射 |
| 配置修改 | 改/etc/mysql下的配置文件 | 改docker-compose.yml和环境变量 |
| 升级 | apt upgrade | 拉新镜像重启容器 |
| 数据持久化 | 数据目录原生管理 | 需要挂载卷,否则容器删了数据就没了 |
| 适合环境 | 生产环境、单机高负载 | 开发环境、微服务多容器编排 |
我的个人原则是:生产环境的数据库,能用原生安装就用原生安装。理由很简单,生产数据库的道路要可控、可预测,容器化虽然带来了部署便利,但也带来了“数据卷权限混乱”“容器重启后IP变化”“日志收集链路复杂”等额外问题。开发环境我反而推荐用Docker Compose,因为可以快速起停、随心所欲地切换版本。
8.2 一个Docker Compose的参考配置
如果你确实需要在开发环境使用Docker部署MySQL,这是一份可以直接用的docker-compose.yml:
services: mysql: image: mysql:8.0 container_name: mysql-dev restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: RootPassw0rd! MYSQL_DATABASE: appdb MYSQL_USER: app_user MYSQL_PASSWORD: AppPassw0rd! TZ: Asia/Shanghai ports: - "3306:3306" volumes: - ./data:/var/lib/mysql - ./my.cnf:/etc/mysql/conf.d/custom.cnf command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci启动:
docker compose up -d这里有个容易踩的坑:./data:/var/lib/mysql这个挂载,第一次启动时Docker会自动以镜像内默认配置初始化数据目录,但如果宿主机./data目录存在且非空,MySQL会直接使用,可能触发“目录初始化失败”的错误。建议第一次启动前让./data目录保持空状态。
8.3 如果原生安装遇到问题,Docker也不失为一种快速恢复手段
有一次我给客户调试一个MySQL 8.0的读写性能问题,原生环境的配置被之前的同事改得乱七八糟,调了半天没还原到干净状态。后来我直接在Docker里起了一个新实例,用同样的my.cnf和数据集,很快就定位到是某个参数设置异常。这个思路仅供参考:Docker不能替代生产数据库,但它是很好的调试沙盒。
到这一步,从环境准备到安装配置、从调优到备份同步、从卸载清理到Docker对比,整条部署路径都走通了。我个人实际操作下来,最有价值的一句话是:装MySQL从来不是难点,难点在装完后的每一步配置都带着“默认安全但不方便”与“开放但危险”的权衡。希望这篇整理能让你少走几趟弯路。