从经典案例开始
我们以用户注册的案例探究数据从无到有,从产生到被处理的完整的流动过程,并由此引出数据库系统的概念。
数据的产生
用户注册可能会填写这些信息:
- 用户名
- 密码
- 密码提示
当用户在前端网页窗口填写完这些用于注册的信息,按下【注册】按钮,用户数据便随之产生,我们可以用json格式表示这条用户注册数据。
{
"user_name": "Nonbeing",
"password": "114514",
"password_hint": "homo",
}
数据的流动
graph LR
注册数据 --> 服务端 --> 数据库 --> 其他系统
当一条数据产生后,会提交给后端服务器;后端服务器对数据进行逻辑业务操作后,会交由数据库进行存储;有的情况下,这条数据还会提交给其他系统进行相关操作。
这其实就是我们在分层设计结构中提到的三层结构:
graph LR
Repository --> Service --> Controller
数据的持久化
数据持久化:在大部分业务中,我们需要将某些数据存储下来,以供查询并根据查询结果执行相关操作。
例如,用户数据需要被持久化,这样在用户登陆时就能对用户填写的登陆信息进行鉴权。
数据库将数据持久化的几个步骤:
- 校验数据的合法性:对数据按照需求进行检验,去除非法数据。
- 修改内存:用高效的数据结构组织大量数据。
- 写入存储介质:以寿命和性能友好的方式写入硬件介质。
但是,数据持久化的过程中有许多潜在的问题:
- 数据可能被多用户同时修改(并发安全)
- 数据如何保证被持久化
- 数据如何保证它的一致性
......
因此,我们就需要存储系统来解决这些存储问题。
存储系统
系统概念
存储系统所涉及的方面:
- User:要设计出方便易用的存储系统,屏蔽大部分底层结构,对用户透明。
- Medium:使用对寿命和性能友好的方式将数据写入存储介质。
- Memory:使用高效的数据结构提高存储系统的操作的性能。
- Network:存储系统不局限于单机系统,需要分布式结构或者是云计算等网络上的服务器集群共同作用。
系统特点
性能敏感:作为后端软件的底部系统,涉及大量数据操作,对高性能的要求十分高。
代码既“简单”又“复杂”:对于错误处理和其它异常的处理代码比较复杂。
容易受硬件影响:对于硬件的变化,存储也需要及时变化。
存储器层级结构
层级越高,容量越小,读取速度越快。
层级越低,容量越大,读取速度越慢。
在数据从应用到存储介质的链路上:
- 缓存非常重要,应该贯穿整个存储体系。
- 拷贝的开销非常昂贵,应该尽量减少。
- 我们需要设计一个抽象统一的接入层来抹平不同硬件设备之间的差异。
数据库
数据库是存储系统中的一个实例。
关系型数据库
关系型数据库在存储之外,还有以下拓展能力:
- 结构化数据友好
- 支持事物,保证ACID特性
- 支持复杂查询语言
关系
关系(集合):关系是任意元素组成的若干有序偶对,它反应的是事物间的关系。
关系代数:对关系作运算的抽象查询语言。
SQL:一种DSL(domain specific language),方便人类阅读的查询语言。
非关系型数据库
非关系型数据库也是存储系统,但不要求严格的结构化:
- 半结构化数据友好
- 不强制支持事物
- 不强制支持复杂查询语言
结构化数据管理
用户数据:
{
"user_name": "Nonbeing",
"password": "114514",
"password_hint": "homo",
}
以表的形式写入关系型数据库:
| id | user_name | password | password_hint |
|---|---|---|---|
| 0 | Nonbing | 114514 | homo |
以文件形式写入存储系统:
将用户数据序列化为字符串写入文件中,按照规定的偏移量指定数据的含义。
{"user_name": "Nonbeing","password": "114514","password_hint": "homo",}
可以看到,使用数据库系统管理结构化的数据更加有序,操作更加方便,数据的结构能够被非常好地展现出来。
复杂查询能力
对于数据库系统来说,我们可以使用SQL的嵌套与组合实现复杂的查询过程,而且我们不需要了解数据存储在底层的具体结构。
对于经典存储来说,我们需要为每个数据的结构编写对应的查询代码,并且该代码的逻辑关系复杂。