MySQL 入门:库、表、行、主键是什么

🥰个人主页:会编程的土豆(欢迎来访)

💎作者简介:后端学习者

❄️个人专栏:数据结构与算法数据库leetcode

那些你一个人走过的夜路,终将化作照亮未 来的光

系列:《影院票务 GO》知识点博客 · 第 6 篇

对应文件:sql/schema.sqlsql/seed.sql,以及 config/config.go 里的 DSN


写在前面

票务系统所有「记得住」的东西------用户账号、电影、场次、座位状态、订单------最后都落在 MySQL 里。

Go 程序重启后,内存 Session 会丢;数据库里的订单还在。

学长可能问:

「库和表啥区别?为什么每张表都有 id?DECIMAL 和 FLOAT 有啥不一样?」

本篇用最直白的方式建立数据库世界观,并对照本项目的 ttms 库。

不涉及复杂 SQL 语法细节(那是第 7 篇),先把名词和表结构 讲透,能和 models/models.go 对上号。


1. 四级概念:从服务器到单元格

复制代码
MySQL 服务器(进程,默认端口 3306)
  └── 数据库 Database(如 ttms)
        └── 表 Table(如 users、movies、orders)
              └── 行 Row(一条用户、一笔订单)
                    └── 列 Column(username、amount、status...)
概念 类比 项目例子
服务器 整栋图书馆大楼 你电脑上的 MySQL 8 服务
数据库 一个分馆 / 一个项目专属书架区 ttms
一种登记册 usersseatsorders
登记册里的一行 用户 admin 那一行
登记项 usernamepasswordrole

脚本开头(schema.sql):

复制代码
DROP DATABASE IF EXISTS ttms;
CREATE DATABASE ttms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE ttms;

含义:

  1. 若已有 ttms 库则删掉(开发脚本危险操作,生产绝不用)
  2. 创建新库,指定字符集
  3. USE ttms 进入该库,后面 CREATE TABLE 都建在这里

和 Go 的衔接:

config.Load() 默认 DSN 末尾是 /ttms?charset=utf8mb4&...------连的就是这个库名。


2. 为什么需要数据库?不用文件行吗?

可以把订单写进 orders.txt。小 demo 可以,票务系统不行,因为数据库擅长:

能力 文件方案的痛点
结构化查询 找「某用户所有待支付订单」要扫全文
并发安全 两人同时买同一座,文件很难原子更新
约束 用户名重复、外键引用靠自觉
持久与恢复 文件也能持久,但缺少事务、备份生态
索引 按时间查场次,表上有 INDEX

课程项目必须用 MySQL,也是为了练 事务、行锁、外键------后面第 16、17 篇的主线。


3. 本项目表关系鸟瞰

schema.sql 建的表(简化关系):

复制代码
users ─────┬──── orders ──── schedules ──── movies
           │                      │
           │                      └── halls
           ├── comments ──── movies
           ├── ratings  ──── movies
           │
movie_groups ── movies

schedules ── seats(每场次一批座位)
存什么
users 登录账号、角色
movie_groups 动作/科幻等分类
movies 电影元数据、评分汇总
halls 影厅名、行列数
schedules 哪部电影、哪个厅、几点、票价
seats 某场次下每个格子的状态
orders 订单号、座位摘要、金额、支付截止
comments 评论与回复(parent_id)
ratings 用户对电影 1--10 分

Go 里 models 包与这些表一一对应(第 5 篇)。


4. 主键 PRIMARY KEY

几乎每张表都有:

复制代码
id BIGINT PRIMARY KEY AUTO_INCREMENT
概念 含义
主键 唯一标识一行,不能重复、不能 NULL(惯例)
AUTO_INCREMENT 插入时不写 id,MySQL 自动分配 1, 2, 3...
BIGINT 64 位整数,id 空间大

为什么几乎都用 surrogate key(代理主键 id)?

  • 关联方便orders.user_idusers.id
  • 业务字段会变:昵称可改,username 理论上也可改,id 稳定
  • Join 性能:整数比长字符串做主键更常见

拓展:自然键?

usernameorder_no 当主键理论上可以,但 order_no 在本项目仍是 UNIQUE 业务键 ,主键仍是 id------两套并存(见思考题)。


5. 常见列类型(对照 schema)

5.1 字符串

复制代码
username VARCHAR(50)  NOT NULL UNIQUE
password CHAR(32)     NOT NULL
description TEXT
seat_info VARCHAR(255)
类型 特点 项目用途
VARCHAR(n) 可变长,最多 n 字符 用户名、标题
CHAR(n) 定长,不足补空格(存比较时) MD5 十六进制固定 32 位
TEXT 长文本 电影简介

5.2 数值

复制代码
role        TINYINT      NOT NULL DEFAULT 0
duration    INT          NOT NULL DEFAULT 120
amount      DECIMAL(10,2) NOT NULL DEFAULT 0
rating_avg  DECIMAL(3,1) NOT NULL DEFAULT 0.0
类型 用途
TINYINT 小整数、枚举状态(0/1/2)
INT 时长分钟、计数
DECIMAL(p,s) 精确小数,金额、评分

钱为什么用 DECIMAL 不用 FLOAT?

  • FLOAT/DOUBLE 是二进制浮点,0.1 + 0.2 在计算机里不精确
  • 票价 45.00、订单 90.00 必须精确到分
  • Go Scanfloat64 对课程够用;金融系统常用整数「分」或 decimal 库

rating_avg DECIMAL(3,1):最多 3 位数字、1 位小数,如 9.3、10.0(思考题:最大值?)

5.3 时间

复制代码
created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
start_time  DATETIME NOT NULL
pay_deadline DATETIME NOT NULL
paid_at     DATETIME NULL

Go DSN 里 parseTime=true&loc=Local 才能把 DATETIME 扫进 time.Time


6. 约束:数据库帮你守门

复制代码
username VARCHAR(50) NOT NULL UNIQUE
约束 含义
NOT NULL 不能空
UNIQUE 不能重复
DEFAULT 缺省值
PRIMARY KEY 主键唯一
FOREIGN KEY 引用父表必须存在

例:订单表

复制代码
CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT fk_order_sch  FOREIGN KEY (schedule_id) REFERENCES schedules(id),

插入 user_id=99999 若 users 里没有 → 拒绝,防止孤儿订单。

座位表:

复制代码
UNIQUE KEY uk_seat (schedule_id, row_no, col_no),

同一场次同一排座不能重复两行------防数据脏。

应用层也要校验,但约束是最后一道防线。


7. 索引 INDEX(入门)

复制代码
INDEX idx_sch_start (start_time),
INDEX idx_order_user (user_id),
INDEX idx_seat_lock (status, lock_until),

索引像「书的目录」:按用户查订单、按时间查场次、清理任务扫超时锁座,不必全表扫描。

代价: 插入/update 要维护索引;小项目数据量小感觉不明显,大数据量关键。


8. 引擎 InnoDB

复制代码
) ENGINE=InnoDB;

每张表末尾都有。

引擎 特点
InnoDB 事务、行级锁、外键------购票必备
MyISAM(老) 无事务,不适合订单锁座

本项目全部 InnoDB

CreateOrderWithSeatsBEGIN + FOR UPDATE 依赖 InnoDB 行锁(第 17 篇)。


9. 字符集 utf8mb4

复制代码
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
名称 说明
utf8mb4 完整 UTF-8,含中文、emoji(4 字节)
utf8(MySQL 旧名) 最多 3 字节,不完整

电影名、评论、昵称可能有 emoji;DSN 也要 charset=utf8mb4

乱码排查:客户端编码、表字符集、连接 charset 三者一致。


10. schema vs seed

文件 作用 本项目特点
schema.sql 建库建表(结构 开头 DROP DATABASE,会清空整个库
seed.sql 插入演示数据(内容 默认账号、电影、场次

典型开发流程:

复制代码
1. 改表结构 → 编辑 schema.sql → 在 MySQL 里执行
2. 需要演示数据 → 执行 seed.sql(依赖 schema 已建好)
3. 启动 Go → db.Init Ping ttms 成功

db.Init 失败时的提示:

复制代码
连接 MySQL 失败: ...(请先执行 sql/schema.sql 和 sql/seed.sql)

说明:库不存在、表不存在、MySQL 没启动都会 Ping 失败。

10.1 seed 里有什么

  • 管理员 admin/admin123、顾客 user/user123(密码 MD5+盐,第 13 篇讲)
  • 5 部电影、3 个影厅、若干未来场次
  • 执行 seed 后才有首页列表可点

11. 单表精读:users

复制代码
CREATE TABLE IF NOT EXISTS users (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    username    VARCHAR(50)  NOT NULL UNIQUE,
    password    CHAR(32)     NOT NULL COMMENT 'MD5(password+salt)',
    salt        VARCHAR(16)  NOT NULL,
    nickname    VARCHAR(50)  NOT NULL DEFAULT '',
    phone       VARCHAR(20)  NOT NULL DEFAULT '',
    role        TINYINT      NOT NULL DEFAULT 0,
    created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
对应 models.User
id ID int64
username Username
password Password(json:"-")
role 0/1 RoleCustomer / RoleAdmin

12. 单表精读:seats 与 orders

seats------购票并发核心:

复制代码
status TINYINT NOT NULL DEFAULT 0,  -- 0空闲 1锁定 2已售
lock_user_id BIGINT NULL,
lock_until   DATETIME NULL,
order_id     BIGINT NULL,

models.SeatSeatFree/Locked/Sold 常量一致。

orders

复制代码
order_no     VARCHAR(32)  NOT NULL UNIQUE,
seat_info    VARCHAR(255) NOT NULL DEFAULT '',
amount       DECIMAL(10,2) NOT NULL DEFAULT 0,
status       TINYINT      NOT NULL DEFAULT 0,
pay_deadline DATETIME     NOT NULL,

order_no 业务可见;id 内部关联 seats.order_id。


13. 和 Go 的三层连接(预告后续篇)

复制代码
Go 应用
  database/sql(标准库接口)
    go-sql-driver/mysql(驱动,db 包 _ 导入)
      MySQL 服务器 :3306
        数据库 ttms
组件 文件
DSN 字符串 config/config.go
sql.Open + Ping db/db.go
执行 SQL dao/*.go
行 → struct Scanmodels.*

本篇不讲 SELECT/INSERT 语法细节------那是 07 · SQL 增删改查

本篇不讲 连接池参数------那是 08 · database/sql


14. 拓展:客户端怎么执行脚本

方式任选:

复制代码
mysql -u root -p < sql/schema.sql
mysql -u root -p < sql/seed.sql

或在 Navicat / DBeaver / MySQL Workbench 里打开文件执行。

执行前确认连的是开发机,不是生产。


15. 常见误区

  1. 「数据库 = MySQL 软件」

    常混:MySQL 是服务器软件;ttms 是它里面的一个 database。

  2. 「没有 schema 也能 go run」

    db.Init Ping 会失败,main Fatal 退出。

  3. 「FLOAT 存票价省空间」

    省那点空间不值得丢精度。

  4. 「DROP DATABASE 和 DELETE 一样」

    DROP 整个库没了;DELETE 删行。schema 开头 DROP 是重置开发环境

  5. 「3306 和 8080 是一个服务」

    MySQL 3306;Go HTTP 8080。浏览器访问 3306 不对(第 1 篇思考题)。


16. 项目代码对照

概念 位置
建库建表 sql/schema.sql
演示数据 sql/seed.sql
连接串 config/config.goTTMS_DSN 环境变量
连接池 db/db.goInit
表 → struct models/models.go
读写行 dao/user.go, dao/order.go, ...

启动检查清单:

复制代码
□ MySQL 服务已启动
□ 已执行 schema.sql(有 ttms 库和表)
□ 已执行 seed.sql(有 admin/user 和电影)
□ config DSN 用户名密码与本地 MySQL 一致
□ go run . 看到「MySQL 连接成功」和 8080 启动日志

17. 小结口述稿

MySQL 用库、表、行、列组织数据;我们项目库名是 ttms。

主键 id 自增,用来稳定关联;业务号 order_no 另设 UNIQUE。

金额用 DECIMAL,状态用 TINYINT,和 Go 常量数值对应。

InnoDB 支持事务和行锁;utf8mb4 支持中文和 emoji。

schema 管结构(会 DROP 重建),seed 管演示数据;Go 通过 DSN 连 ttms。


18. 思考题

  1. 为什么 orders.order_no 也 UNIQUE,同时还要有 id 主键?
  2. rating_avg DECIMAL(3,1) 最大能表示多少?最小呢?
  3. 若把座位 status 存成字符串 "locked" 而不是 0/1/2,在 SQL 和 Go 里各有什么优缺点?
  4. DROP DATABASE IF EXISTS ttms 的危险是什么?生产环境能随便跑 schema 吗?
  5. DSN 里 parseTime=true 若去掉,Go 里 Scan 到 time.Time 会怎样?

19. 下一篇预告

《07 · SQL 增删改查与 dao 对应》 ------SELECT/INSERT/UPDATE/DELETE 在本项目里的真实写法,以及 QueryRowQueryExecScan 怎么配合

相关推荐
阿kun要赚马内1 小时前
DNS了解
网络协议
写代码的强哥1 小时前
云原生数据库与传统数据库相比“新”在哪里
数据库·云原生·架构
paopaokaka_luck2 小时前
基于Springboot3+Vue3的高校选课系统(AI选课助手、协同过滤算法、分享到微博、扣扣、Echarts图形化分析)
网络·spring boot·网络协议·echarts
△曉風殘月〆2 小时前
如何在Linux中安装Qt开发环境
linux·运维·qt
吴声子夜歌2 小时前
MongoDB 4.2——查询
数据库·mongodb
不会就选b3 小时前
Linux之文件--fd,重定向
linux
三十岁老牛再出发3 小时前
07.26每日总结
linux·c语言·mysql
BullSmall3 小时前
AFL++ HTTP Mode(网络 / HTTP 服务模糊测试)完整安装教程
网络·网络协议·http