docs/database/basis.md
数据库知识基础,这部分内容一定要理解记忆。虽然这部分内容只是理论知识,但是非常重要,这是后面学习 MySQL 数据库的基础。PS: 这部分内容由于涉及太多概念性内容,所以参考了维基百科和百度百科相应的介绍。
这四个概念描述了从数据本身到管理整个体系的不同层次,我们常用一个图书馆的例子来把它们串联起来理解。
DB 和 DBMS 我们通常会搞混,这里再简单提一下:通常我们说“用 MySQL 数据库”,其实是用 MySQL(DBMS)来管理一个或多个数据库(DB)。
graph TD
DBMS["🗄️ DBMS
<b>数据库管理系统</b>"]
subgraph define["数据定义"]
DDL["📐 DDL
Data Definition Language"]
DDL_Items["• 创建/修改/删除对象
• 定义表结构
• 定义视图、索引
• 定义触发器
• 定义存储过程"]
end
subgraph operate["数据操作"]
DML["⚡ DML
Data Manipulation Language"]
CRUD["<b>CRUD 操作</b>
• Create 创建
• Read 读取
• Update 更新
• Delete 删除"]
end
subgraph control["数据控制"]
DCL["🔐 数据控制功能"]
Control_Items["• 并发控制
• 事务管理
• 完整性约束
• 权限控制
• 安全性限制"]
end
subgraph maintain["数据库维护"]
Maintenance["🛠️ 维护功能"]
Maintain_Items["• 数据导入/导出
• 备份与恢复
• 性能监控与分析
• 系统日志管理"]
end
DBMS --> DDL
DBMS --> DML
DBMS --> DCL
DBMS --> Maintenance
DDL --> DDL_Items
DML --> CRUD
DCL --> Control_Items
Maintenance --> Maintain_Items
style DBMS fill:#005D7B,stroke:#00838F,stroke-width:4px,color:#fff
style DDL fill:#4CA497,stroke:#00838F,stroke-width:3px,color:#fff
style DDL_Items fill:#f0fffe,stroke:#4CA497,stroke-width:2px,color:#333
style DML fill:#E99151,stroke:#C44545,stroke-width:3px,color:#fff
style CRUD fill:#fff5e6,stroke:#E99151,stroke-width:2px,color:#333
style DCL fill:#00838F,stroke:#005D7B,stroke-width:3px,color:#fff
style Control_Items fill:#e6f7ff,stroke:#00838F,stroke-width:2px,color:#333
style Maintenance fill:#C44545,stroke:#8B0000,stroke-width:3px,color:#fff
style Maintain_Items fill:#ffe6e6,stroke:#C44545,stroke-width:2px,color:#333
style define fill:#E4C189,stroke:#E99151,stroke-width:2px,stroke-dasharray: 5 5,opacity:0.3
style operate fill:#E4C189,stroke:#E99151,stroke-width:2px,stroke-dasharray: 5 5,opacity:0.3
style control fill:#E4C189,stroke:#E99151,stroke-width:2px,stroke-dasharray: 5 5,opacity:0.3
style maintain fill:#E4C189,stroke:#E99151,stroke-width:2px,stroke-dasharray: 5 5,opacity:0.3
DBMS 通常提供四大核心功能:
除了我们最常用的关系型数据库(RDBMS),比如 MySQL(开源首选)、PostgreSQL(功能最全)、Oracle(企业级),它们基于严格的表结构和 SQL,非常适合结构化数据和需要事务保证的场景,例如银行交易、订单系统。
近年来,为了应对互联网应用带来的海量数据、高并发和多样化数据结构的需求,涌现出了一大批 NoSQL 和 NewSQL 数据库。
它们的共同特点是为了极致的性能和水平扩展能力,在某些方面(通常是事务)做了妥协。
1. 键值数据库,代表是 Redis。
2. 文档数据库,代表是 MongoDB。
3. 列式数据库,代表是 HBase, Cassandra。
4. 图形数据库,代表是 Neo4j。
由于 NoSQL 不支持事务,很多对于数据安全要求非常高的系统(比如财务系统、订单系统、交易系统)就不太适合使用了。不过,这类系统往往有存储大量数据的需求。
这些系统往往只能通过购买性能更强大的计算机,或者通过数据库中间件来提高存储能力。不过,前者的金钱成本太高,后者的开发成本太高。
于是,NewSQL 就来了!
简单来说,NewSQL 就是:分布式存储+SQL+事务 。NewSQL 不仅具有 NoSQL 对海量数据的存储管理能力,还保持了传统数据库支持 ACID 和 SQL 等特性。因此,NewSQL 也可以称为 分布式关系型数据库。
NewSQL 数据库设计的一些目标:
NewSQL 数据库代表:Google 的 F1/Spanner、阿里的 OceanBase、PingCAP 的 TiDB 。
在关系型数据库理论中,理解元组、码、候选码、主码、外码、主属性和非主属性这些核心概念,对于数据库设计和规范化至关重要。这些概念构成了关系数据库的理论基础。
graph TD
A[关系数据库概念] --> B[数据组织]
A --> C[码的类型]
A --> D[属性分类]
B --> B1[元组
表中的行记录]
B --> B2[属性
表中的列]
C --> C1[码
唯一标识]
C1 --> C2[候选码
最小唯一标识集]
C2 --> C3[主码
选定的候选码]
C1 --> C4[外码
引用其他表主码]
D --> D1[主属性
候选码中的属性]
D --> D2[非主属性
不在候选码中的属性]
C3 -.关联.-> C4
C2 -.构成.-> D1
style A fill:#4CA497,stroke:#00838F,stroke-width:3px,color:#fff
style B fill:#00838F,stroke:#005D7B,stroke-width:2px,color:#fff
style C fill:#E99151,stroke:#005D7B,stroke-width:2px,color:#fff
style D fill:#005D7B,stroke:#00838F,stroke-width:2px,color:#fff
style B1 fill:#E4C189,stroke:#00838F,stroke-width:1px
style B2 fill:#E4C189,stroke:#00838F,stroke-width:1px
style C1 fill:#E4C189,stroke:#E99151,stroke-width:1px
style C2 fill:#E4C189,stroke:#E99151,stroke-width:1px
style C3 fill:#C44545,stroke:#005D7B,stroke-width:2px,color:#fff
style C4 fill:#E4C189,stroke:#E99151,stroke-width:1px
style D1 fill:#E4C189,stroke:#005D7B,stroke-width:1px
style D2 fill:#E4C189,stroke:#005D7B,stroke-width:1px
我们做一个项目的时候一定要试着画 ER 图来捋清数据库设计,这个也是面试官问你项目的时候经常会被问到的。
ER 图 全称是 Entity Relationship Diagram(实体联系图),提供了表示实体类型、属性和联系的方法。
ER 图由下面 3 个要素组成:
下图是一个学生选课的 ER 图,每个学生可以选若干门课程,同一门课程也可以被若干人选择,所以它们之间的关系是多对多(M: N)。另外,还有其他两种实体之间的关系是:1 对 1(1:1)、1 对多(1: N)。
erDiagram
STUDENT {
string student_id PK "学号"
string name "姓名"
string gender "性别"
date birth_date "出生日期"
string department "学院名称"
}
COURSE {
string course_id PK "课程编号"
string course_name "课程名称"
string location "课程地点"
string instructor "开课教师"
float credits "成绩"
}
ENROLLMENT {
string student_id FK "学号"
string course_id FK "课程编号"
float grade "成绩"
}
STUDENT ||--o{ ENROLLMENT : "选课"
COURSE ||--o{ ENROLLMENT : "被选"
style STUDENT fill:#4CA497,stroke:#00838F,stroke-width:2px
style COURSE fill:#005D7B,stroke:#00838F,stroke-width:2px
style ENROLLMENT fill:#E99151,stroke:#C44545,stroke-width:2px
数据库范式有 3 种:
属性(对应于表中的字段)不能再被分割,也就是这个字段只能是一个值,不能再分为多个其他的字段了。1NF 是所有关系型数据库的最基本要求 ,也就是说关系型数据库中创建的表一定满足第一范式。
2NF 在 1NF 的基础之上,消除了非主属性对于码的部分函数依赖。如下图所示,展示了第一范式到第二范式的过渡。第二范式在第一范式的基础上增加了一个列,这个列称为主键,非主属性都依赖于主键。
一些重要的概念:
3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。符合 3NF 要求的数据库设计,基本上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖,所以该表的设计,不符合 3NF 的要求。
从定义和属性上看,它们的区别是:
用一个简单的电商例子来说明:假设我们有两张表:users (用户表) 和 orders (订单表)。
users 表中,user_id 列是主键。每个用户的 user_id 都是独一无二的,我们用它来区分张三和李四。orders 表中,order_id 是它自己的主键。同时,它会有一个 user_id 列,这个列就是一个外键,它引用了 users 表的 user_id 主键。这个外键约束就保证了:
user_id 在 users 表中不存在)。对于外键和级联,阿里巴巴开发手册这样说到:
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明: 以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度
为什么不要用外键呢?大部分人可能会这样回答:
我个人觉得上面这种回答不是特别的全面,只是说了外键存在的一个常见的问题。实际上,我们知道外键也是有很多好处的,比如:
所以说,不要一股脑的就抛弃了外键这个概念,既然它存在就有它存在的道理,如果系统不涉及分库分表,并发量不是很高的情况还是可以考虑使用外键的。
graph LR
A[存储过程] --> B[定义特征]
A --> C[优势]
A --> D[劣势]
A --> E[应用现状]
B --> B1[SQL语句集合]
B --> B2[包含逻辑控制]
B --> B3[预编译机制]
C --> C1[执行速度快]
C --> C2[运行稳定]
C --> C3[简化复杂操作]
D --> D1[调试困难]
D --> D2[扩展性差]
D --> D3[无移植性]
D --> D4[占用数据库资源]
E --> E1[传统企业
使用较多]
E --> E2[互联网公司
很少使用]
E --> E3[阿里规范
明确禁用]
style A fill:#4CA497,stroke:#00838F,stroke-width:3px,color:#fff
style B fill:#00838F,stroke:#005D7B,stroke-width:2px,color:#fff
style C fill:#E99151,stroke:#C44545,stroke-width:2px,color:#fff
style D fill:#C44545,stroke:#005D7B,stroke-width:2px,color:#fff
style E fill:#005D7B,stroke:#00838F,stroke-width:2px,color:#fff
style B1 fill:#E4C189,stroke:#00838F,stroke-width:1px
style B2 fill:#E4C189,stroke:#00838F,stroke-width:1px
style B3 fill:#E4C189,stroke:#00838F,stroke-width:1px
style C1 fill:#E4C189,stroke:#E99151,stroke-width:1px
style C2 fill:#E4C189,stroke:#E99151,stroke-width:1px
style C3 fill:#E4C189,stroke:#E99151,stroke-width:1px
style D1 fill:#E4C189,stroke:#C44545,stroke-width:1px
style D2 fill:#E4C189,stroke:#C44545,stroke-width:1px
style D3 fill:#E4C189,stroke:#C44545,stroke-width:1px
style D4 fill:#E4C189,stroke:#C44545,stroke-width:1px
style E1 fill:#E4C189,stroke:#005D7B,stroke-width:1px
style E2 fill:#E4C189,stroke:#005D7B,stroke-width:1px
style E3 fill:#E4C189,stroke:#005D7B,stroke-width:1px
存储过程是数据库中预编译的SQL语句集合,它将多条SQL语句和程序逻辑控制语句(如IF-ELSE、WHILE循环等)封装在一起,形成一个可重复调用的数据库对象。
存储过程的优势:
在传统企业级应用中,存储过程具有一定的实用价值。当业务逻辑复杂时,需要执行大量SQL语句才能完成一个业务操作,此时可以将这些语句封装成存储过程,简化调用过程。由于存储过程在创建时就已经编译并存储在数据库中,执行时无需重新编译,因此相比动态SQL语句具有更好的执行性能。同时,一旦存储过程调试完成,其运行相对稳定可靠。
存储过程的局限性:
然而,在现代互联网架构中,存储过程的使用越来越少。主要原因包括:调试困难,缺乏成熟的调试工具;扩展性差,修改业务逻辑需要直接修改数据库对象;移植性差,不同数据库系统的存储过程语法差异较大;占用数据库资源,增加数据库服务器负担;版本管理困难,不便于进行代码版本控制。
行业规范:
基于以上原因,许多互联网公司的开发规范中明确限制或禁止使用存储过程。例如,《阿里巴巴Java开发手册》中明确规定禁止使用存储过程,推荐将业务逻辑放在应用层实现,保持数据库的简单和高效。
在数据库操作中,DROP、DELETE 和 TRUNCATE 是三个常用的数据删除命令,它们在功能、性能和使用场景上存在显著差异。
DROP命令:
DROP TABLE 表名TRUNCATE命令:
TRUNCATE TABLE 表名DELETE命令:
DELETE FROM 表名 WHERE 条件TRUNCATE 和不带 WHERE子句的 DELETE、以及 DROP 都会删除表内的数据,但是 TRUNCATE 和 DELETE 只删除数据不删除表的结构(定义),执行 DROP 语句,此表的结构也会删除,也就是执行DROP 之后对应的表不复存在。
DROP:删除表结构和所有数据,表将不复存在TRUNCATE:仅删除数据,保留表结构和定义DELETE:仅删除数据,保留表结构和定义DELETE 操作会触发相关的DELETE触发器TRUNCATE 和 DROP 不会触发DELETE触发器DROP 和 TRUNCATE 属于DDL操作,执行后立即生效,不能回滚DELETE 属于DML操作,可以回滚(在事务中)一般来说:DROP > TRUNCATE > DELETE(这个我没有实际测试过)。
DELETE命令执行的时候会产生数据库的binlog日志,而日志记录是需要消耗时间的,但是也有个好处方便数据回滚恢复。TRUNCATE命令执行的时候不会产生数据库日志,因此比DELETE要快。除此之外,还会把表的自增值重置和索引恢复到初始大小等。DROP命令会把表占用的空间全部释放掉。Tips:你应该更多地关注在使用场景上,而不是执行效率。
另外,由于SELECT不会对表进行破坏,所以有的地方也会把SELECT单独区分开叫做数据库查询语言 DQL(Data Query Language)。
graph TD
A[数据库设计流程] --> B[1.需求分析]
B --> C[2.概念结构设计]
C --> D[3.逻辑结构设计]
D --> E[4.物理结构设计]
E --> F[5.数据库实施]
F --> G[6.运行和维护]
B --> B1[数据需求
功能需求
性能需求]
C --> C1[E-R建模
实体关系图]
D --> D1[关系模型
表结构设计
规范化]
E --> E1[存储结构
索引设计
分区策略]
F --> F1[编程开发
测试部署
数据迁移]
G --> G1[性能监控
备份恢复
优化调整]
G -.反馈.-> B
style A fill:#4CA497,stroke:#00838F,stroke-width:3px,color:#fff
style B fill:#00838F,stroke:#005D7B,stroke-width:2px,color:#fff
style C fill:#E99151,stroke:#005D7B,stroke-width:2px,color:#fff
style D fill:#005D7B,stroke:#00838F,stroke-width:2px,color:#fff
style E fill:#C44545,stroke:#005D7B,stroke-width:2px,color:#fff
style F fill:#E99151,stroke:#005D7B,stroke-width:2px,color:#fff
style G fill:#00838F,stroke:#005D7B,stroke-width:2px,color:#fff
style B1 fill:#E4C189,stroke:#00838F,stroke-width:1px
style C1 fill:#E4C189,stroke:#E99151,stroke-width:1px
style D1 fill:#E4C189,stroke:#005D7B,stroke-width:1px
style E1 fill:#E4C189,stroke:#C44545,stroke-width:1px
style F1 fill:#E4C189,stroke:#E99151,stroke-width:1px
style G1 fill:#E4C189,stroke:#00838F,stroke-width:1px
目标: 深入了解和分析用户需求,明确系统边界 主要工作:
目标: 将需求转化为信息世界的概念模型 主要工作:
目标: 将概念模型转换为特定DBMS支持的逻辑模型 主要工作:
目标: 确定数据的物理存储方案和访问方法 主要工作:
目标: 将设计转化为实际运行的数据库系统 主要工作:
目标: 确保数据库系统稳定高效运行 主要工作:
在整个设计过程中应遵循:数据独立性原则、完整性原则、安全性原则、可扩展性原则和标准化原则。