7.8 MySQL 数据库教程
简介
MySQL 是常用的开源关系型数据库管理系统(RDBMS),适合 Web 应用、后台服务等需要独立数据库服务、多客户端并发访问的场景。与 SQLite 等嵌入式库相比,MySQL 通常以服务端进程形式运行,通过网络端口对外提供服务。
在 Go 生态中,业务数据常通过 database/sql、GORM 等访问 MySQL(参见 7.6 GORM 框架入门)。本章从安装运维、SQL 基础、类型与事务,到与 Go 连接的衔接,帮助你在本地或测试环境中独立使用 MySQL,并为后续应用开发打基础。
安装与运行环境
常见安装方式
- 包管理器:如 macOS 的 Homebrew(
brew install mysql)、各 Linux 发行版的apt/yum等,适合本机开发。 - 官方安装包:从 MySQL 官网 下载,适合需要指定大版本或图形安装向导的场景。
- Docker:
docker run拉取官方镜像,环境干净、版本可重复,适合团队统一开发环境或 CI。
以下以 Docker 快速启动为例(镜像名与标签请按团队规范选择,此处仅示意):
docker run -d \
--name mysql-dev \
-e MYSQL_ROOT_PASSWORD=your_root_password \
-e MYSQL_DATABASE=appdb \
-p 3306:3306 \
mysql:8服务与端口
- MySQL 服务监听默认端口 3306(可配置)。
- 安装方式不同,启停命令也不同:可能是
systemctl start mysql、brew services start mysql,或 Docker 容器的启动/停止。
本机启停命令示例
- macOS(Homebrew 等常见安装):可用脚本
mysql.server启停本机服务:
mysql.server start # 启动
mysql.server stop # 停止
mysql.server restart # 重启- Linux(systemd):常见为
mysqld或mysql服务名,依发行版而定,例如:
sudo systemctl start mysqld
sudo systemctl stop mysqld- Docker:用
docker start/docker stop控制容器,而非mysql.server。
客户端
- 命令行客户端:安装 MySQL 后通常自带
mysql,用于执行 SQL、管理用户与库。 - 图形工具:如 MySQL Workbench、DBeaver、DataGrip 等,便于浏览表结构与执行查询。
连接与身份
mysql 命令行连接
安装客户端后,在终端使用 mysql 连接服务端。-u 指定用户名,-p 表示需要密码(推荐交互输入,避免密码出现在 shell 历史或进程列表中)。
本机连接(默认套接字或 127.0.0.1:3306)
mysql -u 用户名 -p
# 回车后按提示输入密码,例如 root:
mysql -u root -p注意:不要写成 mysql -u root -p 123456 这种「-p 与密码之间有空格」的形式——此时 123456 会被当作默认数据库名,而不是密码。若必须在非交互场景传入密码(不推荐,易泄露),应紧挨 -p 书写且无空格,例如 mysql -u root -p'你的密码'(仍可能被 ps 看到,生产请用配置文件或 mysql_config_editor 等更安全方式)。
远程连接
指定主机 -h、端口 -P(大写 P)、用户名与 -p:
mysql -h 192.168.1.100 -P 3306 -u root -p连接成功后进入 mysql> 提示符,即可执行 SQL 语句(如后文「创建业务用户与授权」中的语句);输入 exit 或 quit 退出。
用户、主机与权限
- 用户由「用户名
@主机」标识,例如'app'@'%'表示用户app可从任意主机连接(生产环境应收紧主机范围)。 - 权限控制该用户能对哪些库、表执行哪些操作(
SELECT、INSERT等)。
首次登录与安全
首次使用 root 登录后,应尽快设置强密码,并避免在应用中长期共用 root。生产环境应使用专用业务账号,且只授予必要权限。
创建业务用户与授权(示例)
在 mysql 客户端中(以 MySQL 8 为例,语法随版本可能略有差异):
-- 创建用户(从本机连接示例)
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'strong_password_here';
-- 授予对某库的全部权限(按最小权限原则改为具体权限更佳)
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;最小权限原则:应用账号通常不需要 DROP DATABASE、SUPER 等高危权限;只授予业务需要的库与操作。
数据库与字符集
创建与删除库
CREATE DATABASE appdb;
DROP DATABASE appdb; -- 慎用:将删除库及其对象utf8mb4 与排序规则(collation)
- MySQL 早期默认的
utf8实为阉割版 UTF-8(最多三字节),无法完整存储 emoji 等字符。应使用utf8mb4作为库/表/连接的字符集。 - Collation(排序规则)决定比较、排序是否区分大小写等。常见如
utf8mb4_unicode_ci、utf8mb4_0900_ai_ci(MySQL 8 默认系列之一,随版本可能不同)。
创建库时指定字符集与排序规则示例:
CREATE DATABASE appdb
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;应用连接字符串中常带 charset=utf8mb4(见下文「与 Go 衔接」),避免写入乱码。
SQL 基础
数据类型
列类型决定存储方式、取值范围与索引行为;与 Go 映射时需注意有符号/无符号与取值范围是否匹配。
总述
可粗分为:数值、字符串、日期时间、JSON、二进制等。除类型本身外,还要结合 NULL 是否允许、默认值、字符集(字符串类)综合考虑。
数值类型
| 类型 | 说明 |
|---|---|
TINYINT / SMALLINT / MEDIUMINT / INT / BIGINT | 整数,范围依次增大 |
UNSIGNED | 无符号修饰(仅适用于整数类型),上限扩大、不可表示负数 |
DECIMAL(M,D) | 定点数,金额、税率等需要精确小数时优先使用 |
FLOAT / DOUBLE | 浮点近似值,科学计算等场景;不宜用于金额 |
BIT | 位字段,业务中较少直接使用,可用整数或枚举替代 |
主键、外键引用列常用 BIGINT 或 INT;自增主键多与整数类型配合。
字符串类型
| 类型 | 说明 |
|---|---|
CHAR(n) | 定长,n 为字符长度(与字符集有关),短且长度固定时可用 |
VARCHAR(n) | 变长,n 为最大字符数,最常用的可变文本列 |
TINYTEXT / TEXT / MEDIUMTEXT / LONGTEXT | 大文本;避免滥用 LONGTEXT,影响索引与行格式,应设合理上限 |
BINARY(n) / VARBINARY(n) | 二进制字节串 |
TINYBLOB / BLOB / … | 大二进制对象,适合哈希、小图标等;大文件更常见做法是对象存储 + 库中存元数据 |
日期与时间
| 类型 | 说明 |
|---|---|
DATE | 日期 |
TIME | 时间 |
DATETIME | 日期时间,与时区无关的「墙上时间」语义时常用,范围大 |
TIMESTAMP | 带时区转换,范围较 DATETIME 小,可自动 ON UPDATE;行为与服务器/会话时区相关 |
YEAR | 年份 |
与 Go 交互时,连接串中常用 parseTime=True 以便将时间类型扫描为 time.Time;时区可用 loc=Local 或与业务一致的 Asia/Shanghai 等(与 7.6 中 DSN 示例一致)。
JSON
JSON 类型可存结构化文档,并提供函数与运算符查询(如 ->、->>)。何时用 JSON 列:字段结构多变、以文档为中心且查询模式简单时;需要强关系、复杂关联与索引时,仍宜规范化拆表。
ENUM 与 SET
ENUM('a','b','c'):单列只能取其中一个枚举值,约束强;变更枚举值需要ALTER TABLE,成本高时需评估。SET:多选集合,使用相对较少。
空间类型(了解)
GEOMETRY 等空间类型用于地理坐标等场景,入门阶段可略过,需要地图或空间查询时再查阅官方文档。
选型与实践要点
- 主键:常用自增
BIGINT或INT;分布式 ID 则可能用BIGINT存雪花 ID 等。 - 金额:使用
DECIMAL,指定精度与小数位(如DECIMAL(10,2))。 - 字符集:库/表/连接统一
utf8mb4。 - 字符串长度:
VARCHAR按业务实际上限设置;超长全文再考虑TEXT。 - 与 Go:例如 MySQL
BIGINT UNSIGNED与 Gouint64对应关系需一致;INT与int在 32/64 位平台上的习惯用法也宜与团队规范对齐。
表、主键、索引
创建表示例:
CREATE TABLE users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(64) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_users_email (email),
KEY idx_users_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;- 主键(PRIMARY KEY):唯一标识一行,一张表一个主键(可为复合主键)。
- 自增(AUTO_INCREMENT):常用于整数主键,由 MySQL 分配递增值。
- 非空(NOT NULL)、默认值(DEFAULT):减少无意义
NULL,便于应用层处理。 - 存储引擎:生产环境几乎一律 InnoDB(支持事务与行锁);
ENGINE=InnoDB建议显式写出。
索引:
- 普通索引:加速查询与排序,写入略有开销。
- 唯一索引(UNIQUE):列值唯一,如登录名、邮箱。
增删改查与查询
-- 插入
INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');
-- 更新(务必带条件,避免全表更新)
UPDATE users SET username = 'alice2' WHERE id = 1;
-- 删除(务必带条件)
DELETE FROM users WHERE id = 1;
-- 查询
SELECT id, username, email FROM users WHERE id = 1;
-- 排序与分页(页大小与偏移按业务选择)
SELECT id, username FROM users ORDER BY id DESC LIMIT 10 OFFSET 20;多表连接(JOIN)
内连接(INNER JOIN):只保留两表在连接条件上匹配的行。
SELECT u.id, u.username, o.id AS order_id
FROM users u
INNER JOIN orders o ON o.user_id = u.id
WHERE u.id = 1;外连接(LEFT JOIN 等)在需要「保留左表全部行」时再使用。
基础表设计:一对一、一对多、多对多
关系型库里,实体之间的联系通常用 外键(Foreign Key) 表达:在「多」或「从属」一侧存对方主键,必要时配合 UNIQUE 限制为一对一。设计时先分清业务是 一对多、一对一 还是 多对多,再决定几张表、键放在哪一侧。
下列建表示例与上文「表、主键、索引」中的 users 等可能同名;在同一库中练习时,宜在空库执行,或对示例表先 DROP TABLE(注意外键顺序:先删子表)后再建。
一对多(1 : N)
最常见:「一」方不存「多」方的外键;在「多」方表中增加指向「一」方主键的列。例如一个用户有多笔订单:
CREATE TABLE users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(64) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
amount DECIMAL(10,2) NOT NULL,
PRIMARY KEY (id),
KEY idx_orders_user_id (user_id),
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;user_id:多笔订单可指向同一用户,故不加UNIQUE。- 查询「某用户全部订单」即对
orders按user_id过滤,或与users做JOIN(见上文)。
一对一(1 : 1)
两边实体一一对应,常见两种做法:
做法 A:从表主键即外键(共享主键)
扩展信息与主实体严格同生命周期时,从表主键同时引用主表主键,天然保证一对一。
CREATE TABLE user_profiles (
user_id BIGINT UNSIGNED NOT NULL,
bio VARCHAR(512),
avatar_url VARCHAR(512),
PRIMARY KEY (user_id),
CONSTRAINT fk_profiles_user
FOREIGN KEY (user_id) REFERENCES users (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;做法 B:从表自有主键 + 对外键列加 UNIQUE
从表独立自增主键,用一列指向主表,该列 UNIQUE,保证每个用户最多一条扩展记录。
CREATE TABLE user_security (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
password_hash VARBINARY(64) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_user_security_user_id (user_id),
CONSTRAINT fk_security_user
FOREIGN KEY (user_id) REFERENCES users (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;选型简述:强绑定、扩展行与主行同生共灭 时 A 很直观;需要从表独立主键或历史迁移灵活时多用 B。
多对多(N : M)
两边都可以有多条与对方关联(如用户与角色、学生与课程),关系型库中不能直接在两表各放一列外键表达,需增加 中间表(关联表 / 连接表),两行主键各出现一次在中间表的一行里。
CREATE TABLE roles (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_roles_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_roles (
user_id BIGINT UNSIGNED NOT NULL,
role_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users (id),
CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES roles (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;- 复合主键
(user_id, role_id):同一用户同一角色只绑定一次;若中间表还需绑定时间、操作人等属性,可改为代理主键id+ 对(user_id, role_id)建UNIQUE。 - 查询某用户全部角色:从
user_rolesJOINroles;查询拥有某角色的用户则反向JOINusers。
小结对照
| 关系 | 典型建模 |
|---|---|
| 一对多 | 「多」方表含指向「一」方主键的外键列,不加 UNIQUE |
| 一对一 | 从表主键即外键,或从表外键列 UNIQUE |
| 多对多 | 中间表两列分别引用双方主键,常用复合主键或 UNIQUE 防重复 |
与 7.6 GORM 中的「关联」概念一一对应:一对多用外键列;一对一用 UNIQUE 或约定主键相同;多对多对应 GORM 的 many2many 中间表。
约束与数据完整性
- 主键:实体唯一标识。
- 外键(FOREIGN KEY):引用另一张表的主键或唯一键,由数据库保证引用完整性;优点是约束清晰,缺点是变更表顺序、大批量迁移时成本较高,部分团队选择在应用层保证一致性。
- 唯一约束:除主键外,业务上「不能重复」的列可建
UNIQUE。 - 检查约束(CHECK):MySQL 8.0.16 起支持更完整的 CHECK;旧版本行为与文档以官方说明为准。
事务与隔离级别
基本用法
START TRANSACTION;
-- 或多条 DML
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 出错时执行 ROLLBACK;ACID 与 InnoDB
- ACID:原子性、一致性、隔离性、持久性;InnoDB 提供事务与崩溃恢复能力,与日常 Web 开发默认选择一致。
隔离级别(概念)
隔离级别从低到高包括:读未提交、读已提交、可重复读(MySQL InnoDB 默认)、串行化。级别越低,并发越好,但可能出现脏读、不可重复读、幻读等现象;级别越高,锁与一致性成本越高。入门阶段理解「多语句需要原子性时用事务」即可,深入调优可再读官方手册。
备份与恢复(入门)
- 逻辑备份:将库表结构与数据导出为 SQL 文本,常用工具
mysqldump。适合迁移、小中型库、版本控制友好的文本归档。 - 恢复:将导出的 SQL 文件通过
mysql客户端重定向导入,或在图形工具中执行脚本。
具体命令与权限、单库/全库选项以运维规范为准;生产环境往往还有物理备份、主从复制等方案,超出本章范围。
与 Go 衔接
从 Go 连接 MySQL 时,DSN(数据源名称)常包含:用户名、密码、协议与地址、库名、以及查询参数。与 7.6 中示例一致,建议至少包含:
charset=utf8mb4:与库表字符集一致,避免乱码。parseTime=True:将DATE/DATETIME等映射为time.Time。loc=Local或明确时区:与业务时间语义一致。
示例(仅说明格式,密码勿提交到仓库):
user:password@tcp(127.0.0.1:3306)/appdb?charset=utf8mb4&parseTime=True&loc=Local安全习惯:生产库账号密码来自环境变量或密钥管理,不要写死在代码与 Git 仓库中;不同环境使用不同库与权限。
常见排错与注意点
- 无法连接:检查 MySQL 是否监听、防火墙、端口映射;用户的主机部分是否为
'user'@'localhost'与实际连接来源一致。 - Access denied:用户名、密码、库权限或
host不匹配。 - 乱码:库/表/连接字符集是否为
utf8mb4,连接串是否带charset=utf8mb4。 - sql_mode:如
ONLY_FULL_GROUP_BY开启时,SELECT中非聚合列需满足分组规则,否则报错;遇报错可对照官方文档理解语义而非盲目关闭安全选项。
小结
本章介绍了 MySQL 的定位与安装运行思路、用户与权限、utf8mb4 与排序规则,并系统梳理了 SQL 数据类型、建表与索引、增删改查与内连接、一对一 / 一对多 / 多对多 的基础表设计、约束与事务入门、备份思路,以及与 Go DSN 的衔接和常见故障。可自行对照下列清单自检:
- 能用 Docker、
mysql.server(macOS 常见)或systemctl(Linux)等方式启动 MySQL,并用mysql -u … -p(本机)或mysql -h … -P … -u … -p(远程)登录。 - 会创建库、表,理解主键、自增、常用索引与
utf8mb4。 - 能根据场景选择主要数据类型(尤其
DECIMAL与金额、VARCHAR与TEXT)。 - 能区分 一对多、一对一、多对多,会用外键与中间表建模,并理解与 7.6 中 GORM 关联的对应关系。
- 理解事务的提交与回滚,知道 InnoDB 与隔离级别的存在。
- 能配置与 7.6 一致的连接参数,并避免在代码中硬编码生产密钥。
参考资料
- MySQL 8.0 参考手册
- MySQL 字符集与排序规则
- GORM 官方文档(与 7.6 配合阅读)