文章目录

  • Java基础
    • 面向对象
    • 类与接口
    • 变量与方法
    • 重写与重载
      • 构造器(constructor)是否可被重写(override)
      • 重载(Overload)和重写(Override)的区别。重载的方法能 否根据返回类型进行区分?
    • 内部类
    • 集合容器类
      • 什么是哈希(hash)?
      • hashMap之hash算法优化以及寻址算法的优化
      • Collection 和 Collections 有什么区别?
    • 异常处理总结
    • try...异常 catch finally 和try ..异常 catch,我要return一个值,那个会执行return
    • 类与接口
    • 抽象类和接口
  • JVM
  • 多线程
    • 线程是什么?
    • 线程的状态(生命周期)
    • 创建线程的方式
    • Callable与Runnbale的区别
    • sleep() 与 wait() 区别
    • 乐观锁和悲观锁
      • 悲观锁
      • 乐观锁
    • 公平锁和非公平锁
      • 公平锁
      • 非公平锁
    • 独占锁和共享锁
      • 独占锁
      • 共享锁
    • 可重入锁
    • 自旋锁
    • CAS 与 ABA
      • ABA 问题描述
      • ABA 问题的解决
    • synchronized 是哪种锁的实现?为什么?
    • new ReentrantLock() 创建的是公平锁还是非公平锁?
    • synchronized 使用的是公平锁还是非公平锁?
    • 为什么非公平锁吞吐量大于公平锁?
    • volatile 的作用是什么?
    • volatile 对比 synchronized 有什么区别?
    • CAS 是如何实现的?
    • CAS 会产生什么问题?应该怎么解决?
    • 以下说法错误的是?
    • 总结
  • MySQL数据库
    • 索引
      • 什么是索引?
      • 索引的使用场景有哪些(重点)?
        • orderby
        • join
        • 索引覆盖
      • 索引有哪几种类型?
      • 什么是最左前缀原则?什么是最左匹配原则
      • 百万级别或以上数据如何删除?
      • 索引失效的七种情况
      • 在数据库中怎么查看索引的效率
    • 事务
      • 事务的四大特性(ACID)介绍一下?
      • 什么是脏读?幻读?不可重复读?
      • 什么是事务的隔离级别?MySQL的默认隔离级别是什么?
      • 事务怎么提交,怎么应用?
    • 常用SQL语句
      • SQL约束有哪几种?
      • 六种关联查询
        • 举例
      • 左连接和内连接那个效率高?
      • 什么是子查询?
        • 子查询的三种情况?
    • select语句-语法顺序
    • select语句-执行顺序
    • in 和 exists的区别
    • SQL优化
  • SSM框架
    • Spring
    • Spring Boot
    • Spring MVC
      • 4、前端页面怎么调用后端,顺序是什么。
      • 5、restful了解吗?说一说
  • spring cloud微服务架构
    • 1、用过那些组件
    • 2、分别介绍组件的作用
    • 3、feign方式怎么调用,怎么实现的
    • 4、讲讲redis,以及你怎么应用到项目中的。还有单点登录
  • Mybatis
    • mybatis查询数据库的两种写法。以及写完SQL语句后还有干啥?
  • Redis
  • RabbitMQ
  • Linux
  • 前端
    • 1、会什么技术,使用的组件是原生的还是别人写好的。
  • 关于项目的问题
    • 项目有几个,应用了什么技术
    • 自己拿手的项目模块具体说一下

Java基础

面向对象

类与接口

变量与方法

重写与重载

构造器(constructor)是否可被重写(override)

构造器不能被继承,因此不能被重写,但可以被重载。

重载(Overload)和重写(Override)的区别。重载的方法能 否根据返回类型进行区分?

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态 性,而后者实现的是运行
时的多态性。

  • 重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不 同、顺序不同),与方法
    返回值和访问修饰符无关,即重载的方法不能根据返回 类型进行区分
  • 重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛 出的异常小于等于父
    类,访问修饰符大于等于父类(里氏代换原则);如果父类 方法访问修饰符为private则子类中就不是重
    写。

内部类

集合容器类

什么是哈希(hash)?

核心理论: Hash也称散列、哈希,对应的英文都是Hash。基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出。 这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值。

Hash的特点:

  1. 从hash值不可以反向推导出原始的数据。
  2. 输入数据的微小变化会得到完全不同的hash值,相同的数据会得到相同的值。
  3. 哈希算法的执行效率要高效,长的文本也能快速地计算出哈希值。
  4. hash算法的冲突概率要小。

由于hash的原理是将输入空间的值映射成hash空间内,而hash值的空间远小于输入的空间。 根据抽屉原理,一定会存在不同的输入被映射成相同输出的情况。

抽屉原理: 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放不少于两个苹果。
这一现象就是我们所说的“抽屉原理”。

hashMap之hash算法优化以及寻址算法的优化

put数据源里分析

  1. 寻址算法优化(n是hashMap的数组大小)
    不优化的寻址算法:hash对n取模
    优化后的算法:hash & (n - 1)

    优化依据:
    用与运算替代取模,提升性能,由于n都是2的m次方,如下公式(n为2的m次方时)

    hash & (n - 1) = hash %n
    

    使用位运算只能在N为 2^n时使用

  2. hash算法优化

    static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
    

    如上,hash算法是对象key值的(hashCode)异或(hashCode右移16位),右移16位,为何要做这种优化,has算法是hash & (n - 1),由于(n-1)的二进制值的高16位大概率都是0(n是hashMap的大小,不太可能超过2的16次方),为了让hash值的高16位参与运算,所以hash算法中将hash值的高16位和低16位进行了异或运算,使得高低16位都参与了hash计算,减少hash碰撞的概率。

Collection 和 Collections 有什么区别?

  • java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操
    作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为
    各种具体的集合提供了 大化的统一操作方式,其直接继承接口有List与Set。
  • Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进
    行排序、搜索以及线程安全等各种操作。

异常处理总结

  • try 块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch
    块,则必须跟一个 finally 块。
  • catch 块:用于处理 try 捕获到的异常。
  • finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。
    当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。在以下 4 种特殊情况下,finally 块不会被执行
  1. 在 finally 语句块中发生了异常。
  2. 在前面的代码中用了 System.exit()退出程序。
  3. 程序所在的线程死亡。
  4. 关闭 CPU。

try…异常 catch finally 和try …异常 catch,我要return一个值,那个会执行return

假如在try catch里面加一个throw,会return值吗?是既抛异常也有返回值,还是只有异常。
try catch resource

类与接口

抽象类和接口

JVM

多线程

线程是什么?

线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。

线程的状态(生命周期)

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。尤其是当线程启动以后,它不可能一直"霸占"着 CPU 独自运行,所以 CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换
新建状态(NEW)
当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配内存,并初始化其成员变量的值
就绪状态(RUNNABLE):
当线程对象调用了 start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。
阻塞状态(BLOCKED):
阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
等待阻塞(o.wait->等待对列):
运行(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waitting queue)中。
同步阻塞(lock->锁池)
运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。
其他阻塞(sleep/join)
运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入可运行(runnable)状态。
线程死亡(DEAD)
线程会以下面三种方式结束,结束后就是死亡状态。
正常结束
1.run()或 call()方法执行完成,线程正常结束。
异常结束
2.线程抛出一个未捕获的 Exception 或 Error。
调用stop
3. 直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。

创建线程的方式

在JDK1.5之前,创建线程就只有两种方式,即继承java.lang.Thread类和实现java.lang.Runnable接口;而在JDK1.5以后,增加了两个创建线程的方式,即实现java.util.concurrent.Callable接口和线程池。下面是这4种方式创建线程的代码实现。

  1. 继承Thread类
//继承Thread类来创建线程
public class ThreadTest {public static void main(String[] args) {//设置线程名字Thread.currentThread().setName("main thread");MyThread myThread = new MyThread();myThread.setName("子线程:");//开启线程myThread.start();for(int i = 0;i<5;i++){System.out.println(Thread.currentThread().getName() + i);}}
}class MyThread extends Thread{//重写run()方法public void run(){for(int i = 0;i < 10; i++){System.out.println(Thread.currentThread().getName() + i);}}
}
  1. 实现Runnable接口
//实现Runnable接口
public class RunnableTest {public static void main(String[] args) {//设置线程名字Thread.currentThread().setName("main thread:");Thread thread = new Thread(new MyRunnable());thread.setName("子线程:");//开启线程thread.start();for(int i = 0; i <5;i++){System.out.println(Thread.currentThread().getName() + i);}}
}class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + i);}}
}
  1. 实现Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现Callable接口
public class CallableTest {public static void main(String[] args) {//执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());new Thread(futureTask).start();//接收线程运算后的结果try {Integer sum = futureTask.get();System.out.println(sum);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum += i;}return sum;}
}

相较于实现Runnable 接口的实现,方法可以有返回值,并且抛出异常。
4. 线程池
线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提交了响应速度。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//线程池实现
public class ThreadPoolExecutorTest {public static void main(String[] args) {//创建线程池ExecutorService executorService = Executors.newFixedThreadPool(10);ThreadPool threadPool = new ThreadPool();for(int i =0;i<5;i++){//为线程池分配任务executorService.submit(threadPool);}//关闭线程池executorService.shutdown();}
}class ThreadPool implements Runnable {@Overridepublic void run() {for(int i = 0 ;i<10;i++){System.out.println(Thread.currentThread().getName() + ":" + i);}}}

Callable与Runnbale的区别

  1. Callable定义的方法是call,而Runnable定义的方法是run。
  2. call方法有返回值,而run方法是没有返回值的。
  3. call方法可以抛出异常,而run方法不能抛出异常。

sleep() 与 wait() 区别

  1. 对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于 Object类中的。
  2. sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
  3. 在调用 sleep()方法的过程中,线程不会释放对象锁。
  4. 而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

乐观锁和悲观锁

悲观锁和乐观锁并不是某个具体的“锁”而是一种并发编程的基本概念。乐观锁和悲观锁最早出现在数据库的设计当中,后来逐渐被 Java 的并发包所引入。

悲观锁

悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观地认为,不加锁的并发操作一定会出问题。

乐观锁

乐观锁正好和悲观锁相反,它获取数据的时候,并不担心数据被修改,每次获取数据的时候也不会加锁,只是在更新数据的时候,通过判断现有的数据是否和原数据一致来判断数据是否被其他线程操作,如果没被其他线程修改则进行数据更新,如果被其他线程修改则不进行数据更新。

公平锁和非公平锁

根据线程获取锁的抢占机制,锁又可以分为公平锁和非公平锁。

公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。

非公平锁

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。ReentrantLock 提供了公平锁和非公平锁的实现。

  • 公平锁:new ReentrantLock(true)
  • 非公平锁:new ReentrantLock(false)

如果构造函数不传任何参数的时候,默认提供的是非公平锁。

独占锁和共享锁

根据锁能否被多个线程持有,可以把锁分为独占锁和共享锁。

独占锁

独占锁是指任何时候都只有一个线程能执行资源操作。

共享锁

共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。比如 Java 中的 ReentrantReadWriteLock 就是共享锁的实现方式,它允许一个线程进行写操作,允许多个线程读操作。

ReentrantReadWriteLock 共享锁演示代码如下:

public class ReadWriteLockTest {public static void main(String[] args) throws InterruptedException {final MyReadWriteLock rwLock = new MyReadWriteLock();// 创建读锁 r1 和 r2Thread r1 = new Thread(new Runnable() {@Overridepublic void run() {rwLock.read();}}, "r1");Thread r2 = new Thread(new Runnable() {@Overridepublic void run() {rwLock.read();}}, "r2");r1.start();r2.start();// 等待同时读取线程执行完成r1.join();r2.join();// 开启写锁的操作new Thread(new Runnable() {@Overridepublic void run() {rwLock.write();}}, "w1").start();new Thread(new Runnable() {@Overridepublic void run() {rwLock.write();}}, "w2").start();}static class MyReadWriteLock {ReadWriteLock lock = new ReentrantReadWriteLock();public void read() {try {lock.readLock().lock();System.out.println("读操作,进入 | 线程:" + Thread.currentThread().getName());Thread.sleep(3000);System.out.println("读操作,退出 | 线程:" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();} finally {lock.readLock().unlock();}}public void write() {try {lock.writeLock().lock();System.out.println("写操作,进入 | 线程:" + Thread.currentThread().getName());Thread.sleep(3000);System.out.println("写操作,退出 | 线程:" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();} finally {lock.writeLock().unlock();}}}
}

以上程序执行结果如下:

读操作,进入 | 线程:r1
读操作,进入 | 线程:r2
读操作,退出 | 线程:r1
读操作,退出 | 线程:r2
写操作,进入 | 线程:w1
写操作,退出 | 线程:w1
写操作,进入 | 线程:w2
写操作,退出 | 线程:w2

可重入锁

可重入锁指的是该线程获取了该锁之后,可以无限次的进入该锁锁住的代码。

自旋锁

自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗 CPU。

CAS 与 ABA

CAS(Compare and Swap)比较并交换,是一种乐观锁的实现,是用非阻塞算法来代替锁定,其中 java.util.concurrent 包下的 AtomicInteger 就是借助 CAS 来实现的。

但 CAS 也不是没有任何副作用,比如著名的 ABA 问题就是 CAS 引起的。

ABA 问题描述

老王去银行取钱,余额有 200 元,老王取 100 元,但因为程序的问题,启动了两个线程,线程一和线程二进行比对扣款,线程一获取原本有 200 元,扣除 100 元,余额等于 100 元,此时阿里给老王转账 100 元,于是启动了线程三抢先在线程二之前执行了转账操作,把 100 元又变成了 200 元,而此时线程二对比自己事先拿到的 200 元和此时经过改动的 200 元值一样,就进行了减法操作,把余额又变成了 100 元。这显然不是我们要的正确结果,我们想要的结果是余额减少了 100 元,又增加了 100 元,余额还是 200 元,而此时余额变成了 100 元,显然有悖常理,这就是著名的 ABA 的问题。

执行流程如下。

  • 线程一:取款,获取原值 200 元,与 200 元比对成功,减去 100 元,修改结果为 100 元。
  • 线程二:取款,获取原值 200 元,阻塞等待修改。
  • 线程三:转账,获取原值 100 元,与 100 元比对成功,加上 100 元,修改结果为 200 元。
  • 线程二:取款,恢复执行,原值为 200 元,与 200 元对比成功,减去 100 元,修改结果为 100 元。

最终的结果是 100 元。

ABA 问题的解决

常见解决 ABA 问题的方案加版本号,来区分值是否有变动。以老王取钱的例子为例,如果加上版本号,执行流程如下。

  • 线程一:取款,获取原值 200_V1,与 200_V1 比对成功,减去 100 元,修改结果为 100_V2。
  • 线程二:取款,获取原值 200_V1 阻塞等待修改。
  • 线程三:转账,获取原值 100_V2,与 100_V2 对比成功,加 100 元,修改结果为 200_V3。
  • 线程二:取款,恢复执行,原值 200_V1 与现值 200_V3 对比不相等,退出修改。

最终的结果为 200 元,这显然是我们需要的结果。

在程序中,要怎么解决 ABA 的问题呢?

在 JDK 1.5 的时候,Java 提供了一个 AtomicStampedReference 原子引用变量,通过添加版本号来解决 ABA 的问题,具体使用示例如下:

String name = "老王";
String newName = "Java";
AtomicStampedReference<String> as = new AtomicStampedReference<String>(name, 1);
System.out.println("值:" + as.getReference() + " | Stamp:" + as.getStamp());
as.compareAndSet(name, newName, as.getStamp(), as.getStamp() + 1);
System.out.println("值:" + as.getReference() + " | Stamp:" + as.getStamp());

以上程序执行结果如下:

值:老王 | Stamp:1
值:Java | Stamp:2

synchronized 是哪种锁的实现?为什么?

答:synchronized 是悲观锁的实现,因为 synchronized 修饰的代码,每次执行时会进行加锁操作,同时只允许一个线程进行操作,所以它是悲观锁的实现。

new ReentrantLock() 创建的是公平锁还是非公平锁?

答:非公平锁,查看 ReentrantLock 的实现源码可知。

/*** Creates an instance of {@code ReentrantLock}.* This is equivalent to using {@code ReentrantLock(false)}.*/
public ReentrantLock() {sync = new NonfairSync();
}

synchronized 使用的是公平锁还是非公平锁?

答:synchronized 使用的是非公平锁,并且是不可设置的。这是因为非公平锁的吞吐量大于公平锁,并且是主流操作系统线程调度的基本选择,所以这也是 synchronized 使用非公平锁原由。

为什么非公平锁吞吐量大于公平锁?

答:比如 A 占用锁的时候,B 请求获取锁,发现被 A 占用之后,堵塞等待被唤醒,这个时候 C 同时来获取 A 占用的锁,如果是公平锁 C 后来者发现不可用之后一定排在 B 之后等待被唤醒,而非公平锁则可以让 C 先用,在 B 被唤醒之前 C 已经使用完成,从而节省了 C 等待和唤醒之间的性能消耗,这就是非公平锁比公平锁吞吐量大的原因。

volatile 的作用是什么?

答:volatile 是 Java 虚拟机提供的最轻量级的同步机制。当变量被定义成 volatile 之后,具备两种特性:

  • 保证此变量对所有线程的可见性,当一条线程修改了这个变量的值,修改的新值对于其他线程是可见的(可以立即得知的);
  • 禁止指令重排序优化,普通变量仅仅能保证在该方法执行过程中,得到正确结果,但是不保证程序代码的执行顺序。

volatile 对比 synchronized 有什么区别?

答:synchronized 既能保证可见性,又能保证原子性,而 volatile 只能保证可见性,无法保证原子性。比如,i++ 如果使用 synchronized 修饰是线程安全的,而 volatile 会有线程安全的问题。

CAS 是如何实现的?

答:CAS(Compare and Swap)比较并交换,CAS 是通过调用 JNI(Java Native Interface)的代码实现的,比如,在 Windows 系统 CAS 就是借助 C 语言来调用 CPU 底层指令实现的。

CAS 会产生什么问题?应该怎么解决?

答:CAS 是标准的乐观锁的实现,会产生 ABA 的问题(详见正文)。ABA 通常的解决办法是添加版本号,每次修改操作时版本号加一,这样数据对比的时候就不会出现 ABA 的问题了。

以下说法错误的是?

A:独占锁是指任何时候都只有一个线程能执行资源操作
B:共享锁指定是可以同时被多个线程读取和修改
C:公平锁是指多个线程按照申请锁的顺序来获取锁
D:非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁

答:B

题目解析:共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。

总结

本文介绍了 Java 中各种锁,明白了 Java 程序中比较常用的为非公平锁而非公平锁,原因在于非公平锁的吞吐量要更大,并且发生线程“饥饿”的情况很少,是风险远小于收益的事所以可以广而用之。又重点介绍了 CAS 和著名的 ABA 的问题,以及解决 ABA 的常见手段:添加版本号,可以通过 Java 自身提供的 AtomicStampedReference(原子引用变量)来解决 ABA 的问题,至此我们对 Java 多线程的了解又向前迈了一大步。

MySQL数据库

索引

什么是索引?

  • 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它
    们包含着对数据表里所有记录的引用指针。
  • 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
  • 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

索引的使用场景有哪些(重点)?


上图中,根据id查询记录,因为id字段仅建立了主键索引,因此此SQL执行可选
的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据。

#增加一个没有建立索引的字段
alter table innodb1 add sexchar(1);
#按sex检索时可选的索引为null
EXPLAIN SELECT * from innodb1 where sex='男';

可以尝试在一个字段未建立索引时,根据该字段查询的效率,然后对该字段建立索引(altertable表名addindex(字段名)),同样的SQL执行的效率,你会发现查询效率
会有明显的提升(数据量越大越明显)。

orderby

当我们使用orderby将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更无论读到内存之后的排序了。
但是如果我们对该字段建立索引altertable表名addindex(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)

join

对join语句匹配关系(on)涉及的字段建立索引能够提高效率

索引覆盖

如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。
因此我们需要尽可能的在select后只写必要的查询字段,以增加索引覆盖的几
率。
这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。

索引有哪几种类型?

主键索引:数据列不允许重复,不允许为NULL,一个表只能有一个主键。
唯一索引:数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

# 可以通过以下语句创建 唯一索引
ALTER TABLE table_name ADD UNIQUE(column1);# 可以通过以下语句创建 唯一组合索引
ALTER TABLE table_name ADD UNIQUE(column1,column2);

普通索引:基本的索引类型,没有唯一性的限制,允许为NULL值。

# 可以通过以下语句创建 普通索引
ALTER TABLE table_name ADD INDEXindex_name(column1);# 可以通过以下语句创建 组合索引
ALTER TABLE table_name ADD INDEX index_name(column1,column2,column3);

全文索引:是目前搜索引擎使用的一种关键技术。

# 可以通过以下语句创建 全文索引
ALTER TABL Etable_name ADD FULLTEXT(column1);

什么是最左前缀原则?什么是最左匹配原则

顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。

最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a=1andb=2andc>3andd=4如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

=和in可以乱序,比如a=1andb=2andc=3建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化
成索引可以识别的形式

百万级别或以上数据如何删除?

关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数
量是成正比的。

  1. 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
  2. 然后删除其中无用数据(此过程需要不到两分钟)
  3. 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
  4. 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。

索引失效的七种情况

七字口诀:“模型数空运最快”

  1. (模:模糊查询)模糊查询时,使用like关键字的时候,要是以%开头索引就会失效。
  2. (型:数据类型)数据类型错误时索引也会失效。
  3. (数:函数)对索引的字段使用内部函数索引会失效,这种情况应该建立基于函数的索引。
  4. (空:NULL)索引不存储空值,如果不限制索引列是 not null,数据库会认为索引列有可能存在空值,所以也不会按照索引进行计算。
  5. (运:运算)对索引列进行加、减、乘、除等运算,会导致索引失效。
  6. (最:最左原则)在复合索引中,索引列的顺序非常重要,如果不是按照索引列的最左列开始进行查找,则无法使用索引。
  7. (快:全表扫描更快)数据库预计使用全表扫描比使用索引更快,那它就不会使用索引。

在数据库中怎么查看索引的效率

事务

事务的四大特性(ACID)介绍一下?

关系性数据库需要遵循ACID规则,具体内容如下:

  1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作
    要么全部完成,要么完全不起作用;
  2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的
    结果是相同的;
  3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各
    并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该
    对其有任何影响。

什么是脏读?幻读?不可重复读?

  • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这
    可能是两次查询过程中间插入了一个事务更新的原有的数据。
  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

什么是事务的隔离级别?MySQL的默认隔离级别是什么?

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别| 脏读| 不可重复读 |幻影读
|----|----|----|----|----|
READ- UNCOM MITTED(读取未提交) |√ |√ |√
READ- COMMIT TED(读取已提交) |×| √ |√
REPEATA BLE- READ(可重复读)| × |× |√
SERIALIZ ABLE(可串行化) |×| × |×
SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle默认采用的READ_COMMITTED隔离级别

事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。

InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。

事务怎么提交,怎么应用?

#事务的提交
BEGIN; #关闭事务的自动提交,相当于start transaction
INSERT INTO user (id) VALUES(25);
COMMIT; #手动提交事务
#事务回滚
BEGIN;
INSERT INTO user (id) VALUES(15);
ROLLBACK; #事务回滚,就不会再提交了

常用SQL语句

SQL约束有哪几种?

  • NOT NULL(非空约束): 用于控制字段的内容一定不能为空(NULL)。
  • UNIQUE(唯一约束):控件字段内容不能重复,一个表允许有多个 Unique 约束。
  • PRIMARY KEY(主键约束): 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
  • FOREIGN KEY(外键约束):用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向
    的那个表中的值之一。
  • DEFAULT(默认约束):给字段设定默认值。
  • CHECK(检查约束):给字段设置条件。

六种关联查询

全连接(FULL JOIN)
内连接(INNER JOIN)
左连接(LEFT JOIN)
右连接(RIGHT JOIN)
联合查询(UNION与UNION ALL)
交叉连接(CROSS JOIN)

  1. 全连接(FULL JOIN)
    MySQL不支持全连接
    可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用
SELECT * FROM A LEFT JOIN B ON A.id=B.id
UNION
SELECT * FROM A RIGHT JOIN B ON A.id=B.id
  1. 内连接分为三类
#1.等值连接:
SELECT * FROM A RIGHT JOIN B ON A.id=B.id
#2.不等值连接:
SELECT * FROM A RIGHT JOIN B ON A.id > B.id
#3.自连接:
SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid
  1. 左连接(LEFT JOIN)
    左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
  2. 右连接(RIGHT JOIN)
    右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN
  3. 联合查询(UNION与UNION ALL)
SELECT * FROM A
UNION
SELECT * FROM B
UNION
...

就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并,同时进行默认规则排序
如果使用UNION ALL,不会合并重复的记录行,不会进行排序
效率 UNION ALL 高于UNION

  1. 交叉连接(CROSS JOIN)
SELECT * FROM A,B(,C)
#或者
SELECT * FROM A CROSS JOIN B (CROSS JOIN C)
#没有任何关联条件,结果是笛卡尔积,
#结果集会很大,没有意义,很少使用。
#内连接(INNER JOIN)
SELECT * FROM A,B WHERE A.id=B.id
#或者
SELECT * FROM A INNER JOIN B ON A.id=B.id
#多表中同时符合某种条件的数据记录的集合,INNER JOIN可以缩写为JOIN

举例

表连接面试题有2张表,1张R、1张S,R表有ABC三列,S表有CD两列,表中各有三条记录。

R表

A B C
a1 b1 c1
a2 b2 c2
a3 b3 c3
S表
C D
---- ----
c1 d1
c2 d2
c4 d3
  1. 交叉连接(笛卡尔积):
select * from r,s;
A B C C D
a1 b1 c1 c1 d1
a2 b2 c2 c1 d1
a3 b3 c3 c1 d1
a1 b1 c1 c2 d2
a2 b2 c2 c2 d2
a3 b3 c3 c2 d2
a1 b1 c1 c4 d3
a2 b2 c2 c4 d3
a3 b3 c3 c4 d3
  1. 内连接结果:
select * from r inner join s on r.c=s.c;
A B C C D
a1 b1 c1 c1 d1
a2 b2 c2 c2 d2
  1. 左连接结果:
select * from r left join s on r.c=s.c;
A B C C D
a1 b1 c1 c1 d1
a2 b2 c2 c2 d2
a3 b3 c3
  1. 右连接结果:
select * from r right join s on r.c=s.c;
A B C C D
a1 b1 c1 c1 d1
a2 b2 c2 c2 d2
c4 d3
  1. 全表连接的结果(MySql不支持,Oracle支持):
select * from r full join s on r.c=s.c;
A B C C D
a1 b1 c1 c1 d1
a2 b2 c2 c2 d2
a3 b3 c3
c4 d3

左连接和内连接那个效率高?

至于left为什么比inner快。 是因为left时, 数据库在执行时, left左边的表是被优化执行的,因为left左边的表被无条件返回, left右边的表对结果集不存在影响。 但是inner的时候, 就需要对数据进行过滤。 所以速度会慢。

什么是子查询?

  1. 条件:一条SQL语句的查询结果做为另一条查询语句的条件或查询结果
  2. 嵌套:多条SQL语句嵌套使用,内部的SQL查询语句称为子查询。

子查询的三种情况?

  1. 子查询是单行单列的情况:结果集是一个值,父查询使用:=、 <、 > 等运算符
# 查询工资最高的员工是谁?
select * from employee where salary=(select max(salary) from employee);
  1. 子查询是多行单列的情况:结果集类似于一个数组,父查询使用:in 运算符
# 查询工资最高的员工是谁?
select * from employee where salary=(select max(salary) from employee);
  1. 子查询是多行多列的情况:结果集类似于一张虚拟表,不能用于where 条件,用于select子句中做为子表
# 1) 查询出2011年以后入职的员工信息
# 2) 查询所有的部门信息,与上面的虚拟表中的信息比对,找出所有部门ID相等的员工。
select * from dept d, (select * from employee where join_date >'2011-1-1') e
where e.dept_id = d.id;
# 使用表连接:
select d.*, e.* from dept d inner join employee e on d.id = e.dept_id
where e.join_date > '2011-1-1'

select语句-语法顺序

SELECT
DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>

select语句-执行顺序

FROM
<表名> # 选取表,将多个表数据通过笛卡尔积变成一个表。
ON
<筛选条件> # 对笛卡尔积的虚表进行筛选
JOIN <join, left join, right join...>
<join表> # 指定join,用于添加数据到on之后的虚表中,例如left join会将左表的剩余数据添加到虚表中
WHERE
<where条件> # 对上述虚表进行筛选
GROUP BY
<分组条件> # 分组
<SUM()等聚合函数> # 用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的
HAVING
<分组筛选> # 对分组后的结果进行聚合筛选
SELECT
<返回数据列表> # 返回的单列必须在group by子句中,聚合函数除外
DISTINCT
# 数据除重
ORDER BY
<排序条件> # 排序
LIMIT
<行数限制>

in 和 exists的区别

in是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in;
例如:表A(小表),表B(大表)

select * from A where cc in(select cc from B)
# 效率低,用到了A表上cc列的索引;
select * from A where exists(select cc from B where cc=A.cc)
# 效率高,用到了B表上cc列的索引。

相反的:

select * from B where cc in(select cc from A)
# 效率高,用到了B表上cc列的索引
select * from B where exists(select cc from A where cc=B.cc)
# 效率低,用到了A表上cc列的索引。

SQL优化

一、避免不走索引的场景

  1. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE username LIKE '%陈%'

优化方式:尽量在字段后面使用模糊查询。如下:

SELECT * FROM t WHERE username LIKE '陈%'

如果需求是要在前面使用模糊查询,

  • 使用MySQL内置函数INSTR(str,substr) 来匹配,作用类似于java中的indexOf(),查询字符串出现的角标位置
  • 使用FullText全文索引,用match against 检索
  • 数据量较大的情况,建议引用ElasticSearch、solr,亿级数据量检索速度秒级
  • 当表数据量较少(几千条儿那种),别整花里胡哨的,直接用like ‘%xx%’。
  1. 尽量避免使用in 和not in,会导致引擎走全表扫描。如下:
SELECT * FROM t WHERE id IN (2,3)

优化方式:如果是连续数值,可以用between代替。如下:

SELECT * FROM t WHERE id BETWEEN 2 AND 3

如果是子查询,可以用exists代替。如下:

# 不走索引
select * from A where A.id in (select id from B);
# 走索引
select * from A where exists (select * from B where B.id = A.id);
  1. 尽量避免使用 or,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE id = 1 OR id = 3

优化方式:可以用union代替or。如下:

SELECT * FROM t WHERE id = 1UNION
SELECT * FROM t WHERE id = 3
  1. 尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE score IS NULL

优化方式:可以给字段添加默认值0,对0值进行判断。如下:

SELECT * FROM t WHERE score = 0
  1. 尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。
    可以将表达式、函数操作移动到等号右侧。如下:
# 全表扫描
SELECT * FROM T WHERE score/10 = 9
# 走索引
SELECT * FROM T WHERE score = 10*9
  1. 当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描。如下:
SELECT username, age, sex FROM T WHERE 1=1

优化方式:用代码拼装sql时进行判断,没 where 条件就去掉 where,有where条件就加 and。

  1. 查询条件不能用 <> 或者 !=
    使用索引列作为条件进行查询时,需要避免使用<>或者!=等判断条件。如确实业务需要,使用到不等于符号,需要在重新评估索引建立,避免在此字段上建立索引,改由查询条件中其他索引字段代替。
  2. where条件仅包含复合索引非前置列
    如下:复合(联合)索引包含key_part1,key_part2,key_part3三列,但SQL语句没有包含索引前置列"key_part1",按照MySQL联合索引的最左匹配原则,不会走联合索引。
select col1 from table where key_part2=1 and key_part3=2
  1. 隐式类型转换造成不使用索引
    如下SQL语句由于索引对列类型为varchar,但给定的值为数值,涉及隐式类型转换,造成不能正确走索引。
select col1 from table where col_varchar=123;
  1. order by 条件要与where中条件一致,否则order by不会利用索引进行排序
# 不走age索引
SELECT * FROM t order by age;
# 走age索引
SELECT * FROM t where age > 0 order by age;

对于上面的语句,数据库的处理顺序是:

  • 第一步:根据where条件和统计信息生成执行计划,得到数据。
  • 第二步:将得到的数据排序。当执行处理数据(order by)时,数据库会先查看第一步的执行计划,看order by 的字段是否在执行计划中利用了索引。如果是,则可以利用索引顺序而直接取得已经排好序的数据。如果不是,则重新进行排序操作。
  • 第三步:返回排序后的数据。

当order by 中的字段出现在where条件中时,才会利用索引而不再二次排序,更准确的说,order by 中的字段在执行计划中利用了索引时,不用排序操作。

这个结论不仅对order by有效,对其他需要排序的操作也有效。比如group by 、union 、distinct等。

  1. 正确使用hint优化语句
    MySQL中可以使用hint指定优化器在执行时选择或忽略特定的索引。一般而言,处于版本变更带来的表结构索引变化,更建议避免使用hint,而是通过Analyze table多收集统计信息。但在特定场合下,指定hint可以排除其他索引干扰而指定更优的执行计划。
  • USE INDEX 在你查询语句中表名的后面,添加 USE INDEX 来提供希望 MySQL 去参考的索引列表,就可以让 MySQL 不再考虑其他可用的索引。例子: SELECT col1 FROM table USE INDEX (mod_time, name)…
  • IGNORE INDEX 如果只是单纯的想让 MySQL 忽略一个或者多个索引,可以使用 IGNORE INDEX 作为 Hint。例子: SELECT col1 FROM table IGNORE INDEX (priority) …
  • FORCE INDEX 为强制 MySQL 使用一个特定的索引,可在查询中使用FORCE INDEX 作为Hint。例子: SELECT col1 FROM table FORCE INDEX (mod_time) …

在查询的时候,数据库系统会自动分析查询语句,并选择一个最合适的索引。但是很多时候,数据库系统的查询优化器并不一定总是能使用最优索引。如果我们知道如何选择索引,可以使用FORCE INDEX强制查询使用指定的索引。
例如:

SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;

二、SELECT语句其他优化

  1. 避免出现select *

首先,select * 操作在任何类型数据库中都不是一个好的SQL编写习惯。

使用select * 取出全部列,会让优化器无法完成索引覆盖扫描这类优化,会影响优化器对执行计划的选择,也会增加网络带宽消耗,更会带来额外的I/O,内存和CPU消耗。

建议提出业务实际需要的列数,将指定列名以取代select *。

  1. 避免出现不确定结果的函数

特定针对主从复制这类业务场景。由于原理上从库复制的是主库执行的语句,使用如now()、rand()、sysdate()、current_user()等不确定结果的函数很容易导致主库与从库相应的数据不一致。另外不确定值的函数,产生的SQL语句无法利用query cache。

  1. 多表关联查询时,小表在前,大表在后。

在MySQL中,执行 from 后的表关联查询是从左往右执行的(Oracle相反),第一张表会涉及到全表扫描,所以将小表放在前面,先扫小表,扫描快效率较高,在扫描后面的大表,或许只扫描大表的前100行就符合返回条件并return了。

例如:表1有50条数据,表2有30亿条数据;如果全表扫描表2,你品,那就先去吃个饭再说吧是吧。

  1. 使用表的别名

当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少哪些友列名歧义引起的语法错误。

  1. 用where字句替换HAVING字句

避免使用HAVING字句,因为HAVING只会在检索出所有记录之后才对结果集进行过滤,而where则是在聚合前刷选记录,如果能通过where字句限制记录的数目,那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤,除此之外,应该将条件写在where字句中。

where和having的区别:where后面不能使用组函数

  1. 调整Where字句中的连接顺序

MySQL采用从左往右,自上而下的顺序解析where子句。根据这个原理,应将过滤数据多的条件往前放,最快速度缩小结果集。

三、增删改 DML 语句优化

  1. 大批量插入数据

如果同时执行大量的插入,建议使用多个值的INSERT语句(方法二)。这比使用分开INSERT语句快(方法一),一般情况下批量插入效率有几倍的差别。

方法一:

insert into T values(1,2); insert into T values(1,3); insert into T values(1,4);

方法二:

Insert into T values(1,2),(1,3),(1,4);

选择后一种方法的原因有三。

  • 减少SQL语句解析的操作,MySQL没有类似Oracle的share pool,采用方法二,只需要解析一次就能进行数据的插入操作;
  • 在特定场景可以减少对DB连接次数
  • SQL语句较短,可以减少网络传输的IO。
  1. 适当使用commit

适当使用commit可以释放事务占用的资源而减少消耗,commit后能释放的资源如下:

  • 事务占用的undo数据块;
  • 事务在redo log中记录的数据块;
  • 释放事务施加的,减少锁争用影响性能。特别是在需要使用delete删除大量数据的时候,必须分解删除量并定期commit。
  1. 避免重复查询更新的数据

针对业务中经常出现的更新行同时又希望获得改行信息的需求,MySQL并不支持PostgreSQL那样的UPDATE RETURNING语法,在MySQL中可以通过变量实现。

例如,更新一行记录的时间戳,同时希望查询当前记录中存放的时间戳是什么,简单方法实现:

Update t1 set time=now() where col1=1; Select time from t1 where id =1;

使用变量,可以重写为以下方式:

Update t1 set time=now () where col1=1 and @now: = now (); Select @now;

前后二者都需要两次网络来回,但使用变量避免了再次访问数据表,特别是当t1表数据量较大时,后者比前者快很多。

4.查询优先还是更新(insert、update、delete)优先

MySQL 还允许改变语句调度的优先级,它可以使来自多个客户端的查询更好地协作,这样单个客户端就不会由于锁定而等待很长时间。改变优先级还可以确保特定类型的查询被处理得更快。我们首先应该确定应用的类型,判断应用是以查询为主还是以更新为主的,是确保查询效率还是确保更新的效率,决定是查询优先还是更新优先。

下面我们提到的改变调度策略的方法主要是针对只存在表锁的存储引擎,比如 MyISAM 、MEMROY、MERGE,对于Innodb 存储引擎,语句的执行是由获得行锁的顺序决定的。MySQL 的默认的调度策略可用总结如下:

1)写入操作优先于读取操作。

2)对某张数据表的写入操作某一时刻只能发生一次,写入请求按照它们到达的次序来处理。

3)对某张数据表的多个读取操作可以同时地进行。MySQL 提供了几个语句调节符,允许你修改它的调度策略:

  • LOW_PRIORITY关键字应用于DELETE、INSERT、LOAD DATA、REPLACE和UPDATE;
  • HIGH_PRIORITY关键字应用于SELECT和INSERT语句;
  • DELAYED关键字应用于INSERT和REPLACE语句。

如果写入操作是一个 LOW_PRIORITY(低优先级)请求,那么系统就不会认为它的优先级高于读取操作。在这种情况下,如果写入者在等待的时候,第二个读取者到达了,那么就允许第二个读取者插到写入者之前。只有在没有其它的读取者的时候,才允许写入者开始操作。这种调度修改可能存在 LOW_PRIORITY写入操作永远被阻塞的情况。

SELECT 查询的HIGH_PRIORITY(高优先级)关键字也类似。它允许SELECT 插入正在等待的写入操作之前,即使在正常情况下写入操作的优先级更高。另外一种影响是,高优先级的 SELECT 在正常的 SELECT 语句之前执行,因为这些语句会被写入操作阻塞。如果希望所有支持LOW_PRIORITY 选项的语句都默认地按照低优先级来处理,那么 请使用–low-priority-updates 选项来启动服务器。通过使用 INSERTHIGH_PRIORITY 来把 INSERT 语句提高到正常的写入优先级,可以消除该选项对单个INSERT语句的影响。

四、查询条件优化

  1. 对于复杂的查询,可以使用中间临时表 暂存数据

  2. 优化group by语句

默认情况下,MySQL 会对GROUP BY分组的所有值进行排序,如 “GROUP BY col1,col2,…;” 查询的方法如同在查询中指定 “ORDER BY col1,col2,…;” 如果显式包括一个包含相同的列的 ORDER BY子句,MySQL 可以毫不减速地对它进行优化,尽管仍然进行排序。

因此,如果查询包括 GROUP BY 但你并不想对分组的值进行排序,你可以指定 ORDER BY NULL禁止排序。例如:

SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ;
  1. 优化join语句

MySQL中可以通过子查询来使用 SELECT 语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的 SQL 操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)…替代。

例子:假设要将所有没有订单记录的用户取出来,可以用下面这个查询完成:

SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )

如果使用连接(JOIN)… 来完成这个查询工作,速度将会有所提升。尤其是当 salesinfo表中对 CustomerID 建有索引的话,性能将会更好,查询如下:

SELECT col1 FROM customerinfo LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID WHERE salesinfo.CustomerID IS NULL

连接(JOIN)… 之所以更有效率一些,是因为 MySQL 不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

  1. 优化union查询

MySQL通过创建并填充临时表的方式来执行union查询。除非确实要消除重复的行,否则建议使用union all。原因在于如果没有all这个关键词,MySQL会给临时表加上distinct选项,这会导致对整个临时表的数据做唯一性校验,这样做的消耗相当高。

高效:

SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 UNION ALL SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';

低效:

SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 UNION SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';
  1. 拆分复杂SQL为多个小SQL,避免大事务
  • 简单的SQL容易使用到MySQL的QUERY CACHE;
  • 减少锁表时间特别是使用MyISAM存储引擎的表;
  • 可以使用多核CPU。
  1. 使用truncate代替delete

当删除全表中记录时,使用delete语句的操作会被记录到undo块中,删除记录也记录binlog,当确认需要删除全表时,会产生很大量的binlog并占用大量的undo数据块,此时既没有很好的效率也占用了大量的资源。

使用truncate替代,不会记录可恢复的信息,数据不能被恢复。也因此使用truncate操作有其极少的资源占用与极快的时间。另外,使用truncate可以回收表的水位,使自增字段值归零。

  1. 使用合理的分页方式以提高分页效率

使用合理的分页方式以提高分页效率 针对展现等分页需求,合适的分页方式能够提高分页的效率。

案例1:

select * from t where thread_id = 10000 and deleted = 0 order by gmt_create asc limit 0, 15;

上述例子通过一次性根据过滤条件取出所有字段进行排序返回。数据访问开销=索引IO+索引全部记录结果对应的表数据IO。因此,该种写法越翻到后面执行效率越差,时间越长,尤其表数据量很大的时候。

适用场景:当中间结果集很小(10000行以下)或者查询条件复杂(指涉及多个不同查询字段或者多表连接)时适用。

案例2:

select t.* from (select id from t where thread_id = 10000 and deleted = 0order by gmt_create asc limit 0, 15) a, t where a.id = t.id;

上述例子必须满足t表主键是id列,且有覆盖索引secondary key:(thread_id, deleted, gmt_create)。通过先根据过滤条件利用覆盖索引取出主键id进行排序,再进行join操作取出其他字段。数据访问开销=索引IO+索引分页后结果(例子中是15行)对应的表数据IO。因此,该写法每次翻页消耗的资源和时间都基本相同,就像翻第一页一样。

适用场景:当查询和排序字段(即where子句和order by子句涉及的字段)有对应覆盖索引时,且中间结果集很大的情况时适用。

五、建表优化

  1. 在表中建立索引,优先考虑where、order by使用到的字段。

  2. 尽量使用数字型字段(如性别,男:1 女:2),若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。

这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

  1. 查询数据量大的表 会造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段分页进行查询,循环遍历,将结果合并处理进行展示。要查询100000到100050的数据,如下:
SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,* FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050
  1. 用varchar/nvarchar 代替 char/nchar

尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

SSM框架

Spring

Spring Boot

Spring MVC

4、前端页面怎么调用后端,顺序是什么。

5、restful了解吗?说一说

spring cloud微服务架构

1、用过那些组件

2、分别介绍组件的作用

3、feign方式怎么调用,怎么实现的

4、讲讲redis,以及你怎么应用到项目中的。还有单点登录

Mybatis

mybatis查询数据库的两种写法。以及写完SQL语句后还有干啥?

Redis

RabbitMQ

Linux

前端

1、会什么技术,使用的组件是原生的还是别人写好的。

关于项目的问题

项目有几个,应用了什么技术

自己拿手的项目模块具体说一下

用过哪些框架
前端用什么,后端用什么
基本数据类型有哪些
final和finally区别
static是啥
事物的存储过程
使用了哪些数据库
main方法

面试中面试官问的一些问题总结相关推荐

  1. 这些面试中经常被问到的线程池问题,你都能回答的上来吗?

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:Real_man juejin.im/post/5e435ac3f265da5753 ...

  2. 面试中,被问到“哑口无言”的瞬间怎么办?

    在面试过程中,可能会遇到各种意想不到的问题,有些问题会让你感到非常自信,而有些问题却会让你感到犯愁,甚至哑口无言.这种情况发生时,如何应对是很关键的.面试中,被问到"哑口无言"的瞬 ...

  3. Python培训常识:Python面试中常被问到的几种设计模式要知道

    学习Python技术大家都是为了日后能够找到适合自己的工作岗位,那么除了要学习好Python技术外,对于面试环节的问题也要有所了解,本期小编为大家介绍的Python培训教程就算关于Python面试中常 ...

  4. 面试中经常会问的智力题,来看看你会做几道

    转载自   面试中经常会问的智力题,来看看你会做几道 下面是大部分题目来自滴滴出行2017秋招题.开始头脑风暴吧~~~ 问题 question one 有50家人家,每家一条狗.有一天警察通知,50条 ...

  5. e - 数据结构实验之查找五:平方之哈希表_面试中常被问到的Hash表,你了解吗

    #新人扶持计划# Hash表在面试中经常被问到,今天我们来了解下. Hash表也称散列表,也有直接译作哈希表,Hash表是一种特殊的数据结构,它同数组.链表以及二叉排序树等相比较有很明显的区别,它能够 ...

  6. (免费领取Java面试题)Java面试中经常被问到的问题

    (免费领取Java面试题)Java面试中经常被问到的问题 免费领取Java面试题 -------------https://blog.csdn.net/kaikeba/article/details/ ...

  7. 面试中常被问到(11)虚函数/纯虚函数

    虚函数 如何定义一个虚函数?在基类成员函数前加入virtual关键字,但并不代表此函数不被实现,只是说明允许基类指针调用派生类重写的此函数 一个类只要声明有虚函数或者从基类继承了虚函数,在编译过程中就 ...

  8. 淘宝十年资深程序员在面试中最爱问的秒杀的面试题

    淘宝十年资深程序员在面试中最爱问的秒杀的面试题 文章目录 淘宝十年资深程序员在面试中最爱问的秒杀的面试题 1.什么是秒杀?秒杀是什么场景? 2.秒杀场景下有哪些挑战?如何应对这些挑战? 3.如何防止商 ...

  9. 面试中经常被问到Java引用类型原理,带你深入剖析

    1.选择唯一性索引 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录.例如,学生表中学号是具有唯一性的字段.为该字段建立唯一性索引可以很快的确定某个学生的信息.如果使用姓名的话,可能存在同 ...

  10. AUC/ROC:面试中80%都会问的知识点

    本文分享自华为云社区<技术干货 | 解决面试中80%问题,基于MindSpore实现AUC/ROC>,原文作者:李嘉琪. ROC/AUC作为机器学习的评估指标非常重要,也是面试中经常出现的 ...

最新文章

  1. 微信小程序时间戳转化为时间
  2. 第16届东北赛区线上比赛斯赛点时间安排+直播链接
  3. python教学视频p_python接口自动化28requestshtml爬虫框架
  4. 线程----BlockingQueue
  5. Linux基础命令---显示文本grep
  6. threejs 入门中的OrbitControls
  7. 为什么软件开发方法论让你觉得糟糕?
  8. Unity使用ARCore开发AR程序
  9. GFlags使用文档
  10. 计算机组成原理实验箱D7,计算机组成原理与系统结构实验仪教学设备,上海求育...
  11. 拉曼光谱避免荧光效应的方法
  12. iPhone十年,移动用户体验领域已经到达成熟阶段
  13. html safari图片不显示,html - 某些FA图标显示在FF,Chrome和Safari中,但没有浏览器会全部显示它们 - 堆栈内存溢出...
  14. 查看linux系统IPV6地址
  15. 7-1 列出连通集(25 分)
  16. 1月第2周小红书、抖音、B站、淘宝KOL排行榜
  17. oracle修改clob值,oracle 修改 clob字段,查询clob字段
  18. 解决——》Handler dispatch failed; nested exception is java.lang.NoSuchMethodError
  19. 全志D1开发板 XR829蓝牙 Can‘t get device info: No such device 自我分析及解决方案
  20. 设计模式02-动态代理模式

热门文章

  1. 计算机应用与维修电竞与管理,电子竞技运动与管理-五年制高技招生专业-广州市白云工商技师学院_广州市白云工商高级技工学校_信息工程系(计算机系)...
  2. python字符串的比较
  3. 【NLP】⚠️学不会打我! 半小时学会基本操作 2⚠️ 关键词
  4. Unity实战篇 | unity接入QQ登录 详细过程——Android篇
  5. Android 类似360 系统启动时间提示
  6. 【Java学习笔记】斗地主规则抽牌模拟(利用LinkedList集合及Collections集合工具类实现)
  7. 客户端到服务器端的通信过程及 原理图很好
  8. HCIP/HCIE RoutingSwitching / Datacom备考宝典系列(八)VLAN知识点全面总结
  9. 使用高德地图获取拍照图片地理位置
  10. 建立了一个博客园创业者QQ群