基础-数据库
1、数据库基础
1.1 数据库系统和文件系统相比的优点
- 安全可靠
- 运行快速
- 有效完整
- 解决并发
- 简单方便
1.3 数据库三范式
- 1NF: R 的所有属性都不能再分解为更基本的数据单位。
- 2NF: R 的所有非主属性都依赖于 R 的关键属性,所有列都依赖于任意一组候选关键字。
- 3NF:每一列都与任意候选关键字直接相关而不是间接相关, 没有传递依赖。
- BCNF: 3NF 基础上, 关系 R 只有一个单属性,或 R 的子集都是单属性,则 R 满足 BCNF。
1.4 数据库表插入 100 个数据和 100 万个数据有何区别
100 数量级小,可以随意插入;
100 万数量级大,如果表里有索引,则索引更新代价很高,可采取先删除索引再插入,插入后再建索引的策略。
1.5 数据库数据能否无限插入
可以,大小受到主机内存的制约。数据量大时要先删索引。减少提交次数,即减少 IO 次数
1.6 数据库语言分类
- 查询 DQL: SELECT, FROM, WHERE
- 操纵 DML: INSERT, UPDATE, DELETE
- 定义 DDL: CREATE TABLE/VIEW/INDEX/SYN/CLUSTER
- 控制 DCL: GRANT, COMMIT
1.7 存储过程
预编译的SQL语句,优点是 允许封装设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。缺点是 可移植性差。
优点
- 功能强大,限制少。
- 集成度高,可执行一系列SQL语句。
- 存储过程内引用其它存储过程,可以简化一系列复杂语句。
- 创建时即在上进行编译,执行比单个SQL语句快。
- 可以有多个返回值,即多个输出参数,并且可以使用SELECT返回结果集。
1.8 触发器
一种特殊的存储过程,主要是 通过事件来触发而被执行 的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。
1.9 存储过程 VS 函数
存储过程 | 函数 | |
---|---|---|
限制 | 少 | 多 |
功能 | 复杂 | 针对性强 |
返回 | 可参数 | 仅值或者表对象 |
执行 | 作独立部分 | 作查询部分 |
1.10 视图 & 游标
视图:
一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,视图通常是有一个表或者多个表的行或列的子集。对视图的修改会影响基本表。它使得我们获取数据更容易,相比多表查询。
游标:
对查询出来的结果集作为一个单元来有效的处理。游标可以定在该单元中的特定行,从结果集的当前行检索一行或多行。可以对结果集当前行做修改。一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要。
1.11 视图的优缺点
优点:
- 视图可以有选择性的选取数据库里的一部分。
- 通过简单的查询可以从复杂查询中得到结果。
- 维护数据的独立性,试图可从多个表检索数据。
- 对于相同的数据可产生不同的视图
缺点:
性能:查询视图时,必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么就无法更改数据
1.12 drop、truncate、 delete区别
- drop 直接删掉表。
- truncate 删除表中数据,再插入时自增长id又从1开始。
- delete 删除表中数据,可以加where字句。
1.13 关系型数据库 VS 非关系型数据库
关系型数据库 | 非关系型数据库 | |
---|---|---|
定义 | 关系:一(多)对 一(多) | 列、键值对、文档 |
性能 | 低 | 高(基于键值对,不用SQL解析) |
可拓展 | 低拓展 | 高拓展 |
复杂查询 | 强 | 弱 |
事务支持 | 强 | 弱 |
SQL支持 | 提供 | 不提供 |
2、索引
2.0 索引
数据库管理系统中一个查找的数据结构,索引的实现通常使用B树及其变种B+树。
2.1 索引优点
- 协助快速查询、更新数据库表中数据。
- 保证数据唯一性。
- 加快检索速度(主要原因)
- 减少磁盘IO,可以直接定位。
- 使用优化隐藏器,提高系统的性能
- 加速表与表的连接
2.2 索引缺点
- 索引耗时,尤其数据量大时
- 索引占用物理空间
- 操作数据时需要动态维护索引,降低速度
2.3 索引类型
- 普通索引:最基本,无限制
- 唯一索引:值必须唯一,可以有空值。
- 主键索引:不可以有空值,且表中只存在一个该值。
- 组合索引:多列值组成一个索引,用于组合搜索,效率高于索引合并。
- 全文索引:对文本的内容进行分词,进行搜索。
2.4 聚簇索引
聚簇索引:表记录的排列顺序和索引的排列顺序一致,所以查询效率快
非聚簇索引:指定了表中记录的逻辑顺序,但是记录的物理和索引不一定一致
优点
- 数据访问更快
- 查找速度快。
缺点
- 插入速度严重依赖于插入顺序
- 更新主键的代价很高
- 二级索引访问要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
2.5 哈希 VS B + 树
特点
- Hash检索效率非常高,一次定位
- B+ 需要从根节点到枝节点,最后才能访问到页节点
为什么不都用Hash索引而使用B+树索引?
- Hash仅能满足"=",“IN"和”"查询,不能范围查询
- Hash无法排序,因为Hash值的大小关系不一定和运算前的一样
- Hash不能利用部分索引键查询,对于组合索引无法单独计算Hash值
- 不同索引键存在相同Hash值,即使取满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要回表查询数据;
- 大量Hash值相等的情况后性能并不一定就会比B+树索引高。
2.6 B 树和 B+树
- B树,每个非叶节点都存储key和data,所有节点组成这棵树,并且叶子节点无信息。
- B+树,所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。
3、事务
3.1 定义
事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。主要用来保证数据的完整性和一致性。
3.2 事物特性 ACID
- 原子性(Atomicity): 事务操作不可分割,要么全部成功提交,要么失败回滚。
- 一致性(Consistency): 也就是说事务前后数据的完整性必须保持一致。(总值不变)
- 隔离性(Isolation): 一个事务的执行不能有其他事务的干扰,事务的内部操作和使用数据对其他的并发事务是隔离的,互不干扰。
- 持久性(Durability):持久性是指一个事务一旦提交,对数据库中数据的改变就是永久性的。此时即使数据库发生故障,修改的数据也不会丢失。
3.3 事务的并发问题
- **脏读:**事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
- **不可重复读:**事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致。
- **幻读:**第一个事务对一个表中的数据进行了修改。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
不可重复读的和幻读很容易混淆,**不可重复读侧重于修改,幻读侧重于新增或删除。**解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
3.4 四种隔离级别
- **读未提交:**当一个事务开始写数据,另一个事务不准写,可以读。
- **读提交:**事务中的修改只有提交以后才能被读。
- **可重复读:**多次读时未读完不能被访问,保证每次读一样。
- **可序化:**要求事务一个个序列化执行,不能并发执行
3.5 嵌套事务
子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点 save point。
如果子事务回滚,会发生什么?
父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务,父事务之前的操作不会受到影响,更不会自动回滚。
如果父事务回滚,会发生什么?
父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。那么:
事务的提交,是什么情况?
是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。
4、优化
4.1 查询语句不同元素执行顺序
select–from–where–group by–having–order by
其中select和from是必须的,其他关键词是可选的
- **from:**需要从哪个数据表检索数据
- **where:**过滤表中数据的条件
- **group by:**如何将上面过滤出的数据分组
- **having:**对上面已经分组的数据进行过滤的条件
- **select:**查看结果集中的哪个列,或列的计算结果
- **order by:**按照什么样的顺序来查看返回的数据
5、数据库锁
5.1 悲观锁 & 乐观锁
悲观锁有点像是一位比较悲观(也可以说是未雨绸缪)的人,总是会假设最坏的情况,避免出现问题。乐观锁有点像是一位比较乐观的人,总是会假设最好的情况,在要出现问题之前快速解决问题。
相比于乐观锁,悲观锁对性能的影响更大
悲观锁
先上锁再操作,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
悲观锁通常多用于多写场景,避免频繁失败和重试影响性能。
乐观锁
先执行,提交的时候再验证,不行回滚。
乐观锁通常用于多读场景(少写),避免频繁加锁影响性能,大大提升了系统的吞吐量。