第 1 章

MySQL 零基础入门

MySQL 零基础入门教程

本教程面向完全没有数据库经验的读者。我们会从安装 MySQL 开始,一步步学会创建数据库、建表、增删改查(CRUD)、WHERE 筛选、JOIN 联表、索引基础以及数据导入导出。每个知识点都附有可直接运行的 SQL 示例,建议你边读边在终端中练习。

**适用版本:**本教程基于 MySQL 8.0 / 8.4 LTS。所有 SQL 示例同样适用于 5.7+。如果你使用的是 MariaDB 10.x,绝大部分语法也兼容。

1. 安装 MySQL

MySQL 可以安装在几乎所有操作系统上。下面分别介绍四种常见安装方式。

1.1 Linux(Ubuntu / Debian)

Ubuntu / Debian 系统使用 APT 包管理器,只需两条命令即可安装:

# 更新包索引
sudo apt update

# 安装 MySQL Server(默认安装最新可用版本)
sudo apt install mysql-server -y

# 检查服务状态
sudo systemctl status mysql

安装完成后,运行安全初始化脚本设置 root 密码、删除测试数据库等:

sudo mysql_secure_installation

脚本会依次询问你:

  1. 是否启用密码强度验证插件(建议选 Y
  2. 设置 root 密码
  3. 是否删除匿名用户(选 Y
  4. 是否禁止 root 远程登录(选 Y
  5. 是否删除测试数据库(选 Y
  6. 是否立即刷新权限表(选 Y

CentOS / RHEL / Rocky Linux

# 添加 MySQL 官方 YUM 仓库(以 MySQL 8.0 为例)
sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el8-9.noarch.rpm

# 安装
sudo dnf install mysql-server -y

# 启动并设置开机自启
sudo systemctl start mysqld
sudo systemctl enable mysqld

# 获取初始临时密码
sudo grep 'temporary password' /var/log/mysqld.log

**注意:**CentOS/RHEL 安装后,MySQL 会生成一个临时 root 密码写在日志中。首次登录后必须立即修改密码,否则无法执行任何操作。

1.2 macOS

macOS 推荐使用 Homebrew 安装:

# 安装 Homebrew(如果还没有)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装 MySQL
brew install mysql

# 启动 MySQL 服务
brew services start mysql

# 运行安全初始化
mysql_secure_installation

Homebrew 安装的 MySQL 默认 root 密码为空,mysql_secure_installation 会引导你设置密码。

1.3 Windows

Windows 用户推荐使用 MySQL Installer:

  1. 访问 dev.mysql.com/downloads/installer
  2. 下载 mysql-installer-community(约 300MB 完整版)
  3. 运行安装程序,选择 Developer DefaultServer only
  4. 按向导设置 root 密码和端口(默认 3306)
  5. 完成后,MySQL 会注册为 Windows 服务,自动启动

安装完成后,打开 MySQL Command Line Client 或使用 cmd

mysql -u root -p

1.4 Docker(推荐用于学习和测试)

Docker 是最快速的安装方式,不会影响系统环境:

# 拉取 MySQL 8.0 官方镜像
docker pull mysql:8.0

# 启动容器(设置 root 密码为 my-secret-pw)
docker run --name mysql-learn \
  -e MYSQL_ROOT_PASSWORD=my-secret-pw \
  -p 3306:3306 \
  -d mysql:8.0

# 进入 MySQL 命令行
docker exec -it mysql-learn mysql -u root -pmy-secret-pw

**推荐:**如果你只是学习练习,Docker 方式最方便。用完可以直接 docker rm -f mysql-learn 清理,不留痕迹。

1.5 验证安装

无论使用哪种安装方式,都可以用以下命令验证:

# 查看 MySQL 版本
mysql --version
# 输出类似:mysql  Ver 8.0.36 for Linux on x86_64 (MySQL Community Server - GPL)

# 或登录后查看
mysql -u root -p -e "SELECT VERSION();"

2. 连接 MySQL

2.1 命令行客户端

MySQL 自带的命令行客户端 mysql 是最常用的连接方式:

# 基本连接(会提示输入密码)
mysql -u root -p

# 指定主机和端口
mysql -u root -p -h 127.0.0.1 -P 3306

# 直接连接到某个数据库
mysql -u root -p my_database

连接成功后,你会看到 mysql> 提示符,表示已进入交互模式。

2.2 常用连接参数

参数 含义 示例
-u 用户名 -u root
-p 密码(建议不直接写在命令行) -p
-h 主机地址 -h 192.168.1.100
-P 端口号(大写 P) -P 3306
-D 默认数据库 -D mydb
-e 执行 SQL 后退出 -e "SHOW DATABASES;"

2.3 图形化客户端

如果你更喜欢图形界面,以下是几款免费的 MySQL 客户端:

2.4 退出 MySQL

-- 以下三种方式都可以退出
EXIT;
QUIT;
\q

3. 创建数据库

3.1 查看现有数据库

SHOW DATABASES;

安装后默认有以下系统数据库:

3.2 创建新数据库

-- 创建数据库(推荐指定字符集)
CREATE DATABASE shop
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

-- 如果不确定是否已存在,用 IF NOT EXISTS
CREATE DATABASE IF NOT EXISTS shop
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

**为什么用 utf8mb4?**MySQL 的 utf8 实际上只支持最多 3 字节的字符,无法存储 emoji 等 4 字节字符。utf8mb4 才是真正的 UTF-8,是 MySQL 8.0 的默认字符集。强烈建议所有新项目都使用 utf8mb4

3.3 选择数据库

-- 切换到 shop 数据库
USE shop;

-- 查看当前使用的数据库
SELECT DATABASE();

3.4 删除数据库

-- 删除数据库(谨慎操作!数据不可恢复)
DROP DATABASE shop;

-- 安全写法
DROP DATABASE IF EXISTS shop;

危险操作:DROP DATABASE 会删除该数据库中的所有表和数据,且无法撤销。生产环境中请务必先备份。

4. 创建表

4.1 CREATE TABLE 基本语法

下面创建一个用户表(users)和一个订单表(orders),我们后续的示例都会围绕这两张表展开:

USE shop;

-- 用户表
CREATE TABLE users (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
    username    VARCHAR(50)  NOT NULL UNIQUE           COMMENT '用户名',
    email       VARCHAR(100) NOT NULL                  COMMENT '邮箱',
    age         TINYINT UNSIGNED                       COMMENT '年龄',
    balance     DECIMAL(10,2) NOT NULL DEFAULT 0.00    COMMENT '账户余额',
    status      ENUM('active','inactive','banned')
                NOT NULL DEFAULT 'active'              COMMENT '状态',
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
    updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
                ON UPDATE CURRENT_TIMESTAMP            COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 订单表
CREATE TABLE orders (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
    user_id     INT UNSIGNED NOT NULL                  COMMENT '用户ID',
    product     VARCHAR(100) NOT NULL                  COMMENT '商品名',
    quantity    INT UNSIGNED NOT NULL DEFAULT 1         COMMENT '数量',
    price       DECIMAL(10,2) NOT NULL                 COMMENT '单价',
    order_date  DATE NOT NULL                          COMMENT '下单日期',
    FOREIGN KEY (user_id) REFERENCES users(id)
        ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

4.2 关键约束说明

约束 作用 示例
PRIMARY KEY 唯一标识每一行,不允许 NULL id INT PRIMARY KEY
AUTO_INCREMENT 自动递增,通常配合主键 id INT AUTO_INCREMENT
NOT NULL 不允许为空 username VARCHAR(50) NOT NULL
UNIQUE 值必须唯一 email VARCHAR(100) UNIQUE
DEFAULT 默认值 status ENUM(...) DEFAULT 'active'
FOREIGN KEY 外键,引用另一表的主键 FOREIGN KEY (user_id) REFERENCES users(id)

4.3 查看表结构

-- 查看所有表
SHOW TABLES;

-- 查看表结构
DESCRIBE users;
-- 或简写
DESC users;

-- 查看完整建表语句
SHOW CREATE TABLE users\G

4.4 修改表结构(ALTER TABLE)

-- 添加列
ALTER TABLE users ADD COLUMN phone VARCHAR(20) AFTER email;

-- 修改列类型
ALTER TABLE users MODIFY COLUMN phone VARCHAR(30);

-- 重命名列
ALTER TABLE users CHANGE COLUMN phone mobile VARCHAR(30);

-- 删除列
ALTER TABLE users DROP COLUMN mobile;

-- 添加索引
ALTER TABLE users ADD INDEX idx_email (email);

-- 重命名表
ALTER TABLE users RENAME TO customers;
-- 改回来
ALTER TABLE customers RENAME TO users;

5. 常用数据类型

5.1 数值类型

类型 字节 范围(有符号) 典型用途
TINYINT 1 -128 ~ 127 状态码、年龄
SMALLINT 2 -32768 ~ 32767 年份、小计数
INT 4 -21 亿 ~ 21 亿 主键、数量
BIGINT 8 -922 京 ~ 922 京 大表主键、时间戳
DECIMAL(M,D) 变长 精确小数 金额(必须用 DECIMAL,不要用 FLOAT)
FLOAT 4 近似小数 科学计算(有精度丢失)
DOUBLE 8 近似小数 科学计算

**金额千万别用 FLOAT/DOUBLE!**浮点数存在精度丢失问题。例如 0.1 + 0.2 在 FLOAT 中可能不等于 0.3。涉及金钱的字段一律使用 DECIMAL

5.2 字符串类型

类型 最大长度 特点 典型用途
CHAR(N) 255 字符 固定长度,右补空格 国家代码、MD5
VARCHAR(N) 65535 字节 可变长度,需 1-2 字节存长度 用户名、邮箱、URL
TEXT 65535 字节 不能有默认值,不能做索引前缀外的索引 文章正文、评论
MEDIUMTEXT 16 MB 大文本 大型文档
LONGTEXT 4 GB 极大文本 极端场景
ENUM 65535 个值 只能存预定义值之一 状态、性别
JSON 约 1 GB 原生 JSON 类型(MySQL 5.7+) 灵活属性、配置

5.3 日期时间类型

类型 格式 范围 典型用途
DATE YYYY-MM-DD 1000-01-01 ~ 9999-12-31 生日、到期日
DATETIME YYYY-MM-DD HH:MM:SS 同上(含时间) 创建时间、更新时间
TIMESTAMP YYYY-MM-DD HH:MM:SS 1970 ~ 2038 自动记录时间(会自动转 UTC)
TIME HH:MM:SS -838:59:59 ~ 838:59:59 持续时间
YEAR YYYY 1901 ~ 2155 年份

**DATETIME vs TIMESTAMP:**DATETIME 占 8 字节,不受时区影响,范围到 9999 年。TIMESTAMP 占 4 字节,会自动转换为 UTC 存储(读取时按当前时区转换),但 2038 年会溢出。新项目建议优先使用 DATETIME。

6. 插入数据(INSERT)

6.1 插入单行

INSERT INTO users (username, email, age, balance, status)
VALUES ('alice', '[email protected]', 28, 1000.00, 'active');

6.2 插入多行

INSERT INTO users (username, email, age, balance, status) VALUES
  ('bob',     '[email protected]',     32, 500.50,  'active'),
  ('charlie', '[email protected]', 22, 200.00,  'active'),
  ('diana',   '[email protected]',   45, 3500.00, 'active'),
  ('eve',     '[email protected]',     19, 50.00,   'inactive'),
  ('frank',   '[email protected]',   38, 800.00,  'active'),
  ('grace',   '[email protected]',   27, 1200.00, 'active'),
  ('henry',   '[email protected]',   55, 0.00,    'banned');

6.3 插入订单数据

INSERT INTO orders (user_id, product, quantity, price, order_date) VALUES
  (1, 'iPhone 15',        1, 7999.00, '2024-01-15'),
  (1, '手机壳',            2, 29.90,   '2024-01-15'),
  (2, 'MacBook Pro 14',   1, 14999.00,'2024-02-20'),
  (3, '机械键盘',          1, 599.00,  '2024-03-10'),
  (3, '鼠标垫',            1, 39.90,   '2024-03-10'),
  (4, '显示器 27寸',       2, 2499.00, '2024-01-08'),
  (5, '数据线',            3, 19.90,   '2024-04-01'),
  (6, 'AirPods Pro',      1, 1799.00, '2024-03-22'),
  (7, '摄像头',            1, 299.00,  '2024-02-14'),
  (1, 'iPad Air',         1, 4599.00, '2024-04-05');

6.4 INSERT 常用变体

-- 插入或忽略(主键/唯一键冲突时跳过)
INSERT IGNORE INTO users (username, email, age)
VALUES ('alice', '[email protected]', 28);

-- 插入或更新(UPSERT)
INSERT INTO users (username, email, age, balance)
VALUES ('alice', '[email protected]', 29, 1100.00)
ON DUPLICATE KEY UPDATE age = VALUES(age), balance = VALUES(balance);

-- 从查询结果中插入
INSERT INTO user_archive (id, username, email)
SELECT id, username, email FROM users WHERE status = 'banned';

7. 查询数据(SELECT)

7.1 基本查询

-- 查询所有列
SELECT * FROM users;

-- 查询指定列
SELECT id, username, email, balance FROM users;

-- 使用别名
SELECT
    username AS '用户名',
    balance  AS '余额',
    CONCAT(username, ' (', email, ')') AS '用户信息'
FROM users;

7.2 去重查询

-- 查看有哪些不同的状态值
SELECT DISTINCT status FROM users;

-- 查看下过单的用户(去重)
SELECT DISTINCT user_id FROM orders;

7.3 计算列

-- 计算每笔订单的总金额
SELECT
    id,
    product,
    quantity,
    price,
    quantity * price AS total
FROM orders;

8. WHERE 筛选、ORDER BY 排序、LIMIT 分页

8.1 WHERE 条件筛选

-- 等于
SELECT * FROM users WHERE status = 'active';

-- 不等于
SELECT * FROM users WHERE status != 'banned';
-- 或
SELECT * FROM users WHERE status <> 'banned';

-- 大于 / 小于
SELECT * FROM users WHERE age > 30;
SELECT * FROM users WHERE balance <= 500;

-- BETWEEN(包含两端)
SELECT * FROM users WHERE age BETWEEN 20 AND 35;

-- IN(匹配列表中的任何一个值)
SELECT * FROM users WHERE username IN ('alice', 'bob', 'charlie');

-- LIKE(模式匹配)
SELECT * FROM users WHERE email LIKE '%@example.com';
SELECT * FROM users WHERE username LIKE 'a%';      -- 以 a 开头
SELECT * FROM users WHERE username LIKE '_____';    -- 恰好 5 个字符

-- IS NULL / IS NOT NULL
SELECT * FROM users WHERE age IS NOT NULL;

-- AND / OR 组合
SELECT * FROM users
WHERE status = 'active'
  AND age >= 25
  AND balance > 500;

SELECT * FROM users
WHERE status = 'banned' OR balance = 0;

8.2 ORDER BY 排序

-- 按余额降序
SELECT * FROM users ORDER BY balance DESC;

-- 按年龄升序(默认 ASC)
SELECT * FROM users ORDER BY age ASC;

-- 多列排序:先按状态排,同状态按余额降序
SELECT * FROM users ORDER BY status ASC, balance DESC;

8.3 LIMIT 与分页

-- 取前 3 条
SELECT * FROM users ORDER BY balance DESC LIMIT 3;

-- 分页:从第 4 条开始取 3 条(OFFSET 从 0 开始计数)
SELECT * FROM users ORDER BY id LIMIT 3 OFFSET 3;
-- 等价写法
SELECT * FROM users ORDER BY id LIMIT 3, 3;

分页性能提示:当 OFFSET 很大时(如 LIMIT 10 OFFSET 100000),MySQL 仍然需要扫描前 100000 行然后丢弃。对大表分页,建议使用游标分页(基于上一页最后一行的 id): SELECT * FROM users WHERE id > 上一页最后id ORDER BY id LIMIT 10;

8.4 聚合函数

-- 总数
SELECT COUNT(*) AS total_users FROM users;

-- 活跃用户数
SELECT COUNT(*) AS active_count FROM users WHERE status = 'active';

-- 平均余额
SELECT AVG(balance) AS avg_balance FROM users;

-- 最高/最低余额
SELECT MAX(balance) AS max_bal, MIN(balance) AS min_bal FROM users;

-- 余额总和
SELECT SUM(balance) AS total_balance FROM users WHERE status = 'active';

8.5 GROUP BY 分组

-- 按状态分组统计人数和平均余额
SELECT
    status,
    COUNT(*)       AS user_count,
    AVG(balance)   AS avg_balance,
    SUM(balance)   AS total_balance
FROM users
GROUP BY status;

-- 每个用户的订单总额
SELECT
    user_id,
    COUNT(*)               AS order_count,
    SUM(quantity * price)  AS total_amount
FROM orders
GROUP BY user_id
ORDER BY total_amount DESC;

8.6 HAVING(对分组后的结果筛选)

-- 找出订单总额超过 5000 的用户
SELECT
    user_id,
    SUM(quantity * price) AS total_amount
FROM orders
GROUP BY user_id
HAVING total_amount > 5000
ORDER BY total_amount DESC;

WHERE vs HAVING:WHERE 在分组之前筛选原始行,HAVING 在分组之后筛选聚合结果。WHERE 中不能使用聚合函数,HAVING 中可以。

9. 更新与删除

9.1 UPDATE 更新

-- 更新单行
UPDATE users SET balance = 1500.00 WHERE id = 1;

-- 更新多列
UPDATE users
SET age = 29, email = '[email protected]'
WHERE username = 'alice';

-- 基于计算的更新(给所有活跃用户余额增加 10%)
UPDATE users
SET balance = balance * 1.10
WHERE status = 'active';

-- 更新前先查看会影响哪些行
SELECT * FROM users WHERE status = 'active';

**永远加 WHERE!**不带 WHERE 的 UPDATE 会修改表中所有行。这是初学者最常犯的错误之一。建议在执行 UPDATE 之前,先用同样的 WHERE 条件执行 SELECT 确认。

9.2 DELETE 删除

-- 删除单行
DELETE FROM users WHERE id = 8;

-- 删除满足条件的行
DELETE FROM users WHERE status = 'banned';

-- 删除所有数据(保留表结构)
DELETE FROM orders;
-- 或用 TRUNCATE(更快,重置自增 ID,不可回滚)
TRUNCATE TABLE orders;

DELETE vs TRUNCATE: - DELETE 逐行删除,会记录 undo log,可以回滚,支持 WHERE 条件 - TRUNCATE 直接释放数据页,速度极快,但不可回滚,不触发触发器,重置 AUTO_INCREMENT

10. JOIN 联表查询

现实中的数据通常分散在多张表中。JOIN 让你将多张表关联起来一起查询。

10.1 INNER JOIN(内连接)

INNER JOIN 只返回两张表中匹配的行。如果某个用户没有订单,该用户不会出现在结果中。

-- 查询所有订单及对应用户名
SELECT
    o.id        AS order_id,
    u.username,
    o.product,
    o.quantity,
    o.price,
    o.quantity * o.price AS total,
    o.order_date
FROM orders o
INNER JOIN users u ON o.user_id = u.id
ORDER BY o.order_date DESC;

10.2 LEFT JOIN(左外连接)

LEFT JOIN 返回左表的所有行。如果右表中没有匹配,则右表列为 NULL。

-- 查询所有用户及其订单数(包括没有订单的用户)
SELECT
    u.id,
    u.username,
    u.status,
    COUNT(o.id) AS order_count,
    COALESCE(SUM(o.quantity * o.price), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username, u.status
ORDER BY total_spent DESC;

**COALESCE:**当用户没有订单时,SUM 的结果是 NULL。COALESCE(expr, 0) 的作用是把 NULL 替换为 0,让结果更友好。

10.3 RIGHT JOIN(右外连接)

RIGHT JOIN 与 LEFT JOIN 相反,返回右表的所有行。在实践中很少使用,因为交换表的位置后使用 LEFT JOIN 可以达到同样效果,代码更易读。

-- 等价于上面的 LEFT JOIN(交换表的位置)
SELECT
    u.id,
    u.username,
    COUNT(o.id) AS order_count
FROM orders o
RIGHT JOIN users u ON o.user_id = u.id
GROUP BY u.id, u.username;

10.4 CROSS JOIN(交叉连接 / 笛卡尔积)

-- 生成所有用户与所有商品的组合(一般用于生成测试数据)
SELECT u.username, o.product
FROM users u
CROSS JOIN (SELECT DISTINCT product FROM orders) o
LIMIT 20;

10.5 自连接(Self Join)

-- 假设有一张员工表,找出每个员工和他的经理
-- CREATE TABLE employees (id INT, name VARCHAR(50), manager_id INT);

SELECT
    e.name  AS employee,
    m.name  AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

10.6 多表 JOIN

-- 三张表关联示例(假设增加了 products 表)
-- SELECT o.id, u.username, p.product_name, o.quantity
-- FROM orders o
-- JOIN users u ON o.user_id = u.id
-- JOIN products p ON o.product_id = p.id;

**JOIN 性能提示:**确保 JOIN 条件中的列(如 user_idid)上有索引。没有索引的 JOIN 在大表上会非常慢,因为 MySQL 需要逐行扫描。

11. 索引基础

索引就像书的目录。没有索引时,MySQL 查找数据需要扫描整张表(全表扫描);有了索引,MySQL 可以快速定位到目标行。

11.1 创建索引

-- 单列索引
CREATE INDEX idx_users_email ON users(email);

-- 复合索引(联合索引)
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);

-- 唯一索引
CREATE UNIQUE INDEX idx_users_username ON users(username);

-- 使用 ALTER TABLE
ALTER TABLE users ADD INDEX idx_age (age);

11.2 查看索引

SHOW INDEX FROM users;
SHOW INDEX FROM orders;

11.3 何时需要索引

场景 是否需要索引 原因
WHERE 条件列 需要 加速筛选
JOIN 的关联列 需要 加速表连接
ORDER BY 列 需要 避免文件排序
GROUP BY 列 需要 加速分组
频繁更新的列 谨慎 索引需要同步更新,影响写入性能
数据量极小的表 不需要 全表扫描可能比走索引更快
选择性极低的列(如性别) 通常不需要 索引收益小

11.4 用 EXPLAIN 查看执行计划

-- 在 SELECT 前加 EXPLAIN
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';

-- 输出示例中关注的列:
-- type: const / ref / range / ALL
-- possible_keys: 可能使用的索引
-- key: 实际使用的索引
-- rows: 预估扫描行数
-- 对比有索引和没索引的情况

-- 有索引(使用 idx_users_email)
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
-- type=ref, rows=1  ← 很好

-- 没索引的列
EXPLAIN SELECT * FROM users WHERE age = 28;
-- type=ALL, rows=8  ← 全表扫描

-- 添加索引后再查
ALTER TABLE users ADD INDEX idx_age (age);
EXPLAIN SELECT * FROM users WHERE age = 28;
-- type=ref, rows=1  ← 改善了

11.5 删除索引

DROP INDEX idx_age ON users;
-- 或
ALTER TABLE users DROP INDEX idx_age;

**深入学习:**索引是 MySQL 性能优化中最重要的主题。想深入了解 B+Tree 原理、联合索引最左前缀原则、覆盖索引、12 种索引失效场景等高级内容,请阅读 索引优化完全指南

12. 数据导入与导出

12.1 mysqldump 导出

mysqldump 是 MySQL 自带的逻辑备份工具,将数据库导出为 SQL 文件。

# 导出整个数据库
mysqldump -u root -p shop > shop_backup.sql

# 只导出某张表
mysqldump -u root -p shop users > users_backup.sql

# 导出多张表
mysqldump -u root -p shop users orders > users_orders_backup.sql

# 只导出表结构(不含数据)
mysqldump -u root -p --no-data shop > shop_schema.sql

# 只导出数据(不含建表语句)
mysqldump -u root -p --no-create-info shop > shop_data.sql

# 导出所有数据库
mysqldump -u root -p --all-databases > all_databases.sql

12.2 导入 SQL 文件

# 方法 1:命令行导入
mysql -u root -p shop < shop_backup.sql

# 方法 2:在 MySQL 命令行中导入
mysql> USE shop;
mysql> SOURCE /path/to/shop_backup.sql;

12.3 导出为 CSV

-- 在 MySQL 中导出为 CSV
SELECT * FROM users
INTO OUTFILE '/tmp/users.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

-- 注意:INTO OUTFILE 写入的是 MySQL 服务器的文件系统
-- 如果没有权限,可以在客户端导出:
mysql -u root -p -e "SELECT * FROM shop.users" | tr '\t' ',' > users.csv

12.4 从 CSV 导入

-- 使用 LOAD DATA INFILE(速度最快)
LOAD DATA INFILE '/tmp/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;   -- 跳过表头行

-- 如果 CSV 在客户端机器上(非服务器)
LOAD DATA LOCAL INFILE '/path/on/client/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;

**LOAD DATA 比 INSERT 快 20 倍以上:**导入大量数据时(几万行以上),强烈建议使用 LOAD DATA INFILE 而非逐行 INSERT。它通过批量写入减少了磁盘 I/O 和日志开销。

12.5 实用导出技巧

# 导出时压缩(大数据库必备)
mysqldump -u root -p shop | gzip > shop_backup.sql.gz

# 导入压缩文件
gunzip < shop_backup.sql.gz | mysql -u root -p shop

# 导出时包含时间戳(方便管理备份文件)
mysqldump -u root -p shop > "shop_$(date +%Y%m%d_%H%M%S).sql"

常见问题(FAQ)

Q: MySQL 和 MariaDB 有什么区别?我该选哪个?

MariaDB 是 MySQL 的一个分支(fork),由 MySQL 创始人 Monty 在 Oracle 收购 Sun/MySQL 后创建。两者在基础 SQL 语法上高度兼容。 **区别:**MariaDB 有一些独有功能(如 Aria 存储引擎、序列、系统版本表),而 MySQL 也有自己的独有功能(如 InnoDB Cluster、Group Replication、HeatWave)。 **建议:**如果你是初学者,两个都可以。如果是企业项目,根据团队经验和生态选择。大多数云平台(AWS、阿里云、腾讯云)默认提供 MySQL。

Q: 忘记了 root 密码怎么办?

可以通过跳过权限表启动 MySQL 来重置密码: # 1. 停止 MySQL sudo systemctl stop mysql # 2. 以跳过权限表方式启动 sudo mysqld --skip-grant-tables --skip-networking & # 3. 免密登录 mysql -u root # 4. 重置密码(MySQL 8.0+) FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码'; # 5. 重启 MySQL sudo systemctl restart mysql

Q: VARCHAR(50) 和 VARCHAR(255) 性能有区别吗?

存储空间上区别不大 — VARCHAR 只占用实际内容长度 + 1-2 字节的长度前缀。但在以下场景有区别: - **内存排序缓冲区:**MySQL 执行 ORDER BY / GROUP BY 时可能为每行分配 MAX 长度的内存。VARCHAR(255) 会分配比 VARCHAR(50) 更多的临时内存。 - **InnoDB 行格式:**如果行中多个 VARCHAR 的 MAX 长度之和超过 8126 字节,InnoDB 可能将部分列存储到溢出页(overflow page),增加 I/O。 **建议:**根据实际数据长度合理设置。用户名用 VARCHAR(50)、邮箱用 VARCHAR(100)、URL 用 VARCHAR(2048)。不要无脑写 VARCHAR(255)。

Q: 什么时候该用 CHAR,什么时候用 VARCHAR?

**CHAR(N):**固定长度,适合存储长度恒定的数据(如国家代码 CHAR(2)、MD5 哈希 CHAR(32)、UUID CHAR(36))。 **VARCHAR(N):**可变长度,适合长度变化大的数据(用户名、地址、标题等)。 现代 InnoDB 引擎中,CHAR 和 VARCHAR 的性能差异很小。如果不确定,用 VARCHAR 即可。

Q: AUTO_INCREMENT 的值用完了怎么办?

INT UNSIGNED 的最大值约 42.9 亿。如果你担心不够用,使用 BIGINT UNSIGNED(最大约 1.8 x 10^19)。 如果 AUTO_INCREMENT 到达上限,INSERT 会报错 ERROR 1062 (23000): Duplicate entry。解决方法: - 修改列类型:ALTER TABLE t MODIFY id BIGINT UNSIGNED AUTO_INCREMENT; - 这个操作在大表上可能需要较长时间(MySQL 8.0 支持 Instant DDL 加速部分 ALTER 操作)

*Q: 为什么不建议使用 SELECT

教程中为了简洁使用了 SELECT *,但在生产代码中应该避免,原因是: - **浪费网络带宽:**传输了不需要的列 - **无法利用覆盖索引:**SELECT * 通常需要回表读完整行 - **代码可维护性差:**当表结构变更(增删列)时,SELECT * 的结果集会变化,可能导致应用出错 - **大字段问题:**如果表中有 TEXT/BLOB 列,SELECT * 会读取这些大字段,即使你不需要 **建议:**始终明确列出需要的字段名。

Q: 如何创建一个新用户并授权?

-- 创建用户(只允许从 localhost 连接) CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'StrongPassword123!'; -- 授权:只对 shop 数据库有增删改查权限 GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'myapp'@'localhost'; -- 如果需要从任何 IP 连接(生产环境不建议) CREATE USER 'myapp'@'%' IDENTIFIED BY 'StrongPassword123!'; GRANT ALL PRIVILEGES ON shop.* TO 'myapp'@'%'; -- 刷新权限 FLUSH PRIVILEGES; -- 查看用户权限 SHOW GRANTS FOR 'myapp'@'localhost';

Q: 如何查看当前 MySQL 的运行状态?

-- 查看当前连接数和正在执行的查询 SHOW PROCESSLIST; -- 查看全局状态变量 SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Queries'; SHOW GLOBAL STATUS LIKE 'Slow_queries'; -- 查看系统变量 SHOW VARIABLES LIKE 'max_connections'; SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; -- 查看数据库大小 SELECT table_schema AS '数据库', ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS '大小(MB)' FROM information_schema.TABLES GROUP BY table_schema ORDER BY SUM(data_length + index_length) DESC;

Q: MySQL 8.0 有哪些值得关注的新特性?

Q: 接下来应该学什么?

掌握本教程的内容后,建议按以下路径深入学习: 1. 索引优化索引优化完全指南 2. EXPLAIN 分析EXPLAIN 分析器 3. 查询优化查询优化专题 4. 事务与锁锁与死锁分析 5. InnoDB 架构InnoDB 内核深度剖析

本章评分
4.7  / 5  (130 评分)

💬 留言讨论