第六周是你整个学习路线的第三个质变点。前五周你在练 Linux 基础和 Shell 自动化,这周你要进入数据库领域——这是运维工程师能力模型里和 Linux 并列的另一条腿。


第六周详细练习手册:MySQL 深度使用

核心目标

  1. 能独立完成 MySQL 的安装、配置、权限管理

  2. 掌握 SELECT/JOIN/GROUP BY 等核心查询

  3. 理解慢查询分析和索引优化

  4. 能用 mysqldump 做逻辑备份和恢复

    你已经有的环境:CentOS 9 虚拟机 + MySQL 8.0 已安装。


    第一天:确认环境 + 理解 MySQL 的“文件住哪”

    练习 1:确认 MySQL 状态和版本

    # 1. 确认服务在跑
    [xtc@localhost ~]$ systemctl status mysqld
    # 如果是 active (running),继续
    # 如果不是:
    sudo systemctl start mysqld
    sudo systemctl enable mysqld
    
    # 2. 看版本
    [xtc@localhost ~]$ mysql --version
    # 或进 MySQL 后查:
    mysql -u root -p
    mysql> SELECT VERSION();
    
    # 3. 看配置文件在哪
    [xtc@localhost ~]$ ls -la /etc/my.cnf
    [xtc@localhost ~]$ ls -la /etc/my.cnf.d/
    # MySQL 8 的配置分散在主文件和配置目录里
    练习 2:理解 MySQL 的数据目录

    # 看数据存哪(这是生产环境最不能丢的目录)
    mysql -u root -p -e "SHOW VARIABLES LIKE 'datadir';"
    # 通常是 /var/lib/mysql
    
    sudo ls -la /var/lib/mysql
    # 能看到以数据库名命名的目录,一个库一个文件夹
    # ibdata1 — InnoDB 系统表空间
    # ib_logfile* — InnoDB 重做日志
    # binlog.* — 二进制日志

    自检:知道以下三个路径分别放什么

    • /etc/my.cnf — 配置文件

    • /var/lib/mysql — 数据文件(最重要的目录)

    • /var/log/mysql — 日志文件(慢查询日志、错误日志)


      第二天:SQL 核心查询——从零到能干活

      练习 3:创建练习库和表

      -- 登录 MySQL
      mysql -u root -p
      
      -- 创建练习数据库
      CREATE DATABASE practice;
      USE practice;
      
      -- 创建员工表
      CREATE TABLE employees (
          id INT PRIMARY KEY AUTO_INCREMENT,
          name VARCHAR(50) NOT NULL,
          department VARCHAR(50),
          salary DECIMAL(10,2),
          hire_date DATE,
          city VARCHAR(50)
      );
      
      -- 插入测试数据
      INSERT INTO employees (name, department, salary, hire_date, city) VALUES
      ('张三', '运维部', 15000, '2020-03-15', '长春'),
      ('李四', '开发部', 20000, '2019-07-01', '北京'),
      ('王五', '运维部', 18000, '2021-01-10', '上海'),
      ('赵六', '测试部', 12000, '2022-06-20', '长春'),
      ('孙七', '开发部', 22000, '2018-11-05', '深圳'),
      ('周八', '运维部', 16000, '2023-02-28', '长春'),
      ('吴九', '测试部', 11000, '2022-09-01', '北京'),
      ('郑十', '开发部', 25000, '2017-05-15', '上海');
      
      -- 验证
      SELECT * FROM employees;

      练习 4:基础查询(SELECT / WHERE / ORDER BY / LIMIT)

      -- ===== 选列 =====
      SELECT name, department, salary FROM employees;
      
      -- ===== 条件过滤 =====
      SELECT * FROM employees WHERE department = '运维部';
      SELECT * FROM employees WHERE salary > 18000;
      SELECT * FROM employees WHERE city = '长春' AND department = '运维部';
      SELECT * FROM employees WHERE department IN ('运维部', '开发部');
      SELECT * FROM employees WHERE salary BETWEEN 15000 AND 20000;
      
      -- ===== 模糊搜索 =====
      SELECT * FROM employees WHERE name LIKE '张%';    -- 姓张的
      SELECT * FROM employees WHERE city LIKE '%海%';    -- 城市含"海"
      
      -- ===== 排序 =====
      SELECT * FROM employees ORDER BY salary DESC;
      SELECT * FROM employees ORDER BY hire_date ASC;
      
      -- ===== 分页 =====
      SELECT * FROM employees ORDER BY salary DESC LIMIT 3;
      SELECT * FROM employees ORDER BY id LIMIT 2 OFFSET 2;  -- 跳过前2条

      练习 5:聚合查询(统计用,运维做报表常用)

      -- ===== 统计函数 =====
      SELECT COUNT(*) FROM employees;                          -- 总人数
      SELECT AVG(salary) FROM employees;                       -- 平均薪资
      SELECT MAX(salary), MIN(salary) FROM employees;          -- 最高最低
      SELECT SUM(salary) FROM employees WHERE department = '运维部'; -- 部门薪资总额
      
      -- ===== 分组统计(运维最重要的查询) =====
      -- 每个部门多少人
      SELECT department, COUNT(*) AS 人数 FROM employees GROUP BY department;
      
      -- 每个部门平均薪资
      SELECT department, AVG(salary) AS 平均薪资 FROM employees GROUP BY department;
      
      -- 每个城市多少人
      SELECT city, COUNT(*) AS 人数 FROM employees GROUP BY city ORDER BY 人数 DESC;
      
      -- ===== 分组后过滤(HAVING,运维做报表常用) =====
      -- 平均薪资超过 16000 的部门
      SELECT department, AVG(salary) AS 平均薪资
      FROM employees
      GROUP BY department
      HAVING AVG(salary) > 16000;

      练习 6:多表联查(JOIN)

      -- 创建第二个表:项目表
      CREATE TABLE projects (
          id INT PRIMARY KEY AUTO_INCREMENT,
          project_name VARCHAR(100),
          leader_id INT,
          FOREIGN KEY (leader_id) REFERENCES employees(id)
      );
      
      INSERT INTO projects (project_name, leader_id) VALUES
      ('服务器迁移', 1),
      ('CI/CD搭建', 4),
      ('监控系统', 1),
      ('自动化测试', 4),
      ('日志平台', 3);
      
      -- ===== INNER JOIN(两表都有的才显示) =====
      SELECT e.name, e.department, p.project_name
      FROM employees e
      INNER JOIN projects p ON e.id = p.leader_id;
      
      -- ===== LEFT JOIN(左表全显示,右表没有填 NULL) =====
      SELECT e.name, e.department, p.project_name
      FROM employees e
      LEFT JOIN projects p ON e.id = p.leader_id;
      -- 没负责项目的员工也会显示,project_name 为 NULL
      
      -- ===== 多表联查加统计 =====
      SELECT e.name, e.department, COUNT(p.id) AS 负责项目数
      FROM employees e
      LEFT JOIN projects p ON e.id = p.leader_id
      GROUP BY e.id, e.name, e.department
      ORDER BY 负责项目数 DESC;

      自检题

      1. 查出运维部薪资大于 15000 的员工。

      2. 统计每个部门的平均薪资,只看平均超过 16000 的。

      3. LEFT JOIN 和 INNER JOIN 的区别是什么?


        第三天:索引——从“查询慢”到“秒回”

        练习 7:制造慢查询,感受索引

        -- 先造一批测试数据(存储过程批量插入)
        DELIMITER $$
        CREATE PROCEDURE insert_test_data()
        BEGIN
            DECLARE i INT DEFAULT 1;
            WHILE i <= 500000 DO
                INSERT INTO employees (name, department, salary, hire_date, city)
                VALUES (CONCAT('员工', i),
                        CASE i % 4 WHEN 0 THEN '运维部' WHEN 1 THEN '开发部' WHEN 2 THEN '测试部' ELSE '产品部' END,
                        10000 + (i % 20) * 1000,
                        DATE_ADD('2018-01-01', INTERVAL i DAY),
                        CASE i % 5 WHEN 0 THEN '长春' WHEN 1 THEN '北京' WHEN 2 THEN '上海' WHEN 3 THEN '深圳' ELSE '广州' END);
                SET i = i + 1;
            END WHILE;
        END$$
        DELIMITER ;
        
        -- 执行插入(需要几十秒)
        CALL insert_test_data();
        SELECT COUNT(*) FROM employees;  -- 应该约 50 万行

        -- ===== 看没索引的查询有多慢 =====
        SELECT * FROM employees WHERE name = '员工300000';
        -- 记录执行时间
        
        -- ===== 看执行计划(EXPLAIN) =====
        EXPLAIN SELECT * FROM employees WHERE name = '员工300000';
        -- 关注三个字段:
        -- type: ALL (全表扫描,最慢)
        -- rows: 扫描了多少行(约 50 万行)
        -- Extra: Using where
        
        -- ===== 创建索引 =====
        CREATE INDEX idx_name ON employees(name);
        
        -- ===== 再查一次,感受速度 =====
        SELECT * FROM employees WHERE name = '员工300000';
        -- 瞬间返回
        
        -- ===== 再看执行计划 =====
        EXPLAIN SELECT * FROM employees WHERE name = '员工300000';
        -- type: ref 或 const (用了索引,快)
        -- rows: 1 (只扫描 1 行)
        -- key: idx_name (用到了 idx_name 索引)

        练习 8:常见索引类型和场景

        -- 1. 单列索引(最常用)
        CREATE INDEX idx_department ON employees(department);
        
        -- 2. 联合索引(查询条件经常同时出现多列时用)
        CREATE INDEX idx_city_dept ON employees(city, department);
        -- 注意顺序:city 在前 department 在后,因为 WHERE city = '长春' AND department = '运维部'
        -- 查询时先 city 后 department 才能用到索引
        
        -- 3. 唯一索引(这个列的值必须唯一,如身份证号)
        CREATE UNIQUE INDEX idx_name_unique ON employees(name);
        -- 如果 name 有重复会报错(我们造的数据有重复,先别建)
        
        -- ===== 查看表上的索引 =====
        SHOW INDEX FROM employees;
        
        -- ===== 删除索引 =====
        DROP INDEX idx_department ON employees;

        自检题

        1. EXPLAIN 输出中 type=ALL 代表什么?

        2. 联合索引 (city, department) 能加速 WHERE department = '运维部' 吗?(不能,必须从第一个列开始)

        3. 索引建得越多越好吗?(不是,增删改会变慢,索引本身占空间)


          第四天:慢查询分析——运维日常

          练习 9:开启慢查询日志

          # 配置文件里开启(需要 root 权限)
          sudo vim /etc/my.cnf.d/mysql-server.cnf
          
          # 在 [mysqld] 段添加:
          slow_query_log = 1
          slow_query_log_file = /var/log/mysql/slow.log
          long_query_time = 0.5        # 超过 0.5 秒就记录
          log_queries_not_using_indexes = 1   # 没用索引的也记录
          
          # 重启 MySQL
          sudo systemctl restart mysqld

          # 确认慢查询日志已开启
          mysql -u root -p -e "SHOW VARIABLES LIKE 'slow_query%';"
          mysql -u root -p -e "SHOW VARIABLES LIKE 'long_query_time';"

          练习 10:制造慢查询并分析

          -- 先故意删掉索引
          DROP INDEX idx_name ON employees;
          
          -- 执行慢查询
          SELECT * FROM employees WHERE name = '员工400000';
          
          -- 查看慢查询日志

          sudo tail -10 /var/log/mysql/slow.log
          # 能看到刚才那条慢查询:执行时间、扫描行数、时间戳

          练习 11:mysqldumpslow 分析慢日志

          # 看最慢的 5 条
          sudo mysqldumpslow -t 5 /var/log/mysql/slow.log
          
          # 看访问次数最多的 5 条
          sudo mysqldumpslow -s c -t 5 /var/log/mysql/slow.log
          
          # 看总时间最多的 5 条
          sudo mysqldumpslow -s t -t 5 /var/log/mysql/slow.log


          第五天:备份与恢复——运维保命技能

          练习 12:逻辑备份(mysqldump)

          # ===== 备份单个数据库 =====
          mysqldump -u root -p practice > /tmp/practice_backup.sql
          # 输入密码后,所有建表+插入数据的 SQL 都存到了这个文件
          
          # 看备份文件前 50 行
          head -50 /tmp/practice_backup.sql
          
          # ===== 备份所有数据库 =====
          mysqldump -u root -p --all-databases > /tmp/all_databases.sql
          
          # ===== 只备份表结构,不要数据 =====
          mysqldump -u root -p --no-data practice > /tmp/practice_schema.sql
          
          # ===== 只备份数据,不要表结构 =====
          mysqldump -u root -p --no-create-info practice > /tmp/practice_data.sql
          
          # ===== 压缩备份(省空间) =====
          mysqldump -u root -p practice | gzip > /tmp/practice_backup.sql.gz

          练习 13:恢复数据

          -- 先模拟误操作
          USE practice;
          DROP TABLE employees;
          -- 表没了

          # 恢复
          mysql -u root -p practice < /tmp/practice_backup.sql
          -- 验证
          USE practice;
          SELECT COUNT(*) FROM employees;
          -- 数据回来了

          练习 14:编写备份脚本并加入定时任务

          [xtc@localhost ~]$ cd ~/scripts
          [xtc@localhost scripts]$ vim mysql-backup.sh

          #!/bin/bash
          #============================================
          # 脚本名称: mysql-backup.sh
          # 功能描述: 每日 MySQL 逻辑备份
          # 版    本: 1.0
          # 日    期: 2026-05-18
          #============================================
          
          BACKUP_DIR="/backup/mysql"
          DATE=$(date +%Y%m%d)
          DB_NAME="practice"
          MYSQL_USER="root"
          
          # 创建备份目录
          mkdir -p "$BACKUP_DIR"
          
          # 备份
          mysqldump -u"$MYSQL_USER" -p"你的密码" "$DB_NAME" | gzip > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
          
          # 检查是否成功
          if [ $? -eq 0 ]; then
              echo "$(date) 备份成功: $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
          else
              echo "$(date) 备份失败" >&2
          fi
          
          # 清理 7 天前的旧备份
          find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete

          chmod +x mysql-backup.sh
          crontab -e
          # 每天凌晨 2:00 备份
          0 2 * * * /home/xtc/scripts/mysql-backup.sh


          第六天:用户权限管理(生产安全核心)

          练习 15:创建用户和授权

          -- 创建只读用户(给看报表的人用)
          CREATE USER 'readonly'@'localhost' IDENTIFIED BY 'ReadOnly123!';
          GRANT SELECT ON practice.* TO 'readonly'@'localhost';
          
          -- 创建运维用户(能改表结构、增删改查,但不能管权限)
          CREATE USER 'ops'@'%' IDENTIFIED BY 'Ops123456!';
          GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX ON practice.* TO 'ops'@'%';
          -- '%' 允许从任何主机连接
          
          -- 创建备份专用用户(最小权限原则)
          CREATE USER 'backup'@'localhost' IDENTIFIED BY 'Backup123!';
          GRANT SELECT, LOCK TABLES ON practice.* TO 'backup'@'localhost';
          
          -- 查看用户权限
          SHOW GRANTS FOR 'readonly'@'localhost';
          SHOW GRANTS FOR 'ops'@'%';
          
          -- 刷新权限(改了权限后执行)
          FLUSH PRIVILEGES;

          练习 16:验证权限

          # 测试只读用户
          mysql -u readonly -p'ReadOnly123!' practice
          SELECT * FROM employees LIMIT 5;        -- 可以
          INSERT INTO employees (name) VALUES ('测试');  -- 报错 INSERT denied
          # 测试备份用户
          mysql -u backup -p'Backup123!' practice
          SELECT COUNT(*) FROM employees;  -- 可以
          DELETE FROM employees LIMIT 1;   -- 报错 DELETE denied


          第七天:综合验收

          验收清单

          1. 能写出多表联查统计报表

          -- 不查笔记写出:
          -- 每个部门的人数、平均薪资、负责项目数
          SELECT
              e.department,
              COUNT(DISTINCT e.id) AS 人数,
              AVG(e.salary) AS 平均薪资,
              COUNT(p.id) AS 项目数
          FROM employees e
          LEFT JOIN projects p ON e.id = p.leader_id
          GROUP BY e.department;

          2. 会看执行计划判断索引是否生效

          EXPLAIN SELECT * FROM employees WHERE name = '张三';
          -- 能说出 type、rows、key 三个字段的含义

          3. 会做备份和恢复

          mysqldump -u root -p practice | gzip > /tmp/test.sql.gz
          # 删表 → 恢复 → 验证数据回来了

          4. 备份脚本加入定时任务并验证

          mysqldump -u root -p practice | gzip > /tmp/test.sql.gz
          # 删表 → 恢复 → 验证数据回来了

          第六周能力自检

          1. SELECT / WHERE / GROUP BY / HAVING / JOIN 能闭眼写出基本结构。

          2. EXPLAIN 看得懂,type=ALL 知道是全表扫描需要优化。

          3. 慢查询日志会开、会看、会用 mysqldumpslow 分析。

          4. 能用 mysqldump 备份单库、恢复单库。

          5. 会建用户、授权、验证权限。


            六周总回顾

            主题

            核心能力

            里程碑产出

            第一周

            命令行生存

            文件操作、grep、find、systemctl

            命令行不再怕

            第二周

            权限与存储

            chmod 数字、SGID、磁盘挂载

            权限不出错

            第三周

            网络排错

            排错五步法、ss、firewall、tcpdump

            故障能定界

            第四周

            Shell 脚本

            if、awk、for、crontab

            能写自动化脚本

            第五周

            综合实战

            生产级巡检脚本、Git、GitHub

            代码推上 GitHub

            第六周

            MySQL 深度

            SQL 查询、索引、慢查询、备份

            数据库能独立管

            六周前你是一个刚接触 Linux 命令行的新手。六周后你已经能独立写出生产级巡检脚本、排查常见网络故障、用 Git 管理代码、对 MySQL 做基本运维。你已经有初级运维工程师的完整能力框架了。 后面进入云平台(第七周开始),这套排错思维和自动化能力会直接复用。

            Logo

            AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

            更多推荐