# 基础篇

# Java 基础

# 面对对象特征

继承就是子类继承父类的行为,使得子类具有父类的域和方法。继承可以简化代码,提高代码复用性,利于后期维护,但提高了代码的耦合度。

封装是指一种将抽象性函数式接口的实现细节部分包装、隐藏起来的方法。封装可以减少耦合,提高代码复用性。

多态是同一个行为具有多个不同表现形式或形态的能力。多态可以消除类型之间的耦合关系,具有可替换性、可扩充性、接口性、灵活性和简化性。多态存在的三个必要条件是:继承、重写和父类引用指向子类对象。多态实现的三种方式:重写、接口、抽象类和抽象方法。

# final、finally 和 finalize 的区别

final 声明一个常量。如果一个变量被声明为 final,这个变量的值不能被改变,且必须初始化时必须设置值。如果一个方法被声明为 final,这个方法不能被重写。如果一个类被声明为 final,这个类不能被继承。

finally 是 try catch 语句中来执行清除操作的,无论发生任何情况,都会执行 finally 中的语句。若 try 中有 return,回先执行 finally 中的语句再 return。

finalize 是一个方法名,finalize () 方法在 Java 的垃圾收集器将对象清除出去之前执行。

# Exception、Error、运行时异常与一般异常有何异同

所有的异常都继承自 Throwable 类。

Error 类描述了 Java 运行时系统的内部错误和资源耗尽错误。此种异常不应该由程序抛出,若出现这种异常,应通告给用户并尽力使程序安全地终止。

Exception 类是应用程序抛出的错误。Exception 类包括 IO 异常和运行时异常。

RuntimeException 类包括错误的类型转换、数组访问越界和访问 null 指针。在程序中应该杜绝此类异常的发生。

IOException 类包括试图在文件尾部后面读取数据、试图打开一个不存在的文件和试图根据给定的字符串查找 Class 对象,而这个字符串表示的类并不存在。此类异常需要在程序中手动抛出。

# 请写出 5 种常见到的 runtime exception

  • ClassCastException - 错误的类型转换
  • IndexOutOfBoundsException - 数组访问越界
  • NullPointerException - 空指针
  • IllegalArgumentException - 非法参数异常
  • ArithmeticException - 算数运算异常

# int 和 Integer 有什么区别,Integer 的值缓存范围

  • int 是基本数据类型,Integer 是 int 的包装类。

  • int 类型的默认值是 0,Integer 类型的默认值是 null。

Integer 的值缓存范围:[-128, 127]。

# 包装类,装箱和拆箱

Java 语言虽然是面向对象编程,但是八种基本类型不具备对象的特性,不携带属性,没有方法可调用。所以 Java 为每种基本类型设计了对应的包装类。

由基本类型向对应的包装类转换称为装箱,由包装类向对应的基本类型转换称为拆箱。

Integer a = new Integer(1); // 手动装箱
int b = a.intValue(); // 手动拆箱
Integer c = b; // 自动装箱,Integer var3 = Integer.valueOf (var2);
int d = c; // 自动拆箱,int var4 = var3.intValue ();

# String、StringBuilder、StringBuffer

  • String 是常量,StringBuilder 和 StringBuffer 是变量,运行效率:StringBuilder > StringBuffer > String。
  • String 和 StringBuffer 是线程安全的,StringBuilder 是线程不安全的。

# 重载和重写的区别

重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。

重载是在一个类里面,方法名字相同,而参数不同,返回类型可以相同也可以不同。

重载是一个类的多态性表现,重写是子类与父类的多态性表现。

重写规则:

  • 参数列表必须完全与被重写方法的参数相同。
  • 返回类型必须完全与被重写方法的返回类型相同。
  • 访问权限不能比父类中被重写的方法的访问权限更低。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 的方法。
  • 重写的方法能够抛出任何非可查异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的可查异常,或者比被重写方法声明的更广泛的可查异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个方法,则不能重写这个方法。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样)。
  • 被重载的方法可以改变返回类型。
  • 被重载的方法可以改变访问修饰符。
  • 被重载的方法可以声明新的或更广的可查异常。
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

# 抽象类和接口有什么区别

参数抽象类接口
默认的方法实现可以有默认的方法实现可以有方法的实现
实现子类使用 extends 关键字来继承抽象类。如果子类不是抽象类的话,需要提供抽象类中所有抽象方法的实现。子类使用关键字 implements 来实现接口,需要提供接口中所有普通方法的实现。
构造器可以有构造器不可以有构造器
与正常 Java 类的区别不能实例化抽象类完全不同于 Java 类
访问修饰符抽象方法可以有 public、protected 和 default方法默认是 public
多继承可以继承一个类和多个接口可以继承多个接口
速度比接口快较慢,需要时间寻找在类中实现的方法

# 说说反射的用途及实现

Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

Java 反射框架提供以下功能:

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射设置可以调用 private)。
  4. 在运行时调用任意一个对象的方法。

反射的主要用途就是开发各种通用框架,如 Spring 的 IOC,JavaBean 和 jsp 之间的调用,JDBC 的 classForName () 等等。

获取一个对象的反射类:

  1. 通过 getClass () 方法。
  2. 通过 Class.firName () 方法。
  3. 使用类.class。

反射获取对象实例:

  1. 直接使用 Class 对象的 newInstance () 方法。
  2. 使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance () 方法。

# 说说自定义注解的场景及实现

Java 有四种元注解:@Retention、@Inherited、@Documented、@Target

注解的用处:

  1. 生成文档,如 @param、@return 等。
  2. 跟踪代码依赖性,实现替代配置文件功能。
  3. 在编译时进行格式检查。
  4. 类属性自动赋值。

# HTTP 请求的 GET 与 POST 方式的区别

GETPOST
点击返回 / 刷新按钮没有影响数据会重新发送(浏览器将会提示用户 “数据被重新提交”)
添加书签可以不可以
缓存可以不可以
编码类型application/x-www-form-urlencodedapplication/x-www-form-urlencoded or multipart/form-data. 请为二进制数据使用 multipart 编码
历史记录没有
长度限制没有
数据类型限制只允 ASCII 字符类型没有限制,允许二进制数据
安全性不安全数据不会显示在地址栏中,也不会缓存下来或保存在浏览记录里,比较安全
可见性查询字符串显示在地址栏的 URL 中,可见查询字符串不会显示在地址栏中,不可见

# Session 与 Cookie 区别

  1. session 数据存储在服务端,cookie 数据存储在客户端。
  2. session 的大小限制与服务器的内存大小有关,cookie 有大小限制和个数限制。
  3. cookie 有安全隐患,通过拦截或本地文件找到 cookie 后可以进行攻击。
  4. session 在服务器端保存一段时间才会销毁,如果 session 过多会增加服务器端压力。

# 列出自己常用的 JDK 包

java.lang— 系统基础类,如 String、Math、Integer、System、Thread 等。

java.io— 输入输出有关的类,如文件操作。

java.net— 网络有关的类,如 URL、URLConnection 等。

java.util— 系统辅助类,如 Collection、List、Map 等。

java.sql— 数据库操作类,如 Connection、Statement、ResultSet 等。

# MVC 设计思想

MVC——Model 模型、View 视图和 Controller 控制器,模型就是封装业务逻辑和数据的一个一个的模块,控制器负责视图与模型的交互,视图就是看到的页面。

# equals 与 == 的区别

基本数据类型使用 == 来判断是否相等。对象一般使用 equals 来判等,若对象使用 == 符号,判断的是对象的引用指向的地址是否是同一个。equals 方法默认比较的是对象的地址,但是类一般会重写 equals 方法,使对象比较内容是否相等。

# hashCode 和 equals 方法的区别与联系

hashCode 方法返回对象的哈希值,equals 方法比较两个对象是否相等,返回 true 或 false。

hashCode 主要是为了 HashMap 等比较对象的相等性,是为了解决使用 equals 比较相等性效率低的问题。

在没有修改 equals 方法的情况下,相同的对象返回的 hashCode 值一定相等。 如果两个对象 equals 方法返回 true,那么在这两个对象的 hashCode 值一定相等。如果两个对象 equals 方法返回 false,那么不能保证这两个对象的 hashCode 值不相等,但是我们必须意识到,返回 hashCode 值不同的话会提升 HashMap 的效率。

所以在我们的日常开发中,如果一个类重写了 equals 方法,也应该重写 hashCode 方法,保持两个方法的一致性。

# 什么是 Java 序列化和反序列化,如何实现 Java 序列化?或者请解释 Serializable 接口的作用

序列化是将 Java 对象转为字节序列,反序列化是将字节序列转为 Java 对象。

类继承 Serializable 接口,使用 ObjectOutputStream 的 writeObject 方法就可以进行序列化,使用 ObjectInputStream 的 readObject 方法就可以进行反序列化。

Serializable 接口只是一个标志性接口,接口中没有任何变量和方法。实现 Serializable 接口的类就可以进行序列化。

# Object 类中常见的方法,为什么 wait notify 会放在 Object 里边?

常见方法 ——hashCode ()、equals ()、toString ()、clone ()。

wait ()、notify ()、notifyAll () 这三个方法存在于同步中,在调用这三个方法时必须标识同步所属的锁,锁可以是任意对象,所以任意对象调用方法一定定义在 Object 类。

# Java 的平台无关性如何体现出来的

Java 程序通过一次编译,就可以在多个平台运行。各种不同平台的虚拟机与所有平台都统一使用的程序存储格式 -- 字节码是构成平台无关性的基石,字节码是与平台无关的,任何虚拟机都可以载入和执行字节码来实现 “一次编译,到处运行”。

# JDK 和 JRE 的区别

JDK,英文 Java Development Kit,是针对 Java 开发人员的产品,是整个 Java 的核心,包括了 Java 运行环境 JRE、Java 工具和 Java 基础类库。

JRE,英文 Java Runtime Environment,是运行 Java 程序所必须的环境的集合,包含 JVM 标准实现及 Java 核心类库。

JVM,Java Virtual Machine,是整个 Java 实现跨平台的最核心的部分,能够运行以 Java 语言编写的软件程序。

# Java 8 有哪些新特性

  1. Lambda 表达式
  2. 方法引用
  3. 函数式接口
  4. 默认方法和静态方法
  5. Stream
  6. Optional 类
  7. Nashorn JavaScript
  8. 日期时间 API
  9. Base64

# Java 常见集合

# List 和 Set 区别

  1. List 中元素有序,Set 中元素无序。
  2. List 中元素可重复,Set 中元素不可重复。

# Set 和 hashCode 以及 equals 方法的联系

向 Set 中存放元素时,先调用 hashCode 方法获取它的哈希值,通过哈希值定位到放置的位置。如果这个位置没有元素就直接存储在这个位置上,如果这个位置有元素,先调用 equals 方法比较两个元素是否相等,相同的话就不存了,不同的话就散列其他的位置。

如果两个对象相同,那么它们的 hashCode 值一定相同;如果两个对象不同,那么它们的 hashCode 值不一定不同。所以在平时的开发中,一定要保证 hashCode 方法和 equals 方法的一致性,当 equals 方法判断不相等时,hashCode 方法一定要不相等,当 equals 方法判断不相等时,hashCode 方法要尽量避免相等。

# List 和 Map 区别

  1. List 是存储单列数据的集合,Map 是存储键值的双列数据的集合。
  2. List 中存储的数据有序,并且允许重复,Map 中存储的数据无序,键不能重复,值可以重复。

# Arraylist 与 LinkedList 区别

  1. ArrayList 是基于动态数组,LinkedList 基于链表。
  2. 对于随机访问 get 和 set,ArrayList 优于 LinkedList。
  3. 对于新增和删除 add 和 remove,LinkedList 优于 ArrayList。
  4. ArrayList 和 LinkedList 都存在空间浪费现象,ArrayList 主要体现在 list 的结尾会预留一定的容量空间,LinkedList 主要体现在每一个元素都要存储它的上一个元素和下一个元素。

# ArrayList 与 Vector 区别

  1. 同步性:Vector 的所有方法都使用了同步锁,是线程安全的,ArrayList 是线程不安全的。
  2. 容量扩展机制:Vector 扩展指定增量或者 1 倍,ArrayList 扩展 0.5 倍。

# HashMap 和 HashTable 的区别

  1. HashMap 采用数组 + 链表 + 红黑树的数据结构,HashTable 采用数组 + 链表的数据结构。
  2. HashMap 扩容为原来容量的 2 倍,HashTable 扩容为原来容量的 2 倍加 1。
  3. HashMap 的键和值都可以为 null,HashTable 的键和值都不可以为 null。
  4. HashMap 是线程不安全的但是效率比较高,HashTable 是线程安全的但是效率比较低。

# HashSet 和 HashMap 区别

  1. HashMap 实现了 Map 接口,HashSet 实现了 Set 接口。
  2. HashMap 存储键值对,HashSet 存储对象。
  3. HashMap 调用 put () 方法向 map 中添加元素,HashSet 调用 add () 方法向 set 中添加元素。
  4. HashMap 使用 key 计算 hashCode,HashSet 使用对象来计算 hashCode。
  5. HashMap 相比较快,它使用唯一的键来获取对象,HashSet 比较慢。

# HashMap 和 ConcurrentHashMap 的区别

  1. ConrrentHashMap 对整个桶数组进行了分割分段,然后在每一个分段上都用 lock 锁进行保护,相对于 HashTable 的 syn 关键字锁的粒度更精细了一些,并发性能更好,而 HashMap 没有锁机制,不是线程安全的。
  2. HashMap 的键和值允许有 null,但是 ConcurrentHashMap 的键和值都不允许有 null。

# HashMap 的工作原理及代码实现,什么时候用到红黑树

HashMap 采用数组 + 链表 + 红黑树的数据结构。通过键值的哈希值映射到数组的某个位置,如果发生哈希碰撞,就放在链表的末尾。当链表长度等于 8 并且数组容量大于最小树化容量时,链表转化为红黑树。

# 多线程情况下 HashMap 死循环的问题

JDK1.8 之前,HashMap 进行扩容之后的 table 复制时,会将元素添加在链表的头端,两个线程同时进行扩容操作,会导致链表闭环,导致 get 操作死循环。但是 JDK1.8 解决了这个问题,改为在链表末尾添加元素,resize 之后链表顺序不会发生变化,也就不会出现环形链表。但是 JDK1.8 的 HashMap 存在数据丢失等的问题。

# ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数

# 看过那些 Java 集合类的源码

  1. ArrayList 和 LinkedList
  2. HashMap、HashTable 和 LinkedHashMap

# 进程和线程

# 线程和进程的概念、并行和并发的概念

进程是表示资源分配和调度运行的基本单位。

线程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。

并发:时间段内有很多的线程或进程在执行,但任何时间点上都只有一个在执行,多个线程或进程争抢时间片轮流执行。

并行:时间段和时间点上都有多个线程或进程在执行。

单核 CPU 只能并发,多核 CPU 才能做到并行执行。

# 创建线程的方式及实现

  1. 继承 Thread 类创建线程类。
  2. 通过 Runnable 接口创建线程类。
  3. 通过 Callable 和 Future 创建线程。

详细参考 Java 创建线程的三种方式

# sleep ()、join ()、yield () 有什么区别

sleep () 方法让线程在指定时间内休息,可以让其他线程得到运行的机会,当休息时间达到指定之间,线程立马重新开始运行。与 Object 的 wait () 方法不同的是,sleep () 方法在休息期间并不会释放掉锁,但是 wait () 方法会让线程释放掉对象的锁。

join () 方法让指定的线程先执行完在执行其他线程,而且会阻塞主线程。join () 方法也不会释放掉锁。

yield () 方法会先让相同优先级或更高优先级的线程执行,yield () 方法也不会释放资源的锁。

详细参考 Thread 的几种状态以及 sleep/yield/join/wait/notify/notifyAll 方法的区别

# 进程间通信的方式

  1. 管道

    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

  2. 命名管道

    命名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

  3. 信号

    信号是一种比较复杂的通信方式,用于通知接受进程某个事件已经发生。

  4. 消息队列

    消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号量传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  5. 共享内存

    共享内存就是映射一段能被其它进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

  6. 信号量

    信号量是一个计数器,可以用来控制多个进程对共享资源的访问,它常作为一种锁机制,防止某进程正在访问共享资源时,其它进程访问该资源,因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

  7. 套接口

    套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

# 说说 CountDownLatch、CyclicBarrier 原理和区别

CountDownLatch:一个线程(或多个线程)等待另外 N 个线程完成某个事情之后才能执行。

CyclicBarrier:N 的线程互相等待,任何一个线程完成之前,所有的线程都必须等待。

区别:

  1. CountDownLatch 是减计数,CyclicBarrier 是加计数。
  2. CountDownLatch 是一次性的,CyclicBarrier 可以重用。
  3. CountDownLatch 的重点是那一个线程(或多个线程),是它(们)在等待,另外 N 个线程在把某个事情做完之后,可以继续,可以终止。CyclicBarrier 的重点是那 N 个线程,N 个线程互相等待。

# 说说 Semaphore 原理

Semaphore 限定一个资源的访问的线程数量,当访问线程数量超过设定数量时,就阻塞一部分线程,直到有线程师范资源。

# 说说 Exchanger 原理

Exchanger 让两个线程互换数据,当一个线程调用 exchange 方法时如果没有另一个线程的 exchange 方法,就阻塞等待另一个线程的 exchange 方法,然后两者互选数据,之后两个线程进入就绪状态。

# ThreadLocal 原理分析,ThreadLocal 为什么会出现 OOM,出现的深层次原理

# 讲讲线程池的实现原理

# 线程池的几种实现方式

  1. 使用 Java 的 Executors 类创建。
  2. 手动创建 ThreadPoolExecutor。
  3. 使用 Spring 的 ThreadPoolTaskExecutor。

# 线程的生命周期,状态是如何转移的

# 操作系统的进程调度算法

  1. 先来先服务(FCFS)

    作业名到达时间服务时间开始时间完成时间
    182810
    28.50.51010.5
    390.110.510.6
    49.50.210.610.8

    FCFS 是非抢占式的,易于实现,效率不高,性能不好,有利于长作业(CPU 频繁性)而不利于短作业(I/O 繁忙性)。

  2. 最短剩余时间优先(SJF)

    作业名到达时间服务时间开始时间完成时间
    182810.8
    28.50.58.59
    390.199.1
    49.50.29.59.7

    SJF 是抢占式的,由于频繁地抢占和进程切换,系统开销大,该算法实现代价高,一般用于实时系统。

  3. 高响应比优先(HRRF)

    响应比 = (等待时间 + 服务时间) / 服务时间

    作业名到达时间服务时间开始时间完成时间
    182810
    28.50.510.110.6
    390.11010.1
    49.50.210.610.8

    HRRF 是非抢占式的,该算法结余 FCFS 和 SJF 之间,但每次需要计算每个作业的响应比,增加系统开销。

  4. 优先级

    每次挑选优先级最高的一个或多个调入,分为抢占式和非抢占式。

  5. 时间片轮转

    作业名到达时间服务时间开始时间完成时间
    A0303
    B16319
    C24711
    D351120
    E421517

    该算法简单有效,常用于分时系统,但不利于 I/O 频繁而紧凑的,由于这种进程用不完一个时间片,就因为等待 I/O 操作而被阻塞,当 I/O 操作结束后,只能插入到就绪队列的末尾,等待下一轮调度。

# 锁机制

# 说说线程安全问题,什么是线程安全,如何保证线程安全

# 重入锁的概念,重入锁为什么可以防止死锁

# 产生死锁的四个条件(互斥、请求与保持、不剥夺、循环等待)

# 如何检查死锁(通过 Console 检查死锁)

# volatile 实现原理(禁止指令重排、刷新内存)

# synchronized 实现原理(对象监视器)

# synchronized 与 lock 的区别

# AQS 同步队列

# CAS 无锁的概念、乐观锁和悲观所

# 常见的原子操作类

# 什么是 ABA 问题,出现 ABA 问题 JDK 是如何解决的

# 乐观锁的业务场景及实现方式

# Java 8 并发包下常见的并发类

# 偏向锁、轻量级锁、重量级锁、自旋锁的概念

# JVM

# JVM 运行时内存区域划分

# 内存溢出 OOM 和堆栈溢出 SOE 的示例及原因、如何排查与解决

# 如何判断对象是否可以回收会存活

# 常见的 GC 回收算法及其含义

# 常见的 JVM 性能监控和故障处理工具类:jps、jstat、jmap、jinfo、jconsole 等

# JVM 如何设置参数

# JVM 性能调优

# 类加载器、双亲委派模型、一个类的生命周期、类是如何加载到 JVM 中的

# 类加载的过程:加载、验证、准备、解析、初始化

# 强引用、软引用、弱引用、虚引用

# Java 内存模型 JMM

# 设计模式

# 常见的设计模式

# 设计模式的六大原则及其含义

# 常见的单例模式以及各种实现方式的优缺点,哪一种最好,手写常见的单例模式

# 设计模式在实际场景中的应用

# Spring 中用到了哪些设计模式

# Mybatis 中用到了哪些设计模式

# 你项目中有使用哪些设计模式

# 说说常用开源框架中设计模式使用分析

# 动态代理

# 数据结构

# 树(二叉查找树、平衡二叉树、红黑树、B 树、B + 树)

# 深度优先算法、广度优先算法

# 克鲁斯卡尔算法、普林母算法、迪克拉斯算法

# 什么是一致性 Hash 及其原理、Hash 环问题

# 常见的排序算法和查找算法:快排、折半查找、堆排序等

# 网络 / IO 基础

# BIO、NIO、AIO 的概念

# 什么是长连接和短连接

# HTTP1.0 和 2.0 相比有什么区别

# HTTPS 的基本概念

# 三次握手和四次挥手,为什么挥手需要四次

# 从浏览器中输入 URL 到页面加载的发生了什么

# 数据存储和消息队列

# 数据库

# MySQL 索引使用的注意事项

  1. 索引的字段必须是经常作为查询条件的字段。
  2. 如果索引多个字段,第一个字段要是经常作为查询条件的,如果只有第二个字段作为查询条件,这个索引不会起到作用。
  3. 索引的字段必须有足够的区分度。
  4. MySQL 对于长字段支持前缀索引。
  5. MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE。
  6. 尽量不要写!= 或者 <> 的 sql,用 between 或> and < 代替,否则可能用不到索引。
  7. Order by 、Group by 、Distinct 最好在需要这个列上建立索引,利于索引排序。
  8. 尽量利用 mysql 索引排序。
  9. 没办法的情况下,使用强制索引 Force index (index_name)。
  10. 尽量避勉 innodb 用非常大尺寸的字段作为主键。
  11. 较频繁的作为查询条件的字段应该创建索引。
  12. 选择性高的字段比较适合创建索引。
  13. 作为表关联字段一般都需要创索引。
  14. 更新非常频繁的字段不适合创建索引。
  15. 不会出现在 WHERE 子句中的字段不该创建索引。
  16. 选择性太低的字段不适合单独创建索引。

# DDL、DML、DCL 分别指什么

# explain 命令

# left join,right join, inner join

# 数据库事务 ACID(原子性、一致性、隔离性、持久性)

  1. 原子性

    原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。例子:A 给 B 转账,A 的账户减钱,B 的账户加钱,两个操作要么全部成功,要么全部失败。

  2. 一致性

    一致性是指事务必须是数据库从一个一致性状态变换到另一个一致性状态,也就是说事务执行之前和执行之后都必须处于一致性状态。例子:A 和 B 的钱一共是 5000,无论 A 和 B 之间如何转账,钱一共还是 5000。

  3. 隔离性

    隔离性是当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要互相隔离。

  4. 持久性

    持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

# 脏读、幻读、不可重复读

  1. 脏读是指一个事务处理过程里读取了另一个未提交的事务中的数据。
  2. 不可重复读是指对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
  3. 幻读是事务非独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么以后就会发生,操作第一个事务的用户发现表中还有没有修改的数据行,就好像发生了幻觉一样。

不可重复读和脏读的区别:

脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

幻读和不可重复读的区别:

幻读和不可重复读都是读取了另一条已经提交的事务,不同的是不可重复读查询的是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

# 事务的隔离级别

脏读不可重复读幻读
未提交读(Read uncommitted)可能可能可能
已提交读(Read committed)不可能可能可能
可重复读(Repeatable read)不可能不可能可能
可串行化(Serializable)不可能不可能不可能

# 数据库的几大范式

  1. 第一范式(1NF)

    强调的是列的原子性,即列不能够再分成其他几列。

  2. 第二范式(2NF)

    首先满足 1NF,然后表必须有一个主键,最后没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。

  3. 第三范式(3NF)

    首先满足 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。

# 数据库常见的命令

# 说说分库与分表设计

# 分库与分表带来的分布式困境与应对之策(如何解决分布式下的分库分表,全局表)

分库分表带来的问题:

  1. 事务的支持就变成了分布式事务。
  2. join 时需跨库跨表。
  3. 分布式为了保证强一致性,必然带来延迟,导致性能降低,系统的复杂度变高。

常用的解决方案:

  1. 对于不同的方式之间没有严格的界限,特点不同,侧重点不同。需要根据实际情况,结合每种方式的特点来进行处理。
  2. 选用第三方的数据库中间件(Atlas,Mycat,TDDL,DRDS),同时业务系统需要配合数据存储的升级。

# 说说 SQL 优化之道

一。数据库设计

  1. 适度的反范式
  2. 适当建立索引
  3. 对表进行水平划分
  4. 对表进行垂直划分
  5. 选择适当的字段类型,特别是主键
  6. 文件、图片等大文件用文件系统存储,不用数据库
  7. 外键表示清楚,方便建立索引
  8. 掌握表的写入时机
  9. 宁可集中批量操作,避免频繁读写
  10. 选择合适的引擎

二. SQL 语句优化

  1. SQL 语句优化工具
  2. Explain
  3. 全索引扫描

三。数据库参数配置

四。合理的硬件资源和操作系统

  1. 读写分离

# MySQL 遇到的死锁问题、如何排查与解决

# 存储引擎的 InnoDB 与 MyISAM 区别,优缺点,使用场景

不同MyISAMInnoDB
存储结构每个 MyISAM 在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。.frm 文件存储表定义。数据文件的扩展名为.MYD (MYData)。索引文件的扩展名是.MYI (MYIndex)。所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB 表的大小只受限于操作系统文件的大小,一般为 2GB。
存储空间可被压缩,存储空间较小。支持三种不同的存储格式:静态表 (默认,但是注意数据末尾不能有空格,会被去掉)、动态表、压缩表。需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。
可移植性、备份及恢复数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作。免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十 G 的时候就相对痛苦了。
事务支持强调的是性能,每次查询具有原子性,其执行数度比 InnoDB 类型更快,但是不提供事务支持。提供事务支持事务,外部键等高级数据库功能。 具有事务 (commit)、回滚 (rollback) 和崩溃修复能力 (crash recovery capabilities) 的事务安全 (transaction-safe (ACID compliant)) 型表。
AUTO_INCREMENT可以和其他字段一起建立联合索引。引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,他可以根据前面几列进行排序后递增。InnoDB 中必须包含只有该字段的索引。引擎的自动增长列必须是索引,如果是组合索引也必须是组合索引的第一列。
表锁差异只支持表级锁,用户在操作 myisam 表时,select,update,delete,insert 语句都会给表自动加锁,如果加锁以后的表满足 insert 并发的情况下,可以在表的尾部插入新的数据。支持事务和行级锁,是 innodb 的最大特色。行锁大幅度提高了多用户并发操作的新能。但是 InnoDB 的行锁,只是在 WHERE 的主键是有效的,非主键的 WHERE 都会锁全表的。
全文索引支持 FULLTEXT 类型的全文索引不支持 FULLTEXT 类型的全文索引,但是 innodb 可以使用 sphinx 插件支持全文索引,并且效果更好。
表主键允许没有任何索引和主键的表存在,索引都是保存行的地址。如果没有设定主键或者非空唯一索引,就会自动生成一个 6 字节的主键 (用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
表的具体行数保存有表的总行数,如果 select count (*) from table; 会直接取出出该值。没有保存表的总行数,如果使用 select count (*) from table;就会遍历整个表,消耗相当大,但是在加了 wehre 条件后,myisam 和 innodb 处理的方式都一样。
CURD 操作如果执行大量的 SELECT,MyISAM 是更好的选择。如果你的数据执行大量的 INSERT 或 UPDATE,出于性能方面的考虑,应该使用 InnoDB 表。DELETE 从性能上 InnoDB 更优,但 DELETE FROM table 时,InnoDB 不会重新建立表,而是一行一行的删除,在 innodb 上如果要清空保存有大量数据的表,最好使用 truncate table 这个命令。
外键不支持支持

# 索引类别(B + 数索引、全文索引、哈希索引)、索引的原理

# 什么是自适应哈希索引(AHI)

# 为什么要用 B + 树作为 MySQL 索引的数据结构

# 聚焦索引与非聚焦索引的区别

# 遇到过索引失效的情况没,什么时候可能会出现,如何解决

# limit 20000 加载很慢怎么解决

# 如何选择合适的分布式主键方案

# 选择合适的数据存储方案

# 常见的几种分布式 ID 的设计方案

# 常见的数据库优化方案,在你的项目中数据库如何进行优化的