1、数据库的基本概念
1.1、数据库的基本概念
数据库(Database,简称DB)是按照数据结构来组织、存储和管理数据的仓库。我们也可以将数据存储在文件中或者是内存中,但是内存存储的数据都是临时的,在服务器关机后就会被清除,而文件的读写数据速度相对较慢。所以,我们更多的是使用数据库来存储数据。其实数据库就是一个文件系统。
2、数据库的三种模型(关系模型、层次模型、网状模型)
数据库按照数据结构来组织、存储和管理数据,实际上,数据库一共有三种模型:
- 层次模型
- 网状模型
- 关系模型
层次模型就是以“上下级”的层次关系来组织数据的一种方式,层次模型的数据结构看起来就像一颗树:
网状模型把每个数据节点和其他很多节点都连接起来,它的数据结构看起来就像很多城市之间的路网:
关系模型把数据看作是一个二维表格,任何数据都可以通过行号+列号来唯一确定,它的数据模型看起来就是一个Excel表:
相比层次模型和网状模型,关系模型理解和使用起来最简单并且也最流行。
3、关系型数据库
所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。我们可以使用关系型数据库管理系统(RDBMS)来存储和管理大数据量。
RDBMS 即关系型数据库管理系统(Relational Database Management System)的特点:
- 1.数据以表格的形式出现
- 2.每行为各种记录名称
- 3.每列为记录名称所对应的数据域
- 4.许多的行和列组成一张表单
- 5.若干的表单组成database
一个关系型数据是由一个个关系表组成的,对于一个关系表,除了定义每一列的名称外,还需要定义每一列的数据类型。关系数据库支持的标准数据类型包括数值、字符串、时间等:
名称 | 类型 | 说明 |
---|---|---|
INT | 整型 | 4字节整数类型,范围约+/-21亿 |
BIGINT | 长整型 | 8字节整数类型,范围约+/-922亿亿 |
REAL | 浮点型 | 4字节浮点数,范围约+/-1038 |
DOUBLE | 浮点型 | 8字节浮点数,范围约+/-10308 |
DECIMAL(M,N) | 高精度小数 | 由用户指定精度的小数,例如,DECIMAL(20,10)表示一共20位,其中小数10位,通常用于财务计算 |
CHAR(N) | 定长字符串 | 存储指定长度的字符串,例如,CHAR(100)总是存储100个字符的字符串 |
VARCHAR(N) | 变长字符串 | 存储可变长度的字符串,例如,VARCHAR(100)可以存储0~100个字符的字符串 |
BOOLEAN | 布尔类型 | 存储True或者False |
DATE | 日期类型 | 存储日期,例如,2018-06-22 |
TIME | 时间类型 | 存储时间,例如,12:20:59 |
DATETIME | 日期和时间类型 | 存储日期+时间,例如,2018-06-22 12:20:59 |
上面的表中列举了最常用的数据类型。很多数据类型还有别名,例如,REAL
又可以写成FLOAT(24)
。还有一些不常用的数据类型,例如,TINYINT
(范围在0~255)。各数据库厂商还会支持特定的数据类型,例如JSON
。
选择数据类型的时候,要根据业务规则选择合适的类型。通常来说,BIGINT
能满足整数存储的需求,VARCHAR(N)
能满足字符串存储的需求,这两种类型是使用最广泛的。
3.1、主流关系数据库
目前,主流的关系数据库主要分为以下几类:
- 商用数据库,例如:Oracle,SQL Server,DB2等;
- 开源数据库,例如:MySQL,PostgreSQL等;
- 桌面数据库,以微软Access为代表,适合桌面应用程序使用;
- 嵌入式数据库,以Sqlite为代表,适合手机应用和桌面程序。
4、关系模型
关系数据库是建立在关系模型上的。而关系模型本质上就是若干个存储数据的二维表,可以把它们看作很多Excel表。表的每一行称为记录(Record),记录是一个逻辑意义上的数据。表的每一列称为字段(Column),同一个表的每一行记录都拥有相同的若干字段。字段定义了数据类型(整型、浮点型、字符串、日期等),以及是否允许为NULL
。注意NULL
表示字段数据不存在。一个整型字段如果为NULL
不表示它的值为0
,同样的,一个字符串型字段为NULL
也不表示它的值为空串''
。
和Excel表有所不同的是,关系数据库的表和表之间需要建立“一对多”,“多对一”和“一对一”的关系,这样才能够按照应用程序的逻辑来组织和存储数据。
例如,班级表和学生表的关系:一个班级表的每一行对应着一个班级,而一个班级又对应着多个学生,所以班级表和学生表就是“一对多”的关系。反过来,如果我们先在学生表中定位了一行记录,要确定他的班级,只需要根据这条记录的“班级ID”对应的值找到班级表中相同ID对应
的记录即可确定班级。所以,学生表和班级表是“多对一”的关系。
在关系数据库中,关系是通过主键和外键来维护的。
4.1、主键
在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。
主键不能为空 NULL 、不能重复并且一个表中只能有一个主键,当然一个主键可以由多个字段组成。对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。
由于主键的作用十分重要,如何选取主键会对业务开发产生重要影响。如果我们以学生的身份证号作为主键,似乎能唯一定位记录。然而,身份证号也是一种业务场景,如果身份证号升位了,或者需要变更,作为主键,不得不修改的时候,就会对业务产生严重影响。所以选取主键的一个基本原则是:不使用任何业务相关的字段作为主键。因此,身份证号、手机号、邮箱地址这些看上去可以唯一的字段,均不可用作主键。
作为主键最好是完全业务无关的字段,我们一般把这个字段命名为id
。常见的可作为id
字段的类型有:
-
自增整数类型:数据库会在插入数据时自动为每一条记录分配一个自增整数,这样我们就完全不用担心主键重复,也不用自己预先生成主键;
-
全局唯一GUID类型:使用一种全局唯一的字符串作为主键,类似
8f55d96b-8acc-4636-8cb8-76bf8abc2f57
。GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的,大部分编程语言都内置了GUID算法,可以自己预算出主键。
对于大部分应用来说,通常自增类型的主键就能满足需求。我们在students
表中定义的主键也是BIGINT NOT NULL AUTO_INCREMENT
类型。
(如果使用INT自增类型,那么当一张表的记录数超过2147483647(约21亿)时,会达到上限而出错。使用BIGINT自增类型则可以最多约922亿亿条记录)
4.1.1、联合主键
关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。对于联合主键,允许一列有重复,只要不是所有主键列都重复即可:
如果我们把上述表的id_num
和id_type
这两列作为联合主键,那么上面的3条记录都是允许的,因为没有两列主键组合起来是相同的。
没有必要的情况下,我们尽量不使用联合主键,因为它给关系表带来了复杂度的上升。
4.2、外键(FOREIGN KEY)
在一个表中,通过某个字段,可以把数据与另一张表关联起来,这个字段就称为外键。比如在students
表中,通过class_id
的字段可以关联到classes表,可以确定某个学生属于哪个班级,那么 class_id 就可以称为外键。
关系数据库通过外键可以实现一对多、多对多和一对一的关系。外键既可以通过数据库来进行定义设置约束,也可以不设置约束,仅依靠应用程序的逻辑来保证,这样速度会更快。
4.2.1、定义一个外键
外键并不是通过列名实现的,而是通过定义外键约束实现的:
ALTER TABLE students ADD CONSTRAINT fk_class_id -- 将外键命名为fk_class_id,名字可以任意命名 FOREIGN KEY (class_id) -- 指定class_id作为外键 REFERENCES classes (id); -- 指定外键关联到classes表的id列
通过定义外键约束,关系数据库可以保证无法插入无效的数据。即如果classes
表不存在id=99
的记录,students
表就无法插入class_id=99
的记录。
由于外键约束会降低数据库的性能,大部分互联网应用程序为了追求速度,并不设置外键约束,而是仅靠应用程序自身来保证逻辑的正确性。这种情况下,class_id
仅仅是一个普通的列,只是它起到了外键的作用而已。
有一些应用会把一个大表拆成两个一对一的表,目的是把经常读取和不经常读取的字段分开,以获得更高的性能。例如,把一个大的用户表分拆为用户基本信息表user_info
和用户详细信息表user_profiles
,大部分时候,只需要查询user_info
表,并不需要查询user_profiles
表,这样就提高了查询速度。
4.2.2、删除外键
要删除一个外键约束,也是通过ALTER TABLE
实现的:
ALTER TABLE students
DROP FOREIGN KEY fk_class_id;
注意:删除外键约束并没有删除外键这一列。删除列是通过DROP COLUMN ...
实现的。
4.3、约束(Constraints)
约束是在表中定义的用于维护数据库完整性的一些规则,用于限制加入表的列的数据,可以防止将错误的数据插入表中。若某个约束条件只作用于单独的列,可以将其定义为列约束也可定义为表约束;若某个约束条件作用域多个列,则必须定义为表约束。
SQL Server中的约束用来确保系统的完整性。一般约束可以分为:主键约束、外键约束、检查约束、默认约束、唯一约束、非空约束。
可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后也可以(通过 ALTER TABLE 语句)。
4.4、索引(INDEX)
在关系数据库中,如果有上万甚至上亿条记录,在查找记录的时候,想要获得非常快的速度,就需要使用索引。索引是关系数据库中对某一列或多个列的值进行预排序的数据结构。通过使用索引,可以让数据库系统不必扫描整个表,而是直接定位到符合条件的记录,这样就大大加快了查询速度。
例如,如果要经常根据 student 表的 score 列进行查询,就可以对 score 列创建索引:
ALTER TABLE students ADD INDEX idx_score (score); -- 使用列score创建一个名称为idx_score的索引。索引的名称是任意的 ALTER TABLE students ADD INDEX idx_name_score (name, score); -- 索引如果有多列,可以在括号里依次写上
索引的效率取决于索引列的值是否散列,即该列的值如果越互不相同,那么索引效率越高。反过来,如果某列存在大量相同重复的值,那么对该列创建索引也没有什么意义。
可以对一张表创建多个索引。索引的优点是提高了查询效率,缺点是在插入、更新和删除记录时,需要同时修改索引,因此,索引越多,插入、更新和删除记录的速度就越慢。
对于主键,关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。
数据库索引对于用户和应用程序来说都是透明的,即无论是否创建索引,对于用户和应用程序来说,使用关系数据库不会有任何区别。当我们在数据库中查询时,如果有相应的索引可用,数据库系统就会自动使用索引来提高查询效率,如果没有索引,查询也能正常执行,只是速度会变慢。因此,索引可以在使用数据库的过程中再逐步优化。
4.4.1、唯一索引
在设计关系数据表的时候,看上去唯一的列,例如身份证号、邮箱地址等,因为他们具有业务含义,因此不宜作为主键。但是,这些列根据业务要求,又具有唯一性约束:即不能出现两条记录存储了同一个身份证号。这个时候,就可以给该列添加一个唯一索引。
例如,我们假设students
表的name
不能重复:
ALTER TABLE students ADD UNIQUE INDEX uni_name (name); -- 添加唯一索引