目录

一、为什么需要数据库

1. 数据存储的发展

2. 什么是数据库

二、主流数据库

1. 关系型数据库

2. 非关系型数据库

3. 为什么选择 MySQL

三、MySQL 安装

1. Ubuntu 安装

2. 启动与管理

四、连接 MySQL

1. Client 与 Server 架构

2. 登录服务器

3. 常见管理命令

五、数据库与表

1. MySQL 层次结构

2. 数据模型

六、第一个案例

1. 创建数据库

2. 创建表

3. 插入数据

4. 查询数据

七、数据存储逻辑

1. 数据库本质

2. 数据库存储结构

3. 查找案例

总结


一、为什么需要数据库

用户的注册信息、电商系统的商品订单、社交平台的动态评论,这些海量的数据最终应该存放在哪里? 如果仅仅保存在服务器运行内存的变量或对象中,一旦程序重启、服务器断电,所有数据都将消失。因此,我们需要一种能够长久、安全、高效地管理数据的底层基础设施


1. 数据存储的发展

在计算机软件工程的发展演进史中,对于数据的持久化存储方案经历了三个关键的里程碑:

① 物理内存

在编写最简单的控制台程序时,我们使用变量、数组、链表来临时存放数据

  • 缺陷:内存属于易失性存储介质。一旦进程退出或服务器发生物理断电,内存中的数据立刻彻底丢失,无法满足互联网应用 "永久保存" 的基本需求

② 物理文件系统

为了让数据在断电后不丢失,早期程序员最直观的想法是:在程序关闭前,将内存中的数据序列化,然后调用操作系统的 I/O 接口,将其写入磁盘上的物理文件中

  • 缺陷:当数据体量极小、业务简单时,文件读写确实能跑通。但在面对工业级、高并发的互联网场景时,直接基于普通文件存储会带来以下瓶颈:

    • 查找效率低下:假设有一个 10GB 的文本文件存储着千万条用户记录。如果想要查询某一个特定用户,程序必须从磁盘逐行读取(即全表扫描)到内存中进行字符串比对。这种频繁的磁盘 I/O 操作会导致查询延迟达到难以接受的程度

    • 并发读写冲突:在 Web 并发场景下,可能会有成千上万个线程同时尝试修改同一个文件。由于普通文件缺乏并发控制机制,极其容易发生 "覆盖写" 或 "写半包" 导致文件损坏

    • 容错力极差:如果在文件写入的过程中服务器突然意外断电,文件往往会卡在损坏的中间状态,没有原生的机制能够让数据自动回滚或恢复

③ 数据库管理系统(Database Management System,简称 DBMS)

正是在文件系统的技术缺陷无法妥善解决的背景下,数据库管理系统应运而生。它在底层依然依托于操作系统的文件系统,但在上层封装了极其精妙的数据结构(如 B+ 树、红黑树、LSM 树)和并发控制算法,完美解决了高并发、大吞吐、强安全的数据管理痛点


2. 什么是数据库

① 宏观与微观

  • 数据库:从微观层面来看,它指的是长期存储在计算机内、有组织的、可共享的大量数据的集合。简单来说,就是物理磁盘上按照特定格式排列、存放数据的实际文件

  • 数据库管理系统:指的是位于用户与操作系统之间的一层大型数据管理软件。我们常说的 MySQL、Oracle、SQL Server,其本质都是 DBMS。它们就像是数据的 "管家",负责组织和存储数据,高效地获取和维护数据。用户不需要直接去操作磁盘上的二进制文件,而是向这个管家下达指令,由其代为执行高效率的磁盘读写

② 核心工程价值

引入数据库管理系统后,它为后端研发带来了以下四个优势:

  1. 高效率检索:数据库内部采用了高度优化的索引树。即便数据量达到千万级甚至亿级,通过索引,系统也能在几毫秒内精准取出目标数据

  2. 高并发与数据共享:DBMS 内部设计了非常复杂的行级锁、表级锁以及多版本并发控制,使得成千上万个网络线程能够安全地同时读写同一张表,且绝对不发生数据踩踏

  3. 数据独立性:将数据从具体的业务代码中剥离、解耦。同时,DBMS 提供了统一的安全检查、完整性约束(如禁止插入空值、禁止重复的主键)以及数据备份恢复机制

  4. 事务支持:这是数据库区别于普通文件最大的特性。它能够确保一系列关联的写操作 "要么全部成功,要么全部失败"

经典案例(银行转账):张三向李四转账 100 元,这在后端涉及两个核心动作——
动作一:张三的账户扣减 100 元;动作二:李四的账户增加 100 元

如果使用普通文件存储,假设动作一执行成功后,服务器突然断电导致程序崩溃,动作二未能执行。那么这 100 元就会蒸发,造成致命的账目错误

而在数据库的事务机制保护下,这两个动作被打包成了一个不可分割的整体。如果在中间任何一个节点发生断电或崩溃,数据库在重启后会自动回滚,将张三扣掉的 100 元自动退回。数据会退回到转账前的状态,从而确保了资金安全的绝对确定性

二、主流数据库

在当今的互联网技术栈中,数据库早已百花齐放。根据底层数据结构、存储行为等不同,工业界将数据库主要划分为两大类别:关系型数据库 与 非关系型数据库


1. 关系型数据库

关系型数据库是目前企业级开发中历史最悠久、应用最稳固的基石

  • 底层数学模型:基于经典的关系模型(二维表格模型)。在关系型数据库中,世界万物及其关联都可以被高度抽象为一张张由行和列组成的二维表

  • 核心特征

    • 强Schema约束:在向表中插入数据之前,必须严格定义好表的结构(哪些字段、什么数据类型、长度限制等)

    • 强一致性与ACID事务:它能绝对保证并发读写时数据的极度准确,非常适合处理资金、订单、用户账号等不容有失的核心业务

    • 标准SQL支持:统一使用结构化查询语言进行数据操作,不同的关系型数据库在 SQL 语法上高度相似,开发人员的学习和迁移成本极低

  • 代表产品MySQLOracle(闭源、昂贵、性能恐怖,多用于金融/金融/国企)、SQL Server(微软生态)、PostgreSQL(开源界另一大巨头,以功能强大、支持复杂特性著称)


2. 非关系型数据库

随着Web 2.0时代的到来,面对社交网络、高频点赞、海量日志等超高并发、数据结构极其多变的场景,传统的二维表格约束显得过于沉重,催生了非关系型数据库

NoSQL并不是要取代关系型数据库,而是作为其在特定高并发场景下的强力补充。它不采用二维表结构,根据存储形态不同,主要分为以下四大类别:

  • 键值存储数据库

    • 代表Redis

    • 特征:数据以 Key-Value 形式缓存在内存中,读写速度达到微秒级。常用于大并发下的缓存、分布式 Session 等

  • 文档型数据库

    • 代表MongoDB

    • 特征:数据无需固定 Schema,通常以类似 JSON 的形式存储。可以自由嵌套复杂的嵌套结构,非常适合电商商品属性频繁变动、社交动态、内容管理系统等

  • 列族存储数据库

    • 代表HBase、Cassandra

    • 特征:按列而非按行进行物理存储,适合进行海量历史数据(如千亿级用户点击流日志)的分布式离线大数据分析

  • 图形数据库

    • 代表产品Neo4j

    • 特征:专门用于处理复杂的社交人物关系网、知识图谱、风控反欺诈链路,以点和边来高效表达实体间的网状关联


3. 为什么选择 MySQL

在众多数据库产品中,MySQL 占据着举足轻重的地位。从初创公司到腾讯、阿里、美团等一线大厂,甚至 Facebook、Twitter 等国际巨头,MySQL 都是后端标配。我们选择 MySQL 作为入门与进阶的核心数据库,主要基于以下三大优势:

① 开源与商业平衡

MySQL 原生开源且免费,这使得早期的互联网创业公司能够以极低的硬件与软件采购成本搭建起高并发的服务架构。即便后来被相继收购,其开源社区版依然保持着强大的活力与免费特性

② 强大的性能与高扩展性

MySQL 演进出了极其成熟的生态。它不仅单机性能卓越,更在主从复制、读写分离、分库分表、分布式集群等高并发、大数据量架构上有着全套闭环的成熟工程落地方案

③ 生态与就业统治力

由于应用广泛,MySQL 沉淀了海量的文档资料和排障案例。掌握了 MySQL,便能轻松理解大部分关系型数据库

三、MySQL 安装

下面分别介绍在 Linux(Ubuntu)环境与 Windows 环境下的主流安装与管理方式


1. Ubuntu 安装

在 Ubuntu 系统上,我们可以直接利用自带的 apt 包管理器进行一键式自动化部署

① 更新与安装

打开终端,依次键入以下命令:

# 1. 更新本地的软件包索引
sudo apt update

# 2. 安装 MySQL 服务器端软件
sudo apt install mysql-server -y

② 验证安装状态

安装完成后,MySQL 服务通常会自动启动。可以通过以下命令查看其在操作系统底层的网络套接字监听状态

sudo systemctl status mysql

若看到状态显示为 active (running),则说明 MySQL 已经作为一个后台守护进程,开始在 3306 端口 等待网络连接


2. 启动与管理

在日常开发调试中,我们需要高频地对该服务的生命周期进行控制

① Linux (Ubuntu) 治理命令

在 Ubuntu 中,统一采用 systemctl 状态机指令进行调度:

sudo systemctl start mysql    # 启动 MySQL 服务
sudo systemctl stop mysql    # 停止 MySQL 服务
sudo systemctl restart mysql # 重启 MySQL 服务

② Windows 治理命令

在 Windows 环境下,可以按下 Win + R 键,输入 services.msc 打开图形化服务管理器,找到名为 MySQL80(或类似名称)的服务进行点击操作

如果喜欢命令行,必须以管理员身份打开 PowerShell/CMD,利用 net 或 sc 引擎进行驱动:

net start mysql80   :: 启动 Windows 下的 MySQL 服务
net stop mysql80   :: 停止 Windows 下的 MySQL 服务

至此,底层的数据库引擎已经在内存中平稳运行,等待着我们的网络切入与数据指令

四、连接 MySQL

安装好 MySQL 之后,接下来的第一步就是通过客户端连接到数据库服务器。MySQL 的设计本质上是一个高度网络化的软件,理解它的底层连接模型是后续所有数据操作的基石


1. Client 与 Server 架构

MySQL 是典型的 C/S 软件架构。它由两个完全独立、互不干扰的进程在操作系统底层各司其职:

① 服务端守护进程:mysqld

  • 本质:我们在系统服务中启动的 MySQL,在 Linux/Windows 底层的真实进程名字叫做 mysqld(其中的 d 代表 Daemon,即守护进程)

  • 职责:mysqld 才是真正的数据库本体。它长驻在服务器内存中,独占磁盘上的物理数据文件。唯一的工作就是通过网络套接字等待网络连接。它负责管理内存缓冲区、并发锁控制、读写磁盘文件以及执行复杂的 SQL 算法

  • 物理隔离:作为普通开发者,我们无法直接去阅读或修改 mysqld 占有的内存和物理文件。我们必须通过专门的通信协议,向其发送特定的指令

② 客户端进程:mysql 与图形化 Client

  • 本质:我们在终端里敲击的 mysql 命令,或者打开的 Navicat、 Workbench 等软件,其本质都是 Client 进程

  • 职责:客户端进程不负责数据的存储和计算。它的核心职责是建立网络切入通道。它负责接收输入的命令,将其按照网络协议进行封包,通过 TCP/IP 网络传给远端的 mysqld,随后等待服务端的响应回传,最后将结果渲染并打印在屏幕上

正是由于这种 C/S 隔离架构,后端的 mysqld 通常被部署在企业内网中安全的专用高性能服务器上。而研发人员可以在自己的电脑上运行各种 Client 工具,通过网络,同时向 mysqld 发起并发的数据吞吐与调度


2. 登录服务器

在安装配置完环境变量后,我们最常用的登录方式是通过原生自带的终端命令行客户端

登录命令

打开系统的终端,键入以下登录命令:

mysql -h 127.0.0.1 -P 3306 -u root -p

参数拆解

  • -h:(Host)指定要连接的 mysqld 服务端所在的 IP 地址。127.0.0.1 代表本地环回地址,即连接本台电脑上的数据库。如果要连接公司远程服务器,只需将其替换为公网或内网 IP

  • -P:(Port)指定 mysqld 监听的 端口号。如果你的数据库使用的是标准的默认端口,该参数可以省略不写

  • -u:(User)指定本次登录所使用的数据库用户名。root 是整个 MySQL 体系中至高无上的超级管理员账号

  • -p:(Password)该参数后面不要直接紧跟明文密码!如果在命令行里直接写明文密码(如 -p123456),该密码会被操作系统的历史日志物理记录,带来极大的安全隐患。标准的工程做法是直接敲 -p 然后回车,在下一行由操作系统提供掩码保护,盲输你的密码


3. 常见管理命令

一旦成功登录进 MySQL,你会发现终端的提示符变成了 mysql>。此时,你就已经成功通过了网络验证,进入了与 mysqld 的交互状态

在正式编写 SQL 操作业务数据之前,我们需要掌握几个用于查个数据库系统全局的常用管理命令(注意:MySQL 中的每条管理命令和 SQL 语句的末尾,必须以英文分号作为闭环结束符

① 查看数据库

SHOW DATABASES;

该命令会指示 mysqld 将当前权限对你可见的所有数据库列举出来

② 切入某个指定的数据库

USE test_db;

如果你想查看或操作某个特定数据库,必须先使用 USE 命令。该命令比较特殊,它的本质是切换客户端当前会话的上下文,因此它是少数几个在末尾不加分号也能被识别执行的特权命令

③ 查看当前数据库下的表

SHOW TABLES;

在成功执行 USE 命令切入特定数据库后,运行该命令可以看清该库内部托管的所有二维数据表

当前表为空

④ 退出当前连接

EXIT; 
-- 或者键入 QUIT; 也可以直接使用快捷键 Ctrl + D

该命令会通知客户端关闭当前与服务端的 TCP 网络套接字,退出交互界面

五、数据库与表

MySQL 并不是把数据一脑股地塞进磁盘文件里,而是像操作系统的文件系统一样,有着非常严密且清晰的层次解耦结构


1. MySQL 层次结构

MySQL 的数据逻辑存储架构由上至下可以精准地划分为四个层级:

  • 数据库服务器实例:即我们在后台运行的 mysqld 守护进程

    • 职责:管理磁盘空间、并发网络连接,并同时托管其下属的所有逻辑数据库

  • 逻辑数据库:承上启下的命名空间

    • 职责:为了让不同的业务互不干扰,我们在一个 MySQL 实例内可以创建多个独立的数据库。例如,一个公司的 MySQL 服务器里,可以同时存在 shop_db(电商系统库)、oa_db(办公协同库)等。各个逻辑数据库之间在逻辑上完全隔离

  • 数据表:业务核心实体

    • 职责:数据库内部并不能直接存放零散的数据,它必须以数据表作为最小的组织单元。表就是标准的二维结构,专门用来描述现实世界中的某一类特定实体(例如:user_table 用户表、order_table 订单表)

  • 记录与字段:最底层的原子数据

    • 职责:这是二维表的最基本构成要素。字段(列)定义了该实体拥有的属性边界,而记录(行)则是对这些属性在实际场景中的具体呈现


2. 数据模型

业务关联实例:电商用户表(users)

为了将上述层次彻底具象化,我们可以观察下面这张标准的工业二维表:

id username age created_at
1 张三 25 2026-06-01 12:00:00
2 李四 30 2026-06-02 15:30:22
3 王五 22 2026-06-04 09:15:00
  • 纵向观察:整个系统有 4 个字段(列)。其中 username 这一列限定了只能填入字符串,且它代表了用户的名字属性

  • 横向观察:当前表里存在 3 条记录(行)。第一行数据联合表达了 "1号用户张三,今年25岁,注册于6月1日" 这一条真实存在的独立业务上下文

六、第一个案例

在理清了数据库的层级结构后,本节我们将通过 SQL 脚本,亲手在 MySQL 中走完一个资源的完整生命周期:建库 -> 建表-> 插入数据 -> 查询数据


1. 创建数据库

① 创建数据库

我们创建一个名为 test_db 的数据库。为了防止由于误操作导致重复创建而报错,我们通常在工程中加上 IF NOT EXISTS 防御性子句:

CREATE DATABASE IF NOT EXISTS test_db CHARACTER SET utf8mb4;

CHARACTER SET utf8mb4 是现代 Web 开发的硬性标准。传统的 utf8 在 MySQL 中只能存储最大 3 字节的字符,无法支持 Emoji 表情和部分罕见汉字。而 utf8mb4 是完全体的 UTF-8 实现(最大 4 字节),能规避线上因用户昵称包含特殊符号而引发的数据库写入崩溃

创建成功后,我们切入到这个数据库:

USE test_db;

2. 创建表

现在,我们在库中创建一张用于存储学生信息的业务表 students。为了方便理解,我们只定义最常用的三个字段:

  • id:学生的编号,使用整数类型(INT)

  • name:学生的姓名,使用字符串类型(VARCHAR)

  • age:学生的年龄,使用整数类型(INT)

CREATE TABLE students (
    id INT,
    name VARCHAR(20),
    age INT
);

3. 插入数据

表结构创建完毕后,我们使用 INSERT 向其中填入两条真实的数据记录:

-- 插入第一条学生记录:编号为 1,名字叫张三,年龄 20 岁
INSERT INTO students VALUES (1, '张三', 20);

-- 插入第二条学生记录:编号为 2,名字叫李四,年龄 22 岁
INSERT INTO students VALUES (2, '李四', 22);

每执行完一条,终端就会回显 Query OK, 1 row affected。此时,这两条数据就已经通过网络安全地写入到了磁盘文件中


4. 查询数据

最后,我们把刚刚持久化的所有数据一次性捞出来展示:

SELECT * FROM students;

在 SQL 语言中,星号 * 是一个通配符,代表 "这张表里的所有字段(所有列)"

七、数据存储逻辑

完成了第一个 SQL 案例后,你可能会产生一个疑问:我们刚刚创建的 test_db 数据库和 students 表,在计算机的底层究竟是以怎样的形式存在的?


1. 数据库本质

任何打着持久化旗号的软件,其底层必然是操作系统的物理文件系统。MySQL 也不例外

当我们在客户端创建数据库时,mysqld 在底层实际上是通过系统调用,在其数据存储目录下创建了一个名为 test_db 的真实文件夹

当我们在该数据库下创建 students 表时,mysqld 就会在这个文件夹内部创建对应的物理磁盘文件。不同的存储引擎,对应的物理文件后缀和内部结构会有所不同

在 Linux 系统中,MySQL 的默认数据目录通常位于 /var/lib/mysql/。当你切入该目录后执行 ls,你会发现:

  • 每一个你创建的数据库,在这里都对应着一个同名的真实文件夹

  • 文件夹内部,则躺着代表各个数据表的物理文件

这意味着,数据库是一套由 mysqld 统一上锁、高度结构化、专门格式化的物理文件集合


2. 数据库存储结构

既然底层是文件,那为什么它能做到千万级数据毫秒级检索,而我们自己读写文本文件却慢如牛车呢?这是因为 mysqld 在物理文件内部划分了极其严密的块级存储结构

从宏观到微观,MySQL(以最主流的 InnoDB 引擎为例)将数据文件划分为以下四个阶梯级的数据结构:

① 表空间(Tablespace)

表空间是一个最高级别的物理/逻辑组合概念。一个数据库通常由一个或多个表空间文件组成。你可以把它理解为一个巨大的 "虚拟集装箱",整张表的所有数据、索引、日志都存放在这个集装箱内

② 段(Segment)

表空间内部被进一步划分为不同的段。常见的有数据段、索引段等。段是数据库为了管理不同类型的数据而划分的逻辑边界

③ 区(Extent)

  • 底层机制:区是 MySQL 向操作系统申请磁盘物理空间的最小单位。在默认情况下,一个区的大小被硬性固定为 1MB(连续紧凑地排列着 64 个页)

  • 为什么需要区?:如果数据库每次插入都向操作系统申请几个 KB 的磁盘空间,会导致大量的物理磁盘碎片,使得磁头高频寻道或者频繁擦写,极大地拖慢性能。因此,MySQL 采取 "空间预分配" 策略:一旦发现底层的物理文件不够用了,它会直接以区(1MB)为单位,向操作系统一口气批发一大块连续的磁盘空间

④ 页(Page)

  • 底层机制:页是 MySQL 存储引擎与磁盘之间执行 I/O 读写的最小物理单位。在默认情况下,一个页的大小被硬性固定为 16KB

  • 运作机制:当我们插入了 "张三"、"李四" 等记录时,这些行数据其实就一条挨着一条地紧密排布在某个 16KB 的数据页内部

  • 为什么需要页?:假设你想修改表里一条仅占 10 字节的学生记录,MySQL 绝对不会 只去磁盘物理读写这 10 个字节。因为高频读写微小字节会带来灾难性的网络与磁盘 I/O 损耗。MySQL 的策略是 "按箱搬运":它会以 16KB 组成的整页为单元,把这一页数据一股脑从磁盘加载到内存中,在内存中把那 10 个字节改完后,再在将整个 16KB 的页写回磁盘

区(1MB) 解决的是 "空间怎么申请" 的问题——为了减少碎片,MySQL 找操作系统要空间时,一次要 1MB(一个区)

页(16KB) 解决的是 "数据怎么读写" 的问题——为了提高 I/O 效率,存储引擎在内存和磁盘之间搬运业务数据时,一次只搬 16KB(一个页)


3. 查找案例

假设随着学校业务的扩大,students 表里已经插入了成千上万条记录。这些记录在磁盘的物理文件内部,被整密地切分到了编号为 Page 1、Page 2、Page 3…… 的多个 16KB 数据页中

此时,我们使用查询指令希望找出某条特定的学生记录

底层 I/O 与内存的四步流转

  1. 定位:MySQL 的核心引擎接收到查询请求。它首先通过内部的索引结构快速进行计算:目标学生所在的行记录,被物理存放在物理磁盘的 Page 3 这一页上

  2. 整页载入:MySQL 的文件 I/O 模块如前文所述,它绝对不会只去磁盘上取这几十个字节的单条记录。mysqld 会直接将 Page 3 对应的一整块 16KB 的数据页完完整整地从磁盘硬件中捞出来,加载到服务器的物理内存缓冲区中

  3. 内存页内检索:数据页被加载到内存后,真正的查找才开始。每一个 16KB 的数据页内部,其实除了紧密排列的行记录外,还有一个叫页目录的核心结构。它就像是书本的索引一样

    • 引擎通过 "二分查找法" 在页目录中进行近乎零延迟的指针定位

    • 瞬间锁定目标行记录在 Page 3 内部的具体内存偏移量

  4. 解析并回传:定位成功后,引擎将该行记录的二进制数据(2, '李四', 22)按字段解码转换为可读的文本,通过网络套接字响应给客户端。客户端接收到后,便在你的终端屏幕上画出了表格

为什么在数据量极大的时候,盲目执行没有索引的模糊查询会把服务器卡死

如果 MySQL 无法提前锁定目标在哪一个页上,它就必须被迫把磁盘文件里成千上万个 16KB 的数据页全部依次加载到内存中(即全表扫描)。这种操作会引发大量磁盘物理读写,瞬间耗尽服务器的 I/O 带宽,进而导致业务出现大范围延迟

总结

综上所述,从数据存储的发展历程,到数据库的诞生,再到 MySQL 的安装、使用以及数据库与表之间的层次关系,我们已经完成了对 MySQL 的初步认识

通过创建数据库、创建数据表、插入数据以及查询数据等操作,可以发现 MySQL 本质上是在帮助我们更加高效地组织、管理和检索海量数据。相比直接使用文件存储数据,数据库不仅提供了统一的数据管理能力,也极大降低了数据维护和查询的复杂度

与此同时,我们还从文件系统的角度简单分析了 MySQL 中数据的存储逻辑。虽然数据库对外表现为数据库、表、记录等概念,但归根结底,数据最终仍然需要落盘保存到磁盘文件之中

至此,我们已经掌握了 MySQL 的基本使用方法。但一个新的问题也随之而来:

MySQL究竟是如何管理这些数据的?数据库、SQL语句、存储引擎之间又是什么关系?

在下一篇中,我们将进一步深入 MySQL 内部,学习 MySQL 整体架构、SQL 分类以及存储引擎机制,并重点认识目前最主流的 InnoDB 存储引擎,为后续学习索引、事务以及数据库底层原理打下基础

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐