上周帮一个朋友排查线上数据库问题,折腾到凌晨两点,最后发现只是字符集配置不一致导致的乱码和锁表。这种事在 MySQL 环境下太常见了,也让我特别想把这些年积累的 MySQL 数据库管理经验完整梳理一遍。这篇文章不谈虚的,直接围绕 MySQL 的安装、启动、增删改查、备份恢复这几个核心环节展开,覆盖 2026 年目前主流的部署方式和常见坑点。不管你是刚接触数据库的初学者,还是接手老项目需要快速上手的开发者,这篇文章都能提供一套可以直接照做的操作路径。
1. 2026 年 MySQL 环境准备与安装全流程
1.1 版本选择:8.0 依然是生产主力
很多新手上来就问“我该下载哪个版本”,这个问题的答案其实很直接:生产环境选 8.0.x 系列,不要选 9.x 创新版,更不要选已经停止更新的 5.7。
MySQL 8.0 从 2018 年发布到现在,已经经历了多个小版本的迭代,稳定性、性能优化和安全性都得到了充分验证。9.x 虽然引入了不少新特性,但作为创新版,它的迭代节奏快、兼容性风险高,不适合直接用在核心业务上。如果你是在学习或做个人项目,用 8.0 也完全够用,网上绝大多数教程和面试题也都是基于 8.0 的。
下载渠道一定要认准官方地址 dev.mysql.com/downloads/,不要从第三方站点下载安装包,避免被捆绑恶意软件。选择安装包时,Windows 环境下载 MSI Installer,Linux 环境用 apt 或 yum 源安装即可。
1.2 Windows 与 Linux 安装实操对比
Windows 安装 MySQL 相对简单。以 8.0 最新小版本为例,下载 MSI 安装包后双击运行,一路 Next,到 Select Products 页面时选择 MySQL Server 8.0,其他组件按需勾选。重点注意下一步的 Type and Networking,默认端口 3306 就行,除非你本机已经占用了这个端口,否则不建议乱改,后续连接配置会麻烦很多。
Authentication Method 这一步需要留意,新版默认使用 caching_sha2_password,这是 MySQL 8 开始推荐的认证方式。如果后续要用 Navicat 或一些老版本开发库连接,可能会遇到认证协议不兼容的问题,这一步先默认选第一个,后面我会专门讲怎么处理这类报错。
设置 root 密码时建议用强密码,至少 8 位且包含大小写字母和数字。Windows 服务配置那一步直接勾选 Configure MySQL Server as a Windows Service,这样开机就能自动启动,省去每次手动启动的麻烦。
Linux 安装稍显技术流一些。Ubuntu/Debian 系列执行:
bash复制sudo apt update
sudo apt install mysql-server -y
CentOS/RHEL/Fedora 系列执行:
bash复制sudo dnf install mysql-server -y
安装完成后先不着急启动,我一般习惯先看一下安装的版本:
bash复制mysql --version
实测下来,Linux 下用包管理器安装的 MySQL 配置文件位于 /etc/mysql/,数据目录是 /var/lib/mysql,日志在 /var/log/mysql/。后续排查问题这些都是重要的路径。
1.3 Docker 方式部署 MySQL 的详细配置
2026 年,Docker 部署 MySQL 已经是很多团队的首选方式了,特别是开发环境和中小型项目。好处很明显:环境隔离、部署速度快、版本切换方便。但坏处也有,就是数据卷如果不挂载好,容器一删数据就全没了。
这里给出一个我常用的生产级 Docker 运行命令:
bash复制docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=YourStrongPassword \
-e MYSQL_DATABASE=testdb \
-e TZ=Asia/Shanghai \
-v /data/mysql/conf:/etc/mysql/conf.d \
-v /data/mysql/data:/var/lib/mysql \
--restart=always \
mysql:8.0
拆开解释几个关键参数:
-p 3306:3306:把容器内的 3306 端口映射到宿主机,方便外部工具连接。-e MYSQL_ROOT_PASSWORD:设置 root 密码。这里只是第一层堡垒,容器起来后还是要手动进入 MySQL 修改密码策略。-v /data/mysql/data:/var/lib/mysql:数据目录挂载到宿主机,这是最核心的一步,数据安全就靠它。--restart=always:Docker 服务重启或服务器重启后自动拉起容器,减少人工干预。
注意:即使使用了 Docker 的数据卷挂载,也绝对不等于数据已经做了备份。数据卷只是解决了容器生命周期的问题,误删数据库、表结构损坏这种情况,照样需要依赖备份机制。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 服务启动、初始化与基础配置
2.1 启动服务与常见启动失败排查
Windows 下启动服务有几种方法。最简单的是打开服务管理器(Win + R 输入 services.msc),找到 MySQL80 这个服务,右键启动。命令行方式则是:
bash复制net start mysql80
Linux 使用 systemd 启动:
bash复制sudo systemctl start mysqld
sudo systemctl enable mysqld
enable 的作用是设置开机自启,生产环境建议加上。
启动失败的排查思路一定要清晰。第一步看错误日志。Linux 下日志文件在 /var/log/mysql/error.log,Windows 下在 MySQL 数据目录下,文件名通常是 hostname.err 或 hostname.log。
最常见的启动失败原因无非三种:
- 端口被占用。如果你之前装过旧版本 MySQL,或者系统上还有 SQL Server 之类的服务占用了 3306,启动就会失败。排查命令:
bash复制# Linux
sudo lsof -i:3306
# Windows
netstat -ano | findstr 3306
看到 PID 后强制结束进程,或者修改 MySQL 配置文件换一个端口。
- 数据目录权限不对。Linux 下经常是 /var/lib/mysql 目录属主不是 mysql 用户导致的。执行:
bash复制sudo chown -R mysql:mysql /var/lib/mysql
- 配置文件写坏了。比如你改了 my.cnf 里的字符集或缓冲池大小,语法错误会导致启动直接退出。这时候先把配置文件恢复默认,再逐步排查。
2.2 初始化数据库与设置 root 密码
某些情况下(比如手动解压安装包安装),MySQL 的 data 目录是空的,需要手动初始化。8.0 常用的是 mysqld --initialize 命令:
bash复制mysqld --initialize-insecure --user=mysql
--initialize-insecure 会生成一个无密码的 root 用户,初始化完成后立即登录修改密码。
初次登录时可能遇到密码错误的情况,因为有些初始化方式会生成随机密码。查看错误日志中的密码:
bash复制sudo grep 'temporary password' /var/log/mysql/error.log
用临时密码登录后,修改密码:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStrongPassword';
修改 root 密码时如果提示密码不满足安全策略,可以先调整策略级别再修改:
sql复制SET GLOBAL validate_password.policy = LOW;
2.3 字符集、时区与 SQL 模式配置
字符集问题是我见过引发线上事故频率最高的配置项。乱码、排序异常、索引失效,根源往往都是字符集不统一。
8.0 默认字符集是 utf8mb4,这个比老版本的 utf8 强得多,因为 utf8mb4 支持完整的 Unicode,包括 emoji 表情和一些特殊位置的字符。但还是建议在配置文件中显式声明,避免因为环境的差异导致读取到默认值。
Linux 下编辑 /etc/mysql/my.cnf,Windows 下是 my.ini,在 [mysqld] 区域添加:
ini复制[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
default-time-zone='+08:00'
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
default-time-zone 这个配置容易被忽略,尤其是有跨境业务或服务器在海外的情况。如果你的应用面向中国大陆用户,建议显式设置为 +08:00,否则数据库的时间函数(NOW())会返回 UTC 时间,前后端对不上。
SQL 模式中的 STRICT_TRANS_TABLES 非常重要,它会让数据库在写入非法数据时报错,而不是静默截断,这对业务数据的完整性保护意义很大。
修改完配置文件记得重启服务生效。
3. 核心操作:增删改查(CRUD)的高级应用
3.1 数据类型选择与建表规范
增删改查谁都写过,但真正让我区分一个开发者和一个合格 DBA 的,是建表语句的质量。
建表时字段类型的选择遵循几个原则:
- 整数类型:能用 TINYINT 绝不用 INT,能用 INT 绝不用 BIGINT。比如状态字段(0/1),TINYINT 就够了,省空间也能提升索引效率。
- 字符串类型:定长或几乎定长的用 CHAR,比如手机号(虽然现在建议脱敏)、订单号;可变长度的用 VARCHAR,且长度按业务最大值来,不要随手设 255。
- 时间类型:只需要日期用 DATE,需要时分秒用 DATETIME,不要用 TIMESTAMP,因为 TIMESTAMP 有 2038 年问题,DATETIME 的范围更大。
- 金额字段:千万不要用 FLOAT 和 DOUBLE,会有精度丢失的问题,一律用 DECIMAL(10,2) 这类定点数。
建表规范方面,我给自己定了几条硬性要求:
sql复制CREATE TABLE `user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` VARCHAR(64) NOT NULL COMMENT '用户名',
`email` VARCHAR(128) DEFAULT NULL COMMENT '邮箱',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-启用 0-禁用',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
- 必须有主键,强烈推荐使用 BIGINT 自增主键或雪花算法生成的分布式 ID。
- 每个表都要有 create_time 和 update_time 字段,且用 DEFAULT CURRENT_TIMESTAMP 可以让数据库自动维护时间,省去应用层传值。
- 索引不是越多越好。区分度低的字段(比如 status)不适合建索引,联合索引遵循最左前缀原则。
3.2 插入、更新、删除语句的实用技巧
INSERT 语句很多人只会最基本的版本,其实有几个高频场景值得单独说一下。
批量插入的效率远高于单条插入。在代码层可以用 MyBatis 的 batch 模式,在 SQL 层直接写成:
sql复制INSERT INTO `user` (username, email, status) VALUES
('a', 'a@test.com', 1),
('b', 'b@test.com', 1),
('c', 'c@test.com', 0);
一次插入几百条数据的速度,比循环几千次单条插入快一个数量级。
还有一个好用但容易记错的语法是 INSERT ... ON DUPLICATE KEY UPDATE。这个语句解决的是业务上的一个高频场景:主键或唯一键冲突时,要么更新已有记录,要么忽略报错。比如用户注册时,如果邮箱已经存在就自动更新状态:
sql复制INSERT INTO `user` (username, email, status) VALUES ('a', 'a@test.com', 1)
ON DUPLICATE KEY UPDATE
status = VALUES(status);
UPDATE 语句容易踩坑的地方在于条件不完整导致全表更新。测试环境无所谓,生产环境这是严重的生产事故。养成习惯,UPDATE 前先 SELECT 确认条件,或者用事务包裹起来执行。
sql复制BEGIN;
UPDATE `user` SET status = 0 WHERE id = 100;
-- 检查受影响行数,确认无误后 COMMIT,否则 ROLLBACK
COMMIT;
DELETE 语句和 UPDATE 一样的风险,务必带 WHERE。另外,删除逻辑数据比物理删除安全得多。所以业务表我通常不执行 DELETE,而是用 status 字段标记删除。
3.3 查询语句:排序、分组、多表关联
查询是增删改查里占比最高的操作,也是最容易产生性能问题的环节。
排序要注意索引失效的情况。ORDER BY 使用的字段如果没有索引,MySQL 会使用 filesort,数据量大了之后速度会急剧下降。所以高频排序字段记得加索引。
DISTINCT 去重和 GROUP BY 去重的选择。经常有人在网上搜“mysql 的 or 能去重吗”,这里一并说清楚。OR 是逻辑关系,它不会去重,只会决定行的筛选条件。要去重你需要用 DISTINCT 或 GROUP BY:
sql复制-- DISTINCT 去重
SELECT DISTINCT username FROM `user`;
-- GROUP BY 去重(顺便统计数量)
SELECT username, COUNT(*) FROM `user` GROUP BY username;
两者在功能上有重叠,但 GROUP BY 更强大,可以配合聚合函数使用,还是建议优先掌握 GROUP BY。
多表关联查询用 JOIN,但要注意 JOIN 的关联字段必须有索引,否则会产生笛卡尔积式的全表扫描,直接拖垮数据库。先给关联字段建索引,实测性能能提升几十倍。
子查询在 8.0 里性能有所优化,但能改写成 JOIN 的场景还是建议改写成 JOIN,因为 8.0 优化器对复杂子查询的支持仍不如 JOIN 稳定。
3.4 存储过程与触发器的实际应用
存储过程在业务代码不复杂、且需要大量复用 SQL 逻辑的场景下还是很有用的。比如批量生成测试数据、报表统计、对账脚本,这些都可以封装成存储过程。
定义一个简单的存储过程:
sql复制DELIMITER $$
CREATE PROCEDURE GetUserById(IN userId BIGINT, OUT userCount INT)
BEGIN
SELECT COUNT(*) INTO userCount FROM `user` WHERE id > userId;
END$$
DELIMITER ;
调用方式:
sql复制CALL GetUserById(100, @cnt);
SELECT @cnt;
存储过程中的 DELIMITER 很重要。因为 MySQL 自身用分号作为语句结束符,为了在定义存储过程时使用分号,需要先把结束符临时改成 $$,定义完再改回来。
触发器我目前的建议是:业务上慎重使用,准确说,能不用就不用。触发器虽然能自动完成一些表间同步、审计日志的工作,但它隐藏了执行逻辑,会让排查问题变得非常痛苦。三个典型问题:数据变更不符合预期查不到来源、调试困难、影响写入性能。如果你所在团队有人提出用触发器,建议让他把逻辑写进业务代码里。
4. 数据安全与备份恢复策略
4.1 备份方案选型与设计思路
聊到备份恢复,这是数据库日常维护里最容易被忽视、但真出事时唯一能救命的东西。很多团队的状态是“每周跑一次备份脚本”,但从没测试过恢复流程。结果真正需要恢复时,要么备份文件损坏,要么 SQL 版本不兼容,要么恢复过程耗时太长影响业务。
备份方案首先要想清楚用逻辑备份还是物理备份。两张表解释清楚:
| 对比项 | 逻辑备份 (mysqldump) | 物理备份 (xtrabackup) |
|---|---|---|
| 备份内容 | SQL 语句 | 数据文件直接复制 |
| 恢复速度 | 慢,需要逐条执行 SQL | 快,直接还原文件 |
| 业务影响 | 小,可在线备份 | 小到中,取决于工具 |
| 适用场景 | 中小数据量、跨版本迁移 | 大数据量、追求恢复效率 |
从恢复速度的维度来看,增量备份通常比全量备份更受到关注。全量备份数据量太大时单次执行时间久,所以常规方案是“每周一次全量 + 每天一次增量”。恢复时先还原最近一次全量,再叠加增量日志,就能把数据丢失的窗口缩小到分钟级。
4.2 mysqldump 全量备份实战
mysqldump 是 MySQL 自带的最常用的逻辑备份工具。一条基础的全库备份命令:
bash复制mysqldump -uroot -p --single-transaction --routines --triggers --hex-blob --databases testdb > /data/backup/testdb_$(date +%Y%m%d_%H%M%S).sql
参数逐个说明:
--single-transaction:在 InnoDB 引擎下,这个参数可以保证备份期间的数据一致性,不加这个参数备份时会把表锁住,影响线上读写。--routines:导出存储过程和函数。--triggers:导出触发器。--hex-blob:二进制数据以十六进制形式导出,避免乱码。--databases:导出指定数据库。注意这里如果不加 --databases,恢复的时候不会自动建库。
单库单表备份更灵活:
bash复制mysqldump -uroot -p testdb user > /data/backup/user_table.sql
我在生产环境还会给备份文件做压缩,因为 SQL 文件的冗余度很高,压缩率通常能到 70% 以上:
bash复制mysqldump -uroot -p --single-transaction testdb | gzip > /data/backup/testdb_$(date +%Y%m%d).sql.gz
配合 crontab 实现定时备份:
bash复制0 2 * * * mysqldump -uroot -p'密码' --single-transaction --databases testdb | gzip > /data/backup/testdb_$(date +\%Y\%m\%d).sql.gz
字段含义是每天凌晨 2 点执行一次全量备份。crontab 里的百分号需要转义,加了反斜杠才不会出问题。
4.3 binlog 增量备份与时间点恢复
binlog(二进制日志)是 MySQL 实现增量备份和数据恢复的核心。它记录了所有数据变更操作(DDL 和 DML),类似数据库的操作流水账。
首先确认 binlog 已开启。在 my.cnf 中配置:
ini复制[mysqld]
log_bin=mysql-bin
server_id=1
expire_logs_days=7
配置完成后重启 MySQL。查看当前 binlog 文件列表:
sql复制SHOW BINARY LOGS;
增量恢复的核心操作是查询 binlog 中某个时间段内的 SQL,然后重新执行。比如某天凌晨误删了一张表的数据,可以通过 mysqlbinlog 工具找出删除前后的日志进行回放。
bash复制mysqlbinlog --start-datetime="2026-01-01 00:00:00" --stop-datetime="2026-01-01 12:00:00" /var/lib/mysql/mysql-bin.000001 | mysql -uroot -p
mysqlbinlog 也支持按位点(position)恢复,这比按时间恢复更精确。先用 mysqlbinlog 导出来分析,找到关键的误操作位置,然后基于位点恢复:
bash复制mysqlbinlog --start-position=1200 --stop-position=3456 /var/lib/mysql/mysql-bin.000001 | mysql -uroot -p testdb
恢复完成后建议立即做一次全量备份,让增量日志的起点重新归一。
4.4 恢复演练:从零还原一个数据库
备份做得再好,不演练恢复等于白做。我这里提供一个完整的恢复演练流程,建议每个季度在测试环境跑一遍。
第一步,创建测试库并导入全量备份:
bash复制mysql -uroot -p -e "CREATE DATABASE testdb_restore DEFAULT CHARACTER SET utf8mb4;"
mysql -uroot -p testdb_restore < /data/backup/testdb_最新备份.sql
第二步,验证数据完整性。随机查询几张表的数据量,和源库对比:
sql复制SELECT COUNT(*) FROM testdb_restore.user;
SELECT COUNT(*) FROM testdb.user;
第三步,验证增量日志回放后的数据能恢复到最新状态。恢复增量 binlog,然后和源库的时间点数据做对比。
整个演练过程中最容易暴露的问题是字符集和分区表恢复。字符集不一致会导致中文乱码,分区表恢复时需要注意网络层和 SQL 层的兼容性。每次演练后把发现的问题记录下来,优化备份脚本,才能让备份机制真正可靠。
注意:等保合规中明确要求“定期备份数据,并测试恢复过程”,这不仅是安全要求,也是对自己工作负责。别等真出事再去背锅。
5. 日常运维中的错误排查与实用工具
5.1 高频报错与处理方法
先来看一个非常典型的报错,尤其是老系统连接 MySQL 8 时特别常见:
text复制Authentication plugin 'caching_sha2_password' cannot be loaded
或者
Firedac Phys MySQL client does not support authentication protocol requested by the server
这个问题本质是客户端版本和 MySQL 8 默认认证插件不兼容。解决方案有两种。
第一种,推荐且安全的做法:升级驱动库。Java 使用高版本的 mysql-connector-j,Python 使用 pymysql 1.0+ 或 mysqlclient。新驱动对 caching_sha2_password 的支持已经非常完善。
第二种,如果因为业务限制无法升级驱动,只能把用户认证方式改回 mysql_native_password:
sql复制ALTER USER 'testuser'@'%' IDENTIFIED WITH mysql_native_password BY '新密码';
FLUSH PRIVILEGES;
这种方式在 2026 年已经不建议用了,因为 mysql_native_password 的安全强度不如 caching_sha2_password,除非万不得已不要走这条路。
另外一个容易引发事故的是锁表问题。一条大事务长期不提交,或者一个慢查询扫描了大量行,会把其他会话的写入全部卡住。处理步骤:
sql复制-- 查看当前所有正在执行的线程
SHOW FULL PROCESSLIST;
-- 查看更细粒度的事务和锁状态
SELECT * FROM information_schema.innodb_trx\G;
找到卡住的会话 ID 后,根据业务确认后结束对应线程:
sql复制KILL 12345;
用 KILL 时一定要谨慎,确认这个线程不是核心业务在执行,否则可能引发事务回滚和主从延迟。
5.2 常用性能优化:索引、EXPLAIN 与慢查询日志
性能优化的核心是看慢查询。配置文件开启慢查询日志:
ini复制slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
超过 2 秒的 SQL 会被记录到 slow.log,定期分析这个文件,基本就能找到性能瓶颈。
定位到具体 SQL 后,用 EXPLAIN 分析执行计划:
sql复制EXPLAIN SELECT * FROM user WHERE username = 'test' AND status = 1;
重点看 type 列和 rows 列。type 为 ALL 说明全表扫描,这是最大的性能隐患,需要建立索引。rows 是预估扫描行数,数值越大越危险。key 列为空也表示用不到索引。
索引设计的一个实用经验是:能用覆盖索引就用覆盖索引。比如有一个查询只查 username 和 email,那就建立一个联合索引 (username, email),查询时直接读索引返回,不需要回表,效率高出一个数量级。
5.3 实用工具:Navicat、Workbench 与 DataX 同步方案
图形化工具方面,Navicat for MySQL 是老牌工具,功能全面但需要付费。免费方案中,MySQL Workbench 是官方出品,基础功能完全够用。
数据同步场景,如果在做 MySQL、SQL Server、PostgreSQL 之间的数据迁移或准实时同步,DataX 是目前使用比较广泛的开源方案。它是阿里开源的离线数据同步工具,性能很好,配置灵活。
一个从 MySQL 到 MySQL 的 DataX 任务示例,job.json 的核心配置逻辑大概是:
json复制{
"job": {
"content": [{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "root",
"password": "xxx",
"column": ["id", "username", "email"],
"connection": [{
"table": ["user"],
"jdbcUrl": ["jdbc:mysql://127.0.0.1:3306/testdb"]
}]
}
},
"writer": {
"name": "mysqlwriter",
"parameter": {
"username": "root",
"password": "xxx",
"column": ["id", "username", "email"],
"connection": [{
"table": ["user_copy"],
"jdbcUrl": ["jdbc:mysql://127.0.0.1:3306/testdb_copy"]
}]
}
}
}]
}
}
运行时执行:
bash复制python datax.py /path/to/job.json
DataX 的速率控制、异常重试做得都比较成熟,适合做大批量数据迁移。但它是离线同步,不是实时同步,实时同步还是要靠 canal(订阅 binlog)或专门的同步中间件。
5.4 面试高频考点:索引、事务与 SQL 优化
MySQL 是面试重灾区,结合我自己近几年当面试官的经验,集中梳理几个高频考点。
索引这块最爱问的是“为什么用 B+ 树做索引结构,而不是哈希索引或红黑树”。回答思路:哈希索引适合等值查询,但不支持范围查询;红黑树是二叉平衡树,树高度太高,数据量大时 IO 次数多;B+ 树是多路平衡树,叶子节点有序且数据在叶子节点,查询稳定且支持范围扫描,很适合磁盘存储结构。
事务方面必考的是隔离级别和 MVCC。MySQL InnoDB 默认隔离级别是 REPEATABLE READ,能解决脏读和不可重复读,但不能完全解决幻读,InnoDB 通过间隙锁在特定条件下解决了幻读问题。MVCC 的核心是版本链和 ReadView,它是实现可重复读的基础。
SQL 优化考的是实战能力。一般会给一条慢 SQL,让你分析为什么慢、怎么优化。回答思路:先看是否全表扫描 -> 检查 WHERE 条件和 ORDER BY 字段是否有索引 -> 看是否有隐式类型转换 -> 分析是否可以用覆盖索引 -> 看是否需要拆分大查询。
还有一个常问的点是“一张表有 1000 万数据,怎么提升查询效率”。常见的回答包括:加索引、分页优化、读写分离、分库分表。面试官期待听到的不是简单列方案,而是对每个方案的适用条件有判断,比如分页优化用延迟关联比单纯 LIMIT 快很多。
最后分享几点个人心得
做数据库运维这几年,踩过最多的坑不是复杂的性能问题,而是最基础的操作失误。备份恢复这里,多说一句:mysqldump 的备份文件一定要定期抽查校验,单看文件大小完全不够,要真正导入一个临时库验证数据是否完整。我自己吃过一次亏,备份文件有 2GB,但恢复时报错说他表不存在,最后发现是脚本里掉了 --routines 参数,存储过程全部没备份进去。
另外,日常运维中建议把常用的排查命令固化成一个脚本,比如查看慢查询、查看当前的连接数、查看锁等待、查看磁盘空间。脚本化以后,不管是自己用还是交接给同事,都能在关键时刻抢出宝贵的恢复时间。数据库管理这件事,功夫在平时,别等出问题了再来补课。
