Skip to content

7.8 MySQL 数据库教程

简介

MySQL 是常用的开源关系型数据库管理系统(RDBMS),适合 Web 应用、后台服务等需要独立数据库服务多客户端并发访问的场景。与 SQLite 等嵌入式库相比,MySQL 通常以服务端进程形式运行,通过网络端口对外提供服务。

在 Go 生态中,业务数据常通过 database/sqlGORM 等访问 MySQL(参见 7.6 GORM 框架入门)。本章从安装运维、SQL 基础、类型与事务,到与 Go 连接的衔接,帮助你在本地或测试环境中独立使用 MySQL,并为后续应用开发打基础。

安装与运行环境

常见安装方式

  • 包管理器:如 macOS 的 Homebrew(brew install mysql)、各 Linux 发行版的 apt / yum 等,适合本机开发。
  • 官方安装包:从 MySQL 官网 下载,适合需要指定大版本或图形安装向导的场景。
  • Dockerdocker run 拉取官方镜像,环境干净、版本可重复,适合团队统一开发环境或 CI。

以下以 Docker 快速启动为例(镜像名与标签请按团队规范选择,此处仅示意):

bash
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 mysqlbrew services start mysql,或 Docker 容器的启动/停止。

本机启停命令示例

  • macOS(Homebrew 等常见安装):可用脚本 mysql.server 启停本机服务:
bash
mysql.server start    # 启动
mysql.server stop     # 停止
mysql.server restart  # 重启
  • Linux(systemd):常见为 mysqldmysql 服务名,依发行版而定,例如:
bash
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

bash
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

bash
mysql -h 192.168.1.100 -P 3306 -u root -p

连接成功后进入 mysql> 提示符,即可执行 SQL 语句(如后文「创建业务用户与授权」中的语句);输入 exitquit 退出。

用户、主机与权限

  • 用户由「用户名 @ 主机」标识,例如 'app'@'%' 表示用户 app 可从任意主机连接(生产环境应收紧主机范围)。
  • 权限控制该用户能对哪些库、表执行哪些操作(SELECTINSERT 等)。

首次登录与安全

首次使用 root 登录后,应尽快设置强密码,并避免在应用中长期共用 root。生产环境应使用专用业务账号,且只授予必要权限。

创建业务用户与授权(示例)

mysql 客户端中(以 MySQL 8 为例,语法随版本可能略有差异):

sql
-- 创建用户(从本机连接示例)
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'strong_password_here';

-- 授予对某库的全部权限(按最小权限原则改为具体权限更佳)
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'localhost';

FLUSH PRIVILEGES;

最小权限原则:应用账号通常不需要 DROP DATABASESUPER 等高危权限;只授予业务需要的库与操作。

数据库与字符集

创建与删除库

sql
CREATE DATABASE appdb;
DROP DATABASE appdb;   -- 慎用:将删除库及其对象

utf8mb4 与排序规则(collation)

  • MySQL 早期默认的 utf8 实为阉割版 UTF-8(最多三字节),无法完整存储 emoji 等字符。应使用 utf8mb4 作为库/表/连接的字符集。
  • Collation(排序规则)决定比较、排序是否区分大小写等。常见如 utf8mb4_unicode_ciutf8mb4_0900_ai_ci(MySQL 8 默认系列之一,随版本可能不同)。

创建库时指定字符集与排序规则示例:

sql
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位字段,业务中较少直接使用,可用整数或枚举替代

主键、外键引用列常用 BIGINTINT;自增主键多与整数类型配合。

字符串类型

类型说明
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 等空间类型用于地理坐标等场景,入门阶段可略过,需要地图或空间查询时再查阅官方文档。

选型与实践要点

  • 主键:常用自增 BIGINTINT;分布式 ID 则可能用 BIGINT 存雪花 ID 等。
  • 金额:使用 DECIMAL,指定精度与小数位(如 DECIMAL(10,2))。
  • 字符集:库/表/连接统一 utf8mb4
  • 字符串长度VARCHAR 按业务实际上限设置;超长全文再考虑 TEXT
  • 与 Go:例如 MySQL BIGINT UNSIGNED 与 Go uint64 对应关系需一致;INTint 在 32/64 位平台上的习惯用法也宜与团队规范对齐。

表、主键、索引

创建表示例:

sql
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):列值唯一,如登录名、邮箱。

增删改查与查询

sql
-- 插入
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):只保留两表在连接条件上匹配的行。

sql
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)

最常见:「一」方不存「多」方的外键;在「多」方表中增加指向「一」方主键的列。例如一个用户有多笔订单:

sql
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
  • 查询「某用户全部订单」即对 ordersuser_id 过滤,或与 usersJOIN(见上文)。

一对一(1 : 1)

两边实体一一对应,常见两种做法:

做法 A:从表主键即外键(共享主键)
扩展信息与主实体严格同生命周期时,从表主键同时引用主表主键,天然保证一对一。

sql
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,保证每个用户最多一条扩展记录。

sql
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)

两边都可以有多条与对方关联(如用户与角色、学生与课程),关系型库中不能直接在两表各放一列外键表达,需增加 中间表(关联表 / 连接表),两行主键各出现一次在中间表的一行里。

sql
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_roles JOIN roles;查询拥有某角色的用户则反向 JOIN users

小结对照

关系典型建模
一对多「多」方表含指向「一」方主键的外键列,不加 UNIQUE
一对一从表主键即外键,或从表外键列 UNIQUE
多对多中间表两列分别引用双方主键,常用复合主键或 UNIQUE 防重复

7.6 GORM 中的「关联」概念一一对应:一对多用外键列;一对一用 UNIQUE 或约定主键相同;多对多对应 GORM 的 many2many 中间表。

约束与数据完整性

  • 主键:实体唯一标识。
  • 外键(FOREIGN KEY):引用另一张表的主键或唯一键,由数据库保证引用完整性;优点是约束清晰,缺点是变更表顺序、大批量迁移时成本较高,部分团队选择在应用层保证一致性。
  • 唯一约束:除主键外,业务上「不能重复」的列可建 UNIQUE
  • 检查约束(CHECK):MySQL 8.0.16 起支持更完整的 CHECK;旧版本行为与文档以官方说明为准。

事务与隔离级别

基本用法

sql
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 或明确时区:与业务时间语义一致。

示例(仅说明格式,密码勿提交到仓库):

text
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 与金额VARCHARTEXT)。
  • 能区分 一对多、一对一、多对多,会用外键与中间表建模,并理解与 7.6 中 GORM 关联的对应关系。
  • 理解事务的提交与回滚,知道 InnoDB 与隔离级别的存在。
  • 能配置与 7.6 一致的连接参数,并避免在代码中硬编码生产密钥。

参考资料