现在关于Java面试的资料是层出不穷,对于选择困难症的同学来说,无疑是陷入了一次次的抉择与不安中,担心错过了关键内容,现在小曾哥秉持着"融百家之所长,汇精辟之文档"的思想,整理一下目前主流的一些八股文,以达到1+1 > 2 的效果!
上面名词傻傻分不清,一问搞定
介绍主键和外键例子
学生表(学号,姓名,性别,班级),其中每个学生的学号是唯一的,学号就是一个主键;
用户表(用户名、密码、用户级别),其中用户名是唯一的, 用户名就是一个主键
在成绩表(学号,课程号,成绩) 中,成绩表中单一一个属性无法唯一标识一条记录,学号和课程号的组合才可以唯一标识一条记录,所以 学号和课程号的属性组是一个主键 。
成绩表中的学号不是成绩表的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键。
第一范式:数据库表中的每一列都不可再分,也就是原子性
>上述不满足原子性原则,所谓原子就是最小的,不能再把它进行划分了,上述可以将部门岗位进行划分为下面的表
第二范式:在满足第一范式基础上要求每个字段都和主键完整相关,而不是仅和主键部分相关(主要针对联合主键而言)
第二范式的另一种表述方式是:两张表要通过外键关联,不保存冗余字段。
注意:如果不是联合主键(两个字段共同充当表的主键),不存在不遵守第二范式的问题。
“订单详情表”使用**“订单编号”和“产品编号”**作为联合主键。
此时“产品价格”、 “产品数量”都和联合主键整体相关,但“订单金额”和“下单时间” 只和联合主键中的“订单编号”相关,和“产品编号”无关。所以只关联了主键中的部分字段,不满足第二范式。
修改后如下图:
第三范式:表中的非主键字段和主键字段直接相关,不允许间接相关
上面表中的“部门名称”和“员工编号”的关系是“员工编号”→“部门编号” →“部门名称”,不是直接相关。此时会带来下列问题:
1.数据冗余:“部门名称”多次重复出现。
2.插入异常:组建一个新部门时没有员工信息,也就无法单独插入部门信息。就算强行插入部门信息,员工表中没有员工信息的记录同样是非法记录。
3. 删除异常:删除员工信息会连带删除部门信息导致部门信息意外丢失。
4.更新异常:哪怕只修改一个部门的名称也要更新多条员工记录。
解决方案:把上表拆分成两张表,以外键形式关联
一个完整的SELECT语句包括6个子句,其中前面的2个子句是必不可少的,其他子句可以省略。
SELECT 语句的完整格式如下:
SELECT [DISTINCT] 目标列名序列-----要查看的列
FROM 表名或视图名------------------数据来源
[WHERE 条件表达式]----------------查询条件
[GROUP BY + 列名] --------------------分组依据
[HAVING + 组条件表达式] ---------分组条件
[ORDER BY + 列名 + [ASC|DESC]序列]----排序依据
主要包含四个部分(基础查询、操作符、高级查询、多表查询),详细的可以看到MySQL快速入门实例篇(入内不亏)
常用的聚合函数有COUNT()、AVG()、SUM()、MAX()、MIN(),下面以MySQL为例,说明这些函数的作用。
COUNT()函数,它有两种用法:
COUNT(*)计算表中总的行数,不管某列是否有数值或者为空值。
COUNT(字段名)计算指定列下总的行数,计算时将忽略空值的行。
COUNT()函数可以与GROUP BY一起使用来计算每个分组的总和。
AVG()函数可以与GROUP BY一起使用,来计算每个分组的平均值。
SUM()可以与GROUP BY一起使用,来计算每个分组的总和。
MAX()也可以和GROUP BY关键字一起使用,求每个分组中的最大值。 MAX()函数不仅适用于查找数值类型,也可应用于字符类型。
MIN()也可以和GROUP BY关键字一起使用,求出每个分组中的最小值。
MIN()函数与MAX()函数类似,不仅适用于查找数值类型,也可应用于字符类型。
概念:关系型数据库就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。
一对多关联:这种关联形式最为常见,一般是两张表具有主从关系,并且以主表的主键关联从表的外键来实现这种关联关系。另外,以从表的角度来看,它们是具有多对一关系的,所以不再赘述多对一关联了。
多对多关联:这种关联关系比较复杂,如果两张表具有多对多的关系,那么它们之间需要有一张中间表来作为衔接,以实现这种关联关系。这个中间表要设计两列,分别存储那两张表的主键。因此,这两张表中的任何一方,都与中间表形成了一对多关系,从而在这个中间表上建立起了多对多关系。
自关联:自关联就是一张表自己与自己相关联,为了避免表名的冲突,需要在关联时通过别名将它们当做两张表来看待。一般在表中数据具有层级(树状)时,可以采用自关联一次性查询出多层级的数据。
下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到客户端的一条 SQL 语句在 MySQL 内部是如何执行的
1.应用程序把查询SQL语句发送给服务器端执行;
2.查询缓存,如果查询缓存是打开的,服务器在接收到查询请求后,并不会直接去数据库查询,而是在数据库的 查询缓存中找是否有相对应的查询数据,如果存在,则直接返回给客户端。只有缓存不存在时,才会进行下面 的操作;
3.查询优化处理,生成执行计划。这个阶段主要包括解析SQL、预处理、优化SQL执行计划;
4. MySQL根据相应的执行计划完成整个查询;
5.将查询结果返回给客户端。
从上图可以看出, MySQL 主要由下面几部分构成:
连接器: 身份认证和权限相关(登录 MySQL 的时候)。
查询缓存: 执行查询语句的时候,会先查询缓存
分析器: 没有命中缓存的话,SQL语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
优化器: 按照 MySQL认为最优的方案去执行。
执行器: 执行语句,然后从存储引擎返回数据。 执行语句之前会先判断是否有权限,如果没有权限的话,就会报错。
插件式存储引擎 : 主要负责数据的存储和读取,采用的是插件式架构,支持 InnoDB MyISAM、Memory 等多种存储引擎。
概念:索引是一个单独的、存储在磁盘上的数据库结构,包含着对数据表里所有记录的引用指针。
功能:可以快速找出在某个或多个列中有一特定值的行,提高数据的查询效率。
通俗理解:正如新华字典,我想查询"索引"这个词,那我得首先根据字典目录找到对应偏旁或者拼音,下一步再对应到具体的字,而索引本身的含义就类似于新华字典中目录,而新华字典对应的也就是数据库中的表。
优点:高效
缺点:耗费时间和空间成本
建立索引的原则:
1.在最频繁使用的、用以缩小查询范围的字段上建立索引;
2.在频繁使用的、需要排序的字段上建立索引。
不适合建立索引的情况:
1.对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引;
2.对于一些特殊的数据类型,不宜建立索引,比如:文本字段(text) 等。
MySQL的索引可以分为以下几类:
普通索引和唯一索引
普通索引是MySQL中的基本索引类型,允许在定义索引的列中插入重复值和空值。
唯一索引要求索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
主键索引是一种特殊的唯一索引,不允许有空值。
单列索引和组合索引
单列索引即一个索引只包含单个列,一个表可以有多个单列索引。
组合索引是指在表的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用。使用组合索引时遵循最左前缀集合。
全文索引
全文索引类型为FULLTEXT,在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复值和空值。全文索引可以在CHAR、VARCHAR或者TEXT类型的列上创建。
空间索引
空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING和POLYGON。MySQL使用SPATIAL关键字进行扩展,使得能够用创建正规索引类似的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MyISAM的表中创建。
在MySQL中,索引是在存储引擎层实现的,不同存储引擎对索引的实现方式是不同的,下面我们探讨一下MyISAM和InnoDB两个存储引擎的索引实现方式。
MyISAM索引实现:
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,MyISAM索引的原理图如下。这里假设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。
InnoDB索引实现:
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
第一个重大区别是InnoDB的数据文件本身就是索引文件:从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。
索引的数据结构和具体存储引擎的实现有关,在MySQL 中使用较多的索引有Hash索引、B+树索引等。而我们经常使用的InnoDB存储引擎的默认索引实现为B+树索引。
B+树由B树和索引顺序访问方法演化而来,它是为磁盘或其他直接存取辅助设备设计的一种平衡查找树,在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点,各叶子节点通过指针进行链接。
用B+树不用B树考虑的是IO对性能的影响,B树的每个节点都存储数据,而B+树只有叶子节点才存储数据,所以查找相同数据量的情况下,B树的高度更高,IO更频繁。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。
在InnoDB存储引擎中,可以将B+树索引分为聚簇索引和辅助索引(非聚簇索引)
聚簇索引是根据主键创建的一棵B+树,聚簇索引的叶子节点存放了表中的所有记录。
辅助索引是根据索引键创建的一棵B+树,与聚簇索引不同的是,其叶子节点仅存放索引键值,以及该索引键值指向的主键。
从数据结构角度:树索引、Hash索引。
从物理存储角度:1.聚集索引(clustered index);2.非聚集索引(non-clustered index)
从逻辑角度:1.普通索引 2.唯一索引 3.主键索引 4.联合索引 5.全文索引
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
经典例子:转账,假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作,这两个操作必须都成功或者都失败。
- 将小明的余额减少 1000 元
- 将小红的余额增加 1000 元。
事务会把这两个操作就可以看成逻辑上的一个整体,这个整体包含的操作要么都成功,要么都要失败。这样就不会出现小明余额减少而小红的余额却并没有增加的情况。
只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!
并发情况下,读操作可能存在的三类问题:
细节请参考下图:
SQL 标准定义了四种隔离级别,这四种隔离级别分别是:
MySQL默认采用的REPEATABLE_ READ隔离级别
数据库并发场景:
1.读-读:不存在任何问题,也不需要并发控制;
2.读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读;
3.写-写:有线程安全问题,可能会存在更新丢失问题。
多版本并发控制(MVCC) 是一种用来解决读写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本, 版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。
MVCC可以为数据库解决以下问题:
1.在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能;
2.同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题。
MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。
SERIALIZABLE 隔离级别,是通过锁来实现的。除了 SERIALIZABLE 隔离级别,其他的隔离级别都是基于 MVCC 实现。
MyISAM 仅仅支持表级锁(table-level locking),一锁就锁整张表,这在并发写的情况下性非常差。
InnoDB 不光支持表级锁(table-level locking),还支持行级锁(row-level locking),默认为行级锁。行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。
表级锁和行级锁对比 :
InnoDB存储引擎实现了如下两种标准的行级锁:
共享锁(S Lock),允许事务读一行数据。
排他锁(X Lock),允许事务删除或更新一行数据。
SELECT … LOCK IN SHARE MODE;
SELECT … FOR UPDATE;
下图显示了共享锁和排他锁的兼容性,可以发现X锁与任何的锁都不兼容,而S锁仅和S锁兼容。需要特别注意的是,S和X锁都是行锁,兼容是指对同一记录(row)锁的兼容性情况。
如果需要用到表锁的话,如何判断表中的记录没有行锁呢?一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东东来快速判断是否可以对某个表使用表锁。
InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力作用,事务都将无法推进下去。
解决死锁的方案:
1、超时机制:当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
2、wait-for graph(等待图):这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息:锁的信息链表;事务等待链表;
通过上述链表可以构造出一张图,而在这个图中若存在回路,就代表存在死锁,因此资源间相互发生等待。这是一种较为主动的死锁检测机制,在每个事务请求锁并发生等待时都会判断是否存在回路,若存在则有死锁,通常来说InnoDB存储引擎选择回滚undo量最小的事务。
MySQL提供了多个不同的存储引擎,包括处理事务安全表的引擎和处理非事务安全表的引擎。在MySQL中,不需要在整个服务器中使用同一种存储引擎,针对具体的要求,可以对每一个表使用不同的存储引擎。
最常用的引擎是InnoDB和MyISAM:
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键
MyISAM拥有较高的插入、查询速度,但不支持事务
1.是否支持行级锁
MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
也就说,MyISAM 一锁就是锁住了整张表,这在并发写的情况下是多么滴憨憨啊!这也是为什么 InnoDB 在并发写的时候,性能更牛皮了!
2.是否支持事务
MyISAM 不提供事务支持。
InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别,具有提交(commit)和回滚(rollback)事务的能力。并且,InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC 和 Next-Key Lock)。
3.是否支持外键
MyISAM 不支持,而 InnoDB 支持。
外键对于维护数据一致性非常有帮助,但是对性能有一定的损耗。因此,通常情况下,我们是不建议在实际生产项目中使用外键的,在业务代码中进行约束即可!
4.是否支持数据库异常崩溃后的安全恢复
MyISAM 不支持,而 InnoDB 支持。
使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log 。
5.索引实现不一样。
虽然 MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
InnoDB 引擎中,其数据文件本身就是索引文件。相比 MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。
二进制日志文件就是常说的binlog。二进制日志记录了MySQL所有修改数据库的操作,然后以二进制的形式记录在日志文件中,其中还包括每条语句所执行的时间和所消耗的资源,以及相关的事务信息。
默认情况下,二进制日志功能是开启的,启动时可以重新配置–log-bin[=file_name]选项,修改二进制日志存放的目录和文件名称。
重做日志用来实现事务的持久性,即事务ACID中的D。
它由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),它是持久的。redo log用来保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。
重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要undo。因此在对数据库进行修改时,InnoDB存储引擎不但会产生redo,还会产生一定量的undo。这样如果用户执行的事务或语句由于某种原因失败了,又或者用户用一条ROLLBACK语句请求回滚,就可以利用这些undo信息将数据回滚到修改之前的样子。
redo存放在重做日志文件中,与redo不同,undo存放在数据库内部的一个特殊段(segment)中,这个段称为undo段(undo segment),undo段位于共享表空间内。
欢迎各位小伙伴们阅读以下内容,定能收获满满!
参考文档: