MySQL 学习笔记

从零开始的 MySQL 学习笔记,覆盖安装、SQL 基础、进阶优化、运维管理。

图表在 /MySQL学习笔记/diagrams/ 目录。

1 · MySQL 概述

1.1 什么是 MySQL

  • MySQL 是最流行的开源关系型数据库管理系统(RDBMS)
  • 由瑞典 MySQL AB 公司开发,现属 Oracle 公司
  • 使用 SQL(Structured Query Language)操作数据
  • LAMP/LNMP 栈核心组件:Linux + Nginx/Apache + MySQL + PHP/Python
TIP

📌 打个比方:MySQL 像一个严格的档案室管理员。你按规定的格式(SQL)提交请求,他帮你存取档案(数据),还负责档案的排序(索引)、安全(权限)、备份(dump)。你不用关心档案放在哪个柜子,管理员全搞定。

1.2 MySQL 版本演进

版本 特性 状态
5.6 InnoDB 全文索引、在线 DDL EOL
5.7 JSON 类型、生成列、性能提升 EOL
8.0 窗口函数、CTE、原子 DDL、降序索引 推荐
8.4 LTS 长期支持版 最新 LTS

2 · 安装与配置

2.1 安装

# Ubuntu/Debian
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation

# CentOS/RHEL
sudo yum install mysql-server
sudo systemctl start mysqld

# Docker(推荐开发环境)
docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:8.0

# Windows:下载 MySQL Installer,下一步安装

2.2 核心配置文件

# /etc/mysql/my.cnf 或 my.ini(Windows)
[mysqld]
port = 3306
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
max_connections = 200
innodb_buffer_pool_size = 2G    # 最关键参数,建议设为可用内存 60-70%
innodb_log_file_size = 512M
slow_query_log = 1
long_query_time = 2             # 慢查询阈值(秒)
TIP

📌 innodb_buffer_pool_size 是最重要的参数:InnoDB 把数据和索引缓存在内存中,buffer pool 越大,磁盘 I/O 越少。生产环境建议设为物理内存的 60-70%。

2.3 连接

mysql -h 127.0.0.1 -P 3306 -u root -p
# -h 主机地址 -P 端口 -u 用户名 -p 密码

3 · SQL 基础

图 1 \xb7 SQL 语句分类

3.1 数据库与表操作

-- 创建数据库
CREATE DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE shop;

-- 创建表
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100),
    age INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 修改表
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN age;
ALTER TABLE users MODIFY COLUMN email VARCHAR(200);

-- 删除
DROP TABLE users;
DROP DATABASE shop;

3.2 CRUD 操作

-- 插入
INSERT INTO users (username, email) VALUES ('alice', 'alice@test.com');
INSERT INTO users (username, email) VALUES ('bob', 'bob@test.com'), ('carol', 'carol@test.com');

-- 查询
SELECT id, username, email FROM users WHERE age > 18 ORDER BY id DESC LIMIT 10;
SELECT * FROM users WHERE username LIKE 'a%';      -- 以 a 开头
SELECT * FROM users WHERE email IS NOT NULL;        -- 非空
SELECT COUNT(*) FROM users;                          -- 总数

-- 更新
UPDATE users SET email = 'new@test.com' WHERE id = 1;

-- 删除
DELETE FROM users WHERE id = 1;                      -- 条件删除
TRUNCATE TABLE users;                                -- 清空表(比 DELETE 快,不可回滚)

3.3 聚合与分组

-- 聚合函数
SELECT COUNT(*), AVG(age), MAX(age), MIN(age), SUM(age) FROM users;

-- 分组
SELECT department, COUNT(*) AS cnt, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING cnt > 5                    -- 分组后过滤(WHERE 在分组前过滤)
ORDER BY avg_salary DESC;

-- DISTINCT 去重
SELECT DISTINCT department FROM employees;

3.4 连接查询

-- 内连接(交集)
SELECT u.username, o.order_id, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

-- 左连接(左表全保留)
SELECT u.username, o.order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

-- 右连接(右表全保留)
SELECT u.username, o.order_id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
TIP

📌 JOIN 的本质:INNER JOIN 取两表交集,LEFT JOIN 左表全保留(右表没有的填 NULL),RIGHT JOIN 右表全保留。最常用 INNER 和 LEFT。

3.5 子查询

-- 标量子查询
SELECT * FROM users WHERE age > (SELECT AVG(age) FROM users);

-- IN 子查询
SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE age > 20);

-- EXISTS 子查询
SELECT * FROM users u WHERE EXISTS (
    SELECT 1 FROM orders o WHERE o.user_id = u.id
);

4 · 数据类型

类型 说明 示例
TINYINT 1 字节整数 状态值 0/1
INT 4 字节整数 ID、数量
BIGINT 8 字节整数 自增主键
DECIMAL(M,D) 定点数 金额 DECIMAL(10,2)
VARCHAR(N) 变长字符串 用户名
TEXT 长文本 文章内容
DATETIME 日期时间 创建时间
TIMESTAMP 时间戳(自动更新) 更新时间
JSON JSON 文档(8.0+) 扩展属性
BLOB 二进制数据 图片存储
TIP

📌 金额永远用 DECIMAL:浮点数 FLOAT/DOUBLE 有精度问题,0.1 + 0.2 != 0.3。DECIMAL(10,2) 表示最多 10 位数字,2 位小数,精确存储。

📌 VARCHAR vs CHAR:VARCHAR 按实际长度存储(省空间),CHAR 固定长度(定长字段如 MD5 用 CHAR 更快)。VARCHAR(N) 的 N 是字符数不是字节数。


5 · 索引

图 2 \xb7 B+ 树索引结构

5.1 为什么需要索引

没有索引时,查询要全表扫描(逐行检查),百万行表可能要几秒。有索引时,通过 B+ 树二分查找,百万行只需 3-4 次 I/O。

5.2 索引类型

类型 说明 创建方式
主键索引 自动创建,唯一且非空 PRIMARY KEY (id)
唯一索引 值不能重复 UNIQUE KEY uk_email (email)
普通索引 加速查询 INDEX idx_name (username)
联合索引 多列组合 INDEX idx_dept_age (dept, age)
全文索引 文本搜索 FULLTEXT KEY ft_content (content)

5.3 联合索引与最左前缀原则

-- 联合索引 (a, b, c)
INDEX idx_abc (a, b, c)

-- 能命中索引
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3

-- 不能命中索引
WHERE b = 2
WHERE c = 3
WHERE b = 2 AND c = 3
TIP

📌 最左前缀原则:联合索引 (a,b,c) 相当于创建了 (a)、(a,b)、(a,b,c) 三个索引。查询条件必须从最左列开始连续使用。把区分度高的列放最前面。

📌 索引不是越多越好:索引加速查询但减慢写入(每次 INSERT/UPDATE/DELETE 都要更新索引)。一张表 5-6 个索引为宜。

5.4 EXPLAIN 分析查询

EXPLAIN SELECT * FROM users WHERE username = 'alice';
关键列 关注点
type const > eq_ref > ref > range > index > ALL(ALL = 全表扫描,要避免)
key 实际使用的索引名
rows 预估扫描行数(越少越好)
Extra Using index = 覆盖索引(好);Using filesort = 额外排序(需优化);Using temporary = 临时表(需优化)

5.5 什么时候该建索引

  • WHERE 条件频繁使用的列
  • JOIN 的连接列
  • ORDER BY / GROUP BY 的列
  • 区分度高的列(性别只有男女,区分度低,建索引意义不大)
  • 数据量小的表(全表扫描更快)
  • 频繁更新的列

6 · 事务与锁

图 3 \xb7 事务 ACID 与隔离级别

6.1 事务基础

START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;  -- 转出
UPDATE accounts SET balance = balance + 100 WHERE id = 2;  -- 转入
COMMIT;    -- 提交(两条同时生效)
-- 或 ROLLBACK;  -- 回滚(两条都不生效)

6.2 ACID 特性

特性 含义 实现机制
原子性 (A) 事务要么全部成功,要么全部回滚 undo log
一致性 (C) 事务前后数据保持一致 应用层 + 数据库约束
隔离性 (I) 并发事务互不干扰 锁 + MVCC
持久性 (D) 提交后数据不丢失 redo log

6.3 隔离级别

隔离级别 脏读 不可重复读 幻读 性能
READ UNCOMMITTED 可能 可能 可能 最高
READ COMMITTED 不可能 可能 可能 高
REPEATABLE READ(默认) 不可能 不可能 可能 中
SERIALIZABLE 不可能 不可能 不可能 最低
TIP

📌 MySQL 默认 REPEATABLE READ,且通过 MVCC + 间隙锁解决了幻读问题。大多数场景用默认级别即可。

📌 脏读 vs 不可重复读 vs 幻读:脏读 = 读到别人未提交的数据;不可重复读 = 同一查询两次结果不同(别人改了);幻读 = 同一查询两次行数不同(别人新增了)。

6.4 锁

锁类型 说明
共享锁 (S) SELECT ... LOCK IN SHARE MODE,读锁,多个事务可同时持有
排他锁 (X) SELECT ... FOR UPDATE,写锁,独占
行锁 锁单行,粒度小并发高
间隙锁 锁一个范围(防止插入),RR 级别特有
表锁 锁整表,粒度大并发低
TIP

📌 死锁:事务 A 锁了行 1 等行 2,事务 B 锁了行 2 等行 1。MySQL 检测到死锁会自动回滚代价较小的事务。预防:按固定顺序加锁。


7 · 存储引擎

特性 InnoDB(默认) MyISAM
事务 支持 不支持
行锁 是 否(表锁)
外键 是 否
崩溃恢复 是 否
全文索引 是(5.6+) 是
适用场景 大多数场景 只读/统计表
TIP

📌 InnoDB 是默认引擎,99% 的场景用它。MyISAM 已基本被淘汰,除非纯只读的统计表。


8 · SQL 优化

8.1 慢查询分析

-- 开启慢查询日志
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 2;  -- 超过 2 秒记录

-- 查看慢查询
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;

8.2 优化原则

原则 说明
避免 SELECT * 只查需要的列,减少 I/O,可能命中覆盖索引
用 LIMIT 分页 LIMIT 1000000, 10 很慢,用游标分页 WHERE id > last_id LIMIT 10
避免函数包裹列 WHERE YEAR(created_at) = 2024 索引失效,改 WHERE created_at >= '2024-01-01'
避免隐式类型转换 WHERE phone = 13800138000(phone 是 VARCHAR),索引失效
JOIN 小表驱动大表 让小表做驱动表,大表做被驱动表
避免 OR WHERE a=1 OR b=2 可能索引失效,改 UNION ALL
批量插入 INSERT INTO t VALUES (...),(...),(...) 比 100 次单条 INSERT 快 10 倍
TIP

📌 深分页优化:LIMIT 1000000, 10 要扫描 1000010 行。优化方式:① 游标分页 WHERE id > 1000000 LIMIT 10 ② 延迟关联 SELECT t.* FROM t INNER JOIN (SELECT id FROM t ORDER BY id LIMIT 1000000,10) tmp ON t.id = tmp.id


9 · 备份与恢复

# 逻辑备份(导出 SQL 脚本)
mysqldump -u root -p shop > shop_backup.sql
mysqldump -u root -p --all-databases > all_backup.sql
mysqldump -u root -p shop users orders > partial_backup.sql

# 恢复
mysql -u root -p shop < shop_backup.sql

# 物理备份(直接复制数据文件,更快)
# 需停 MySQL 或使用 xtrabackup 热备
TIP

📌 mysqldump vs xtrabackup:mysqldump 是逻辑备份(导出 SQL),恢复慢但跨版本兼容。xtrabackup 是物理备份(复制数据文件),速度快适合大库。


10 · 主从复制

主库 (Master) 从库 (Slave) │ │ ├── 写入 binlog ──────────────→ 复制 binlog │ ├── 重放 relay log │ ├── 写入数据 │ │ └── 读写 ──────────────────────→ 只读
-- 主库配置
-- my.cnf: server-id=1, log_bin=mysql-bin, binlog_format=ROW
CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

-- 从库配置
-- my.cnf: server-id=2
CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='192.168.1.100',
    SOURCE_USER='repl',
    SOURCE_PASSWORD='password',
    SOURCE_LOG_FILE='mysql-bin.000001',
    SOURCE_LOG_POS=154;
START REPLICA;
TIP

📌 主从复制原理:主库把变更写入 binlog → 从库 IO 线程拉取 binlog 写入 relay log → 从库 SQL 线程重放 relay log。复制是异步的,从库可能有延迟。


11 · 用户与权限

-- 创建用户
CREATE USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'secure_password';

-- 授权
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'appuser'@'192.168.1.%';
GRANT ALL PRIVILEGES ON shop.* TO 'admin'@'localhost';
FLUSH PRIVILEGES;

-- 查看权限
SHOW GRANTS FOR 'appuser'@'192.168.1.%';

-- 撤销权限
REVOKE DELETE ON shop.* FROM 'appuser'@'192.168.1.%';

-- 删除用户
DROP USER 'appuser'@'192.168.1.%';
TIP

📌 权限最小化原则:应用账号只给 CRUD 权限,不给 DDL(CREATE/DROP/ALTER)权限。限制来源 IP,不要用 %(所有 IP)。


12 · 监控与调优

12.1 关键指标

指标 命令 关注点
连接数 SHOW STATUS LIKE 'Threads_%' Threads_connected 不应接近 max_connections
慢查询 SHOW STATUS LIKE 'Slow_queries' 持续增长需优化
缓冲池命中率 SHOW STATUS LIKE 'Innodb_buffer_pool%' 命中率应 > 99%
临时表 SHOW STATUS LIKE 'Created_tmp%' Created_tmp_disk_tables 高需优化
锁等待 SHOW STATUS LIKE 'Innodb_row_lock%' 锁等待高需优化

12.2 调优清单

  • innodb_buffer_pool_size 设为物理内存 60-70%
  • 开启慢查询日志,定期分析
  • 高频查询建立合适索引
  • 避免 SELECT *,使用覆盖索引
  • 大表分页用游标
  • 连接池合理配置(应用端)
  • 定期 ANALYZE TABLE 更新统计信息
  • 监控缓冲池命中率 > 99%

附录 · 常用命令速查

命令 说明
SHOW DATABASES; 列出所有数据库
SHOW TABLES; 列出当前库的表
DESC table_name; 查看表结构
SHOW INDEX FROM table_name; 查看索引
SHOW PROCESSLIST; 查看当前连接
SHOW STATUS; 查看服务器状态
SHOW VARIABLES; 查看配置变量
EXPLAIN SELECT ...; 分析查询计划