跳到主要内容

安装与快速上手

装之前先定三件事​

同一套安装命令在不同环境下结果不一样,动手前先确认:

  • 版本:生产优先选 8.0 的最新小版本(8.0.4x),8.4 是新的 LTS、支持周期更长,但周边工具和云厂商仍以 8.0 为主。本篇以 8.0 为准,示例环境 Ubuntu 22.04。
  • 部署方式:物理机/虚机走 apt 或二进制包,容器化环境走 Docker,云上用 RDS。三种方式的参数口径一致,但文件路径、服务管理完全不同。
  • 数据目录:默认在 /var/lib/mysql,生产要挂独立磁盘。数据目录和 binlog 分盘放,IO 互不干扰。
Ubuntu 22.04 的默认版本

Ubuntu 22.04 官方源里的 mysql-server 就是 8.0.x,直接装即可。想要更新的小版本,加 MySQL 官方 APT 源(mysql-apt-config),别去手动下 deb 包硬装,升级时会很难受。

方式一:apt 安装​

最省事的方式,适合虚机和本地环境:

# 更新索引并安装
sudo apt update
sudo apt install -y mysql-server-8.0

# 确认版本和服务状态
mysql --version
sudo systemctl status mysql

# 开机自启
sudo systemctl enable mysql

安装完成后 MySQL 已经自动初始化了数据目录,root 账号通过 auth_socket 插件认证——也就是本机 sudo mysql 直接进,不需要密码。这是 Ubuntu 的打包策略,不是 bug:

# 本机 root 免密登录(走 auth_socket)
sudo mysql

# 普通账号走 TCP + 密码
mysql -h 127.0.0.1 -P 3306 -u app -p
不要急着把 root 改成密码登录

auth_socket 让 root 只能从本机通过系统用户身份登录,安全性比密码好。需要远程管理时,正确做法是新建一个管理账号并授权,而不是给 root 开密码 + 放开远程。真要为 root 设密码,也得先想清楚 bind-address 和防火墙。

方式二:Docker 安装​

本地验证、CI、快速起复刻环境都用这个。一条命令拉起:

docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD='Root@2024' \
-e TZ=Asia/Shanghai \
-v /data/mysql/conf:/etc/mysql/conf.d \
-v /data/mysql/data:/var/lib/mysql \
mysql:8.0.36 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_0900_ai_ci

参数逐个说:

  • -p 3306:3306:宿主机端口映射,冲突就改前面那个数字。
  • MYSQL_ROOT_PASSWORD:初始化时设置的 root 密码,不设的话容器会拒绝启动(除非用 MYSQL_ALLOW_EMPTY_PASSWORD,别这么干)。
  • -v .../conf:/etc/mysql/conf.d:把自己的 .cnf 放进去,容器启动时自动加载。
  • -v .../data:/var/lib/mysql:数据卷,这行必须有,否则容器一删数据全没。
  • 末尾的 --character-set-server 是传给 mysqld 的启动参数,效果等同写在配置文件里。
# 看初始化日志,出现 "ready for connections" 才算起来
docker logs -f mysql8

# 进容器执行 SQL
docker exec -it mysql8 mysql -uroot -p
容器里的 my.cnf 放哪

官方镜像读 /etc/mysql/my.cnf,里面 !includedir /etc/mysql/conf.d/。所以挂载目录比挂载单个文件稳,改配置不用重建容器,docker restart 即可。改完记得 docker exec mysql8 mysqld --verbose --help | grep -A1 'Default options' 确认配置被读到。

安全初始化​

mysql_secure_installation 是官方提供的交互式加固脚本,apt 装完直接跑:

sudo mysql_secure_installation

它会依次问六件事,怎么答:

提示建议原因
VALIDATE PASSWORD COMPONENT生产选 Y 并设强度 MEDIUM弱密码检查,本地实验可以跳过
Change the root password按需Ubuntu 的 root 用 auth_socket,可以不改
Remove anonymous usersY匿名账号是历史遗留风险
Disallow root login remotelyYroot 就该只从本机进
Remove test databaseY测试库对谁都没用
Reload privilege tablesY让改动立即生效

手工做等效的加固也不难,核心就几条:

-- 删匿名账号(8.0 的 mysql.user 是只读视图,必须用 DROP USER)和 test 库
DROP USER IF EXISTS ''@'localhost';
DROP USER IF EXISTS ''@'%';
DROP DATABASE IF EXISTS test;

-- 确认没有无密码账号
SELECT User, Host, plugin FROM mysql.user WHERE authentication_string = '';

FLUSH PRIVILEGES;
密码强度组件会拦下你的测试密码

开了 validate_password 之后,CREATE USER ... IDENTIFIED BY '123456' 会报 ERROR 1819。实验环境嫌烦可以调低策略(validate_password.policy=LOW),但生产环境别关。

my.cnf 关键配置​

配置文件的加载顺序要先搞清楚,否则改了不生效会怀疑人生:

/etc/my.cnf
→ /etc/mysql/my.cnf
→ /etc/mysql/conf.d/*.cnf
→ /etc/mysql/mysql.conf.d/*.cnf

后面的覆盖前面的。Ubuntu 的 apt 包把主要配置放在 /etc/mysql/mysql.conf.d/mysqld.cnf,改这个文件最直接。

[mysqld]
# 监听地址:0.0.0.0 表示所有网卡,只给本机用就写 127.0.0.1
bind-address = 0.0.0.0

# 端口与 socket
port = 3306
socket = /var/run/mysqld/mysqld.sock

# 字符集:8.0 默认就是 utf8mb4,显式写上更稳
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci

# 最大连接数:按内存和并发算,不是越大越好
max_connections = 1000

# Buffer Pool:专用库一般是物理内存的 50%~70%
innodb_buffer_pool_size = 8G
innodb_buffer_pool_instances = 4

# 每连接缓冲,连接多时这些参数乘起来很吓人
sort_buffer_size = 2M
join_buffer_size = 4M

# 双 1:数据安全优先
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1

# 事务隔离级别(8.0 默认就是 REPEATABLE-READ)
transaction_isolation = REPEATABLE-READ

# 时间与时区
default-time-zone = '+08:00'

[client]
default-character-set = utf8mb4

四个最常被问的项展开说:

  • bind-address:默认 127.0.0.1 时远程连不上,第一嫌疑就是它。改成 0.0.0.0 后必须配防火墙或安全组,数据库裸奔在公网是最常见的被勒索入口。
  • character-set-server:8.0 默认已经是 utf8mb4(4 字节,支持 emoji),保持默认即可。真正要小心的是客户端连接字符集,老代码里 SET NAMES utf8 仍然是 3 字节的坑。
  • max_connections:每个连接至少占几百 KB 到几 MB 内存,1000 个连接轻松吃掉几个 G。先看 Max_used_connections 的历史峰值,再留 20% 余量。
  • innodb_buffer_pool_size:最重要的性能参数,原则是「大到能装下热数据,小到不给系统留 OOM 风险」。它支持在线调整,不用重启。
-- 动态调整 Buffer Pool,立即生效(8.0 支持在线改大小)
SET GLOBAL innodb_buffer_pool_size = 16 * 1024 * 1024 * 1024;

-- 看看现在生效的值
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'max_connections';
改完配置先验证语法

sudo mysqld --validate-config 能在不启动的情况下检查配置文件语法,写错一个参数名就会报出来。生产上改配置前跑一遍,比 systemctl restart 之后看日志快得多。

建库、建表、建用户​

环境通了,走一遍最基础的操作。字符集统一 utf8mb4,排序规则统一 utf8mb4_0900_ai_ci(8.0 的新默认,比 utf8mb4_general_ci 更准确)。

-- 建库:显式指定字符集和排序规则
CREATE DATABASE IF NOT EXISTS shop
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_0900_ai_ci;

USE shop;

-- 建表:主键自增、时间字段带默认值、常用查询列建索引
CREATE TABLE orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID',
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
order_no VARCHAR(32) NOT NULL COMMENT '订单号',
amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '金额',
status TINYINT NOT NULL DEFAULT 0 COMMENT '0待付 1已付 2已发 3完成 4取消',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_order_no (order_no),
KEY idx_user_created (user_id, created_at)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '订单表';

-- 插入
INSERT INTO orders (user_id, order_no, amount, status)
VALUES (1001, 'SN20240101001', 199.00, 1);

-- 查询、更新、删除
SELECT id, order_no, amount FROM orders WHERE user_id = 1001 ORDER BY created_at DESC LIMIT 10;
UPDATE orders SET status = 2 WHERE order_no = 'SN20240101001';
DELETE FROM orders WHERE id = 1;

用户和授权分开做,遵循最小权限:

-- 建应用账号:只允许从内网网段登录
CREATE USER 'app'@'10.0.0.%' IDENTIFIED BY 'App@Strong#2024';

-- 只给业务库的增删改查,不给 DROP、不给其他库
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'app'@'10.0.0.%';

-- 只读账号给报表和监控用
CREATE USER 'readonly'@'10.0.0.%' IDENTIFIED BY 'Read@Strong#2024';
GRANT SELECT ON shop.* TO 'readonly'@'10.0.0.%';

-- 让权限立即生效
FLUSH PRIVILEGES;

-- 验证:看这个账号最终有哪些权限
SHOW GRANTS FOR 'app'@'10.0.0.%';

授权后常见两个坑:

  • 'app'@'%' 和 'app'@'localhost' 是两条不同的记录。从本机连可能匹配到 localhost 那条空权限记录,表现为「明明授权了却没权限」。用 SELECT CURRENT_USER() 看实际匹配到的账号更准。
  • FLUSH PRIVILEGES 其实不必每次执行,GRANT/CREATE USER 本身就会更新内存里的权限表。只有直接 UPDATE mysql.user 这种野路子才需要它。
认证插件的兼容性

8.0 默认 caching_sha2_password,老版本的 PHP、Navicat、Go 驱动可能连不上。确认客户端确实不支持时,再单独降级这个账号:

ALTER USER 'app'@'10.0.0.%' IDENTIFIED WITH mysql_native_password BY 'App@Strong#2024';

升级客户端才是正解,降级只是过渡手段——mysql_native_password 在 8.4 默认已禁用。

装完做这几项验证​

别装完就完事,五项检查过一遍再交付:

# 1. 服务活着,且是开机自启
systemctl is-active mysql && systemctl is-enabled mysql

# 2. 端口在听,且只听了该听的地址
ss -lntp | grep 3306

# 3. 版本、字符集、数据目录
mysql -e "SELECT VERSION(); SHOW VARIABLES LIKE 'character_set_server';"
mysql -e "SHOW VARIABLES LIKE 'datadir';"

# 4. 错误日志有没有异常(路径以实际为准)
sudo tail -n 50 /var/log/mysql/error.log

# 5. 用应用账号真连一次,跑一条真实 SQL
mysql -h 10.0.0.11 -u app -p shop -e "SELECT COUNT(*) FROM orders;"
时区问题会在半年后咬你

default-time-zone 没设时,MySQL 用系统时区。容器里通常是 UTC,NOW() 比北京时间少 8 小时,created_at 全错。建库前就把时区定死,比事后改数据便宜太多。

小结​

  • 安装两条路:apt 适合虚机(Ubuntu 22.04 源里就是 8.0),Docker 适合本地验证(务必挂数据卷和配置目录)。
  • 装完先跑 mysql_secure_installation:删匿名账号、禁 root 远程、删 test 库。
  • my.cnf 四个关键项:bind-address 管谁能连、character-set-server 管编码、max_connections 管并发上限、innodb_buffer_pool_size 管性能;后者可在线调整。
  • 建库建表统一 utf8mb4 + utf8mb4_0900_ai_ci;用户按最小权限授权,注意 localhost 与 % 是两条记录。
  • 交付前验证五件事:服务自启、端口监听、字符集、错误日志、真实账号连通。

环境有了,下一篇《SQL 语法详解》开始写 SQL:DDL/DML 的常用写法、查询怎么组织、函数和窗口函数怎么用。