适用人群:零基础、后端开发者、数据分析入门
学习时长:约2-3天(每天4小时)
MySQL版本:8.0+
配套工具:Navicat / DBeaver / phpMyAdmin
一、MySQL 是什么?
MySQL 是全球最流行的开源关系型数据库,被广泛应用于Web开发、企业应用、数据分析等领域。
| 特点 | 说明 |
|---|---|
| 上手难度 | ★★☆☆☆(SQL语法接近自然语言) |
| 运行模式 | 客户端-服务器架构,支持远程连接 |
| 主要用途 | 网站后端数据存储、企业数据管理、数据分析 |
| 市场需求 | 所有后端开发者必备技能 |
| 学习顺序 | 先学SQL语法 → 再学索引优化 → 最后学架构设计 |
二、环境搭建
2.1 宝塔面板安装(推荐)
宝塔面板 → 软件商店 → MySQL 8.0 → 一键安装
安装后在"数据库"菜单中管理
2.2 Docker 安装
# 拉取镜像
docker pull mysql:8.0
# 启动容器
docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=your_password \
-v mysql_data:/var/lib/mysql \
mysql:8.0 --default-authentication-plugin=mysql_native_password
# 进入MySQL命令行
docker exec -it mysql8 mysql -uroot -p
2.3 本地安装
# macOS
brew install mysql
brew services start mysql
# Ubuntu
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
# Windows
# 下载 MySQL Installer:https://dev.mysql.com/downloads/installer/
三、SQL 基础语法
3.1 数据库操作
-- 创建数据库
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 查看所有数据库
SHOW DATABASES;
-- 切换数据库
USE mydb;
-- 删除数据库
DROP DATABASE IF EXISTS mydb;
3.2 表操作
-- 创建表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
age INT DEFAULT 0,
status ENUM('active', 'inactive', 'banned') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 查看表结构
DESC users;
SHOW CREATE TABLE users;
-- 修改表
ALTER TABLE users ADD COLUMN phone VARCHAR(20) AFTER email;
ALTER TABLE users MODIFY COLUMN username VARCHAR(100);
ALTER TABLE users DROP COLUMN phone;
-- 删除表
DROP TABLE IF EXISTS users;
3.3 增删改查(CRUD)
-- ====================
-- 插入数据 (INSERT)
-- ====================
-- 单条插入
INSERT INTO users (username, email, age)
VALUES ('张三', 'zhangsan@example.com', 25);
-- 批量插入
INSERT INTO users (username, email, age) VALUES
('李四', 'lisi@example.com', 30),
('王五', 'wangwu@example.com', 28),
('赵六', 'zhaoliu@example.com', 22);
-- 从查询结果插入
INSERT INTO active_users (username, email)
SELECT username, email FROM users WHERE status = 'active';
-- ====================
-- 查询数据 (SELECT)
-- ====================
-- 基本查询
SELECT * FROM users;
SELECT username, email FROM users;
-- 条件查询
SELECT * FROM users WHERE age > 25;
SELECT * FROM users WHERE status = 'active' AND age >= 18;
SELECT * FROM users WHERE username LIKE '%张%'; -- 模糊查询
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
SELECT * FROM users WHERE status IN ('active', 'pending');
-- 排序
SELECT * FROM users ORDER BY age DESC;
SELECT * FROM users ORDER BY created_at DESC, username ASC;
-- 分页
SELECT * FROM users LIMIT 10; -- 前10条
SELECT * FROM users LIMIT 10 OFFSET 20; -- 第21-30条
-- 去重
SELECT DISTINCT status FROM users;
-- 别名
SELECT username AS 用户名, age AS 年龄 FROM users;
-- ====================
-- 更新数据 (UPDATE)
-- ====================
UPDATE users SET age = 26 WHERE username = '张三';
UPDATE users SET status = 'inactive', updated_at = NOW() WHERE age < 18;
-- ====================
-- 删除数据 (DELETE)
-- ====================
DELETE FROM users WHERE id = 1;
DELETE FROM users WHERE status = 'banned' AND created_at < '2024-01-01';
四、聚合查询与分组
-- ====================
-- 聚合函数
-- ====================
SELECT COUNT(*) AS total FROM users; -- 总数
SELECT AVG(age) AS avg_age FROM users; -- 平均年龄
SELECT MAX(age) AS max_age FROM users; -- 最大年龄
SELECT MIN(age) AS min_age FROM users; -- 最小年龄
SELECT SUM(amount) AS total FROM orders; -- 总金额
-- ====================
-- 分组查询 (GROUP BY)
-- ====================
-- 按状态统计用户数
SELECT status, COUNT(*) AS count
FROM users
GROUP BY status;
-- 按部门统计平均薪资
SELECT department,
COUNT(*) AS 人数,
AVG(salary) AS 平均薪资,
MAX(salary) AS 最高薪资
FROM employees
GROUP BY department
HAVING AVG(salary) > 10000 -- 过滤分组结果
ORDER BY 平均薪资 DESC;
-- 按日期统计订单数
SELECT DATE(created_at) AS date,
COUNT(*) AS 订单数,
SUM(amount) AS 总金额
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY DATE(created_at)
ORDER BY date;
五、多表联查(JOIN)
-- ====================
-- 准备示例数据
-- ====================
CREATE TABLE departments (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL
);
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
department_id INT,
salary DECIMAL(10, 2),
FOREIGN KEY (department_id) REFERENCES departments(id)
);
-- ====================
-- INNER JOIN(内连接):只返回两表都有的数据
-- ====================
SELECT e.name AS 员工, d.name AS 部门, e.salary AS 薪资
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;
-- ====================
-- LEFT JOIN(左连接):返回左表所有数据,右表没有的显示NULL
-- ====================
SELECT e.name AS 员工, d.name AS 部门
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
-- ====================
-- 多表联查示例
-- ====================
SELECT
u.username AS 用户名,
COUNT(o.id) AS 订单数,
SUM(o.amount) AS 总消费
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active'
GROUP BY u.id, u.username
HAVING 总消费 > 1000
ORDER BY 总消费 DESC;
六、子查询
-- WHERE 子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
-- EXISTS 子查询
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
-- FROM 子查询(派生表)
SELECT dept, avg_salary FROM (
SELECT department AS dept, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) AS dept_stats
WHERE avg_salary > 10000;
七、索引优化
-- ====================
-- 创建索引
-- ====================
-- 普通索引
CREATE INDEX idx_email ON users(email);
-- 唯一索引
CREATE UNIQUE INDEX idx_username ON users(username);
-- 复合索引(最左前缀原则)
CREATE INDEX idx_status_created ON users(status, created_at);
-- 查看索引
SHOW INDEX FROM users;
-- 删除索引
DROP INDEX idx_email ON users;
-- ====================
-- EXPLAIN 分析查询
-- ====================
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- 关键字段说明:
-- type: ALL(全表扫描) → index → range → ref → eq_ref → const(最优)
-- key: 实际使用的索引
-- rows: 预估扫描行数
-- Extra: Using index(覆盖索引) | Using filesort(需优化) | Using temporary(需优化)
-- ====================
-- 索引优化原则
-- ====================
-- 1. 最左前缀:复合索引(a,b,c)可以匹配(a)、(a,b)、(a,b,c)
-- 2. 避免在索引列上使用函数:WHERE YEAR(created_at) = 2024 → 不好
-- 3. 避免隐式类型转换:WHERE id = '123' → 字符串和数字比较
-- 4. 范围查询后的索引列失效:WHERE a > 1 AND b = 2 → b不走索引
八、事务处理
-- ====================
-- 事务基础
-- ====================
START TRANSACTION;
-- 转账操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 检查结果
SELECT * FROM accounts WHERE id IN (1, 2);
-- 提交事务
COMMIT;
-- 或回滚
-- ROLLBACK;
-- ====================
-- 事务特性 (ACID)
-- ====================
-- A - Atomicity(原子性):要么全部成功,要么全部回滚
-- C - Consistency(一致性):事务前后数据保持一致
-- I - Isolation(隔离性):并发事务互不干扰
-- D - Durability(持久性):提交后数据永久保存
-- ====================
-- 隔离级别
-- ====================
-- READ UNCOMMITTED:可能脏读
-- READ COMMITTED:避免脏读,可能不可重复读
-- REPEATABLE READ:MySQL默认,避免脏读和不可重复读
-- SERIALIZABLE:最高隔离,性能最差
-- 查看当前隔离级别
SELECT @@transaction_isolation;
九、视图与存储过程
-- ====================
-- 视图(虚拟表)
-- ====================
CREATE VIEW active_user_orders AS
SELECT
u.id, u.username, u.email,
o.id AS order_id, o.amount, o.created_at AS order_date
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active';
-- 使用视图
SELECT * FROM active_user_orders WHERE username = '张三';
-- ====================
-- 存储过程
-- ====================
DELIMITER //
CREATE PROCEDURE GetUserStats(IN userId INT)
BEGIN
SELECT
u.username,
COUNT(o.id) AS order_count,
COALESCE(SUM(o.amount), 0) AS total_amount,
COALESCE(AVG(o.amount), 0) AS avg_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = userId
GROUP BY u.id, u.username;
END //
DELIMITER ;
-- 调用存储过程
CALL GetUserStats(1);
十、常用函数速查表
字符串函数
| 函数 | 说明 | 示例 |
|---|---|---|
CONCAT(s1, s2) | 字符串拼接 | CONCAT('Hello', ' ', 'World') |
LENGTH(s) | 字符串长度 | LENGTH('Hello') → 5 |
SUBSTRING(s, pos, len) | 截取子串 | SUBSTRING('Hello', 2, 3) → 'ell' |
REPLACE(s, from, to) | 替换 | REPLACE('Hello', 'l', 'L') |
TRIM(s) | 去除首尾空格 | TRIM(' Hello ') |
UPPER(s) / LOWER(s) | 大小写转换 | UPPER('hello') → 'HELLO' |
LIKE | 模糊匹配 | WHERE name LIKE '%张%' |
日期函数
| 函数 | 说明 | 示例 |
|---|---|---|
NOW() | 当前时间 | 2024-01-15 10:30:00 |
CURDATE() | 当前日期 | 2024-01-15 |
DATE_FORMAT(d, fmt) | 格式化日期 | DATE_FORMAT(NOW(), '%Y年%m月') |
DATEDIFF(d1, d2) | 日期差(天) | DATEDIFF('2024-12-31', '2024-01-01') → 365 |
DATE_ADD(d, INTERVAL n unit) | 日期加减 | DATE_ADD(NOW(), INTERVAL 7 DAY) |
数学函数
| 函数 | 说明 | 示例 |
|---|---|---|
ROUND(x, d) | 四舍五入 | ROUND(3.14159, 2) → 3.14 |
CEIL(x) / FLOOR(x) | 向上/向下取整 | CEIL(3.1) → 4 |
ABS(x) | 绝对值 | ABS(-5) → 5 |
MOD(x, y) | 取模 | MOD(10, 3) → 1 |
十一、MySQL 配置优化
# my.cnf 推荐配置
[mysqld]
# 基础设置
max_connections = 500
max_connect_errors = 100
wait_timeout = 600
# InnoDB 设置
innodb_buffer_pool_size = 4G # 建议为物理内存的70%
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
# 查询缓存(MySQL 8.0已移除)
# 查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # 超过2秒的查询记录
# 字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
十二、备份与恢复
# ====================
# 备份
# ====================
# 备份单个数据库
mysqldump -u root -p mydb > mydb_backup.sql
# 备份所有数据库
mysqldump -u root -p --all-databases > all_backup.sql
# 备份指定表
mysqldump -u root -p mydb users orders > tables_backup.sql
# 压缩备份
mysqldump -u root -p mydb | gzip > mydb_backup.sql.gz
# ====================
# 恢复
# ====================
# 恢复数据库
mysql -u root -p mydb < mydb_backup.sql
# 恢复压缩备份
gunzip < mydb_backup.sql.gz | mysql -u root -p mydb
常见问题
| 问题 | 解答 |
|---|---|
| MySQL和MongoDB怎么选? | 关系复杂、需要事务用MySQL;灵活变化、快速原型用MongoDB |
| 如何优化慢查询? | 用EXPLAIN分析、添加合适索引、避免SELECT * |
| utf8和utf8mb4区别? | utf8mb4支持emoji等4字节字符,推荐使用 |
| 如何防止SQL注入? | 使用预处理语句(PDO/MySQLi的prepare) |
| 什么是N+1查询问题? | 循环中每条数据单独查询关联表,应使用JOIN或预加载 |
学习建议
- 先掌握基本CRUD,这是80%的日常操作
- 理解JOIN的几种类型,多表联查是核心技能
- 学会用EXPLAIN分析查询,这是优化的基础
- 掌握索引原理,特别是最左前缀原则
- 在实际项目中练习,配合PHP/Laravel使用