数据库系统概念

1.数据库系统

  • 数据库系统DBS :是一个采用了数据库技术,有组织地、动态地存储大量相关

    数据,方便多用户访问的计算机系统。其由下面四个部分组成:

    • 数据库(统一管理、长期存储在计算机内的,有组织的相关数据的集合)
    • 硬件(构成计算机系统包括存储数据所需的外部设备)
    • 软件(操作系统、数据库管理系统及应用程序)
    • 人员(系统分析和数据库设计人员、应用程序员、最终用户、数据库管理员DBA)
  • 数据库管理系统DBMS的功能

    实现对共享数据有效的组织、管理和存取。包括数据定义、数据库操作、数据库运行管理、数据的存储管理、数据库的建立和维护等。

2.三级模式-两级映射

1.模式

  • 内模式:管理如何存储物理的数据,对应具体物理存储文件。

  • 模式:又称为概念模式,就是我们通常使用的基本表,根据应用、需求将物理数据划分成一张张表。

  • 外模式:对应数据库中的视图这个级别,将表进行一定的处理后再提供给用户使用

2.映射

2.1 外模式一模式映像

外模式一模式映像:是表和视图之间的映射,存在于概念级和外部级之间,若表中数据发生了修改,只需要修改此映射,而无需修改应用程序。

当数据库的底层逻辑结构(模式)发生变化时,那些依赖于视图(外模式)的应用程序代码无需进行任何修改,就能继续正常工作。这是逻辑数据独立性带来的直接好处。

初始状态
  • 模式(基本表): 有一张员工信息表 Employees

    sql 复制代码
    CREATE TABLE Employees (
        EmpID INT PRIMARY KEY,
        Name VARCHAR(100),
        Salary DECIMAL(10,2),
        DeptID INT
    );
  • 外模式(视图): 为了方便财务部门只查看工资信息,DBA创建了一个视图 FinanceView

    sql 复制代码
    CREATE VIEW FinanceView AS
    SELECT EmpID, Name, Salary
    FROM Employees;
  • 应用程序: 财务部门的薪资系统程序 FinanceApp,它的代码里只查询这个视图

    sql 复制代码
    -- FinanceApp 内部的查询语句是:
    SELECT * FROM FinanceView WHERE Salary > 5000;

    FinanceApp 完全不知道 Employees 表的存在,它只知道 FinanceView

场景:模式发生改变(数据库重构)

公司业务发展,需要将员工信息拆分到更专业的表中。模式(表结构)被修改了

  1. 新建 EmployeeBasic 表存储基本信息。
  2. 新建 EmployeeSalary 表存储薪资信息。
  3. 删除原来的 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 需要修改吗?------ 完全不需要!
  • 为什么?
    1. 应用程序的代码里访问的仍然是 FinanceView(外模式名称没变)。
    2. 视图 FinanceView 返回的数据"形态"没有变:依然是包含 EmpID, Name, Salary 三列的记录集(外模式结构没变)。
    3. 虽然底层数据从一个表变成了两个表的连接,但这个复杂性被 "外模式-模式映像" (即新的视图定义)完全屏蔽和消化了。
    4. 应用程序就像一个最终用户,它看到并使用的"界面"(外模式)没有发生任何变化。

"不变"的具体含义

需要改变的部分 负责方 无需改变的部分 受益方
物理存储(内模式) 存储管理员、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内部适配的过程:

  1. 用户查询SELECT * FROM books WHERE category='计算机'

  2. DBMS解析:这是一个计算机类书籍查询

  3. 新映射规则生效

    sql 复制代码
    -- 旧规则:全表扫描books.MYD文件
    -- 新规则:只扫描p_computer分区文件
    --       利用SSD快速随机读取
    --       使用分区索引快速定位
  4. 物理操作

    • 访问 /ssd_storage/mysql/library/books#P#p_computer.ibd
    • 使用InnoDB的B+tree索引
    • 返回结果给用户

实际生产中的例子

案例:双十一前的数据库升级

阿里云某电商平台,订单表原来存储在:

  • 存储设备:普通SAS硬盘RAID5
  • 存储结构:单表,按订单ID哈希分片
  • 索引:普通B-tree索引

问题:双十一期间查询太慢

升级方案

  1. 存储设备:换成NVMe SSD
  2. 存储结构:改为按时间范围分区(每天一个分区)
  3. 索引:添加覆盖索引,使用列存储索引

实施后:

  • 查询速度快了10倍
  • 存储空间节省40%
  • 但业务系统完全没改一行代码!

为什么?因为DBA只是修改了:

  1. 表的分区策略(ALTER TABLE ... PARTITION BY RANGE ...
  2. 存储路径指向新的SSD阵列
  3. 添加了新类型的索引

所有的应用程序仍然访问原来的表名和视图,SQL语句完全不变。


这就是"物理数据独立性"的魅力!

就像你家的地址没变(模式),但:

  • 从平房搬到了楼房(存储设备变了)
  • 从普通门锁换成了指纹锁(索引方式变了)
  • 房间从按大小排列改为按功能排列(存储结构变了)

但所有朋友寄信时:

  • 还是写"XX市XX路XX号"(逻辑地址不变)
  • 快递员还是能送到(DBMS内部处理了映射)
  • 你不需要通知所有朋友"我搬家了,请用新地址"(应用程序不变)

这种机制让数据库可以:

  1. 无缝升级硬件:HDD → SSD → NVMe
  2. 优化存储结构:单表 → 分区 → 分片
  3. 改进索引算法:B-tree → B+tree → LSM-tree
  4. 迁移到云存储:本地磁盘 → 云盘 → 对象存储

而所有这些变化,对使用数据库的成百上千个应用程序来说,都是透明无感知的。这就是数据库系统设计的精妙之处!

3.数据库设计

  • 需求分析:即分析数据存储的要求,产出物有数据流图、数据字典、需求说明书。
  • 概念结构设计:就是设计E-R图,也即实体-属性图,与物理实现无关,说明有哪些实体,实体有哪些属性。
  • 逻辑结构设计:将E-R图,转换成关系模式,也即转换成实际的表和表中的列属性,这里要考虑很多规范化的东西。
  • 物理设计:根据生成的表等概念,生成物理数据库。

4.数据模型

  1. 数据模型有以下几种模型:
  • 关系模型是二维表的形式表示的实体-联系模型,是将实体-联系模型转换而来的,经过开发人员设计的;
  • 概念模型是从用户的角度进行建模的,是现实世界到信息世界的第一抽象,是真正的实体-联系模型。
  • 网状模型表示实体类型及其实体之间的联系,一个事物和另外几个都有联系形成一张网。
  • 面向对象模型是采用面向对象的方法设计数据库,以对象为单位,每个对象包括属性和方法,具有类和继承等特点。
  1. 数据模型的三要素
  • 数据结构(所研究的对象类型的集合)
  • 数据操作(对数据库中各种对象的实例允许执行的操作的集合)
  • 数据的约束条件 (一组完整性规则的集合)

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) x

    x,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. 使用建议

  1. 主键选择

    • 使用自增整数
    • 业务无关
    • 永不更新
  2. 外键使用场景

    • 需要强制引用完整性时
    • 表间有明确的父子关系
    • 需要级联操作
  3. 注意事项

    • 外键会影响性能
    • 某些分布式数据库不支持外键
    • 需要考虑删除和更新策略

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 自引用

自引用是指在一个表内部,通过某种关联条件将自身与另一个记录或自身相关联。 这种关联条件通常是外键关系,即表中某个字段的值引用了该表中的其他记录的主键值。

相关推荐
Ashley_Amanda16 小时前
人事事件配置梳理
数据库
学好statistics和DS16 小时前
两个子进程都sleep, `waitpid` 系统调用
linux·服务器·数据库
cici1587416 小时前
基于正交匹配追踪(OMP)算法的信号稀疏分解MATLAB实现
数据库·算法·matlab
Web极客码16 小时前
释放WordPress磁盘空间并减少Inode使用量
服务器·数据库·ubuntu
Knight_AL16 小时前
Redis ZSet 实现排行榜(支持分数相同按时间顺序排序)
数据库·redis·缓存
w***954916 小时前
mysql之如何获知版本
数据库·mysql
火星数据-Tina16 小时前
如何构建一个支持多终端同步的体育比分网站?
大数据·前端·数据库·websocket
BD_Marathon16 小时前
SpringMVC——5种类型参数传递
android·java·数据库
计算机毕设VX:Fegn089516 小时前
计算机毕业设计|基于springboot + vue律师咨询系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计