数据库原理 - 面试准备

数据库

存储过程

  • 存储过程是一个预编译的代码块,执行效率比较高
  • 一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率
  • 可以一定程度上确保数据安全

索引

索引定义

索引能快速找到某一列中有一特定值的行。不必挨个儿去查看记录的内容。索引是对数据库中一列或者多列的值进行排序的一种数据结构,以索引文件的形式存储在磁盘上,占据一定的物理空间

索引优点

1.提高查询的性能,大大减少表的检索行数
2.可以建立唯一索引或者主键索引,保证数据库中每一行数据的唯一性
3.加速表与表之间的连接
4.在使用分组group by和排序order by子句进行数据检索时,可以显著减少查询中分组和排序的时间(数据库的记录会重新排序)

索引缺点

1.索引文件占据物理空间(空间)
2.对表中数据进行增删改查时,索引也要动态地维护,降低了数据的维护速度(时间)

索引的分类

  • 唯一索引 数据列不允许重复但是允许为null,一个表允许多列创建多个唯一索引
  • 主键索引 数据列不允许重复,也不允许为null,一个表中只能有一个主键,但是可以有多个列共同组成的联合主键
  • 普通索引 没有唯一性的限制,允许为null,只是简单的加速查询
  • 联合索引 多个索引的组合,必须满足最左前缀原则
  • 全文索引 查找全文中的关键字, mysql的Innodb引擎不支持 myISAM引擎支持

索引设计原则

1.对查询频次较高、数据量较大的表建立索引
2.使用唯一索引,区分度越高,使用索引的效率越高
3.使用短索引,减少存储空间,提升I/O效率
4.利用最左前缀,在组合索引中比如有(name,city,age)的话,只支持(name)、(name,city)、(name,city,age)这三种组合的检索,查询时必须包含索引的最左列,不能跳过某个字段进行查询
5.为经常需要排序 分组和联合操作的字段建立索引
6.限制索引的数目,索引并非越多越好

索引失效的场景

  • 复合索引不满足最左前缀原则
  • 模糊查找时like ‘%'以%开头
  • where索引列有运算
  • where索引列有函数
  • mysql估计用全表扫描要比用索引更快,则不使用索引
  • 查询条件中有or的话可能会造成索引失效,除非or的每个列都加上索引

不推荐使用索引的场景

  • 数据唯一性比较差,重复比较多的情况下不要使用索引
  • 频繁更新的字段不适用索引(导致索引维护困难)

索引的数据结构

1.B树

  • 优点:层级结构较低,且冗余节点较少

2.B+树

  • n叉B+树最多含有n个key,B树最多含有n-1个key
  • B+树的叶子节点保存所有的键值信息和数据,依key大小排序,所有的非叶子节点只存储键值,所有的叶子节点都通过指针连接在一起,形成了一个有序链表(支持翻页)。(B树中每个节点都存储有键值和数据)
  • 支持翻页:每个磁盘块存储一个节点,称为一页。连续查询多个节点则称为翻页
  • 优势:相同数据集来说B+树的层级结构比二叉树、B树小,因此搜索速度更快;查询任何key都要从root走到叶子,因此查询效率更稳定;所有数据均有序存储在叶子节点,使得范围查找、排序查找、去重查找变得简单易行(B树数据分布在各个节点,包括非叶子节点,不便于范围等查找)
  • 缺陷:因为有冗余节点数据,因此会造成内存的浪费

3.hash

  • 特点:
    1.hash表是key-value形式,通过一个散列函数,能够根据key快速找到对应的value
    2.检索时无需使用树状结构那样从根节点到叶子节点逐级查找,只需要一次hash算法即可定位到相应位置,速度较快
  • hash索引的缺点
    1.hash索引只能够进行单值查找,不支持范围查询,而B+树支持范围查询(hash函数过滤后的键值大小关系不能保证和源数据的大小关系一致)
    2.hash索引不能利用索引完成排序,以及像like 'xxx%'这样的模糊查询(本质上也是一种范围查询)
    3.hash索引不支持多列联合索引的最左匹配原则
    4.hash索引在重复值较高的时候,因为存在哈希碰撞导致性能极低。
    5.hash索引只适用于存储数据重复度很低、对数据等值查询、无排序和范围查询的情况,效率较高。

事务以及四大特性

  • 事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
    • 原子性(Atomicity):事务中的所有元素作为一个整体提交或回滚,事务的个元素是不可分的,事务是一个完整操作。
      • 保持事务的原子性是指操作发生异常时,需要对该事务所有之前执行过的操作进行回滚。首先要设置autocommit=0,就是默认不能隐式提交,需要手动commit提交。回滚需要undo日志实现,undo日志存放之前修改过的记录,事务发生异常触发roll back,会按照日志逻辑回滚undo日志的操作。
    • 一致性(Consistemcy):事物完成时,数据必须是一致的,也就是说,和事物开始之前,数据存储中的数据处于一致状态。保证数据的无损。
      • 一致性可以理解为事务对数据完整性约束的遵循。事务执行前后都是合法的数据状态,不会违背任何数据完整性。从数据库层面,数据库通过原子性、隔离性、持久性来保持一致性。
    • 隔离性(Isolation):对数据进行修改的多个事务是彼此隔离的。这表明事务必须是独立的,不应该以任何方式以来于或影响其他事务。
      • 用锁和隔离机制。锁是需要用户自己定义的,隔离机制是数据库提供的。
    • 持久性(Durability):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库
      • 即使数据库系统遇到故障也不会丢失已提交事务的操作,通过redo日志来实现的。基本步骤如下图 :①当在事务中尝试对数据进行更改时;②首先将数据从磁盘读入内存,更新内存缓存的数据。③生成一条redo日志缓存,放在redo日志的缓冲区;④事务真正提交时将缓冲区中的日志写入redo日志做持久化保存;⑤把内存中的数据同步到磁盘上。

隔离级别

在并发状态下,事务会出现一些问题,主要有三种问题:

  • 脏读 一个事务能读到另外一个事务没有提交的数据。(举例:A给B转了100块,但是A转完并没有提交该事务,B读到了自己的账户多了100块,此时A发现转账错误之后就回滚了该操作,此时就称为脏读)
  • 不可重复读 一个事务的两次查询操作数据不一致,可能是两次查询过程中插入了一个事务更新了原有的数据(举例:两个并发事务A和B,A首先查询自己的账户是100块,B此时提走了A账户的50块,A再次查询发现此时账户只剩下了50块,两次查询操作结果不同)
  • 幻读 在一个事务的两次查询中数据不一致,发现了原来没有的数据或者原有的数据不见了
  • 不可重复读与幻读相似,不可重复读侧重于另一个事务对数据库的修改操作,而幻读则侧重于另一个事务对数据库的增加和删除操作

四个隔离级别:

  • 读未提交
    允许读取另一个事务尚未提交的数据,可能会造成脏读、不可重复读、幻读
  • 读已提交
    允许读取并发事务已经提交了的数据,可以阻止脏读,但是不能避免不可重复读和幻读
  • 可重复读
    在一个事务的操作过程中,不能读取到别的事务对该数据库的修改增删操作,可以阻止脏读和不可重复读,但是不能避免幻读(mysql默认级别)
  • 串行化
    所有的事务依次逐个执行,当表被一个事务操作时,其他事务的操作不可以进行,进入排队状态,等待当前操作事务提交后才能继续执行操作。

并发控制

悲观乐观锁

使用方式分为乐观锁、悲观锁

  • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
    • MySQL:for update语句,行级锁,表级锁
  • 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
    • 数据版本,时间戳

粒度分类

按粒度分为表级锁、行级锁、页级锁 (InnoDB支持行级锁、表锁,MyISAM只支持表锁)

  • 锁的粒度越小,系统开销越大,但相应的并发性就越高。因此选择锁粒度的时候需要在系统开销和并发性间权衡。

标准行级锁:

  • 互斥锁/写锁/X锁 + 共享锁/读锁/S锁
  • 仅共享锁互相兼容

InnoDB中支持意向锁:

  • IS + IX,实现为表级别锁
  • 由于InnoDB存储引擎支持的是行级别的锁, 因此意向锁其实不会阻塞除全表扫以外的任何请求。

一致性的非锁定读

InnoDB存储引擎通过行多版本控制(multi versioning) 的方式来读取当前执行时间数据库中行的数据。 如果读取的行正在执行DELETE或UPDATE操作, 这时读取操作不会因此去等待行上锁的释放。 相反地, InnoDB存储引擎会去读取行的一个快照数据。

在事务隔离级别READ COMMITTED和REPEATABLE READ(InnoDB存储引擎的默认事务隔离级别) 下, InnoDB存储引擎使用非锁定的一致性读。

  • 然而, 他们对于快照数据的定义却不相同。 在READ COMMITTED事务隔离级别下, 对于快照数据, 非一致性读总是读取被锁定行的最新一份快照数据。
  • 而在REPEATABLE READ事务隔离级别下, 对于快照数据, 非一致性读总是读取事务开始时的行数据版本。

一致性锁定读

在默认配置下, 即事务的隔离级别为REPEATABLE READ模式下, InnoDB存储引擎的SELECT操作使用一致性非锁定读。 但是在某些情况下, 用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。 而这要求数据库支持加锁语句, 即使是对于SELECT的只读操作。 InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read) 操作:

  • SELECT…FOR UPDATE:对读取的行记录加一个X锁, 其他事务不能对已锁定的行加上任何锁。
  • SELECT…LOCK IN SHARE MODE:对读取的行记录加一个S锁, 其他事务可以向被锁定的行加S锁, 但是如果加X锁, 则会被阻塞。

行锁算法

❑ Record Lock: 单个行记录上的锁

  • Record Lock总是会去锁住索引记录, 如果InnoDB存储引擎表在建立的时候没有设置任何一个索引, 那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。

❑ Gap Lock: 间隙锁, 锁定一个范围, 但不包含记录本身

  • Gap Lock的作用是为了阻止多个事务将记录插入到同一范围内, 而这会导致Phantom Problem问题的产生。

❑ Next-Key Lock∶ Gap Lock+Record Lock, 锁定一个范围, 并且锁定记录本身

  • Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法
  • 在Next-Key Lock算法下, InnoDB对于行的查询都是采用这种锁定算法。采用Next-Key Lock的锁定技术称为Next-Key Locking。 其设计的目的是为了解决Phantom Problem。
  • 当查询的索引含有唯一属性时, InnoDB存储引擎会对Next-KeyLock进行优化, 将其降级为Record Lock, 即仅锁住索引本身, 而不是范围。

Phantom Problem

在默认的事务隔离级别下, 即REPEATABLE READ下, InnoDB存储引擎采用Next-Key Locking机制来避免Phantom Problem(幻像问题) 。 这点可能不同于与其他的数据库, 如Oracle数据库, 因为其可能需要在SERIALIZABLE的事务隔离级别下才能解决Phantom Problem。

丢失更新

丢失更新是另一个锁导致的问题, 简单来说其就是一个事务的更新操作会被另一个事务的更新操作所覆盖, 从而导致数据的不一致。 例如:
1) 事务T1将行记录r更新为v1, 但是事务T1并未提交。
2) 与此同时, 事务T2将行记录r更新为v2, 事务T2未提交。
3) 事务T1提交。
4) 事务T2提交。

但是, 在当前数据库的任何隔离级别下, 都不会导致数据库理论意义上的丢失更新问题。 这是因为, 即使是READ UNCOMMITTED的事务隔离级别, 对于行的DML操作, 需要对行或其他粗粒度级别的对象加锁。 因此在上述步骤2) 中, 事务T2并不能对行记录r进行更新操作, 其会被阻塞, 直到事务T1提交。
虽然数据库能阻止丢失更新问题的产生, 但是在生产应用中还有另一个逻辑意义的丢失更新问题, 而导致该问题的并不是因为数据库本身的问题。 实际上, 在所有多用户计算机系统环境下都有可能产生这个问题。

简单地说来, 出现下面的情况时, 就会发生丢失更新:
1) 事务T1查询一行数据, 放入本地内存, 并显示给一个终端用户
User1。
2) 事务T2也查询该行数据, 并将取得的数据显示给终端用户User2。
3) User1修改这行记录, 更新数据库并提交。
4) User2修改这行记录, 更新数据库并提交。

显然, 这个过程中用户User1的修改更新操作“丢失”了, 而这可能会导致一个“恐怖”的结果。 设想银行发生丢失更新现象, 例如一个用户账号中有10 000元人民币, 他用两个网上银行的客户端分别进行转账操作。第一次转账9000人民币, 因为网络和数据的关系, 这时需要等待。 但是这时用户操作另一个网上银行客户端, 转账1元, 如果最终两笔操作都成功了, 用户的账号余款是9999人民币, 第一次转的9000人民币并没有得到更新, 但是在转账的另一个账号却会收到这9000元, 这导致的结果就是钱变多, 而账不平。
要避免丢失更新发生, 需要让事务在这种情况下的操作变成串行化, 而不是并行的操作。 即在上述四个步骤的1) 中, 对用户读取的记录加上一个排他X锁。 同样, 在步骤2) 的操作过程中, 用户同样也需要加一个排他X锁。 通过这种方式, 步骤2) 就必须等待一步骤1) 和步骤3)完成, 最后完成步骤4) 。 表6-17所示的过程演示了如何避免这种逻辑上丢失更新问题的产生。

drop、delete与truncate

  • delete和truncate只删除表的数据不删除表的结构
  • 速度,一般来说: drop> truncate >delete
  • delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效,如果有相应的trigger,执行的时候将被触发。
  • truncate,drop是ddl,操作立即生效,原数据不放到rollback segment中,不能回滚,操作不触发trigger

超键、候选键、主键、外键

  • 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
  • 候选键:是最小超键,即没有冗余元素的超键。
  • 主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
  • 外键:在一个表中存在的另一个表的主键称此表的外键。

视图

  • 视图是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。使得我们获取数据更容易。
  • 相比多表查询:
    • 只暴露部分字段给访问者,所以就建一个虚表,就是视图。
    • 查询的数据来源于不同的表,而查询者希望以统一的方式查询,这样也可以建立一个视图,把多个表查询结果联合起来,查询者只需要直接从视图中获取数据,不必考虑数据来源于不同表所带来的差异。
  • 要通过视图更新基本表数据,必须保证视图是可更新视图,即可以在INSET、UPDATE或DELETE等语句当中使用它们。对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系。还有一些特定的其他结构,这类结构会使得视图不可更新。
    如果视图包含下述结构中的任何一种,那么它就是不可更新的:
    • 聚合函数;
    • DISTINCT关键字;
    • GROUP BY子句;
    • ORDER BY子句;
    • HAVING子句;
    • UNION运算符;
    • 位于选择列表中的子查询;
    • FROM子句中包含多个表;
    • SELECT语句中引用了不可更新视图;
    • WHERE子句中的子查询,引用FROM子句中的表;
    • ALGORITHM 选项指定为TEMPTABLE(使用临时表总会使视图成为不可更新的)。

三范式

  • 范式:一张数据表的表结构所符合的某种设计标准的级别
  • 1NF的定义为:符合1NF的关系中的每个属性都不可再分。
  • 2NF在1NF的基础之上,消除了非主属性对于码的部分函数依赖。(存在组合关键字中的某些字段决定非关键字段的情况)
  • 3NF在2NF的基础之上,消除了非主属性对于码的传递函数依赖。(关键字段 → 非关键字段 x → 非关键字段y)

Join

在这里插入图片描述

  • INNER JOIN:如果表中有至少一个匹配,则返回行
  • LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN:只要其中一个表中存在匹配,则返回行

猜你喜欢

转载自blog.csdn.net/qq_42739587/article/details/115934378