第44章:MySQL/MariaDB

第四十四章:MySQL/MariaDB

44.1 MySQL/MariaDB 简介

MySQL——开源数据库的"扛把子"

MySQL,江湖人称"闷声发大财"的典型代表。

它有多厉害?

  • 全球使用量排名第一的开源数据库
  • Facebook、Twitter、YouTube、阿里巴巴、微信…全在用
  • “LAMP黄金搭档"的C位成员(Linux + Apache + MySQL + PHP)
  • 无数创业公司的第一选择

MySQL的故事,要从1995年说起…

MySQL的诞生史

1995年,瑞典的两个大神——Michael “Monty” Widenius 和 David Axmark,创建了MySQL。

Monty是个传奇人物,他在芬兰长大,后来跑到瑞典创业。他创建的MySQL AB公司,最初只有一个目标:做一个又快又小的SQL数据库

当时的商业数据库(比如Oracle、DB2)都贵得要命,动辄几十万美金。MySQL的出现,就像数据库界的"价格屠夫”,瞬间引爆市场!

MySQL的"变形记"

MySQL的发展历程,堪称一部"变形记":

1995年:MySQL 1.0诞生
    ↓
2001年:MySQL 3.23,性能大幅提升,开始火起来
    ↓
2004年:MySQL 4.1,引入子查询和R-tree空间索引
    ↓
2005年:MySQL 5.0,存储过程、触发器、视图全来了
    ↓
2008年:Sun公司10亿美元收购MySQL AB
    ↓
2010年:Oracle收购Sun,MySQL落入Oracle手中
    ↓
社区分裂:MariaDB正式诞生!

MariaDB——MySQL的"完美替身"

2010年,Oracle收购Sun之后,开源社区慌了:

“Oracle会不会把MySQL往死里整?” “会不会突然收费?” “代码会不会闭源?”

Monty一咬牙,脚一跺,决定:

“老子自己干!”

于是,Monty fork了MySQL 5.5的代码,创造了MariaDB。为什么叫MariaDB?因为Monty的小女儿叫Maria(这是程序员给女儿最好的礼物啊!)

MariaDB和MySQL的关系:

  • MariaDB是MySQL的分支(fork)
  • 代码高度兼容,API几乎一模一样
  • MariaDB是100%开源
  • 很多Linux发行版默认使用MariaDB(比如Ubuntu 16.04+)

MySQL/MariaDB的核心架构

graph TB
    subgraph "MySQL/MariaDB架构"
        A[连接层<br/>Connection Layer] --> B[服务层<br/>Server Layer]
        B --> C[存储引擎层<br/>Storage Engine Layer]
    end
    
    A --> A1[连接管理]
    A --> A2[线程池]
    A --> A3[认证服务]
    
    B --> B1[SQL解析器]
    B --> B2[查询优化器]
    B --> B3[缓存]
    B --> B4[日志管理]
    
    C --> C1[InnoDB<br/>默认主力]
    C --> C2[MyISAM<br/>老将犹存]
    C --> C3[Memory<br/>内存飞车]
    C --> C4[Archive<br/>归档专家]

存储引擎——MySQL的"变速箱"

MySQL最厉害的地方,就是支持多种存储引擎。你可以把它想象成汽车的不同档位——同样是开车,但可以选择省油的、提速快的、还是爬坡强的。

不同的引擎,适合不同的场景:

引擎特点适用场景支持事务
InnoDB默认主力,支持行锁、外键、MVCC高并发、事务需求
MyISAM老将,查询快,不支持事务只读场景、表数据
Memory数据存内存,速度飞快临时表、缓存
Archive压缩存储,占用空间小日志归档
NDB集群专用高可用集群

InnoDB vs MyISAM 就像 自动挡 vs 手动挡

  • InnoDB(自动挡):新手友好,功能齐全,出了问题还能自动恢复
  • MyISAM(手动挡):老司机专属,速度快,但一旦出事(断电)可能丢数据

InnoDB vs MyISAM:

对比项InnoDBMyISAM
事务支持
外键支持
行锁
全文索引5.6+支持
崩溃恢复自动恢复需要手动
存储空间较大较小

MySQL vs MariaDB——该选谁?

选MySQL的场景:

  • 公司技术栈偏向Oracle/商业方案
  • 需要官方企业级支持
  • 依赖某些MySQL特有功能
  • Docker镜像生态更成熟

选MariaDB的场景:

  • 追求100%开源
  • 需要更快的性能和新特性
  • Ubuntu/Debian用户(系统自带)
  • 不想被Oracle"卡脖子"

好消息是:两者基本可以无缝切换,语法兼容度超过99%!

一图总结

flowchart LR
    A[MySQL] -->|Oracle收购| B[Oracle MySQL]
    A -->|社区fork| C[MariaDB]
    
    B --> B1[商业支持]
    B --> B2[稳定兼容]
    B --> B3[生态成熟]
    
    C --> C1[完全开源]
    C --> C2[更新更快]
    C --> C3[更强性能]

小结

MySQL/MariaDB是开源世界最流行的关系型数据库,它们:

  • 体积小、速度快、成本低
  • 支持多种存储引擎,InnoDB是主流
  • 生态成熟,文档丰富
  • 互联网公司的标配

下一节我们将学习如何安装MySQL/MariaDB,开始动手实践!

44.2 MySQL/MariaDB 安装

44.2.1 apt install mysql-server

Ubuntu/Debian 安装 MySQL

第一步:更新软件源

1
2
3
4
5
# 先更新一下软件包列表
sudo apt update

# 看看能不能找到mysql-server
apt search mysql-server

第二步:一键安装

1
2
3
4
5
6
# 安装MySQL服务器和客户端
sudo apt install mysql-server -y

# 安装完成后,MySQL服务应该已经自动启动了
# 检查一下状态
sudo systemctl status mysql

执行后应该看到类似输出:

● mysql.service - MySQL Community Server
     Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-01-01 00:00:00 CST; 1min 30s ago
       Docs: man:mysqld(8)
       http://dev.mysql.com/doc/refman/en/index.html
   Main PID: 1234 (mysqld)
     Status: "Server is operational"

第三步:安全配置

1
2
# 运行安全加固脚本
sudo mysql_secure_installation

这个脚本会问你几个问题:

# 是否启用密码验证插件?(生产环境建议启用)
VALIDATE PASSWORD component? [Y/n] n

# 设置root密码(生产环境一定要设强密码!)
New password: 
Confirm password: 

# 是否删除匿名用户?建议删除
Remove anonymous users? [Y/n] Y

# 是否禁止root远程登录?建议禁止
Disallow root login remotely? [Y/n] Y

# 是否删除test数据库?建议删除
Remove test database and access to it? [Y/n] Y

# 是否重新加载权限表?必须选Y
Reload privilege tables now? [Y/n] Y

第四步:登录测试

1
2
3
4
5
6
7
# 本地登录(会提示输入密码)
mysql -u root -p

# 登录成功后,应该看到MySQL的欢迎界面:
# Welcome to the MySQL monitor.  Commands end with ; or \g.
# Your MySQL connection id is 8
# Server version: 8.0.35 MySQL Community Server - GPL

退出MySQL:

1
2
3
exit
-- 或者
\q

CentOS/RHEL 安装 MySQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 添加MySQL Yum仓库
sudo yum install https://dev.mysql.com/get/mysql80-community-release-el8-7.noarch.rpm

# 安装MySQL
sudo dnf install mysql-community-server -y

# 启动服务
sudo systemctl start mysqld
sudo systemctl enable mysqld

# MySQL 8.0安装时会生成临时密码,在日志里找
sudo grep 'temporary password' /var/log/mysqld.log

44.2.2 apt install mariadb-server

Ubuntu/Debian 安装 MariaDB

MariaDB在Ubuntu和Debian上是MySQL的默认替代品,安装更简单!

第一步:安装

1
2
3
4
5
# 更新软件源
sudo apt update

# 安装MariaDB服务器和客户端
sudo apt install mariadb-server mariadb-client -y

第二步:检查服务状态

1
2
# 查看MariaDB服务状态
sudo systemctl status mariadb

输出类似:

● mariadb.service - MariaDB 10.6.12 database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-01-01 00:00:00 CST; 5s ago
       Docs: man:mariadbd(1)
   Main PID: 5678 (mariadbd)
       Status: "Taking your SQL requests..."

第三步:安全配置

1
2
# 运行安全脚本(MariaDB的安全配置比MySQL更简洁)
sudo mysql_secure_installation

交互式问答:

# 设置root密码(如果是全新安装,这一步会跳过,直接回车即可)
Switch to unix_socket authentication [Y/n] n

# 设置MySQL root密码
New password: 
Confirm new password: 

# 其他安全设置一路Y
Remove anonymous users? [Y] Y
Disallow root login remotely? [Y] Y
Remove test database and access to it? [Y] Y
Reload privilege tables now? [Y] Y

第四步:登录测试

1
2
3
4
5
6
# 在Ubuntu上,MariaDB默认可以使用unix_socket认证
# 也就是直接用系统账号登录,不需要密码
sudo mysql

# 或者用密码登录(如果设置了的话)
mysql -u root -p

CentOS/RHEL 安装 MariaDB

1
2
3
4
5
6
7
8
9
# CentOS 7/8 默认源里有MariaDB
sudo yum install mariadb-server mariadb -y

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

# 安全配置
sudo mysql_secure_installation

Docker 安装 MySQL/MariaDB(通用方法)

不想在主机上装来装去?Docker让你5分钟搞定!

安装Docker(如果还没装)

1
2
3
4
5
6
7
# Ubuntu
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# 启动Docker
sudo systemctl start docker
sudo systemctl enable docker

用Docker运行MySQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 拉取MySQL镜像
docker pull mysql:8.0

# 运行MySQL容器
docker run -d \
    --name mysql-server \
    -p 3306:3306 \
    -e MYSQL_ROOT_PASSWORD=MySecurePassword123 \
    -v mysql-data:/var/lib/mysql \
    mysql:8.0

# 查看运行状态
docker ps

用Docker运行MariaDB

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 拉取MariaDB镜像
docker pull mariadb:10.11

# 运行MariaDB容器
docker run -d \
    --name mariadb-server \
    -p 3307:3306 \
    -e MARIADB_ROOT_PASSWORD=MySecurePassword123 \
    -v mariadb-data:/var/lib/mysql \
    mariadb:10.11

# 查看运行状态
docker ps

连接MySQL容器

1
2
3
4
5
# 进入容器
docker exec -it mysql-server mysql -u root -p

# 或者用docker exec直接执行SQL
docker exec -it mysql-server mysql -u root -pMySecurePassword123 -e "SHOW DATABASES;"

安装后必做清单

无论你用哪种方式安装,以下几步必须做:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 1. 检查版本
mysql --version
# mysql  Ver 8.0.35 for Linux on x86_64 (MySQL Community Server - GPL)

mariadb --version
# mariadb  Ver 15.1 Distrib 10.6.12-MariaDB, for debian-linux-gnu (x86_64)

# 2. 检查服务是否运行
sudo systemctl status mysql  # 或 mariadb

# 3. 检查端口是否监听
sudo ss -tlnp | grep 3306
# LISTEN  0  151  127.0.0.1:3306  0.0.0.0:*  users:(("mysqld",pid=1234,fd=21))

# 4. 测试连接
mysql -u root -p

一图总结安装流程

flowchart TD
    A[开始安装] --> B{选择操作系统}
    
    B --> C[Ubuntu/Debian]
    B --> D[CentOS/RHEL]
    B --> E[通用方法]
    
    C --> C1[apt install mysql-server<br/>或 apt install mariadb-server]
    D --> D1[yum install mysql-server<br/>或 yum install mariadb-server]
    E --> E1[Docker run]
    
    C1 --> F[systemctl enable & start]
    D1 --> F
    E1 --> F
    
    F --> G[安全配置 mysql_secure_installation]
    G --> H[测试连接]
    H --> I[✅ 安装完成]
    
    style I fill:#90EE90

常见问题排查

问题原因解决方法
连接被拒绝端口没开/防火墙sudo ufw allow 3306
密码错误忘了密码重置密码或重新安装
服务启动失败端口被占用lsof -i:3306 查看
无法远程连接bind-address配置修改为 0.0.0.0
Docker容器退出了配置错误docker logs mysql-server

小结

安装MySQL/MariaDB的方法:

  • Ubuntu/Debianapt install mysql-serverapt install mariadb-server
  • CentOS/RHELyum install mysql-community-serveryum install mariadb-server
  • Docker:一行命令搞定,随用随删

下一节我们将学习MySQL/MariaDB的基础配置,让数据库更安全、更高效!

44.3 基础配置

44.3.1 /etc/mysql/mariadb.conf.d/

配置文件在哪儿?

MySQL和MariaDB的配置文件位置不同,但思路一致:

MySQL 8.0 配置文件位置:

1
2
3
4
5
# 主配置文件
/etc/mysql/mysql.conf.d/mysqld.cnf

# 额外配置目录(放自定义配置)
/etc/mysql/conf.d/

MariaDB 配置文件位置:

1
2
3
4
5
# 主配置文件
/etc/mysql/mariadb.conf.d/50-server.cnf

# 额外配置目录
/etc/mysql/mariadb.conf.d/

认识主配置文件

注意:配置文件路径因发行版而异!

  • Ubuntu/Debian: /etc/mysql/mysql.conf.d/mysqld.cnf
  • CentOS/RHEL: /etc/my.cnf/etc/mysql/my.cnf

catless 查看配置文件内容:

1
2
3
4
5
# Ubuntu/Debian 系统查看 MySQL 主配置
sudo cat /etc/mysql/mysql.conf.d/mysqld.cnf

# CentOS/RHEL 系统查看 MySQL 主配置
sudo cat /etc/my.cnf

典型配置内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# MySQL服务进程配置
[mysqld]
#
# 基础设置
#
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
# 数据目录,重要!记录你的数据存在哪儿
datadir     = /var/lib/mysql
# 日志文件目录
log-error   = /var/log/mysql/error.log
# PID文件目录
!includedir /etc/mysql/conf.d/
1
2
# 查看MariaDB主配置
sudo cat /etc/mysql/mariadb.conf.d/50-server.cnf

典型配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# MariaDB服务进程配置
[mysqld]
#
# 基础设置
#
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
# 数据目录
datadir     = /var/lib/mysql
# 字符集设置
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

配置文件的结构

MySQL/MariaDB的配置文件使用 INI格式,方括号 [] 里面的是组名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[client]           # 客户端配置
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld]           # 服务器端配置
port        = 3306
datadir     = /var/lib/mysql
max_connections = 151

[mysql]            # mysql命令行客户端配置
default-character-set = utf8mb4

常用配置项详解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 服务端配置
[mysqld]

# 端口号,默认3306(生产环境建议改成别的)
port = 3306

# 数据存放目录(重要!磁盘空间要够大)
datadir = /var/lib/mysql

# PID文件位置
pid-file = /var/run/mysqld/mysqld.pid

# Socket文件位置(本地连接用)
socket = /var/run/mysqld/mysqld.sock

# 最大连接数(根据服务器配置调整)
# 小水管服务器:100-200
# 中等服务器:200-500
# 大型服务器:500-1000+
max_connections = 200

# 查询缓存大小(MySQL 8.0已移除,MariaDB还在用)
# query_cache_size = 128M

# 线程池大小(通常设为CPU核心数的2-4倍)
thread_cache_size = 8

# 表缓存大小
table_open_cache = 2000

# 日志文件路径
log_error = /var/log/mysql/error.log

# 慢查询日志(超过这个时间的SQL会被记录)
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2  # 超过2秒算慢查询

# 二进制日志(用于主从复制和数据恢复)
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 7  # 日志保留7天
max_binlog_size = 100M

为什么要修改配置?

默认配置是"万金油",什么场景都能跑,但什么都不极致。

生产环境调优主要是:

  • 增加 max_connections:连接数不够会报 “Too many connections”
  • 调整 table_open_cache:表多的话要加大
  • 开启 slow_query_log:找出慢SQL优化性能
  • 设置 log_bin:开启主从复制和数据恢复

修改配置的正确姿势

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. 先备份原配置
sudo cp /etc/mysql/mariadb.conf.d/50-server.cnf /etc/mysql/mariadb.conf.d/50-server.cnf.bak

# 2. 用nano或vim编辑
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

# 3. 修改后,检查语法
sudo mysqld --validate-config
# 如果输出 "Safely checked" 就没有语法错误

# 4. 重启服务使配置生效
sudo systemctl restart mariadb  # 或 mysql

44.3.2 字符集配置

字符集是什么?

字符集(Character Set) 就是一套编码规则,告诉计算机"abc"和"你好"应该怎么存储。

如果字符集配置不对:

  • 中文会变成乱码:“你好” → “浣犲ソ”
  • Emoji直接消失:"😂" → ""
  • 数据库迁移时数据丢失

MySQL/MariaDB支持的字符集:

字符集说明存储一个字符需要
latin1拉丁语系,不支持中文1字节
utf8MySQL老版本,只支持3字节UTF-83字节
utf8mb4**推荐!**完整UTF-8,支持Emoji和所有Unicode4字节
utf16UTF-16编码2或4字节

为什么推荐 utf8mb4?

  • utf8 在MySQL里是"伪UTF-8",不支持 😂 这种4字节的字符
  • utf8mb4 才是真正的UTF-8,支持所有Unicode字符,包括 Emoji、生僻字
  • 现代应用(微信、微博、抖音)到处都是Emoji,不用 utf8mb4 你就等着被用户投诉吧!

配置字符集

方法一:修改服务器配置(推荐)

1
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

[mysqld] 部分添加:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[mysqld]
#
# 字符集配置 - 强烈建议使用utf8mb4
#
# 服务器默认字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

# 连接字符集
init_connect = 'SET NAMES utf8mb4'

# 跳过-name解析加速(可选)
# skip-name-resolve

方法二:在MySQL会话中设置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- 查看当前字符集配置
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';

-- 设置会话字符集
SET NAMES utf8mb4;

-- 设置全局字符集(需要权限)
SET GLOBAL character_set_server = utf8mb4;
SET GLOBAL collation_server = utf8mb4_unicode_ci;

验证字符集配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 创建测试数据库,验证字符集
CREATE DATABASE test_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 查看数据库字符集
SHOW CREATE DATABASE test_db;

-- 创建测试表
USE test_db;
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    bio TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 插入带Emoji的数据测试
INSERT INTO users (name, bio) VALUES 
    ('小明', '今天心情😄,终于写完代码了!'),
    ('小红', '收到老板的消息📧,又要加班了💔'),
    ('前端小哥', '修复了一个🪲,测试说还有3个🪲');

-- 查询验证
SELECT * FROM users;

-- 查看建表语句,确认字符集
SHOW CREATE TABLE users;

执行结果应该看到正常的Emoji,而不是乱码!

字符集配置检查清单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1. 检查配置文件
grep -E "(character|collation)" /etc/mysql/mariadb.conf.d/50-server.cnf

# 2. 检查MySQL服务配置
mysql -u root -p -e "SHOW VARIABLES LIKE 'character_set%';"

# 3. 检查数据库默认字符集
mysql -u root -p -e "SHOW VARIABLES LIKE 'collation%';"

# 4. 测试中文存储
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS test_charset; USE test_charset; CREATE TABLE t (c VARCHAR(100)); INSERT INTO t VALUES ('测试中文和Emoji🎉'); SELECT * FROM t;"

一图总结字符集配置

flowchart LR
    A[配置文件] --> B[mysqld组]
    B --> C[character-set-server<br/>= utf8mb4]
    B --> D[collation-server<br/>= utf8mb4_unicode_ci]
    
    E[创建数据库] --> E1[CHARACTER SET utf8mb4<br/>COLLATE utf8mb4_unicode_ci]
    
    F[创建表] --> F1[DEFAULT CHARSET=utf8mb4]
    
    G[连接时] --> H[SET NAMES utf8mb4]

小结

配置MySQL/MariaDB要注意:

  • 配置文件在 /etc/mysql/mariadb.conf.d//etc/mysql/mysql.conf.d/
  • 常用配置项:max_connectionscharacter-set-serverslow_query_loglog_bin
  • 强烈推荐使用 utf8mb4 字符集,支持Emoji和所有Unicode字符
  • 修改配置后要验证语法,重启服务

下一节我们将学习用户与权限管理,这是数据库安全的重中之重!

44.4 用户与权限管理

“权力越大,责任越大。” —— 蜘蛛侠

“权限越多,危险越大。” —— 数据库管理员

用户与权限的核心概念

MySQL/MariaDB的权限系统有三层结构:

flowchart TB
    A[MySQL权限系统] --> B[用户账户<br/>who are you?]
    A --> C[权限<br/>what can you do?]
    A --> D[数据库对象<br/>on what?]
    
    B --> B1[用户名@主机名]
    C --> C1[SELECT]
    C --> C2[INSERT]
    C --> C3[UPDATE]
    C --> C4[DELETE]
    C --> C5[ALL PRIVILEGES]
    
    D --> D1[数据库.表]

用户名的格式: 用户名@主机

  • 'root'@'localhost' - 只能从本机连接
  • 'app'@'192.168.1.%' - 从IP段连接
  • 'app'@'%' - 从任意主机连接(不推荐!)

44.4.1 CREATE USER - 创建用户

基本语法

1
CREATE USER '用户名'@'主机' IDENTIFIED BY '密码';

创建用户的各种场景

场景1:创建普通应用用户

1
2
-- 创建一个只能从本地连接的应用用户
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'AppPass2024!';

场景2:创建远程连接用户

1
2
3
4
5
-- 创建一个可以从局域网任何机器连接的用户
CREATE USER 'app_user'@'192.168.1.%' IDENTIFIED BY 'AppPass2024!';

-- 或者允许从任何主机连接(生产环境慎用!)
CREATE USER 'app_user'@'%' IDENTIFIED BY 'AppPass2024!';

场景3:创建密码永不过期用户(MySQL 8.0+)

1
2
3
-- 创建用户,密码永不过期
CREATE USER 'dev_user'@'localhost' IDENTIFIED BY 'DevPass2024!';
ALTER USER 'dev_user'@'localhost' PASSWORD EXPIRE NEVER;

场景4:创建带限制的用户

1
2
3
-- 创建用户,限制最大连接数为10
CREATE USER 'limited_user'@'localhost' IDENTIFIED BY 'LimitPass2024!'
WITH MAX_CONNECTIONS_PER_HOUR 10;

场景5:创建只读用户

1
2
-- 创建只读用户(后续会讲如何授权)
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'ReadOnly2024!';

查看用户

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- 查看所有用户(MySQL用户信息在mysql.user表)
SELECT user, host, plugin FROM mysql.user;

-- 查看更详细的信息
SELECT 
    user, 
    host, 
    authentication_string, 
    plugin,
    account_locked,
    password_expired
FROM mysql.user;

执行结果类似:

+---------------+-----------+-----------------------+-------------+---------------+------------------+
| user          | host      | authentication_string | plugin      | account_locked | password_expired |
+---------------+-----------+-----------------------+-------------+---------------+------------------+
| root          | localhost |                       | unix_socket | N             | N                |
| mysql.session | localhost | *THISISNOTAVALIDPASS..| mysql_native | Y             | N                |
| mysql.sys     | localhost | *THISISNOTAVALIDPASS..| mysql_native | Y             | N                |
| app_user      | localhost | *XXXXXXXXXXXXXXXXXXXX | mysql_native | N             | N                |
+---------------+-----------+-----------------------+-------------+---------------+------------------+

44.4.2 GRANT - 授权

基本语法

1
GRANT 权限 ON 数据库.TO '用户名'@'主机';

常用权限一览

权限说明适用场景
ALL PRIVILEGES所有权限(除了GRANT OPTION)DBA
SELECT查询数据报表用户、只读用户
INSERT插入数据数据录入账号
UPDATE更新数据数据修改账号
DELETE删除数据慎用!单独授权
CREATE创建表/数据库应用账号
DROP删除表/数据库极少授权
ALTER修改表结构应用账号
INDEX创建/删除索引应用账号
REFERENCES外键权限一般不给
CREATE TEMPORARY TABLES创建临时表导出报表
LOCK TABLES锁表权限备份账号
EXECUTE执行存储过程应用账号
REPLICATION SLAVE主从复制从服务器
RELOAD执行FLUSH操作DBA

授权场景实战

场景1:给应用最小权限

1
2
3
4
5
-- 应用需要连接数据库,查询、插入、更新、删除表数据
-- 但不能修改表结构、不能删除表
GRANT SELECT, INSERT, UPDATE, DELETE 
ON myapp.* 
TO 'app_user'@'localhost';

场景2:给开发人员读写权限

1
2
3
4
-- 开发人员需要读写所有业务数据库,但不能管理用户
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX
ON myapp.* 
TO 'dev_user'@'192.168.1.%';

场景3:给DBA全部权限

1
2
3
4
5
-- DBA需要管理一切,但不能给其他DBA授权(安全考虑)
GRANT ALL PRIVILEGES 
ON *.* 
TO 'dba_admin'@'localhost'
WITH GRANT OPTION;

场景4:给报表用户只读权限

1
2
3
4
-- 报表系统只能读取,不能写入
GRANT SELECT 
ON analytics_db.* 
TO 'report_user'@'%';

场景5:给备份账号特殊权限

1
2
3
4
-- 备份账号需要锁表、查看定义,但不能读数据内容
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER
ON myapp.* 
TO 'backup_user'@'localhost';

场景6:跨库授权(需要分别授权)

1
2
3
4
-- MySQL不支持一次授权多个数据库,需要分别执行
GRANT SELECT, INSERT, UPDATE, DELETE ON db1.* TO 'multi_db_user'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON db2.* TO 'multi_db_user'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON db3.* TO 'multi_db_user'@'%';

创建用户+授权一条搞定

1
2
3
4
5
6
-- 如果用户不存在,可以直接用GRANT创建(MySQL特性)
-- 同时创建用户并授权
GRANT SELECT, INSERT, UPDATE, DELETE 
ON myapp.* 
TO 'new_app_user'@'%' 
IDENTIFIED BY 'NewUserPass2024!';

44.4.3 REVOKE - 回收权限

基本语法

1
REVOKE 权限 ON 数据库.FROM '用户名'@'主机';

回收权限实战

场景1:收回用户的DELETE权限

1
2
3
4
-- 发现DELETE权限被滥用,先收回来
REVOKE DELETE 
ON myapp.* 
FROM 'app_user'@'localhost';

场景2:收回用户的所有权限

1
2
3
-- 用户要离职了,回收所有权限
REVOKE ALL PRIVILEGES, GRANT OPTION 
FROM 'leaving_employee'@'%';

场景3:只保留SELECT权限(降级为只读)

1
2
3
4
5
6
7
8
-- 用户滥用权限,先降级为只读
REVOKE SELECT, INSERT, UPDATE, DELETE 
ON myapp.* 
FROM 'troublesome_user'@'%';

GRANT SELECT 
ON myapp.* 
TO 'troublesome_user'@'%';

44.4.4 DROP USER - 删除用户

基本语法

1
DROP USER '用户名'@'主机';

删除用户实战

场景1:删除一个用户

1
2
-- 用户已离职,删除账号
DROP USER 'former_employee'@'%';

场景2:删除多个用户

1
2
3
4
-- 批量删除测试账号
DROP USER 'test_user1'@'localhost', 
           'test_user2'@'localhost', 
           'temp_user'@'%';

场景3:删除后检查

1
2
3
-- 确认用户已删除
SELECT user, host FROM mysql.user WHERE user = 'former_employee';
-- 结果应该是空集

权限管理完整流程

flowchart TD
    A[创建用户<br/>CREATE USER] --> B[分配权限<br/>GRANT]
    B --> C[用户使用数据库]
    C --> D{需要调整权限?}
    D -->|增加权限| E[追加GRANT]
    D -->|减少权限| F[REVOKE]
    D -->|删除用户| G[DROP USER]
    F --> B
    E --> B

重要安全建议

安全第一!最小权限原则!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- ❌ 错误示范:root用户给外部应用使用
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password';

-- ❌ 错误示范:密码太简单
CREATE USER 'app'@'%' IDENTIFIED BY '123456';

-- ❌ 错误示范:允许任意主机连接
CREATE USER 'app'@'%' IDENTIFIED BY 'password';

-- ✅ 正确示范:最小权限 + 强密码 + 指定主机
CREATE USER 'app'@'192.168.1.100' IDENTIFIED BY 'AppStr0ngP@ssw0rd!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app'@'192.168.1.100';

修改用户密码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- 方法1:用ALTER USER改自己的密码
ALTER USER 'username'@'host' IDENTIFIED BY 'NewPassword123!';

-- 方法2:用SET改密码
SET PASSWORD FOR 'username'@'host' = 'NewPassword123!';

-- 方法3:用UPDATE直接修改(需要FLUSH PRIVILEGES)
UPDATE mysql.user SET authentication_string = PASSWORD('NewPassword123!') 
WHERE user = 'username' AND host = 'host';
FLUSH PRIVILEGES;

查看用户权限

1
2
3
4
5
6
7
8
-- 查看当前用户权限
SHOW GRANTS;

-- 查看指定用户权限
SHOW GRANTS FOR 'username'@'host';

-- 查看权限的另一种方式(MySQL 8.0+)
SHOW PRIVILEGES;

刷新权限

1
2
3
4
5
6
-- 修改用户表后需要刷新权限
FLUSH PRIVILEGES;

-- 这两个命令也可以reload权限
FLUSH TABLES;
FLUSH STATUS;

小结

用户权限管理的核心命令:

命令作用
CREATE USER创建用户
GRANT授予权限
REVOKE回收权限
DROP USER删除用户
ALTER USER修改用户
FLUSH PRIVILEGES刷新权限

安全原则:

  • 最小权限原则:只给必要的权限
  • 强密码策略:包含大小写字母、数字、特殊字符
  • 限制来源:尽量用具体IP,不用 %
  • 定期审计:检查有哪些用户和权限

下一节我们将学习数据库的基本操作,创建和管理数据库!

44.5 数据库操作

数据库基础概念

在MySQL的世界里,数据的层次结构是这样的:

flowchart TB
    A[MySQL服务器] --> B[Database<br/>数据库]
    B --> C[Table<br/>表]
    C --> D[Row<br/>行/记录]
    C --> E[Column<br/>列/字段]
    
    style A fill:#ff9999
    style B fill:#99ccff
    style C fill:#99ff99
    style D fill:#ffff99
    style E fill:#ff99ff

简单理解:

  • 数据库 = 文件夹(用来组织表)
  • = Excel表格(用来存储数据)
  • = 表格中的一行记录
  • = 表格中的一列属性

44.5.1 CREATE DATABASE - 创建数据库

基本语法

1
CREATE DATABASE 数据库名;

创建数据库实战

场景1:创建最基本的数据库

1
2
-- 创建一个叫 myapp 的数据库
CREATE DATABASE myapp;

场景2:创建时指定字符集(强烈推荐!)

1
2
-- 指定utf8mb4字符集
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

场景3:创建数据库(如果不存在才创建)

1
2
-- 防止重复创建报错
CREATE DATABASE IF NOT EXISTS myapp;

场景4:创建指定位置的数据库(高级)

1
2
3
-- 在指定路径创建数据库(需要先创建目录)
CREATE DATABASE myapp 
DATA DIRECTORY '/mnt/fast-disk/myapp/';

创建数据库时的参数说明

参数说明推荐值
CHARACTER SET字符集utf8mb4
COLLATE排序规则utf8mb4_unicode_ci
DATA DIRECTORY数据存放路径自定义

排序规则(COLLATE)的作用:

  • utf8mb4_unicode_ci:基于Unicode标准排序,支持多语言
  • utf8mb4_general_ci:通用排序,速度快(MySQL 5.7默认)
  • utf8mb4_0900_ai_ci:MySQL 8.0新增,更好的Unicode支持

44.5.2 SHOW DATABASES - 查看数据库

基本语法

1
SHOW DATABASES;

查看数据库实战

1
2
-- 查看所有数据库
SHOW DATABASES;

执行结果:

+--------------------+
| Database           |
+--------------------+
| information_schema |  -- MySQL内部系统数据库
| mysql              |  -- MySQL用户和权限信息
| performance_schema |  -- 性能监控数据
| sys                |  -- 系统库,用于诊断
| myapp              |  -- 我们刚创建的库
+--------------------+

系统数据库说明:

数据库用途能否删除
information_schema存储MySQL元数据(表信息、列信息等)❌ 系统必需
mysql存储用户账号、权限等❌ 系统必需
performance_schema性能监控和诊断❌ 系统必需
sys系统库,提供诊断视图❌ 系统必需
自定义数据库你的业务数据✅ 可以

条件查询数据库

1
2
3
4
5
-- 模糊匹配查找数据库
SHOW DATABASES LIKE '%app%';

-- 查找以 my 开头的数据库
SHOW DATABASES LIKE 'my%';

44.5.3 USE 数据库 - 选择数据库

基本语法

1
USE 数据库名;

使用数据库实战

1
2
3
4
5
-- 选择要操作的数据库
USE myapp;

-- 选择后,后续所有操作都在这个数据库中进行
-- 直到执行 USE 切换到其他数据库

查看当前使用的数据库

1
2
3
4
5
6
7
8
-- 方法1:SELECT查询
SELECT DATABASE();

-- 方法2:查看连接状态
STATUS;

-- 方法3:查看当前数据库
SELECT @current_db;

执行结果:

+------------+
| DATABASE() |
+------------+
| myapp      |
+------------+

数据库操作的完整流程

flowchart LR
    A[CREATE DATABASE<br/>创建数据库] --> B[SHOW DATABASES<br/>查看数据库]
    B --> C[USE database<br/>选择数据库]
    C --> D[开始操作表<br/>CREATE TABLE...]
    
    E[DROP DATABASE<br/>删除数据库]
    
    style A fill:#90EE90
    style D fill:#FFFFE0
    style E fill:#FFB6C1

完整的数据库管理示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- 1. 查看当前有哪些数据库
SHOW DATABASES;

-- 2. 创建一个新数据库(指定字符集)
CREATE DATABASE IF NOT EXISTS shop 
CHARACTER SET utf8mb4 
COLLATE utf8mb4_unicode_ci;

-- 3. 选择这个数据库
USE shop;

-- 4. 在数据库中创建表(下一节会详细讲)
CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL
);

-- 5. 查看当前数据库中的表
SHOW TABLES;

-- 6. 查看建表语句
SHOW CREATE TABLE products;

-- 7. 切换回myapp数据库
USE myapp;

-- 8. 确认当前数据库
SELECT DATABASE();

删除数据库

1
2
3
4
5
-- 删除数据库(危险操作!会删除所有表和数据!)
DROP DATABASE myapp;

-- 安全删除(只删除已存在的数据库)
DROP DATABASE IF EXISTS myapp;

警告! 删除数据库会删除所有表和数据,不可恢复!删除前请务必三思而后行,最好先备份!

修改数据库

1
2
3
4
5
-- 修改数据库的字符集
ALTER DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 注意:MySQL不允许直接修改数据库名
-- 如果要改名,需要导出数据,重建数据库,再导入

小结

数据库操作三板斧:

命令说明
CREATE DATABASE创建数据库,记得加 utf8mb4
SHOW DATABASES查看所有数据库
USE database选择要操作的数据库
DROP DATABASE删除数据库(危险!别乱用!)

创建数据库的标准写法:

1
2
3
CREATE DATABASE IF NOT EXISTS 数据库名 
CHARACTER SET utf8mb4 
COLLATE utf8mb4_unicode_ci;

下一节我们将学习表操作,这是数据库最核心的部分!

44.6 表操作

表(Table)是关系型数据库的核心概念。你可以把它想象成一个Excel表格:

列1(ID)列2(姓名)列3(年龄)
1小明25
2小红23
3小刚28

44.6.1 CREATE TABLE - 创建表

基本语法

1
2
3
4
5
6
CREATE TABLE 表名 (
    列名1 数据类型 [约束],
    列名2 数据类型 [约束],
    ...
    [表级约束]
);

数据类型一览

类型说明示例
INT整数1, 100, -5
BIGINT大整数9223372036854775807
FLOAT单精度浮点3.14
DOUBLE双精度浮点3.1415926535
DECIMAL(p,s)定点数(精确)DECIMAL(10,2)
VARCHAR(n)可变字符串‘Hello’
CHAR(n)固定长度字符串‘Hi ’ (补空格)
TEXT长文本文章内容
DATE日期‘2024-01-01’
DATETIME日期时间‘2024-01-01 12:30:00’
TIMESTAMP时间戳1704067200
BOOLEAN布尔值TRUE/FALSE
JSONJSON数据‘{“key”: “value”}’

创建表实战

场景1:创建用户表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
USE myapp;

CREATE TABLE users (
    -- 主键:唯一标识每行,自动增长
    id INT AUTO_INCREMENT PRIMARY KEY,
    
    -- 用户名:可变字符串,最多50字符,非空
    username VARCHAR(50) NOT NULL,
    
    -- 邮箱:可变字符串,最多100字符,唯一
    email VARCHAR(100) NOT NULL UNIQUE,
    
    -- 密码:哈希存储,固定长度64字符(SHA256)
    password_hash CHAR(64) NOT NULL,
    
    -- 年龄:整数,可空
    age INT,
    
    -- 性别:枚举类型,只能是'M'或'F'
    gender ENUM('M', 'F') DEFAULT 'M',
    
    -- 个人简介:长文本,可空
    bio TEXT,
    
    -- 创建时间:自动记录当前时间
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    -- 更新时间:自动更新
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

场景2:创建商品表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    
    -- 商品名称,必填
    name VARCHAR(200) NOT NULL,
    
    -- 价格,最多10位数字,其中2位小数(精准计算金钱)
    price DECIMAL(10, 2) NOT NULL,
    
    -- 库存数量,整数
    stock INT DEFAULT 0,
    
    -- 分类ID,关联分类表
    category_id INT,
    
    -- 商品描述,长文本
    description TEXT,
    
    -- 是否上架,布尔值
    is_active BOOLEAN DEFAULT TRUE,
    
    -- 创建时间
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

场景3:创建带外键的订单表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    
    -- 订单号,唯一字符串
    order_no VARCHAR(32) NOT NULL UNIQUE,
    
    -- 用户ID,外键关联users表
    user_id INT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id),
    
    -- 订单总金额
    total_amount DECIMAL(10, 2) NOT NULL,
    
    -- 订单状态
    status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending',
    
    -- 创建时间
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

场景4:创建带索引的表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    
    title VARCHAR(200) NOT NULL,
    
    content TEXT,
    
    author_id INT,
    
    -- 手动添加索引
    INDEX idx_author (author_id),
    
    -- 复合索引(多列组合索引)
    INDEX idx_title_content (title(50), content(100)),
    
    -- 全文索引(用于文章搜索)
    FULLTEXT INDEX ft_content (content),
    
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

约束类型

约束说明语法
PRIMARY KEY主键,唯一标识id INT PRIMARY KEY
NOT NULL非空约束name VARCHAR(50) NOT NULL
UNIQUE唯一约束email VARCHAR(100) UNIQUE
DEFAULT默认值status INT DEFAULT 1
CHECK检查约束age INT CHECK (age > 0)
FOREIGN KEY外键约束FOREIGN KEY (uid) REFERENCES users(id)
AUTO_INCREMENT自动增长id INT AUTO_INCREMENT

44.6.2 ALTER TABLE - 修改表结构

基本语法

1
ALTER TABLE 表名 操作;

修改表结构实战

场景1:添加新列

1
2
3
-- 给users表添加手机号字段
ALTER TABLE users 
ADD COLUMN phone VARCHAR(20) AFTER email;

场景2:删除列

1
2
3
-- 删除users表的bio字段
ALTER TABLE users 
DROP COLUMN bio;

场景3:修改列

1
2
3
-- 把users表的username长度改大
ALTER TABLE users 
MODIFY COLUMN username VARCHAR(100) NOT NULL;

场景4:重命名列

1
2
3
-- 把phone改名为mobile
ALTER TABLE users 
CHANGE COLUMN phone mobile VARCHAR(20);

场景5:添加索引

1
2
3
-- 给email字段添加唯一索引
ALTER TABLE users 
ADD UNIQUE INDEX idx_email (email);

场景6:修改表名

1
2
3
-- 把users表改名为app_users
ALTER TABLE users 
RENAME TO app_users;

44.6.3 DROP TABLE - 删除表

基本语法

1
DROP TABLE 表名;

删除表实战

1
2
3
4
5
6
7
8
-- 删除一个表(危险!会删除数据和结构!)
DROP TABLE orders;

-- 安全删除(只删除已存在的表)
DROP TABLE IF EXISTS orders;

-- 一次性删除多个表
DROP TABLE IF EXISTS order_items, order_logs, order_history;

警告! 删除表会删除所有数据,不可恢复!删除前请务必备份!

查看表信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
-- 查看当前数据库所有表
SHOW TABLES;

-- 查看表结构(desc = describe)
DESC users;
-- 或
DESCRIBE users;

-- 查看建表语句
SHOW CREATE TABLE users;

-- 查看表的所有列
SHOW COLUMNS FROM users;

-- 查看表的状态
SHOW TABLE STATUS LIKE 'users';

小结

表操作三板斧:

命令说明
CREATE TABLE创建表
ALTER TABLE修改表结构
DROP TABLE删除表(危险!)

创建表的标准写法:

1
2
3
4
5
6
CREATE TABLE 表名 (
    id INT AUTO_INCREMENT PRIMARY KEY,
    字段1 类型 NOT NULL,
    字段2 类型 DEFAULT 默认值,
    ...
);

下一节我们将学习SQL基础,增删改查四兄弟!

44.7 SQL 基础

SQL(Structured Query Language)是操作数据库的"普通话"。无论你用的是MySQL、PostgreSQL还是Oracle,SQL语法基本通用!

增删改查,CRUD,就是这么朴实无华!

44.7.1 SELECT 查询

基本语法

1
SELECT 列名 FROM 表名 WHERE 条件 ORDER BY 列名 LIMIT n;

查询实战

场景1:查询所有数据

1
2
3
4
5
-- 查询users表所有记录的所有列
SELECT * FROM users;

-- 查询users表所有记录的指定列
SELECT id, username, email FROM users;

场景2:条件查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 查询年龄大于18的用户
SELECT * FROM users WHERE age > 18;

-- 查询状态为'active'的用户
SELECT * FROM users WHERE status = 'active';

-- 组合条件:年龄大于18且状态为active
SELECT * FROM users WHERE age > 18 AND status = 'active';

-- 或条件:年龄小于20或大于50
SELECT * FROM users WHERE age < 20 OR age > 50;

场景3:模糊查询

1
2
3
4
5
6
7
8
-- 查询用户名以'张'开头的用户
SELECT * FROM users WHERE username LIKE '张%';

-- 查询邮箱包含'gmail'的用户
SELECT * FROM users WHERE email LIKE '%gmail%';

-- 下划线_匹配单个字符
SELECT * FROM users WHERE username LIKE '张_';

场景4:排序

1
2
3
4
5
6
7
8
-- 按年龄升序排列(从小到大)
SELECT * FROM users ORDER BY age ASC;

-- 按年龄降序排列(从大到小)
SELECT * FROM users ORDER BY age DESC;

-- 先按年龄降序,年龄相同则按用户名升序
SELECT * FROM users ORDER BY age DESC, username ASC;

场景5:分页查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 查询第1-10条记录
SELECT * FROM users LIMIT 10;

-- 查询第11-20条记录(OFFSET跳过前10条)
SELECT * FROM users LIMIT 10 OFFSET 10;

-- 简写形式:查询第11-20条
SELECT * FROM users LIMIT 10, 10;

-- 查询年龄最大的前5个用户
SELECT * FROM users ORDER BY age DESC LIMIT 5;

场景6:聚合查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
-- 统计用户总数
SELECT COUNT(*) FROM users;

-- 统计年龄大于18的用户数
SELECT COUNT(*) FROM users WHERE age > 18;

-- 计算平均年龄
SELECT AVG(age) FROM users;

-- 计算最大年龄和最小年龄
SELECT MAX(age), MIN(age) FROM users;

-- 按性别分组统计
SELECT gender, COUNT(*) as cnt FROM users GROUP BY gender;

-- 分组后筛选(只显示数量大于10的组)
SELECT gender, COUNT(*) as cnt 
FROM users 
GROUP BY gender 
HAVING cnt > 10;

场景7:多表连接查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
-- 内连接:查询订单和对应的用户信息
SELECT 
    orders.id,
    orders.order_no,
    orders.total_amount,
    users.username,
    users.email
FROM orders
INNER JOIN users ON orders.user_id = users.id;

-- 左连接:查询所有用户,包括没有订单的用户
SELECT 
    users.username,
    orders.order_no,
    orders.total_amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id;

44.7.2 INSERT 插入

基本语法

1
INSERT INTO 表名 (1,2, ...) VALUES (1,2, ...);

插入实战

场景1:插入单条数据

1
2
3
4
5
6
7
8
9
-- 指定列插入
INSERT INTO users (username, email, password_hash, age) 
VALUES ('xiaoming', 'xiaoming@example.com', 'hash123', 25);

-- 所有列插入(省略列名,但顺序必须与表结构完全一致)
INSERT INTO users 
VALUES (NULL, 'xiaoming', 'xiaoming@example.com', 'hash123', 25, 'M', NULL, NOW(), NOW());

-- 注意:VALUES顺序必须匹配:id, username, email, password_hash, age, gender, bio, created_at, updated_at

场景2:批量插入

1
2
3
4
5
6
-- 一次插入多条数据
INSERT INTO users (username, email, password_hash, age) VALUES 
    ('user1', 'user1@example.com', 'hash1', 20),
    ('user2', 'user2@example.com', 'hash2', 22),
    ('user3', 'user3@example.com', 'hash3', 24),
    ('user4', 'user4@example.com', 'hash4', 26);

场景3:插入查询结果

1
2
3
-- 从旧表复制数据到新表
INSERT INTO users_backup (username, email, age)
SELECT username, email, age FROM users WHERE created_at > '2024-01-01';

44.7.3 UPDATE 更新

基本语法

1
UPDATE 表名 SET1 =1,2 =2 WHERE 条件;

更新实战

场景1:更新单条数据

1
2
3
4
5
-- 更新用户ID=1的年龄
UPDATE users SET age = 26 WHERE id = 1;

-- 同时更新多个字段
UPDATE users SET age = 26, status = 'active' WHERE id = 1;

场景2:批量更新

1
2
3
4
5
6
7
8
-- 把所有年龄小于18的用户状态改为'inactive'
UPDATE users SET status = 'inactive' WHERE age < 18;

-- 把所有用户的年龄加1
UPDATE users SET age = age + 1;

-- 把所有没有邮箱的用户设置默认邮箱
UPDATE users SET email = 'no-email@example.com' WHERE email IS NULL;

警告! UPDATE语句的WHERE条件很重要,没有WHERE会更新所有行!

1
2
3
4
5
6
-- ❌ 错误示范:忘了WHERE,更新了所有用户的密码
UPDATE users SET password_hash = 'hacked123';

-- ✅ 正确做法:先SELECT确认,再UPDATE
SELECT * FROM users WHERE id = 1;  -- 先确认
UPDATE users SET password_hash = 'new_hash' WHERE id = 1;  -- 再更新

44.7.4 DELETE 删除

基本语法

1
DELETE FROM 表名 WHERE 条件;

删除实战

场景1:删除单条数据

1
2
-- 删除用户ID=1的记录
DELETE FROM users WHERE id = 1;

场景2:批量删除

1
2
3
4
5
-- 删除所有年龄大于100的用户(数据有问题)
DELETE FROM users WHERE age > 100;

-- 删除所有状态为'deleted'的用户
DELETE FROM users WHERE status = 'deleted';

警告! DELETE语句的WHERE条件同样重要,没有WHERE会删除所有行!

1
2
3
4
5
-- ❌ 错误示范:忘了WHERE,删了整张表
DELETE FROM users;

-- ✅ 正确做法:删除前先备份或确认
DELETE FROM users WHERE id IN (1, 2, 3);

CRUD完整示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- ==================== C - Create 创建 ====================

-- 创建用户
INSERT INTO users (username, email, password_hash, age) 
VALUES ('xiaoming', 'xiaoming@example.com', SHA2('password123', 256), 25);

-- ==================== R - Read 读取 ====================

-- 查询所有用户
SELECT * FROM users;

-- 查询年龄大于18的用户
SELECT username, email FROM users WHERE age > 18 ORDER BY age DESC;

-- ==================== U - Update 更新 ====================

-- 更新用户信息
UPDATE users SET age = 26, status = 'active' WHERE username = 'xiaoming';

-- ==================== D - Delete 删除 ====================

-- 删除用户
DELETE FROM users WHERE username = 'xiaoming';

小结

SQL增删改查四兄弟:

操作关键字危险程度
SELECT⭐ 安全,只读
INSERT⭐⭐ 谨慎
UPDATE⭐⭐⭐ 注意WHERE
DELETE⭐⭐⭐⭐ 非常危险

安全原则:

  • UPDATEDELETE 前必加 WHERE
  • 重要数据先 SELECT 确认再操作
  • 生产环境执行前先备份

下一节我们将学习索引与优化,这是提升数据库性能的关键!

44.8 索引与优化

索引的概念

索引(Index) 是什么?打个比方:

  • 没有索引的表 = 没有目录的书,想找某页只能一页一页翻
  • 有索引的表 = 有目录的书,想找某个内容看目录就行

索引会占用额外的磁盘空间,但能大幅提升查询速度(尤其是大数据量时)。

44.8.1 CREATE INDEX - 创建索引

基本语法

1
2
3
4
5
6
7
8
-- 创建普通索引
CREATE INDEX 索引名 ON 表名 (列名);

-- 创建唯一索引(索引值必须唯一)
CREATE UNIQUE INDEX 索引名 ON 表名 (列名);

-- 创建复合索引(多列组合)
CREATE INDEX 索引名 ON 表名 (1,2,3);

创建索引实战

场景1:给常用查询字段加索引

1
2
3
4
5
6
7
8
-- 给users表的email字段加索引(登录时经常用邮箱查询)
CREATE INDEX idx_email ON users(email);

-- 给orders表的user_id加索引(关联查询)
CREATE INDEX idx_user_id ON orders(user_id);

-- 给orders表的created_at加索引(按时间查询)
CREATE INDEX idx_created_at ON orders(created_at);

场景2:创建复合索引

1
2
3
4
5
-- 创建一个复合索引,用于查询"某用户在某时间段的订单"
CREATE INDEX idx_user_date ON orders(user_id, created_at);

-- 这样的查询可以利用索引:
SELECT * FROM orders WHERE user_id = 1 AND created_at BETWEEN '2024-01-01' AND '2024-12-31';

场景3:创建唯一索引

1
2
3
4
5
-- 给email加唯一索引,防止重复注册
CREATE UNIQUE INDEX idx_unique_email ON users(email);

-- 给订单号加唯一索引,防止重复订单
CREATE UNIQUE INDEX idx_unique_order_no ON orders(order_no);

场景4:创建全文索引

1
2
3
4
5
-- 给文章标题和内容加全文索引(用于搜索引擎)
CREATE FULLTEXT INDEX ft_article ON articles(title, content);

-- 使用全文索引查询
SELECT * FROM articles WHERE MATCH(title, content) AGAINST('关键词');

在创建表时添加索引

1
2
3
4
5
6
7
8
9
CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    price DECIMAL(10, 2),
    
    -- 创建时直接加索引
    INDEX idx_name (name),
    UNIQUE INDEX idx_unique_name (name)
);

查看和删除索引

1
2
3
4
5
6
7
8
-- 查看表的所有索引
SHOW INDEX FROM users;

-- 删除索引
DROP INDEX idx_email ON users;

-- 或用ALTER TABLE删除
ALTER TABLE users DROP INDEX idx_email;

44.8.2 索引类型:B+树

MySQL索引的数据结构

MySQL(InnoDB)的索引使用 B+树 数据结构。

B+树的特点:

  • 所有数据都在叶子节点
  • 叶子节点之间用链表连接(范围查询快)
  • 树高低,查询次数少(IO次数少)
                    [根节点]
                        │
            ┌───────────┼───────────┐
            │           │           │
        [页1]       [页2]       [页3]    [内部节点]
            │           │           │
        ┌───┴───┐   ┌───┴───┐   ┌───┴───┐
        │       │   │       │   │       │
    [叶1]   [叶2]   [叶3]   [叶4]   [叶5]  [叶子节点,用链表连接]

什么字段适合建索引?

适合建索引不适合建索引
WHERE 条件常用的字段区分度低的字段(如性别只有男女)
ORDER BY 排序的字段数据量很小的表
JOIN 连接的字段频繁更新的字段
SELECT 要查询的字段主键(自动有索引)

索引使用技巧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- ✅ 好的写法:使用索引
SELECT * FROM users WHERE email = 'test@example.com';

-- ❌ 差的写法:索引失效
SELECT * FROM users WHERE email LIKE '%test%';  -- 前缀模糊查询无法使用索引

-- ✅ 好的写法:前缀模糊查询可以(需要建立前缀索引)
SELECT * FROM users WHERE email LIKE 'test%';

-- ❌ 差的写法:在索引列上使用函数,索引失效
SELECT * FROM users WHERE YEAR(created_at) = 2024;

-- ✅ 好的写法:避免在索引列上使用函数
SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

小结

索引是提升查询性能的利器:

  • CREATE INDEX 创建索引
  • UNIQUE INDEX 创建唯一索引
  • 复合索引要注意字段顺序
  • 索引不是越多越好,太多会影响写入性能
  • MySQL使用B+树作为索引结构

下一节我们将学习数据备份,这是DBA最重要的工作!

44.9 mysqldump 备份

“数据不备份,等于裸奔!”

备份是数据库管理员最重要的职责之一。没有备份的数据库,就像没系安全带的过山车——出事就是大事!

44.9.1 mysqldump 备份命令

基本语法

1
mysqldump -u 用户名 -p 数据库名 > 备份文件.sql

备份实战

场景1:备份单个数据库

1
2
3
4
5
# 备份myapp数据库到文件
mysqldump -u root -p myapp > /backup/myapp_backup_20240101.sql

# 备份时输入密码(不推荐,密码会显示在history)
mysqldump -u root -pMyPassword myapp > /backup/myapp_backup.sql

场景2:备份多个数据库

1
2
# 备份多个数据库
mysqldump -u root -p --databases myapp shop blog > /backup/multi_db_backup.sql

场景3:备份所有数据库

1
2
# 备份所有数据库(包括系统数据库)
mysqldump -u root -p --all-databases > /backup/all_databases_backup.sql

场景4:只备份表结构(不备份数据)

1
2
# 只备份建表语句,用于快速部署新环境
mysqldump -u root -p --no-data myapp > /backup/myapp_structure.sql

场景5:只备份数据(不备份结构)

1
2
# 只备份数据,用于数据迁移
mysqldump -u root -p --no-create-info myapp > /backup/myapp_data.sql

场景6:压缩备份(节省空间)

1
2
3
4
5
# 备份并压缩
mysqldump -u root -p myapp | gzip > /backup/myapp_backup.sql.gz

# 解压恢复
gunzip < /backup/myapp_backup.sql.gz | mysql -u root -p

场景7:定时自动备份脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# backup.sh - MySQL自动备份脚本

# 配置
DB_USER="root"
DB_PASS="YourPassword"
DB_NAME="myapp"
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)

# 创建备份目录(如果不存在)
mkdir -p $BACKUP_DIR

# 执行备份
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz

# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete

# 输出备份信息
echo "备份完成: ${DB_NAME}_${DATE}.sql.gz"
1
2
3
4
5
6
7
# 给脚本添加执行权限
chmod +x /backup/backup.sh

# 将脚本加入crontab,每天凌晨3点执行
crontab -e
# 添加行:
# 0 3 * * * /backup/backup.sh

备份结果查看

1
2
# 查看备份文件内容(前100行)
head -100 /backup/myapp_backup.sql

备份文件内容示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- MySQL dump 10.13  Distrib 8.0.35, for Linux (x86_64)
--
-- Host: localhost    Database: myapp
-- ------------------------------------------------------
-- Server version	8.0.35

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!50503 SET NAMES utf8mb4 */;

--
-- Table structure for table `users`
--

DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password_hash` char(64) NOT NULL,
  `age` int DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_unique_email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

--
-- Dumping data for table `users`
--

INSERT INTO `users` VALUES (1,'xiaoming','xiaoming@example.com','hash123',25,'2024-01-01 00:00:00');

小结

mysqldump备份要点:

  • mysqldump -u -p 数据库 > 文件.sql 基本语法
  • --all-databases 备份所有数据库
  • --no-data 只备份结构
  • | gzip 压缩备份
  • 定期备份 + 异地存储 = 数据安全

下一节我们将学习如何从备份恢复数据!

44.10 mysql 命令行恢复

备份的目的就是为了恢复!万一哪天手滑删了数据,或者服务器炸了,有了备份就能起死回生!

44.10.1 mysql 恢复命令

基本语法

1
mysql -u 用户名 -p 数据库名 < 备份文件.sql

恢复实战

场景1:从备份文件恢复数据库

1
2
3
4
# 恢复myapp数据库
mysql -u root -p myapp < /backup/myapp_backup_20240101.sql

# 输入密码后,数据就回来了

场景2:恢复所有数据库

1
2
# 如果备份的是所有数据库
mysql -u root -p < /backup/all_databases_backup.sql

场景3:恢复压缩的备份

1
2
3
4
5
6
# 解压并恢复
gunzip < /backup/myapp_backup.sql.gz | mysql -u root -p myapp

# 或者分两步
gunzip -c /backup/myapp_backup.sql.gz > /tmp/myapp_backup.sql
mysql -u root -p myapp < /tmp/myapp_backup.sql

场景4:恢复时指定字符集

1
2
# 确保恢复时使用正确的字符集
mysql -u root -p --default-character-set=utf8mb4 myapp < /backup/myapp_backup.sql

场景5:source命令在MySQL客户端内恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 先登录MySQL
mysql -u root -p

-- 选择数据库
USE myapp;

-- 使用source命令恢复
SOURCE /backup/myapp_backup_20240101.sql;

-- 查看恢复的数据
SELECT COUNT(*) FROM users;

完整备份恢复流程

flowchart TD
    A[正常状态] --> B{灾难发生}
    
    B -->|数据丢失| C[恢复备份]
    B -->|服务器迁移| D[迁移数据]
    B -->|测试环境| E[克隆数据]
    
    C --> C1[mysqldump备份]
    C1 --> C2[mysql恢复]
    C2 --> F[数据恢复成功]
    
    D --> D1[mysqldump导出]
    D1 --> D2[mysql导入到新服务器]
    D2 --> G[迁移完成]
    
    E --> E1[mysqldump导出]
    E1 --> E2[导入到测试环境]
    E2 --> H[测试环境就绪]

增量备份与恢复(高级)

全量备份恢复太慢?试试增量备份!

xtrabackup工具(推荐用于生产环境):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 安装xtrabackup(需要添加Percona源)
# Ubuntu
apt install xtrabackup

# 全量备份
xtrabackup --backup --target-dir=/backup/full --user=root --password=xxx

# 增量备份(在全量备份之后)
xtrabackup --backup --target-dir=/backup/incr1 --incremental-basedir=/backup/full --user=root --password=xxx

# 恢复(先prepare全量,再apply增量)
xtrabackup --prepare --target-dir=/backup/full
xtrabackup --prepare --target-dir=/backup/full --incremental-dir=/backup/incr1
xtrabackup --copy-back --target-dir=/backup/full

小结

mysql恢复要点:

  • mysql -u -p 数据库 < 文件.sql 基本语法
  • 恢复时目标数据库需要存在
  • SOURCE 命令可在MySQL客户端内使用
  • 压缩备份用 gunzip -c 解压后导入
  • 重要数据要定期备份,备份文件要异地存储

下一节我们将学习主从复制,这是数据库高可用的基础!

44.11 主从复制

主从复制的概念

主从复制(Replication) 是什么?

想象一下,你是公司老板(主库),你有几个分身(从库):

  • 你的分身和你一模一样(数据同步)
  • 有人找你办事,你可以让他去找分身(读写分离)
  • 你万一有个三长两短,分身还在(高可用)

这就是主从复制!

flowchart LR
    A[主库 Master] -->|Binlog日志| B[从库 Slave1]
    A -->|Binlog日志| C[从库 Slave2]
    A -->|Binlog日志| D[从库 Slave3]
    
    style A fill:#ff9999
    style B fill:#99ccff
    style C fill:#99ccff
    style D fill:#99ccff

44.11.1 配置主服务器

主服务器配置

步骤1:修改主服务器配置文件

1
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

添加/修改以下配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[mysqld]
# 服务器唯一ID(必填,从库不能相同)
server-id = 1

# 开启二进制日志(必填,用于复制)
log_bin = /var/log/mysql/mysql-bin.log

# 只复制哪些数据库(可选)
# binlog-do-db = myapp

# 不复制哪些数据库(可选)
binlog-ignore-db = mysql
binlog-ignore-db = information_schema
binlog-ignore-db = performance_schema

# 日志格式(推荐MIXED或ROW)
binlog_format = MIXED

# 从服务器读取位置(过期日志自动删除,保留7天)
expire_logs_days = 7

步骤2:重启MySQL服务

1
sudo systemctl restart mariadb

步骤3:创建复制账号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 登录MySQL
mysql -u root -p

-- 创建专门用于复制的用户(从库用这个账号连接主库)
CREATE USER 'repl_user'@'%' IDENTIFIED BY 'ReplPass2024!';

-- 授予复制权限
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';

-- 刷新权限
FLUSH PRIVILEGES;

步骤4:查看主库状态

1
2
-- 查看主库状态(记录File和Position,从库要用)
SHOW MASTER STATUS;

执行结果:

+---------------+----------+--------------+------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------+----------+--------------+------------------+
| mysql-bin.000003|     456 | myapp        | mysql,information_schema |
+---------------+----------+--------------+------------------+

44.11.2 配置从服务器

从服务器配置

步骤1:修改从服务器配置文件

1
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

添加/修改以下配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[mysqld]
# 服务器唯一ID(必填,不能和主库相同)
server-id = 2

# 开启中继日志(接收主库的Binlog)
relay_log = /var/log/mysql/mysql-relay-bin

# 只读模式(从库建议开启,防止意外写入)
read_only = ON

# 禁止超级权限用户读写(root用户也从只读)
# super_read_only = ON

步骤2:重启从服务器

1
sudo systemctl restart mariadb

步骤3:连接主服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- 登录从库MySQL
mysql -u root -p

-- 停止从库复制线程
STOP SLAVE;

-- 配置复制连接参数(替换为你的实际值)
CHANGE MASTER TO
    MASTER_HOST = '192.168.1.100',    -- 主库IP地址
    MASTER_USER = 'repl_user',         -- 复制账号
    MASTER_PASSWORD = 'ReplPass2024!', -- 复制密码
    MASTER_PORT = 3306,               -- 主库端口
    MASTER_LOG_FILE = 'mysql-bin.000003',  -- 主库的File
    MASTER_LOG_POS = 456;             -- 主库的Position

步骤4:启动复制

1
2
3
4
5
-- 启动从库复制线程
START SLAVE;

-- 查看从库状态
SHOW SLAVE STATUS\G;

执行结果(关键字段):

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.1.100
                  Master_User: repl_user
                  Master_Port: 3306
                Master_Log_File: mysql-bin.000003
            Read_Master_Log_Pos: 456
               Relay_Log_File: mysql-relay-bin.000002
                Relay_Log_Pos: 720
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes        -- IO线程运行中
            Slave_SQL_Running: Yes        -- SQL线程运行中
              Last_IO_Error: 
             Last_SQL_Error: 

检查点:

  • Slave_IO_Running: Yes ✅ IO线程正常
  • Slave_SQL_Running: Yes ✅ SQL线程正常
  • Last_IO_Error: (空) ✅ 没有错误

主从复制原理

sequenceDiagram
    participant 主 as 主库 Master
    participant 从 as 从库 Slave
    
    Note over 主: 用户执行SQL写操作<br/>INSERT/UPDATE/DELETE
    
    主->>主: 写入Binlog日志
    
    从->>主: IO线程请求Binlog
    
    主->>从: 发送Binlog日志
    
    从->>从: 写入RelayLog中继日志
    
    从->>从: SQL线程执行RelayLog中的SQL
    
    Note over 从: 数据同步完成!

小结

主从复制配置要点:

  1. 主库配置server-idlog_bin创建repl_user
  2. 从库配置server-idrelay_logread_only
  3. 启动复制CHANGE MASTER TO + START SLAVE
  4. 验证状态SHOW SLAVE STATUS\G

下一节我们将学习如何配置远程连接!

44.12 远程连接配置

远程连接的问题

默认情况下,MySQL只允许从本地连接(localhost)。如果你想从其他机器连接,需要配置!

44.12.1 bind-address

问题原因

MySQL默认只监听本地连接:

1
2
3
4
5
# 查看3306端口监听情况
sudo ss -tlnp | grep 3306

# 默认情况:
# LISTEN 127.0.0.1:3306  -- 只监听本地

解决方案

方法1:修改bind-address

1
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

找到 bind-address,修改:

1
2
3
4
5
6
7
8
9
[mysqld]
# 只监听本地(默认)
# bind-address = 127.0.0.1

# 监听所有网卡(允许远程连接)
bind-address = 0.0.0.0

# 或者只监听指定IP
# bind-address = 192.168.1.100

重启服务:

1
sudo systemctl restart mariadb

验证:

1
2
3
sudo ss -tlnp | grep 3306
# 现在应该看到:
# LISTEN  0.0.0.0:3306  -- 监听所有网卡

方法2:创建远程用户并授权

1
2
3
4
5
6
7
8
-- 创建一个可以从任何IP登录的用户(生产环境建议限制IP)
CREATE USER 'remote_user'@'%' IDENTIFIED BY 'RemotePass2024!';

-- 授予适当权限
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'remote_user'@'%';

-- 刷新权限
FLUSH PRIVILEGES;

方法3:开放防火墙端口

1
2
3
4
5
6
7
8
9
# Ubuntu/Debian (ufw)
sudo ufw allow 3306/tcp

# CentOS/RHEL (firewalld)
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload

# 或者直接关闭防火墙(不推荐生产环境)
sudo systemctl stop firewalld

远程连接测试

从远程机器连接:

1
2
3
4
5
# 使用MySQL客户端连接
mysql -h 192.168.1.100 -u remote_user -p

# 或者
mysql -h your-server-ip -u remote_user -p myapp

参数说明:

  • -h : 服务器IP地址或主机名
  • -u : 用户名
  • -p : 提示输入密码
  • 最后的 myapp : 要连接的数据库名

安全建议

开放远程连接要慎重!安全第一!

安全建议:

1
2
3
4
5
6
7
8
-- ❌ 危险:允许任意IP连接
CREATE USER 'app'@'%' IDENTIFIED BY 'weak';

-- ✅ 安全:只允许特定IP段连接
CREATE USER 'app'@'192.168.1.%' IDENTIFIED BY 'Str0ngP@ss!';

-- ✅ 更安全:只允许特定IP连接
CREATE USER 'app'@'192.168.1.100' IDENTIFIED BY 'Str0ngP@ss!';

额外安全措施:

  1. 使用SSL连接(MySQL 8.0+支持)
  2. 配置防火墙,只允许特定IP访问3306
  3. 使用跳板机/VPN访问数据库
  4. 考虑使用SSH隧道连接

小结

远程连接配置三步走:

  1. 修改bind-addressbind-address = 0.0.0.0
  2. 创建远程用户CREATE USER 'user'@'%' ...
  3. 开放防火墙ufw allow 3306/tcp

本章小结

本章我们深入学习了MySQL/MariaDB,从安装到配置,从用户权限到SQL操作,从索引优化到备份恢复,从主从复制到远程连接。内容丰富,干货满满!

核心命令回顾

分类命令
安装apt install mysql-server / apt install mariadb-server
连接mysql -u root -p
用户CREATE USER / GRANT / REVOKE / DROP USER
数据库CREATE DATABASE / SHOW DATABASES / USE
CREATE TABLE / ALTER TABLE / DROP TABLE
SQLSELECT / INSERT / UPDATE / DELETE
索引CREATE INDEX
备份mysqldump -u -p db > file.sql
恢复mysql -u -p db < file.sql
主从CHANGE MASTER TO / START SLAVE
远程bind-address = 0.0.0.0

关键概念回顾

  1. MySQL vs MariaDB:MariaDB是MySQL的社区分支,高度兼容
  2. 存储引擎:InnoDB是主流,支持事务和行锁
  3. 字符集:强烈推荐 utf8mb4
  4. 最小权限原则:用户只给必要的权限
  5. 索引:加速查询,但不是越多越好
  6. 备份重于一切:定期备份,异地存储
  7. 主从复制:读写分离 + 高可用

配置示例

字符集配置(/etc/mysql/mariadb.conf.d/50-server.cnf):

1
2
3
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

创建数据库标准写法:

1
2
3
CREATE DATABASE IF NOT EXISTS myapp 
CHARACTER SET utf8mb4 
COLLATE utf8mb4_unicode_ci;

下章预告

下一章我们将学习 PostgreSQL,这是功能最强大的开源关系型数据库。PostgreSQL支持更多高级特性,如自定义数据类型、复杂的触发器、全文检索、地理信息系统等。敬请期待!

趣味彩蛋:MySQL和MariaDB的关系,其实很像一个有趣的程序员故事:

Montyfork了MySQL,创造了MariaDB。Oracle收购了MySQL,却"意外"让MariaDB变得更流行。

这告诉我们:有时候,“被抛弃"可能是最好的安排。

记住:数据库千万个,备份第一个,操作不规范,亲人两行泪! 🔥

最后修改 March 24, 2026: 新增JavaScript教程 (37305c4)