MySQL 零基础入门教程 — 数据库从入门到实战

适用人群:零基础、后端开发者、数据分析入门
学习时长:约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或预加载

学习建议

  1. 先掌握基本CRUD,这是80%的日常操作
  2. 理解JOIN的几种类型,多表联查是核心技能
  3. 学会用EXPLAIN分析查询,这是优化的基础
  4. 掌握索引原理,特别是最左前缀原则
  5. 在实际项目中练习,配合PHP/Laravel使用

下一步学习

返回首页