java 面试题(持续学习更新)

java目录概览:

一、java

1、== 和 equals 的区别是什么?

  • 对于基本类型,== 比较的是值。
  • 对于引用类型, == 比较的是地址。
  • equals不能用于基本类型的比较。
  • 如果没有重写equals,equals就相当于 == 。
  • 如果重写了equals方法,equals比较的是对象的内容。
    // 若依重写的StringUtils下的equals
    public static boolean equals(CharSequence cs1, CharSequence cs2) {
        if (cs1 == cs2) {
            return true;
        } else if (cs1 != null && cs2 != null) {
            if (cs1.length() != cs2.length()) {
                return false;
            } else if (cs1 instanceof String && cs2 instanceof String) {
                return cs1.equals(cs2);
            } else {
                int length = cs1.length();

                for(int i = 0; i < length; ++i) {
                    if (cs1.charAt(i) != cs2.charAt(i)) {
                        return false;
                    }
                }

                return true;
            }
        } else {
            return false;
        }
    }

2、final 在 java 中有什么作用?

  • final修饰的成员变量,必须在声明的同时赋值,一旦创建不能修改。
  • final修饰的方法,不能被子类重写。
  • final类中的方法默认是final的。
  • private类型的方法默认是final的。

3、String 属于基础的数据类型吗?

不属于。
八种基本数据类型:byte short int long char float double boolean。

4、String str="i"与 String str=new String(“i”)一样吗?

String str =“i” 会将其分配到常量池中,常量池中没有重复的元素。
如果常量池中存在i,就将 i 的地址直接赋给变量; 如果没有就创建一个再赋给变量。
String str=new String(“i”)会将对象分配到堆中,即使内存一样,还是会重新创建一个新的对象。

String s = new String(a); 

创建1个或2个对象。
  若常量池中已经存在"a",那么就不会再在常量池中创建对象,⽽是执⾏new操作,在堆中创建⼀个存储"a"的String对象,对象的引⽤
赋值给s,这样就创建了1个对象;
  若常量池中没有"a",就会在常量池中创建⼀个对象"a",然后再执⾏new操作,在堆中创建⼀个存储"a"的String对象,对象的引⽤赋值给s,这样就创建了2个对象。

注意: 这⾥的情况其实堆中的String对象的char value[]属性指向常量池中的char> value[],可以通过debug的形式查看两个String对象的> value值的引⽤相同,也就是说,虽然是两个对象,但它们的value值均指向常量池中的同⼀个地址。上述过程中检查常量池是否有相同Unicode的字符串常量时,使⽤的⽅法便是String中的intern()⽅法。

String s ="a"+"b";

这⾥涉及到字符串常量重载“+”的问题,当⼀个字符串由多个字符串常量拼接成⼀个字符串时,它⾃⼰也肯定是字符串常量。字符串常量的“+”号连接Java虚拟机会在程序编译期将其优化为连接后的值。  在编译时已经被合并成“ab”字符串,因此,只会创建1个对象。并没有创建临时字符串对象a和b,这样减轻了垃圾收集器的压⼒。

String s ="a"+new String("b");

创建5个对象。
  上述的代码Java虚拟机在编译的时候同样会优化,会创建⼀个StringBuilder来进⾏字符串的拼接,实际效果类似:

String s =new String("b");
new StringBuilder().append("a").append(s).toString()

很显然,常量池中分别有“a”和“b”,堆中对象new String(“b”)和“ab”,还多出了⼀个StringBuilder对象,那就应该是5个对象。

疑问:
  为什么会有⼈说,StringBuilder最后toString()之后的“ab”难道不在常量池存⼀份吗?这样不就是6个对象?
解析:
答案是不对的。
  StringBuilder的toString()⽅法是如何将拼接的结果转化为字符串的:

@Override
public String toString(){
// Create a copy, don't share the array
return new String(value,0, count);
}

很显然,在toString⽅法中⼜新创建了⼀个String对象,⽽该String对象传递数组的构造⽅法来创建的:

public String(char value[],int offset,int count)

也就是说,String对象的value值直接指向了⼀个已经存在的数组,⽽并没有指向常量池中的字符串。

5、如何将字符串反转?

将对象封装到stringBuilder中,调用reverse方法反转。

     String str = "asdfg";
     StringBuilder stringBuilder = new StringBuilder(str);
     String ret =stringBuilder.reverse().toString();
     System.out.println(ret); //gfdsa

6、String 类的常用方法都有那些?

  1. 常见String类的获取功能
  • length:获取字符串长度;
  • charAt(int index):获取指定索引位置的字符;
  • indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引;
  • substring(int start):从指定位置开始截取字符串,默认到末尾;
  • substring(int start,int end):从指定位置开始到指定位置结束截取字符串;
  1. 常见String类的判断功能
  • equals(Object obj): 比较字符串的内容是否相同,区分大小写;
  • contains(String str): 判断字符串中是否包含传递进来的字符串;
  • startsWith(String str): 判断字符串是否以传递进来的字符串开头;
  • endsWith(String str): 判断字符串是否以传递进来的字符串结尾;
  • isEmpty(): 判断字符串的内容是否为空串"";
  1. 常见String类的转换功能
  • byte[] getBytes(): 把字符串转换为字节数组;
  • char[] toCharArray(): 把字符串转换为字符数组;
  • String valueOf(char[] chs): 把字符数组转成字符串。valueOf可以将任意类型转为字符串;
  • toLowerCase(): 把字符串转成小写;
  • toUpperCase(): 把字符串转成大写;
  • concat(String str): 把字符串拼接;
  1. 常见String类的其他常用功能
  • replace(char old,char new) 将指定字符进行互换
  • replace(String old,String new) 将指定字符串进行互换
  • trim() 去除两端空格
  • int compareTo(String str) 会对照ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是0。

7、普通类和抽象类有哪些区别?

  • 抽象类不能被实例化。
  • 抽象类可以有抽象方法,只需声明,无须实现。
  • 有抽象方法的,类一定是抽象类。
  • 抽象类的子类必须实现抽象类中的所有抽象方法,否则子类仍然是抽象类。
  • 抽象方法不能声明为静态,并且不能被static、final修饰。

8、接口和抽象类有什么区别?

  1. 接口
  • 接口使用interface修饰
  • 接口不能实例化
  • 类可以实现多个接口
  • ①java8之前,接口中的方法都是抽象方法,省略了public abstract。
    ②java8之后;接口中可以定义静态方法,静态方法必须有方法体。
  1. 抽象类
  • 抽象类使用abstract修饰;
  • 抽象类不能被实例化;
  • 抽象类只能单继承;
  • 抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体;
  • 如果一个类继承了抽象类,①如果实现了所有的抽象方法,子类可以不是抽象类;②如果没有实现所有的抽象方法,子类仍然是抽象类。

9、为什么要使用克隆?如何实现对象克隆?深拷贝和浅拷贝区别是什么?

  1. 什么要使用克隆?
    想对一个对象进行复制,又想保留原有的对象进行接下来的操作,这个时候就需要克隆了。
  2. 如何实现对象克隆。
  • 实现Cloneable接口,重写clone方法;
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
  • BeanUtils,apache和Spring都提供了bean工具,只是这都是浅克隆。
  1. 深拷贝和浅拷贝的区别。
  • 浅拷贝
    在这里插入图片描述
  • 深拷贝
    在这里插入图片描述

10、throw 和 throws 的区别?

  1. throw
  • 作用在方法内,表示抛出具体异常,由方法体内的语句处理;
  • 一定抛出了异常;
  1. throws
  • 作用在方法的声明上,表示抛出异常,由调用者来进行异常处理;
  • 可能出现异常,不一定会发生异常;

11、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?在这里插入图片描述

12、final、finally、finalize 有什么区别?

  • final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写
  • finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
  • finalize方法用于垃圾回收。
    一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。但是当调用finalize方法后,并不意味着gc会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用finalize方法。

13、常见的异常类有哪些?

  • NullPointerException:空指针异常;
  • SQLException:数据库相关的异常;
  • IndexOutOfBoundsException:数组下角标越界异常;
  • FileNotFoundException:打开文件失败时抛出;
  • IOException:当发生某种IO异常时抛出;
  • ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;
  • NoSuchMethodException:无法找到某一方法时,抛出;
  • ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;
  • NumberFormatException:当试图将字符串转换成数字时,失败了,抛出;
  • IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
  • ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。

14、hashcode是什么?有什么作用?

Java中Object有一个方法:

public native int hashcode();
  1. hashcode()方法的作用
    hashcode()方法主要配合基于散列的集合一起使用,比如HashSet、HashMap、HashTable。
    当集合需要添加新的对象时,先调用这个对象的hashcode()方法,得到对应的hashcode值,实际上hashmap中会有一个table保存已经存进去的对象的hashcode值,如果table中没有改hashcode值,则直接存入,如果有,就调用equals方法与新元素进行比较,相同就不存了,不同就存入。
  2. equals和hashcode的关系
    如果equals为true,hashcode一定相等;
    如果equals为false,hashcode不一定不相等;
    如果hashcode值相等,equals不一定相等;
    如果hashcode值不等,equals一定不等;
  3. 重写equals方法时,一定要重写hashcode方法。

15、Java 容器都有哪些?

  1. Collection
    (1) set :HashSet 、 TreeSet
    (2) list : ArrayList、LinkedList、Vector
  2. Map
    HashMap、HashTable、TreeMap
    在这里插入图片描述

16、Collection 和 Collections 有什么区别?

  1. Collection是最基本的集合接口,Collection派生了两个子接口list和set,分别定义了两种不同的存储方式。

  2. Collections是一个包装类,它包含各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等)。
    此类不能实例化,就像一个工具类,服务于Collection框架。

17、list与Set区别

1、List简介
实际上有两种List:一种是基本的ArrayList,其优点在于随机访问元素,另一种是LinkedList,它并不是为快速随机访问设计的,而是快速的插入或删除。
ArrayList:由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。
LinkedList :对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢。
还具有下列方 法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 这些方法 (没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。

2、Set简介
Set具有与Collection完全一样的接口,因此没有任何额外的功能。实际上Set就是Collection,只是行为不同。这是继承与多态思想的典型应用:表现不同的行为。Set不保存重复的元素(至于如何判断元素相同则较为负责) 。
Set : 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。
HashSet:为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。
TreeSet: 保存次序的Set, 底层为树结构。使用它可以从Set中提取有序的序列。

3、list与Set区别
(1)List,Set都是继承自Collection接口
(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
(3)Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

18、HashMap 和 Hashtable 有什么区别?

  • HashMap是线程不安全的,HashTable是线程安全的;
  • HashMap中允许键和值为null,HashTable不允许;
  • HashMap的默认容器是16,为2倍扩容,HashTable默认是11,为2倍+1扩容;

19、switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上?

在这里插入图片描述

20、static注意事项

1、静态只能访问静态。
2、非静态既可以访问非静态的,也可以访问静态的

21、 break ,continue ,return 的区别及作用

  • break 跳出总上一层循环,不再执行循环(结束当前的循环体)
  • continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
  • return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)

22、Integer a= 127 与 Integer b = 127相等吗

在这里插入图片描述

二、 java集合

1、ArrayList 和 LinkedList 的区别是什么?

ArrayList是动态数组的数据结构实现,查找和遍历的效率较高;
LinkedList 是双向链表的数据结构,增加和删除的效率较高;

2、如何实现数组和 List 之间的转换?

String[] arr = {"zs","ls","ww"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
 
ArrayList<String> list1 = new ArrayList<String>();
list1.add("张三");
list1.add("李四");
list1.add("王五");
String[] arr1 = list1.toArray(new String[list1.size()]);
System.out.println(arr1);
for(int i = 0; i < arr1.length; i++){
    System.out.println(arr1[i]);
}

3、在 Queue 中 poll()和 remove()有什么区别?

1、offer()和add()区别:
增加新项时,如果队列满了,add会抛出异常,offer返回false。
2、poll()和remove()区别:
poll()和remove()都是从队列中删除第一个元素,remove抛出异常,poll返回null。
3、peek()和element()区别:
peek()和element()用于查询队列头部元素,为空时element抛出异常,peek返回null。

4、迭代器 Iterator 是什么?

为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator)。

5、Iterator 怎么使用?有什么特点?

List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
}

Iterator 接口源码中的方法:

  • java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象;
  • next() 方法获得集合中的下一个元素;
  • hasNext() 检查集合中是否还有元素;
  • remove() 方法将迭代器新返回的元素删除;

6、怎么确保一个集合不能被修改?

集合(map,set,list…)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。
我们可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。

  @Test
    public void test13(){
        Map<String,String> map = new HashMap<>();
        map.put("1","a");
        map.put("1","a");
        map.put("1","a");
        Map<String, String> stringStringMap = Collections.unmodifiableMap(map);
        stringStringMap.put("1","a");
        System.out.println(stringStringMap);
    }

在这里插入图片描述

同理:Collections包也提供了对list和set集合的方法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)

7、HashMap和HashSet的区别

在这里插入图片描述

8、集合框架底层数据结构

在这里插入图片描述

9、如何边遍历边移除 Collection 中的元素?

在这里插入图片描述

10、 Iterator 和 ListIterator 有什么区别?

  • Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
  • Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
  • ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

11、ArrayList 和 LinkedList 的区别是什么?

  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList 增删操作要影响数组内的其他数据的下标。
  • 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
  • 线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
  • 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。
  • LinkedList 的双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

12、多线程场景下如何使用 ArrayList?

ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样:

List<String> synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");
for (int i = 0; i < synchronizedList.size(); i++) {
System.out.println(synchronizedList.get(i));
}

13、说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层HashMap 的相关方法来完成,HashSet 不允许重复的值。

14、HashSet如何检查重复?HashSet是如何保证数据不可重复的?

  • 向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。
  • HashSet 中的add ()方法会使用HashMap 的put()方法。
  • HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复(HashMap 比较key是否相等是先比较hashcode 再比较equals )。
  • 以下是HashSet 部分源码:
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
    map = new HashMap<>();
}
public Boolean add(E e) {
    // 调用HashMap的put方法,PRESENT是一个至始至终都相同的虚值
    return map.put(e, PRESENT)==null;
}

hashCode()方法的返回值和equals()方法的关系如下:

x.equals(y)返回true,即两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode()方法都必须产生同样的整数结果。如果x.equals(y)返回false,即两个对象根据equals()方法比较是不相等的,那么x和y的hashCode()方法的返回值有可能相等,也有可能不相等。反之,hashCode()方法的返回值不相等,一定能推出equals()方法的返回值也不相等,而hashCode()方法的返回值相等,equals()方法的返回值则可能相等,也可能不相等。

15、Java获取反射的三种方法

在这里插入图片描述

16、 什么是反射机制?

在这里插入图片描述

17、

三、springboot

1、什么是SpringBoot?

通过Spring Boot,可以轻松地创建独立的,基于生产级别的Spring的应用程序,您可以“运行”它们。大 多数Spring Boot应用程序需要最少的Spring配置。

2、为什么要用SpringBoot,优点 ?缺点

快速开发,快速整合,配置简化、内嵌服务容器

  1. 优点
  • 快速构建项目,可以选一些必要的组件;
  • 对主流框架的无配置集成;
  • 内嵌Tomcat容器,项目可独立运行;
  • 删除了繁琐的xml配置文件;
  • 极大地提高了开发和部署效率;
  • 提供starter,简化maven配置;
  1. 缺点
  • 版本迭代速度快,一些模块改动很大;
  • 由于无须配置,报错时很难定位;

3、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

  • @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
  • @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项, 例 如: java 如关闭数据源自动配置功能:
  • @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
  • @ComponentScan:Spring组件扫描。

四、mysql

1、数据库三大范式是什么

  • 第一范式:每个列都不能再拆分
  • 第二范式:在第一范式的基础上,非主键列完全依赖于主键
  • 第三范式:在第二范式的基础上,非主键列只依赖于主键

2、mysql有哪些数据类型

分类类型名称说明
整数类型tinyint1个字节(8位二进制)
smallint2个字节(16位二进制)
mediumint3个字节(24位二进制)
smallint2个字节(16位二进制)
小数类型float单精度浮点数
double双精度浮点数
float单精度浮点数
日期类型yearYYYY 1901~2155
timeHH:MM:SS -838:59:59~838:59:59
dateYYYY-MM-DD 1000-01-01~9999-12-3
datetimeYYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59
timestampYYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC
文本、二进制类型CHAR(M)M为0~255之间的整数
VARCHAR(M)M为0~65535之间的整数
TINYBLOB允许长度0~255字节
BLOB允许长度0~65535字节
MEDIUMBLOB允许长度0~167772150字节
LONGBLOB允许长度0~4294967295字节
TINYTEXT允许长度0~255字节
TEXT允许长度0~65535字节
MEDIUMTEXT允许长度0~167772150字节
LONGTEXT允许长度0~4294967295字节
VARBINARY(M)允许长度0~M个字节的变长字节字符串
BINARY(M)允许长度0~M个字节的定长字节字符串
  • 1、整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整数。
    长度:整数类型可以被指定长度,例如:INT(11)表示长度为11的INT类型。长度在大多数场景是没有意义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNED ZEROFILL属性配合使用才有意义。
    例子,假定类型设定为INT(5),属性为UNSIGNED ZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012。

  • 2、实数类型,包括FLOAT、DOUBLE、DECIMAL。
    DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。
    而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。
    计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。

  • 3、字符串类型,包括VARCHAR、CHAR、TEXT、BLOB
    VARCHAR用于存储可变长字符串,它比定长类型更节省空间。
    VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字节表示。
    VARCHAR存储的内容超出设置的长度时,内容会被截断。
    CHAR是定长的,根据定义的字符串长度分配足够的空间。
    CHAR会根据需要使用空格进行填充方便比较。
    CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
    CHAR存储的内容超出设置的长度时,内容同样会被截断。

  • 使用策略:
    对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。
    对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。
    使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。
    尽量避免使用TEXT/BLOB类型,查询时会使用临时表,导致严重的性能开销。

  • 4、枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。
    有时可以使用ENUM代替常用的字符串类型。
    ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。
    ENUM在内部存储时,其实存的是整数。
    尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。
    排序是按照内部存储的整数

  • 5、日期和时间类型,尽量使用timestamp,空间效率高于datetime,
    用整数保存时间戳通常不方便处理。
    如果需要存储微妙,可以使用bigint存储。
    看到这里,这道真题是不是就比较容易回答了。

3、MyISAM索引与InnoDB索引的区别?

  • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
  • InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
  • MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
  • InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

4、InnoDB引擎的4大特性

  • 插入缓冲(insert buffer)

  • 二次写(double write)

  • 自适应哈希索引(ahi)

  • 预读(read ahead)

5、什么是索引?

更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

6、索引有哪些优缺点?

  • 索引的优点

可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

  • 索引的缺点

时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
空间方面:索引需要占物理空间。

7、数据库为什么要优化

  • 系统的吞吐量瓶颈往往出现在数据库的访问速度上
  • 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
  • 数据是存放在磁盘上的,读写速度无法和内存相比
  • 优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。

五、mybatis

1、mybatis 中 #{}和 ${}的区别是什么?

  • #{}带引号,${}不带引号。
    *(1) ’% ${question}%’ 可能引起SQL注入,不推荐;
    *(2)“%”#{question}“%” 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果。

  • ${}常用于数据库表名,order by 子句;

  • 一般能用#{}就不要使用${};

  • CONCAT(’%’,#{question},’%’) 使用CONCAT()函数,推荐

  • 使用bind标签

<select id="listUserByUsername" resultType="com.cuicuicui.pojo.User">
  <bind name="pattern" value="'%' + username + '%'" />
  select id,sex,age,username,password from person where username LIKE #{pattern}
</select>

2、XML映射文件中,除了常见的select、insert、update、delete,还有哪些标签?

<resultMap>,
<parameterMap>,
<sql>:sql片段标签
<include>:引入sql片段
<selectKey>:指定主键的生成策略并且拿回主键的值
比如
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
        SELECT LAST_INSERT_ID()
    </selectKey>
    insert into t_user (username,password,create_date) values(#{username},#{password},#{createDate})
</insert>
会将 SELECT LAST_INSERT_ID()的结果放入到传入的model的主键里面
<selectKey keyProperty="id" order="BEFORE" resultType="String">
            select uuid()
        </selectKey>
        insert into t_customer (id,c_name,c_sex,c_ceroNo,c_ceroType,c_age)
        values (#{id},#{name},#{sex},#{ceroNo},#{ceroType},#{age})
    </insert>
会将生成的uuid放入model的主键中去。
还有几个动态sql的标签:
trim,where,set,foreach,if,choose,when,otherwise,bind

3、通常一个XML配置文件,对应一个Dao接口。这个接口的工作原理是什么?Dao接口里的方法是否可以重载?

接口的全限定名对应着映射文件的namespace的值,接口的方法可以和xml的全限定名加上标签的id对应。
比如com.jd.dao.UserDao.findAll()对应着命名空间为com.jd.UserDao的id为findAll的sql。
工作原理是JDK动态代理。Mybatis运行时会使用jdk动态代理生成代理对象,拦截接口方法转而执行MappedStatement的sql,然后返回结果。
可以重载,但是对应的xml的MappedStatement的id只能有一个。
比如
List<Student> getAllStu();
List<Student> getAllStu(@Param("id") Integer id);
MappedStatement只能一个
<select id="getAllStu" resultType="com.pojo.Student">
    select * from student
    <where>
        <if test="id!=null">
            id=#{id}
        </if>
    </where>
</select>

4、Mybatis是否支持延迟加载?原理是什么?

支持。association(一对一)和collection(一对多)关联集合对象都支持。
原理是:使用CGLIB创建目标对象的代理对象,当调用目标对象时,进入拦截器方法,这个方法中调用比如a.getB().getName(),拦截器的invoke会发现a.getB()是空值,就会查询关联对象的sql,把B也查出来。

5、Mybatis都有哪些执行器?

三种基本的执行器:

  • SimpleExecutor
    每执行一次update,select,就开启一个Statement对象,用完立刻关闭。
  • ReuseExecutor
    以sql为key查找Statement对象,存在则使用,不存在则创建,用完不关,而是放在Map<String,Statement>中,供下次使用。
  • BatchExecutor
    执行update的时候,将所有sql都添加到批处理中,等待统一执行。

6、XML映射文件和MyBatis内部数据结构的映射关系

所有的xml配置信息都封装到Configuration内部。
被解析为ParameterMap对象。
被解析为ResultMap对象,子元素被解析为ResultMapping对象
每个,,,都被解析为MappedStatement对象
标签内的sql被解析为BoundSql对象

7、MyBatis编程步骤是什么样的?

1、 创建SqlSessionFactory
2、 通过SqlSessionFactory创建SqlSession
3、 通过sqlsession执行数据库操作
4、 调用session.commit()提交事务
5、 调用session.close()关闭会话
6、如图所示:
在这里插入图片描述

8、Mybatis如何执行批量操作

foreach标签的属性主要有item,index,collection,open,separator,close。

  • item   : 表示集合中每一个元素进行迭代时的别名,随便起的变量名;
  • index  : 指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;
  • open   : 表示该语句以什么开始,常用“(”;
  • separator : 表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
  • close   : 表示以什么结束,常用“)”。
  • collection : 在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
  1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
  2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
  3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
<!-- 批量保存(foreach插入多条数据两种方法)
       int addEmpsBatch(@Param("emps") List<Employee> emps); -->
<!-- MySQL下批量保存,可以foreach遍历 mysql支持values(),(),()语法 --> //推荐使用
<insert id="addEmpsBatch">
    INSERT INTO emp(ename,gender,email,did)
    VALUES
    <foreach collection="emps" item="emp" separator=",">
        (#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
    </foreach>
</insert>

9、如何获取生成的主键

对于支持主键自增的数据库(MySQL)
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userId" >
    insert into user( 
    user_name, user_password, create_time) 
    values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>
不支持主键自增的数据库(Oracle)
<insert id="insertUser" >
	<selectKey keyColumn="id" resultType="long" keyProperty="userId" order="BEFORE">
		SELECT USER_ID.nextval as id from dual 
	</selectKey> 
	insert into user( 
	user_id,user_name, user_password, create_time) 
	values(#{userId},#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
</insert>

扩展

  • 如果Mysql 使用selectKey的方式获取主键,需要注意下面两点:
  • order : AFTER
  • 获取递增主键值 :SELECT LAST_INSERT_ID()

10、当实体类中的属性名和表中的字段名不一样 ,怎么办

<select id="getOrder" parameterType="int" resultMap="orderResultMap">
	select * from orders where order_id=#{id}
</select>

<resultMap type="com.cuicuicui.pojo.Order" id="orderResultMap">
    <!–用id属性来映射主键字段–>
    <id property="id" column="order_id">

    <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
    <result property ="orderno" column ="order_no"/>
    <result property="price" column="order_price" />
</reslutMap>

11、MyBatis实现一对一,一对多有几种方式,怎么操作的

  1. 联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对一,一对多的类就可以完成。
    在这里插入图片描述
    在这里插入图片描述
  2. 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

12、MyBatis实现多对一的方式

使用association实现多对一
在这里插入图片描述
在这里插入图片描述

13、MyBatis的二级缓存

  • 定义
  • 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
  • 二级缓存开启的条件
  1. 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置。
  2. 在映射文件中设置标签 < cache />
  3. 二级缓存必须在SqlSession关闭或提交之后有效
  4. 查询的数据所转换的实体类类型必须实现序列化的接口
  • 使二级缓存失效的情况
  • 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

14、Mybatis的一级缓存

  • 定义
    一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
  • 使一级缓存失效的四种情况:
  1. 不同的SqlSession对应不同的一级缓存
  2. 同一个SqlSession但是查询条件不同
  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
  4. 同一个SqlSession两次查询期间手动清空了缓存

六、redis

1、Redis是什么?

Redis 是一个使用 C 语言写成的,开源的高性能key-value非关系缓存数据库。Redis 是完全开源免费的,遵守 BSD 协议,是一个高性能的 key-value 数据库。

  • Redis 与其他 key - value 缓存产品有以下三个特点:
  1. Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  2. Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。
  3. Redis 支持数据的备份,即 master-slave 模式的数据备份。
  • Redis 优势
  1. 性能极高 – Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 。
  2. 丰富的数据类型 – Redis 支持二进制案例的 Strings, Lists, Hashes, Sets 及Ordered Sets 数据类型操作。
  3. 原子 – Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC指令包起来。
  4. 丰富的特性 – Redis 还支持 publish/subscribe, 通知, key 过期等等特性。
  • Redis 与其他 key-value 存储有什么不同?
  1. Redis 有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis 的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
  2. Redis 运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样 Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

2、使用 Redis 有哪些好处?

  • 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都很低;
  • 支持丰富数据类型,支持string,list,set,Zset,hash;
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行;
  • 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除;

3、Redis 的数据类型?

答:Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zsetsorted set:有序集合)。

4、Redis 相比 Memcached 有哪些优势?

  • Memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类

  • Redis 的速度比 Memcached 快很

  • Redis 可以持久化其数据

5、Memcache 与 Redis 的区别都有哪些?

  • 存储方式 Memecache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis 有部份存在硬盘上,这样能保证数据的持久性。
  • 数据支持类型 Memcache 对数据类型支持相对简单。 Redis 有复杂的数据类型。
  • 使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

6、Redis 是单进程单线程的?

答:Redis 是单进程单线程的,redis 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。

7、一个字符串类型的值能存储最大容量是多少?

512M

8、Redis 的持久化机制是什么?各自的优缺点?

Redis提供两种持久化机制 RDB 和 AOF 机制:

  1. RDB(Redis DataBase)持久化方式:
    是指用数据集快照的方式(半持久化模式)记录 redis 数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。

优点:

  • 只有一个文件 dump.rdb,方便持久化。
  • 容灾性好,一个文件可以保存到安全的磁盘。
  • 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis的高性能)
  • 相对于数据集大时,比 AOF 的启动效率更高。

缺点:

  • 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候
  1. AOF(Append-only file)持久化方式:
    是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储)保存为 aof 文件。

优点:

  • 数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次命令操作就记录到 aof 文件中一次。
  • 通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof工具解决数据一致性问题。
  • AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flushall)

缺点:

  • AOF 文件比 RDB 文件大,且恢复速度慢。
  • 数据集大的时候,比 rdb 启动效率低。

9、Redis 常见性能问题和解决方案:

  • Master 最好不要写内存快照,如果 Master 写内存快照,save 命令调度 rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务

  • 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一

  • 为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网

  • 尽量避免在压力很大的主库上增加从

  • 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1<- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现 Slave 对 Master的替换。如果 Master 挂了,可以立刻启用 Slave1 做 Master,其他不变。

10、redis 过期键的删除策略?

  • 定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
  • 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
  • 定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

11、Redis 的回收策略(淘汰策略)?

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰;
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰;
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰;
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰;
no-enviction(驱逐):禁止驱逐数据;
注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。
使用策略规则:
(1)如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru;
(2)如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random;

maxmemory-policy noeviction  #配置策略
maxmemory <bytes>  #配置内存大小 阈值

12、为什么 redis 需要把所有数据放到内存中?

Redis为了达到最快的读写速度,将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以Redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度会严重影响Redis的性能,在内存越来越便宜的今天,redis会越来越收欢迎,如果设置了最大使用的内存,则数据已有数据数达到内存限值后 ,不能继续插入新值。

13、Redis 的同步机制了解么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer中,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存中。

14、是否使用过 Redis 集群,集群的原理是什么?

  • Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为master,继续提供服务。
  • Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行分片存储。

15、Redis 集群方案什么情况下会导致整个集群不可用?

有 A,B,C 三个节点的集群,在没有复制模型的情况下,如果节点 B 失败了,那么整个集群就会以为缺少 5501-11000 这个范围的槽而不可用。

16、Redis 如何设置密码及验证密码?

设置密码:config set requirepass 123456

授权密码:auth 123456

17、说说 Redis 哈希槽的概念?

Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。

18、Redis 集群之间是如何复制的?

异步复制

19、Redis 集群最大节点个数是多少?

16384

20、怎么测试 Redis 的连通性?

ping命令

21、怎么理解 Redis 事务?

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

22、Redis 事务相关的命令有哪几个?

MULTI、EXEC、DISCARD、WATCH

23、Redis key 的过期时间和永久有效分别怎么设置?

EXPIRE、PERSIST

24、Redis 如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。

25、Redis 回收进程如何工作的?

一个客户端运行了新的命令,添加了新的数据。Redi 检查内存使用情况,如果大于 maxmemory 的限制, 则根据设定好的策略进行回收。一个新的命令被执行。

26、都有哪些办法可以降低 Redis 的内存使用情况呢?

如果你使用的是 32 位的 Redis 实例,可以好好利用 Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的 Key-Value 可以用更紧凑的方式存放到一起。

27、Redis 的内存用完了会发生什么?

如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将 Redis 当缓存来使用配置淘汰机制,当 Redis 达到内存上限时会冲刷掉旧的内容。

28、一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set 他们最多能存放多少元素?

理论上 Redis 可以处理多达 232 的 keys,并且在实际中进行了测试,每个实例至少存放了 2 亿 5 千万的 keys。我们正在测试一些较大的值。任何 list、set、和 sorted set 都可以放 232 个元素。换句话说,Redis 的存储极限是系统中的可用内存值。

29、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

相关知识:Redis 提供 6 种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

30、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用 keys 指令可以扫出指定模式的 key 列表。

对方接着追问:如果这个 redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?

这个时候你要回答 redis 关键的一个特性:redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。

31、如果有大量的 key 需要设置同一时间过期,一般需要注意什么?

如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

32、使用过 Redis 做异步队列么,你是怎么用的?

一般使用 list 结构作为队列,rpush 生产消息,lpop 消费消息。当 lpop 没有消息的时候,要适当 sleep 一会再重试。如果对方追问可不可以不用 sleep 呢?list 还有个指令叫 blpop,在没有消息的时候,它会阻塞住直到消息到来。如果对方追问能不能生产一次消费多次呢?使用 pub/sub 主题订阅者模式,可以实现1:N 的消息队列。

如果对方追问 pub/sub 有什么缺点?

在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 RabbitMQ等。

如果对方追问 redis 如何实现延时队列?

我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用 sortedset,拿时间戳作为score,消息内容作为 key 调用 zadd 来生产消息,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。

33、使用过 Redis 分布式锁么,它是什么回事?

先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。

这时候对方会告诉你说你回答得不错,然后接着问如果在 setnx 之后执行 expire之前进程意外 crash 或者要重启维护了,那会怎么样?这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得 set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和expire 合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

七、rabbitmq

1、为什么使用MQ?MQ的优点

异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
日志处理 - 解决大量日志传输。
消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

八、linux

九、Nginx

十、sprincloud

十一、学习方法

1、如何学习一个框架或者技术

  • 是什么,简介,概述

  • 有什么用,用途,使用场景

  • 怎么用,在实际开发中的应用,注意事项

  • 优缺点

  • 框架原理,工作流程,工作原理

  • 常见面试题

  • 源码分析,核心类,核心方法,设计模式

  • 发布博客,在开发和实践中,博客反馈中持续改进

  • 与同事朋友交流,技术论坛,技术分享中持续丰富知识

2、常用框架

集成开发工具(IDE):Eclipse、MyEclipse、Spring Tool Suite(STS)、Intellij IDEA、NetBeans、JBuilder、JCreator

JAVA服务器:tomcat、jboss、websphere、weblogic、resin、jetty、apusic、apache

负载均衡:nginx、lvs

web层框架:Spring MVC、Struts2、Struts1、Google Web Toolkit(GWT)、JQWEB

服务层框架:Spring、EJB

持久层框架:Hibernate、MyBatis、JPA、TopLink

数据库:Oracle、MySql、MSSQL、Redis

项目构建:maven、ant

持续集成:Jenkins

版本控制:SVN、CVS、VSS、GIT

私服:Nexus

消息组件:IBM MQ、RabbitMQ、ActiveMQ、RocketMq

日志框架:Commons Logging、log4j 、slf4j、IOC

缓存框架:memcache、redis、ehcache、jboss cache

RPC框架:Hessian、Dubbo

规则引擎:Drools

工作流:Activiti

批处理:Spring Batch

通用查询框架:Query DSL

JAVA安全框架:shiro、Spring Security

代码静态检查工具:FindBugs、PMD

Linux操作系统:CentOS、Ubuntu、SUSE Linux、

常用工具:PLSQL Developer(Oracle)、Navicat(MySql)、FileZilla(FTP)、Xshell(SSH)、putty(SSH)、SecureCRT(SSH)、jd-gui(反编译)

参考文章

https://blog.csdn.net/guorui_java/article/details/108049191

https://blog.csdn.net/guorui_java/article/details/108076036

https://blog.csdn.net/guorui_java/article/details/108121583

https://blog.csdn.net/guorui_java/article/details/108153368

https://blog.csdn.net/guorui_java/article/details/108151596

https://blog.csdn.net/guorui_java/article/details/109043769

https://www.cnblogs.com/QLCZ/p/14968925.html

https://www.cnblogs.com/QLCZ/p/15041523.html

https://www.cnblogs.com/QLCZ/p/14977264.html

https://www.cnblogs.com/QLCZ/p/14968927.html

https://wenku.baidu.com/view/71380354f142336c1eb91a37f111f18583d00c66.html

https://blog.csdn.net/Design407/article/details/103242874?spm=1001.2014.3001.5506

https://blog.csdn.net/h1774733219/article/details/122825139

https://www.pdai.tech/md/db/nosql-redis/db-redis-x-rdb-aof.html

https://www.cnblogs.com/varchar-pig/p/14224637.html

https://www.kuangstudy.com/bbs/1375055950792208385

https://blog.csdn.net/ThinkWon/article/details/103592572

https://blog.csdn.net/ThinkWon/article/details/101292950

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值