【Q3——30min】

1、介绍一下数据库的三大范式
第一范式(1NF):属性不可分割,即每个属性都是不可分割的原子项。(实体的属性即表中的列)
第二范式(2NF):满足第一范式;且不存在部分依赖,即非主属性必须完全依赖于主属性。(主属性即主键;完全依赖是针对于联合主键的情况,非主键列不能只依赖于主键的一部分)
第三范式(3NF):满足第二范式;且不存在传递依赖,即非主属性不能与非主属性之间有依赖关系,非主属性必须直接依赖于主属性,不能间接依赖主属性。(A -> B, B ->C, A -> C)

2、介绍一下数据库的隔离级别
事务的特性(ACID):
原子性(Atomicity):原子性是指一个事务中的操作,要么全部成功,要么全部失败,如果失败,就回滚到事务开始前的状态。
一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。那转账举栗子,A账户和B账户之间相互转账,无论如何操作,A、B账户的总金额都必须是不变的。
隔离性(Isolation):隔离性是当多个用户并发的访问数据库时,如果操作同一张表,数据库则为每一个用户都开启一个事务,且事务之间互不干扰,也就是说事务之间的并发是隔离的。再举个栗子,现有两个并发的事务T1和T2,T1要么在T2开始前执行,要么在T2结束后执行,如果T1先执行,那T2就在T1结束后在执行。关于数据的隔离性级别,将在后文讲到。
持久性(Durability):持久性就是指如果事务一旦被提交,数据库中数据的改变就是永久性的,即使断电或者宕机的情况下,也不会丢失提交的事务操作。
事务的隔离性(Isolation)是指,多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离。
如果不考虑隔离性,会发生什么事呢?
1.脏读:
脏读是指一个事务在处理数据的过程中,读取到另一个为提交事务的数据。
2.不可重复读:
不可重复读是指对于数据库中的某个数据,一个事务范围内的多次查询却返回了不同的结果,这是由于在查询过程中,数据被另外一个事务修改并提交了。
不可重复读和脏读的区别是,脏读读取到的是一个未提交的数据,而不可重复读读取到的是前一个事务提交的数据。
而不可重复读在一些情况也并不影响数据的正确性,比如需要多次查询的数据也是要以最后一次查询到的数据为主。
3.幻读
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
总的来说,解决不可重复读的方法是锁行,解决幻读的方式是锁表。
四种隔离级别解决了上述问题。
1.读未提交(Read uncommitted):
这种事务隔离级别下,select语句不加锁。
此时,可能读取到不一致的数据,即“读脏 ”。这是并发最高,一致性最差的隔离级别。
2.读已提交(Read committed):
可避免 脏读 的发生。
在互联网大数据量,高并发量的场景下,几乎 不会使用 上述两种隔离级别。
3.可重复读(Repeatable read):
MySql默认隔离级别。
可避免 脏读 、不可重复读 的发生。
4.串行化(Serializable ):
可避免 脏读、不可重复读、幻读 的发生。
以上四种隔离级别最高的是 Serializable 级别,最低的是 Read uncommitted 级别,当然级别越高,执行效率就越低。像 Serializable 这样的级别,就是以 锁表 的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读) 。
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读) ;而在 Oracle数据库 中,只支持Serializable (串行化) 级别和 Read committed (读已提交) 这两种级别,其中默认的为 Read committed(读已提交) 级别。

3、单例模式
1. 什么是单例模式
面试官问什么是单例模式时,千万不要答非所问,给出单例模式有两种类型之类的回答,要围绕单例模式的定义去展开。
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
2. 单例模式的类型
单例模式有两种类型:
懒汉式:在真正需要使用对象时才去创建该单例类对象
饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
懒汉式创建单例对象。
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象,否则则先执行实例化操作。

4、violate 的作用是什么?
使用volatile关键字可以防止指令重排序,使用volatile关键字修饰的变量,可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换,这样在多线程环境下就不会发生NPE异常了。
volatile还有第二个作用:使用volatile关键字修饰的变量,可以保证其内存可见性,即每一时刻线程读取到该变量的值都是内存中最新的那个值,线程每次操作该变量都需要先读取该变量。

总结:
(1)单例模式常见的写法有两种:懒汉式、饿汉式
(2)懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock,解决了并发安全和性能低下问题。
(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。
(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;
(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题。
(6)为了防止多线程环境下,因为指令重排序导致变量报NPE,需要在单例对象上添加volatile关键字防止指令重排序。
(7)最优雅的实现方式是使用枚举,其代码精简,没有线程安全问题,且 Enum 类内部防止反射和反序列化时破坏单例。

5、synchronized 的实现(方法、代码块、对象)
synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是同步,也称之为同步锁。
synchronized的作用是保证在同一时刻,被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。
特性:
(1)原子性:synchronized保证语句块内操作是原子的.
(2)可见性:synchronized保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实现)
(3)有序性:synchronized保证有序性(通过“一个变量在同一时刻只允许一条线程对其进行lock操作”)
(4)重入性:synchronized 是可重入锁,也就是说,允许一个线程二次请求自己持有对象锁的临界资源,这种情况称为可重入锁。(是因为 synchronized 锁对象有个计数器,会随着线程获取锁后 +1 计数,当线程执行完毕后 -1,直到清零释放锁。)
Synchronized的四种状态(优化):
(1)无锁状态:表示没有线程占用此锁。
(2)偏向锁:一个线程一直访问此同步代码,一种占用此锁,那么该线程就会自动获取锁,提高效率。
(3)轻量级锁:当锁是偏向锁时,此时又有其他的线程来抢占,就会升级为轻量级锁,未抢占到锁的线程就会通过自旋的形式尝试去获取锁,提高性能。
(4)重量级锁:当锁是重量级锁时,未抢占到锁的线程自旋到一定次数(对CPU消耗大),还未拿到锁时,就会升级为重量级锁,此线程会等待操作系统的调动,就不在主动的去抢占获取锁了。
对象结构:
Synchronized锁的状态存储在锁对象的对象头里,对象头中有一块区域为Mark Word,用于存储对象运行时的数据,比如:hashcode,GC次数,锁状态标识。32位操作系统Mark Word为32位,64位操作系统Mark Word为64位。
锁的具体实现:
线程代码进入到Synchronized代码块时会自动获取锁对象,这时其他线程访问时会被阻塞,直到Synchroinzed代码块执行完毕或抛出异常,调用wait()方法都会释放锁对象。在进入Synchronized代码块时会将主内存的变量读取到自己的工作内存,在退出的时候会把工作内存的更新值写入到主内存。Java中Synchronized通过在锁对象的对象头设置标记,达到获取锁和释放锁的目的。

6. 冲突:对不同的关键字可能得到同一哈希地址,即key1≠key2而f(key1)=f(key2)这种现象称冲突(collision)。具有相同函数值的关键词对该哈希函数来说称作同义词(synonym)。

一 、哈希函数
(1)除留余数法(K%P P一般情况下取小于表长的最大素数 表长是100则p取97 若表长是25则p取23)
(2)数字分析法
(3)平方取中法
(4)直接定址法

二 、解决冲突的方法
(1)开放定址法(1.线性探测法 +1 +2 +3… 2.二次探测法 +12 -12 +22 -22)
(2)再哈希法(增加了不算的时间 不容易产生聚集现象)
注意:冲突只能减小 不可以避免

7.Switch case语句、常用情况
switch case 语句:根据表达式的结果,寻找匹配的case,并执行case后面的语句,一直到break为止,如果没有遇到 break 那就接着执行下面的语句。如果所有的case都不匹配,那么就执行default 后面的语句;如果没有default,那么程序就什么都不会执行,直接跳过switch case 语句。
注意:
1.switch case 语句的结果只能是整数(int)类型
2.控制表达式只能是整数型的结果
3.常量可以是常数,也可以是常数计算的表达式
4.break 在程序中是代表 “结束” 的意思

8.结构体和共同体的区别:
定义:
结构体struct:把不同类型的数据组合成一个整体,自定义类型。
共同体union:使几个不同类型的变量共同占用一段内存。

地址:struct和union都有内存对齐,结构体的内存布局依赖于CPU、操作系统、编译器及编译时的对齐选项。

结构体struct:不同之处,struct里每个成员都有自己独立的地址。sizeof(struct)是内存对齐后所有成员长度的加和。
共同体union:当共同体中存入新的数据后,原有的成员就失去了作用,新的数据被写到union的地址中。sizeof(union)是最长的数据成员的长度。

你可能感兴趣的:(数据库,c++,开发语言)