5329 字
27 分钟
数据库:数据库系统架构

Web 应用很少是完全独立运行的。用户请求经过 Web 服务器和应用程序后,通常还需要读取或修改数据库中的数据。

因此,理解数据库系统不能只停留在“会写 SQL”,还需要知道 DBMS、数据库、表、连接、事务、索引以及关系型与 NoSQL 数据库之间的关系。

本文从数据库系统的整体架构开始,介绍 DBMS、关系型数据库与 NoSQL、客户端与数据库连接、SQL、事务、ACID、并发控制、索引以及 Web 应用中的数据库访问流程,建立对数据库系统的整体认知。

数据库系统是什么#

数据库系统(Database System)可以理解为:

用于组织、存储、管理和访问数据的一整套系统。

一个完整的数据库系统通常不仅包括:

数据

还包括:

数据库管理系统
客户端 / 应用程序
数据库
用户

可以简单表示为:

用户
│
▼
Web Application
│
▼
DBMS
│
▼
Database
│
▼
数据

例如一个 Web 网站可能使用:

浏览器
↓
Nginx
↓
Java / Python 应用
↓
MySQL / PostgreSQL

数据库位于整个 Web 服务体系的后端。


DBMS#

什么是 DBMS#

DBMS(Database Management System,数据库管理系统)是:

负责创建、管理、访问和维护数据库的软件系统。

例如:

MySQL
PostgreSQL
MariaDB
Oracle Database
Microsoft SQL Server
MongoDB
Redis

都属于数据库系统中的具体产品,但它们的模型和能力并不完全相同。

可以把 DBMS 理解成:

Application
│
│ SQL / API
▼
DBMS
│
├── 连接管理
├── 查询解析
├── 查询执行
├── 事务管理
├── 并发控制
├── 权限控制
├── 缓存
├── 日志
└── 存储管理
│
▼
Database

因此:

数据库是数据本身的组织与存储,而 DBMS 是管理这些数据的软件。


DBMS 与 Database 的区别#

这两个概念非常容易混淆。

可以简单理解成:

Database
↓
数据以及数据组织结构
DBMS
↓
管理 Database 的软件

例如:

MySQL
↓
DBMS
my_app
↓
MySQL 中的一个 Database

可以进一步理解成:

MySQL Server
│
├── Database A
│
├── Database B
└── Database C

不同 DBMS 对:

Database
Schema
Table
Namespace

等概念的组织方式可能有所不同,因此不能把一种数据库产品的层级结构机械套用到所有数据库上。


数据库系统的基本架构#

一个典型 Web 系统可以表示为:

Client
│
▼
Nginx / Apache
│
▼
Web Application
│
Database Connection
│
▼
DBMS
│
▼
Database
│
▼
Data

例如:

Browser
│
│ HTTP
▼
Nginx
│
│ Proxy
▼
Java Application
│
│ JDBC
▼
MySQL

其中:

HTTP
↓
Web 层通信
JDBC
↓
Java 应用访问数据库的接口
MySQL
↓
数据库管理系统

所以数据库并不是直接面对浏览器的。


数据模型#

数据库首先需要解决:

数据应该以什么方式组织?

不同数据库采用不同的数据模型。

常见的有:

关系模型
键值模型
文档模型
列族模型
图模型

在 Web 开发中,最常见的两大类可以先理解成:

关系型数据库
+
NoSQL 数据库

关系型数据库#

什么是关系型数据库#

关系型数据库(Relational Database)以:

关系(Relation)

为核心组织数据。

实际使用中通常表现为:

Database
│
├── Table
│ ├── Row
│ └── Column
│
└── Table

例如用户表:

idnameage
1Alice20
2Bob21

可以理解成:

Table
│
├── Column
│ ├── id
│ ├── name
│ └── age
│
└── Row
├── 1
├── Alice
└── 20

Table、Row 与 Column#

关系型数据库最基础的三个概念:

Table
Row
Column

可以简单理解:

Table
↓
一类数据
Row
↓
一条记录
Column
↓
某种属性

例如:

users

是一张用户表。

其中:

id
name
email

是列。

1
Alice
alice@example.com

是一行记录。


Primary Key#

关系型数据库中的一张表通常需要一种能够唯一标识记录的字段:

Primary Key(主键)

例如:

CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100)
);

这里:

id
↓
Primary Key

主键的核心作用是:

唯一标识表中的一条记录。

例如:

id = 1

只能对应一个用户。


Foreign Key#

不同表之间通常存在关系。

例如:

users
orders

一个用户可以拥有多个订单。

可以设计成:

users
┌────┐
│ id │
└─┬──┘
│
│ 1
│
│ N
▼
orders
┌─────────┐
│ user_id │
└─────────┘

这里:

orders.user_id

可以作为:

Foreign Key(外键)

引用:

users.id

从而表达:

用户
↓
订单

之间的关系。


SQL#

关系型数据库通常使用:

SQL(Structured Query Language)

进行数据定义、查询和修改。

常见操作:

SELECT
INSERT
UPDATE
DELETE
CREATE
ALTER
DROP

SELECT#

查询:

SELECT * FROM users;

表示:

查询 users 表中的记录

也可以只查询部分列:

SELECT id, name
FROM users;

INSERT#

插入:

INSERT INTO users (id, name)
VALUES (1, 'Alice');

UPDATE#

修改:

UPDATE users
SET name = 'Bob'
WHERE id = 1;

DELETE#

删除:

DELETE FROM users
WHERE id = 1;

需要特别注意:

不带正确条件的 UPDATE / DELETE 可能影响大量记录。

例如:

DELETE FROM users;

就是删除表中全部记录。

因此生产环境执行数据库修改操作时必须谨慎。


NoSQL#

什么是 NoSQL#

NoSQL 通常泛指:

不以传统关系模型为核心的数据存储系统。

NoSQL 并不是:

“完全不要 SQL”

而更接近:

Not Only SQL

现代 NoSQL 数据库包含很多不同的数据模型。

例如:

Key-Value
Document
Column Family
Graph

Key-Value#

键值数据库:

Key
↓
Value

例如:

user:1001
↓
Alice

典型数据库:

Redis

非常适合:

缓存
Session
计数器
分布式锁
排行榜

等场景。


Document Database#

文档数据库使用类似:

{
"id": 1,
"name": "Alice",
"age": 20
}

这样的文档组织数据。

典型数据库:

MongoDB

它适合某些:

结构灵活
文档型数据
Schema 变化较快

的场景。


Column Family#

列族数据库常用于:

大规模分布式数据
高吞吐写入

典型系统包括:

Cassandra
HBase

其数据模型与传统关系型数据库不同。


Graph Database#

图数据库重点描述:

Node
+
Edge

例如:

Alice
│
│ 好友
▼
Bob
│
│ 好友
▼
Carol

典型场景:

社交关系
知识图谱
推荐关系
网络拓扑

关系型与 NoSQL 的区别#

可以从数据模型角度简单对比:

对比关系型数据库NoSQL
核心模型表 / 关系多种模型
Schema通常较明确通常更灵活
查询方式SQL各产品 API / 查询语言
事务通常支持成熟 ACID取决于具体产品
数据关系擅长结构化关系取决于模型
扩展方式常见纵向扩展,也支持分布式方案很多产品强调水平扩展
典型产品MySQL / PostgreSQLRedis / MongoDB / Cassandra

需要注意:

不能把“关系型 = 单机、NoSQL = 分布式”简单画等号。

现代关系型数据库同样可以运行在:

主从架构
集群
分片
云数据库

环境中。

而 NoSQL 数据库之间的设计差异也非常大。

因此数据库选型应该根据:

数据模型
事务要求
查询方式
一致性要求
性能
扩展需求
运维能力

综合判断。


数据库连接#

应用为什么需要连接数据库#

Web 应用要访问数据库,首先需要:

建立数据库连接。

例如:

Java Application
│
│ JDBC
▼
Database

连接通常需要:

Host
Port
Database
Username
Password

例如:

Host = 127.0.0.1
Port = 3306
Database = app
User = app_user

Database Connection#

一个数据库连接可以简单理解成:

Application
│
│ Connect
▼
DBMS
│
▼
Database

连接建立后:

Application
│
├── SQL
│
├── Result
│
└── Transaction
│
▼
DBMS

为什么需要连接池#

如果每个 HTTP 请求都:

创建数据库连接
↓
执行 SQL
↓
关闭连接

那么大量请求会产生:

连接建立开销
认证开销
Socket 开销
数据库资源消耗

例如:

1000 Requests
│
├── 建立连接
├── 建立连接
├── 建立连接
└── ...

因此 Web 应用通常使用:

Connection Pool(连接池)


Connection Pool#

连接池提前创建并维护一些数据库连接:

Connection Pool
┌────┬────┬────┐
Application ──►│ C1 │ C2 │ C3 │
├────┼────┼────┤
│ C4 │ C5 │ C6 │
└────┴────┴────┘
│
▼
DBMS

请求到来:

HTTP Request
│
▼
Application
│
▼
从连接池获取连接
│
▼
执行 SQL
│
▼
归还连接

而不是:

每次重新建立连接

连接池中的重要问题#

连接池并不是越大越好。

例如:

Connection Pool = 1000

并不意味着:

性能一定更好

因为数据库本身也存在:

CPU
内存
锁
连接数
磁盘 I/O

等资源限制。

如果连接池过大:

Application
│
├── 100 connections
├── 100 connections
└── ...
▼
Database
│
▼
资源耗尽

反而可能导致数据库压力增加。

因此:

连接池大小需要结合应用并发量、SQL 耗时以及数据库能力进行配置。


事务#

什么是 Transaction#

事务(Transaction)可以理解为:

一组需要作为一个逻辑整体执行的数据库操作。

例如银行转账:

A -100
B +100

不能只执行一半。

应该:

开始事务
│
▼
A -100
│
▼
B +100
│
▼
COMMIT

如果中间失败:

ROLLBACK

恢复之前的状态。


BEGIN、COMMIT 与 ROLLBACK#

事务可以简单理解为:

BEGIN
│
▼
SQL 1
│
▼
SQL 2
│
▼
SQL 3
│
├── 成功 → COMMIT
│
└── 失败 → ROLLBACK

例如:

START TRANSACTION;
UPDATE accounts
SET balance = balance - 100
WHERE id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE id = 2;
COMMIT;

如果中间发现错误,可以:

ROLLBACK;

ACID#

数据库事务经常使用:

ACID

描述事务的重要特性。

A → Atomicity
C → Consistency
I → Isolation
D → Durability

Atomicity#

原子性:

一个事务中的操作要么全部成功,要么整体回滚。

例如:

A -100
B +100

不能只完成:

A -100

而:

B +100

失败后仍提交。


Consistency#

一致性:

事务执行前后,数据库应该满足定义好的完整性约束和业务规则。

例如:

账户余额
主键唯一
外键约束

等。

需要注意:

ACID 中的 Consistency 不只是“所有查询马上看到一样的数据”,而是事务需要把数据库从一个合法状态带到另一个合法状态。


Isolation#

隔离性:

并发执行的事务之间应该按照数据库定义的隔离规则互相影响。

例如:

Transaction A
│
├── 修改数据
│
Transaction B
│
└── 同时访问数据

数据库需要控制:

并发读写

带来的问题。


Durability#

持久性:

事务提交成功后,其结果应该具有持久保存的保证。

例如:

COMMIT
↓
数据库系统发生重启
↓
已提交的数据仍然应该被保留

具体保障机制涉及:

WAL
Redo Log
Flush
Storage

等底层机制。


并发事务#

现实中的数据库很少只有一个事务。

例如:

User A
│
▼
Transaction A
User B
│
▼
Transaction B

两个事务可能同时操作相同数据。

因此数据库需要:

Concurrency Control(并发控制)


常见并发问题#

Dirty Read#

事务 A 修改了数据:

A
↓
修改

事务 B 读取到这个尚未提交的数据。

如果 A 最后:

ROLLBACK

那么 B 读到的数据就不存在于最终状态中。


Non-Repeatable Read#

事务 A 两次读取同一条记录:

第一次
↓
100
第二次
↓
200

中间被其他事务修改并提交。

于是同一个事务中:

同一查询

前后得到不同结果。


Phantom Read#

第一次查询:

SELECT ...

得到:

10 行

之后另一个事务插入满足条件的新记录。

再次执行:

SELECT ...

得到:

11 行

新增的记录就像:

“突然出现的幻影”

因此称为:

Phantom Read


事务隔离级别#

SQL 标准定义了多个事务隔离级别,常见名称为:

READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

可以粗略理解成:

隔离性增强
↑
│
READ UNCOMMITTED
│
READ COMMITTED
│
REPEATABLE READ
│
SERIALIZABLE

一般来说:

隔离程度越高,并发事务之间的可见性限制越严格,但并发性能和锁 / 等待代价可能也越高。

不同数据库的具体实现并不完全一样。

例如:

MySQL
PostgreSQL
Oracle
SQL Server

对隔离级别的实现细节存在差异,因此实际使用时应该参考具体 DBMS 的文档。


索引#

什么是 Index#

数据库中的索引可以理解成:

为了加快数据查找而建立的额外数据结构。

如果一张表有:

10000000

条记录。

查询:

SELECT *
FROM users
WHERE email = 'alice@example.com';

如果没有合适索引,数据库可能需要:

逐行检查
↓
Row 1
Row 2
Row 3
...
Row 10000000

这种方式通常称为:

Full Table Scan(全表扫描)


有索引时#

假设:

email

有索引。

数据库可以:

查询条件
↓
Index
↓
定位记录
↓
读取数据

可以理解成:

Table
│
├── id
├── name
└── email
Index(email)
│
▼
目标记录

B+Tree 索引#

关系型数据库中非常常见的一类索引结构是:

B-Tree / B+Tree 家族

它适合:

等值查询
范围查询
排序

例如:

SELECT *
FROM users
WHERE id = 100;

或者:

SELECT *
FROM users
WHERE age BETWEEN 20 AND 30;

B+Tree 可以通过树结构减少需要访问的数据范围。

可以简化理解成:

Root
/ \
Node Node
/ \ / \
... ... ... ...
│
▼
Target

实际数据库实现远比这个模型复杂,但理解:

索引
↓
缩小搜索范围

就足够建立第一层认识。


Hash Index#

另一类常见思路是:

Hash Index

可以通过:

Hash(Key)

快速定位。

例如:

email
↓
Hash
↓
Bucket

它适合某些:

等值查询

但不适合传统意义上的:

范围查询

因此:

WHERE id = 100

和:

WHERE id BETWEEN 100 AND 200

对索引结构的需求并不完全一样。


为什么不是索引越多越好#

索引能够加快查询,但也有成本。

例如:

INSERT
UPDATE
DELETE

发生时,相关索引也需要维护。

因此:

索引增加
↓
查询可能更快
↓
写入成本增加
↓
占用更多存储

所以:

索引设计本质上是在查询性能与写入 / 存储成本之间进行权衡。


组合索引#

数据库还经常使用:

Composite Index(联合索引)

例如:

CREATE INDEX idx_user
ON users (name, age);

表示索引包含:

name
age

两个字段。

联合索引涉及重要的:

最左前缀原则

例如索引:

(name, age)

通常更适合:

WHERE name = 'Alice';

以及:

WHERE name = 'Alice'
AND age = 20;

而仅仅:

WHERE age = 20;

通常不能充分利用这个联合索引的前导部分。

具体行为仍取决于 DBMS 的优化器和索引实现。


索引与执行计划#

数据库并不会因为:

“存在索引”

就一定使用索引。

数据库通常会通过:

Query Optimizer(查询优化器)

分析查询。

例如:

SQL
↓
Parser
↓
Optimizer
↓
Execution Plan
↓
Executor

可能最终选择:

Index Scan

也可能选择:

Seq Scan

或者其他执行方式。

因此:

判断索引是否真正有效,需要观察执行计划,而不是只看数据库里有没有建立索引。

例如 PostgreSQL:

EXPLAIN
SELECT *
FROM users
WHERE email = 'alice@example.com';

MySQL 则可以:

EXPLAIN
SELECT *
FROM users
WHERE email = 'alice@example.com';

数据库查询执行#

一条 SQL 并不是直接:

SQL
↓
Disk

而是经过多个步骤。

可以简化为:

SQL
│
▼
Parser
│
▼
Query Analysis
│
▼
Optimizer
│
▼
Execution Plan
│
▼
Executor
│
▼
Storage Engine / Data
│
▼
Result

例如:

SELECT *
FROM users
WHERE id = 100;

数据库可能:

解析 SQL
↓
识别 users 表
↓
识别 id 条件
↓
检查可用索引
↓
生成执行计划
↓
读取索引 / 数据
↓
返回结果

Buffer Cache#

数据库并不会每次都直接访问磁盘。

通常会在内存中维护大量缓存:

Application
│
▼
DBMS
│
▼
Buffer / Page Cache
│
├── Hit → 直接使用
│
└── Miss
│
▼
Disk

因此数据库性能通常不仅取决于:

CPU

还取决于:

Memory
Disk
IOPS
Latency
Cache Hit

等因素。

这也是数据库性能分析比单纯查看 SQL 更复杂的原因之一。


数据持久化与日志#

数据库需要解决一个非常重要的问题:

系统突然崩溃时,已经提交的数据怎么办?

因此现代数据库通常会使用某种日志机制。

例如 PostgreSQL 中的重要机制:

WAL

即:

Write-Ahead Logging

核心思想可以简单理解为:

先记录恢复所需的日志
↓
再认为相关数据修改可以安全持久化

系统崩溃后,可以根据日志进行恢复。

因此:

Transaction
↓
Log
↓
Storage

是数据库持久化机制中的重要思想。


数据库备份#

需要注意:

事务持久性不等于备份。

即使数据库具有:

ACID
WAL
Crash Recovery

也不能解决:

误删
逻辑损坏
勒索
错误 UPDATE
错误 DROP

等问题。

因此生产环境仍然需要:

数据库备份
+
恢复测试

完整的数据库运维还需要进一步考虑:

全量备份
增量备份
日志备份
恢复
RPO
RTO

Web 应用访问数据库#

将前面的概念串起来,一个典型 Web 请求可能是:

Browser
│
│ HTTP
▼
Nginx
│
▼
Web Application
│
│ Connection Pool
▼
DBMS
│
│ SQL
▼
Database

例如用户访问:

GET /users/100

应用可能执行:

SELECT *
FROM users
WHERE id = 100;

数据库处理:

SQL
↓
Parser
↓
Optimizer
↓
Index
↓
Data
↓
Result

然后:

Database
│
▼
Web Application
│
▼
HTTP Response
│
▼
Browser

数据库连接与事务#

实际 Web 应用中,数据库访问通常还会包含:

HTTP Request
│
▼
Application
│
▼
Get Connection
│
▼
Begin Transaction
│
├── SQL 1
├── SQL 2
└── SQL 3
│
▼
Commit / Rollback
│
▼
Return Connection
│
▼
HTTP Response

因此:

HTTP
↓
Application
↓
Connection Pool
↓
Transaction
↓
SQL
↓
Index
↓
Database

实际上是一整条完整链路。


数据库常见性能问题#

数据库出现性能问题时,通常不能只看:

CPU

还应该考虑:

慢 SQL
索引缺失
索引失效
锁等待
事务过长
连接池耗尽
磁盘 I/O
缓存命中率
数据量增长

例如:

请求变慢
│
▼
Application
│
▼
等待数据库
│
▼
慢 SQL
│
▼
全表扫描
│
▼
Disk I/O 增加

这种情况下:

Web Server

本身可能完全正常。

真正的瓶颈在:

Database

数据库故障排查思路#

可以形成一个基本流程:

Web 请求变慢
│
▼
Application
│
▼
数据库连接是否正常?
│
▼
连接池是否耗尽?
│
▼
SQL 是否执行?
│
▼
SQL 是否很慢?
│
▼
EXPLAIN / Execution Plan
│
▼
是否使用合理索引?
│
▼
是否存在锁等待?
│
▼
数据库 CPU / Memory / I/O
│
▼
进一步定位

这也是为什么数据库运维不能只停留在:

“会写 SELECT”

而需要同时理解:

连接
事务
锁
索引
执行计划
资源

关系型数据库与 Web 服务#

在传统 Web 系统中,一个非常典型的架构是:

Browser
│
▼
Nginx
│
▼
Application
│
┌──────┴──────┐
│ │
▼ ▼
Connection Pool Cache
│
▼
DBMS
│
┌───────┴───────┐
│ │
▼ ▼
Table Index
│
▼
Data

其中:

Nginx
↓
处理 HTTP / 代理
Application
↓
处理业务逻辑
Database
↓
持久化结构化数据
Cache
↓
减少部分数据库访问

这就是常见的:

Web + Application + Database

基本架构。


NoSQL 在 Web 系统中的位置#

NoSQL 数据库也经常出现在 Web 系统中。

例如:

Application
│
├── MySQL
│ ↓
│ 业务主数据
│
└── Redis
↓
Cache

典型组合:

MySQL / PostgreSQL
↓
核心业务数据
Redis
↓
缓存 / Session / Counter
MongoDB
↓
文档型数据

因此:

关系型数据库和 NoSQL 并不是一定互相替代。

实际系统中经常是:

Polyglot Persistence

即根据不同数据和访问场景选择不同的数据存储系统。


数据库系统整体模型#

到这里,可以把数据库系统完整串起来:

Database System
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
DBMS Data Model Client
│ │ │
┌─────┴─────┐ ┌────┼────┐ │
│ │ │ │ │
Relational NoSQL Table Document App
│ │ │ │ │
│ ┌───┼───┐ │ │ │
│ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼
SQL KV Doc Graph Row Data Connection
│
▼
Transaction
│
├── Atomicity
├── Consistency
├── Isolation
└── Durability
│
▼
Index
│
▼
Query Optimization
│
▼
Data Storage

可以进一步浓缩成:

Application
↓
Connection
↓
DBMS
↓
Query
↓
Transaction
↓
Optimizer
↓
Index
↓
Storage
↓
Data

从 Web 运维角度理解数据库#

对于 Web 运维来说,真正需要建立的并不是:

“数据库就是一个存数据的地方”

而是:

请求
↓
应用
↓
数据库连接
↓
事务
↓
SQL
↓
查询优化
↓
索引
↓
磁盘 / 内存

任何一环出现问题,都可能最终表现为:

页面变慢
API 超时
请求失败
数据库连接耗尽
CPU 飙高
I/O 飙高

例如:

API 响应变慢
│
▼
应用等待 DB
│
▼
数据库查询变慢
│
▼
执行计划异常
│
▼
没有使用合理索引
│
▼
大量 I/O

因此数据库系统实际上是 Web 服务架构中非常核心的一层。

理解:

DBMS
关系型 / NoSQL
Connection
Transaction
Index
Query

之后,再学习:

MySQL
PostgreSQL
Redis
MongoDB
数据库备份
主从复制
高可用
数据库监控
慢查询

就会有比较清晰的基础。

外部参考#

数据库:数据库系统架构
https://tamakara.top/posts/学习笔记/数据库数据库系统架构/
作者
魂辛カラ
发布于
2026-09-13
许可协议
CC BY-NC-SA 4.0