1.数据库系统
-
数据库系统DBS :是一个采用了数据库技术,有组织地、动态地存储大量相关
数据,方便多用户访问的计算机系统。其由下面四个部分组成:
- 数据库(统一管理、长期存储在计算机内的,有组织的相关数据的集合)
- 硬件(构成计算机系统包括存储数据所需的外部设备)
- 软件(操作系统、数据库管理系统及应用程序)
- 人员(系统分析和数据库设计人员、应用程序员、最终用户、数据库管理员DBA)
-
数据库管理系统DBMS的功能
实现对共享数据有效的组织、管理和存取。包括数据定义、数据库操作、数据库运行管理、数据的存储管理、数据库的建立和维护等。
2.三级模式-两级映射
1.模式
-
内模式:管理如何存储物理的数据,对应具体物理存储文件。
-
模式:又称为概念模式,就是我们通常使用的基本表,根据应用、需求将物理数据划分成一张张表。
-
外模式:对应数据库中的视图这个级别,将表进行一定的处理后再提供给用户使用
2.映射
2.1 外模式一模式映像
外模式一模式映像:是表和视图之间的映射,存在于概念级和外部级之间,若表中数据发生了修改,只需要修改此映射,而无需修改应用程序。
当数据库的底层逻辑结构(模式)发生变化时,那些依赖于视图(外模式)的应用程序代码无需进行任何修改,就能继续正常工作。这是逻辑数据独立性带来的直接好处。
初始状态
-
模式(基本表): 有一张员工信息表
Employees。sqlCREATE TABLE Employees ( EmpID INT PRIMARY KEY, Name VARCHAR(100), Salary DECIMAL(10,2), DeptID INT ); -
外模式(视图): 为了方便财务部门只查看工资信息,DBA创建了一个视图
FinanceView。sqlCREATE VIEW FinanceView AS SELECT EmpID, Name, Salary FROM Employees; -
应用程序: 财务部门的薪资系统程序
FinanceApp,它的代码里只查询这个视图 。sql-- FinanceApp 内部的查询语句是: SELECT * FROM FinanceView WHERE Salary > 5000;FinanceApp完全不知道Employees表的存在,它只知道FinanceView。
场景:模式发生改变(数据库重构)
公司业务发展,需要将员工信息拆分到更专业的表中。模式(表结构)被修改了:
- 新建
EmployeeBasic表存储基本信息。 - 新建
EmployeeSalary表存储薪资信息。 - 删除原来的
Employees表。
新的模式如下:
sql
CREATE TABLE EmployeeBasic (
EmpID INT PRIMARY KEY,
Name VARCHAR(100),
DeptID INT
);
CREATE TABLE EmployeeSalary (
EmpID INT PRIMARY KEY,
Salary DECIMAL(10,2),
FOREIGN KEY (EmpID) REFERENCES EmployeeBasic(EmpID)
);
关键操作:更新"外模式-模式映像"
此时,数据库管理员(DBA)只需要修改 FinanceView 视图的定义(即更新外模式-模式映像),将视图的查询逻辑从指向旧的单表,改为关联新的两张表。
sql
-- 旧的视图定义被替换
-- CREATE VIEW FinanceView AS SELECT EmpID, Name, Salary FROM Employees;
-- 新的视图定义(映像被更新了)
CREATE OR REPLACE VIEW FinanceView AS
SELECT b.EmpID, b.Name, s.Salary
FROM EmployeeBasic b
JOIN EmployeeSalary s ON b.EmpID = s.EmpID;
结果:应用程序发生了什么?
- 应用程序
FinanceApp需要修改吗?------ 完全不需要! - 为什么?
- 应用程序的代码里访问的仍然是
FinanceView(外模式名称没变)。 - 视图
FinanceView返回的数据"形态"没有变:依然是包含EmpID, Name, Salary三列的记录集(外模式结构没变)。 - 虽然底层数据从一个表变成了两个表的连接,但这个复杂性被 "外模式-模式映像" (即新的视图定义)完全屏蔽和消化了。
- 应用程序就像一个最终用户,它看到并使用的"界面"(外模式)没有发生任何变化。
- 应用程序的代码里访问的仍然是
"不变"的具体含义
| 需要改变的部分 | 负责方 | 无需改变的部分 | 受益方 |
|---|---|---|---|
| 物理存储(内模式) | 存储管理员、DBMS | 模式、外模式、应用程序 | 所有用户和程序 |
| 全局逻辑结构(模式) | 数据库管理员(DBA) | 外模式、应用程序 | 应用程序员、最终用户 |
| 外模式定义(视图) | 数据库管理员(DBA) | 应用程序 | 应用程序所有者 |
总结一下:
"与外模式相关的应用程序可以保持不变"指的就是程序的源代码、编译后的二进制文件、配置文件等都不需要因为数据库底层结构的调整而重新编写、编译或部署 。程序的数据访问接口、SQL语句、业务逻辑代码都保持原样。
逻辑数据独立性魅力
这种机制极大地降低了软件维护成本 ,提高了系统的可维护性 和可扩展性。当数据库为了优化性能或适应新需求而进行重构时,大量的现有业务程序可以安然无恙,这是数据库三级模式架构设计的巨大成功。
2.2 模式一内模式映像
模式一内模式映像:是表和数据的物理存储之间的映射存在于概念级和内部级之间,若修改了数据存储方式,只需要修改此映射,而不需要去修改应用程序。
假设books表原来是MyISAM引擎,现在要换成InnoDB引擎并分区:
修改前:
sql
-- books表使用MyISAM,存储在单个文件
CREATE TABLE books (
book_id INT PRIMARY KEY,
title VARCHAR(100),
author VARCHAR(50),
category VARCHAR(30),
publish_year INT
) ENGINE=MyISAM;
-- 数据文件:/var/lib/mysql/library/books.MYD
-- 索引文件:/var/lib/mysql/library/books.MYI
修改内模式(物理存储):
sql
-- 1. 修改存储引擎为InnoDB
-- 2. 添加分区(按category分区存储)
ALTER TABLE books
ENGINE=InnoDB
PARTITION BY LIST COLUMNS(category) (
PARTITION p_computer VALUES IN ('计算机'),
PARTITION p_literature VALUES IN ('文学'),
PARTITION p_science VALUES IN ('科学'),
PARTITION p_others VALUES IN (DEFAULT)
);
-- 3. 将数据文件迁移到SSD存储
-- 修改MySQL配置
-- 原数据目录:/var/lib/mysql/library/
-- 新数据目录:/ssd_storage/mysql/library/
发生了什么变化?
| 变化项 | 修改前 | 修改后 |
|---|---|---|
| 存储设备 | 机械硬盘 | SSD固态硬盘 |
| 存储结构 | 单个数据文件 | 按类别分4个物理文件 |
| 索引方式 | B-tree索引 | B+tree索引 + 分区裁剪 |
| 文件位置 | /var/lib/mysql/ |
/ssd_storage/mysql/ |
最重要的部分:用户和程序完全无感知!
用户角度:
sql
-- 用户还是这样查询,完全不变
SELECT * FROM computer_books WHERE title LIKE '%数据库%';
-- 应用程序代码完全不变
-- Java/Python/PHP中的查询语句不需要修改
String sql = "SELECT * FROM new_books ORDER BY publish_year DESC";
为什么用户感觉不到变化?
因为模式-内模式映像(DBMS内部的数据访问层)完成了所有适配:
用户查询
↓
视图层(外模式)←→ 没变!
↓
基本表层(模式)←→ 没变!表名、列名、数据类型都没变
↓
╔═══════════════════════════════════╗
║ 模式-内模式映像(DBMS内部) ║ ← 这里变了!
║ 负责将逻辑查询转换为物理操作 ║
╚═══════════════════════════════════╝
↓
物理存储层(内模式)←→ 完全变了!
DBMS内部适配的过程:
-
用户查询 :
SELECT * FROM books WHERE category='计算机' -
DBMS解析:这是一个计算机类书籍查询
-
新映射规则生效 :
sql-- 旧规则:全表扫描books.MYD文件 -- 新规则:只扫描p_computer分区文件 -- 利用SSD快速随机读取 -- 使用分区索引快速定位 -
物理操作 :
- 访问
/ssd_storage/mysql/library/books#P#p_computer.ibd - 使用InnoDB的B+tree索引
- 返回结果给用户
- 访问
实际生产中的例子
案例:双十一前的数据库升级
阿里云某电商平台,订单表原来存储在:
- 存储设备:普通SAS硬盘RAID5
- 存储结构:单表,按订单ID哈希分片
- 索引:普通B-tree索引
问题:双十一期间查询太慢
升级方案:
- 存储设备:换成NVMe SSD
- 存储结构:改为按时间范围分区(每天一个分区)
- 索引:添加覆盖索引,使用列存储索引
实施后:
- 查询速度快了10倍
- 存储空间节省40%
- 但业务系统完全没改一行代码!
为什么?因为DBA只是修改了:
- 表的分区策略(
ALTER TABLE ... PARTITION BY RANGE ...) - 存储路径指向新的SSD阵列
- 添加了新类型的索引
所有的应用程序仍然访问原来的表名和视图,SQL语句完全不变。
这就是"物理数据独立性"的魅力!
就像你家的地址没变(模式),但:
- 从平房搬到了楼房(存储设备变了)
- 从普通门锁换成了指纹锁(索引方式变了)
- 房间从按大小排列改为按功能排列(存储结构变了)
但所有朋友寄信时:
- 还是写"XX市XX路XX号"(逻辑地址不变)
- 快递员还是能送到(DBMS内部处理了映射)
- 你不需要通知所有朋友"我搬家了,请用新地址"(应用程序不变)
这种机制让数据库可以:
- 无缝升级硬件:HDD → SSD → NVMe
- 优化存储结构:单表 → 分区 → 分片
- 改进索引算法:B-tree → B+tree → LSM-tree
- 迁移到云存储:本地磁盘 → 云盘 → 对象存储
而所有这些变化,对使用数据库的成百上千个应用程序来说,都是透明无感知的。这就是数据库系统设计的精妙之处!
3.数据库设计
- 需求分析:即分析数据存储的要求,产出物有数据流图、数据字典、需求说明书。
- 概念结构设计:就是设计E-R图,也即实体-属性图,与物理实现无关,说明有哪些实体,实体有哪些属性。
- 逻辑结构设计:将E-R图,转换成关系模式,也即转换成实际的表和表中的列属性,这里要考虑很多规范化的东西。
- 物理设计:根据生成的表等概念,生成物理数据库。
4.数据模型
- 数据模型有以下几种模型:
关系模型是二维表的形式表示的实体-联系模型,是将实体-联系模型转换而来的,经过开发人员设计的;概念模型是从用户的角度进行建模的,是现实世界到信息世界的第一抽象,是真正的实体-联系模型。- 网状模型表示实体类型及其实体之间的联系,一个事物和另外几个都有联系形成一张网。
- 面向对象模型是采用面向对象的方法设计数据库,以对象为单位,每个对象包括属性和方法,具有类和继承等特点。
- 数据模型的三要素
数据结构(所研究的对象类型的集合)数据操作(对数据库中各种对象的实例允许执行的操作的集合)数据的约束条件(一组完整性规则的集合)
4.1 概念模型
-
用
E-R图来描述概念数据模型,世界是由一组称作实体的基本对象和这些对象之间的联系构成的。
-
在E-R模型中,使用椭圆表示属性(一般没有),长方形表示实体、菱形表示联系,联系的两端要填写联系类型,示例如下图:
-
实体:
客观存在并可相互区别的事物。可以是具体的人、事、物或抽象概念如人、汽车、图书、账户、贷款。弱实体和强实体:弱实体(长方形里面带了两个竖杠)依赖于强实体的存在而存在。
-
实体集:具有相同类型和共享相同属性的实体的集合,如学生、课程。
-
属性:实体所具有的特性
-
属性分类:简单属性和复合属性: 单值属性和多值属性: NULL属性: 派生属性。
-
域:属性的取值范围称为该属性的域
-
码 (key):唯一标识实体的属性集。
-
联系:现实世界中事物内部以及事物之间的联系,在E-R图中反映为实体内部的联系和实体之间的联系
-
联系类型:一对一1:1、一对多1:N、多对多M:N。多也可以用*表示
判断联系类型方法:
实体A 实体B
x (x个A有y个B) y
y (x个B有y个A) xx,y 可以是1个或者多个(*)
数据库中key解释
1. 键(Key)
键是用于标识表中记录唯一性的字段或字段组合,主要用于建立表间关系和优化查询。
2. 主键(Primary Key)
-
作用:
- 唯一标识表中的每一行
- 不允许 NULL 值
- 一个表只能有一个主键
- 自动创建聚簇索引
- 用于与其他表建立关系
-
示例:
sql
CREATE TABLE students (
student_id INT PRIMARY KEY, -- 主键
name VARCHAR(50) NOT NULL,
age INT
);
-- 或使用复合主键
CREATE TABLE course_registration (
student_id INT,
course_id INT,
reg_date DATE,
PRIMARY KEY (student_id, course_id) -- 复合主键
);
3. 外键(Foreign Key)
外键(Foreign Key) 是一个表中的一个字段(或一组字段),它的值必须匹配另一个表(或同一表)中的主键(Primary Key) 或 唯一键(Unique Key) 的值。外键常用于建立两个表之间的关联,确保数据的一致性和有效性。
-
作用:
- 建立两个表之间的关联
- 维护引用完整性
- 确保数据一致性
- 防止无效数据插入
- 外键字段的数量和类型必须与引用的主键字段完全匹配
-
示例:
sql
-- 主表
CREATE TABLE departments (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL
);
-- 从表(包含外键)
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
dept_id INT,
-- 定义外键
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE CASCADE -- 级联删除
ON UPDATE CASCADE -- 级联更新
);
4. 完整示例
sql
-- 创建客户表(主表)
CREATE TABLE customers (
customer_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
-- 创建订单表(从表)
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
order_date DATE NOT NULL,
total_amount DECIMAL(10,2),
customer_id INT NOT NULL,
-- 外键约束
FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
ON DELETE RESTRICT -- 如果有订单,不能删除客户
ON UPDATE CASCADE -- 客户ID更新时,订单中的客户ID同步更新
);
-- 插入数据
INSERT INTO customers (name, email)
VALUES ('张三', 'zhangsan@example.com');
INSERT INTO orders (order_date, total_amount, customer_id)
VALUES ('2023-10-01', 199.99, 1); -- customer_id=1必须存在于customers表
CREATE TABLE parent (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(20),
UNIQUE KEY uk_parent (id, code) -- 复合唯一键
);
CREATE TABLE child_correct (
child_id INT PRIMARY KEY,
parent_id INT,
parent_code VARCHAR(20),
FOREIGN KEY (parent_id, parent_code) REFERENCES parent(id, code) -- 都是2列
);
# 虽然MySQL允许外键引用唯一键,但最佳实践是外键应该引用主键。
约束名称的结构
通常采用类型_表名_字段的命名约定:
-
pk_表名_字段:主键约束(Primary Key)
-
fk_子表_父表_字段:外键约束(Foreign Key)
-
uk_表名_字段:唯一键约束(Unique Key)
-
ck_表名_条件:检查约束(Check Constraint)
--
CREATE DATABASE IF NOT EXISTS school_db;
USE school_db;
-- 1. 学生表(单主键)
CREATE TABLE students (
student_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
class VARCHAR(20)
);
-- 2. 课程表(单主键)
CREATE TABLE courses (
course_id INT AUTO_INCREMENT PRIMARY KEY,
course_name VARCHAR(100) NOT NULL,
credit INT
);
-- 3. 选课表(联合主键 + 联合外键)
CREATE TABLE student_courses (
-- 联合主键的两个字段
student_id INT NOT NULL,
course_id INT NOT NULL,
-- 额外字段
score DECIMAL(4,1) COMMENT '成绩',
selected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- 1️⃣ 联合主键:防止同一学生重复选同一门课
PRIMARY KEY (student_id, course_id),
-- 2️⃣ 联合外键1:引用students表
FOREIGN KEY (student_id)
REFERENCES students(student_id)
ON DELETE CASCADE,
-- 3️⃣ 联合外键2:引用courses表
FOREIGN KEY (course_id)
REFERENCES courses(course_id)
ON DELETE CASCADE
);
5. 其他类型键
sql
-- 唯一键(Unique Key)
CREATE TABLE users (
user_id INT PRIMARY KEY,
username VARCHAR(50) UNIQUE, -- 唯一键
email VARCHAR(100) UNIQUE -- 另一个唯一键
);
# 1.确保表中该列(或列组合)的值不重复
# 2.允许NULL值:与主键不同,唯一键允许有NULL值(但通常只允许一个NULL)
-- 索引键
CREATE INDEX idx_name ON students(name);
# 1.加速数据检索:避免全表扫描,提高查询性能
# 2.排序优化:加速ORDER BY、GROUP BY操作
# 3.连接优化:提高JOIN操作的效率
6. 使用建议
-
主键选择:
- 使用自增整数
- 业务无关
- 永不更新
-
外键使用场景:
- 需要强制引用完整性时
- 表间有明确的父子关系
- 需要级联操作
-
注意事项:
- 外键会影响性能
- 某些分布式数据库不支持外键
- 需要考虑删除和更新策略
7. 查看键信息
sql
-- 查看表结构(包括键)
SHOW CREATE TABLE table_name;
-- 查看索引信息
SHOW INDEX FROM table_name;
-- 查看外键约束
SELECT *
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'your_database'
AND REFERENCED_TABLE_NAME IS NOT NULL;
总结
- 主键:唯一标识,实体完整性
- 外键:表间关联,引用完整性
- 键:提高查询效率,保证数据一致性
合理使用这些键可以确保数据完整性、提高查询效率,并建立清晰的表关系结构。
4.2 ER模型转关系模型
E-R(entity-relationship,实体-联系)模型中有三个主要概念是: 实体集 、 属性 、 联系集 。一个实体集(class)对应于数据库中的一个表(table),一个实体(instance)则对应于数据库表中的一行(row),也称为一条记录(record)。一个属性(attribute)对应于数据库表中的一列(column),也称为一个字段(field)。
E-R模型转换为关系模型: 每个实体都对应一个关系模式;联系分为三种:
- 1:1联系中,联系可以放到任意的两端实体中,作为一个属性(要保证1:1的两端
关联),也可以转换为一个单独的关系模式; - 1:N的联系中,联系可以单独作为一个关系模式,也可以在N端中加入1端实体的
主键(N端表加一个字段); - M:N的联系中,联系必须作为一个单独的关系模式(关系表),其主键是M和N端的联合主键。
4.2.1 一对一关联(one-to-one)
- 在实际的开发中应用不多,因为一对一可以创建成一张表。
- 举例:设计 学生表 :学号、姓名、手机号码、班级、系别、身份证号码、家庭住址、籍贯、紧急联系人、...
- 拆为两个表:两个表的记录是一一对应关系。
- 基础信息表 (常用信息):学号、姓名、手机号码、班级、系别
- 档案信息表 (不常用信息):学号、身份证号码、家庭住址、籍贯、紧急联系人、...
- 两种建表原则:
- 外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一。
- 外键是主键:主表的主键和从表的主键,形成主外键关系。
4.2.2 一对多关系(one-to-many)
- 常见实例场景: 客户表和订单表 , 分类表和商品表 , 部门表和员工表 。
- 举例:
- 员工表:编号、姓名、...、所属部门
- 部门表:编号、名称、简介
- 一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键,也可以创建第三个表,该表通常称为
联接表
4.2.3 多对多(many-to-many)
- 要表示多对多关系,必须创建第三个表,该表通常称为
联接表,它将多对多关系划分为两个一对多关系。将这两个表的主键都插入到第三个表中。 - 举例1:学生-课程
- 学生信息表 :一行代表一个学生的信息(学号、姓名、手机号码、班级、系别...)
- 课程信息表 :一行代表一个课程的信息(课程编号、授课老师、简介...)
- 选课信息表 :一个学生可以选多门课,一门课可以被多个学生选择
4.2.4 自引用
自引用是指在一个表内部,通过某种关联条件将自身与另一个记录或自身相关联。 这种关联条件通常是外键关系,即表中某个字段的值引用了该表中的其他记录的主键值。