Java面试复习指南

本文深入探讨了MySQL的索引机制,包括不同类型的索引、锁机制、事务隔离级别以及主从复制。接着介绍了Redis的五种数据结构及其应用场景,以及如何处理数据一致性。此外,还涵盖了Java基础,如字符串处理、集合操作和并发控制。这些知识对于优化数据库查询和构建高性能系统至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

​强烈推荐一个大神的人工智能的教程:http://www.captainai.net/zhanghan

一、mysql篇

1.索引

  • 数据结构:传送门
    二叉树、平衡二叉树、B树、B+树
  • 索引类型
    聚簇索引【主键索引,叶子节点存储的是整行数据】
    非聚簇索引【非主键索引,叶子节点存储的是该行数据的主键id】
    innodb使用的是聚簇索引,因为他是数据和索引在一起的
  • 回表
    基于普通索引查询,查询到主键id后需要回到主键索引上再查询一次,取到真正的数据
  • 覆盖索引
  • 前缀索引
  • 索引下推:传送门
-- 联合索引(name,age)
select * from tuser where name like '张%' and age=10 and ismale=1;

在MySQL 5.6之后,引入索引下推,在索引内部就可以判断符合条件的
有效减少回表次数,提高检索效率

2.锁

📢:锁不是用完立即释放,而是会等到事务结束才会释放

  • 全局锁:锁定整个数据库为只读状态,用于备份过程
  • 表锁:MDL读锁和MDL写锁,读读共享,读写互斥,写写互斥
  • 行锁:两阶段锁协议,读锁、写锁
  • 间隙锁:前后都是开区间,解决了幻读的问题
    幻读:一个事务在前后两次查询同一个范围的时候,后一次查询查看到了前一次查询没有查看到的行【新增的行】
  • next-key lock【行锁+间隙锁,前开后闭】
    加锁规则:
    a.原则1:加锁的基本单位是next key lock
    b.原则2:查找过程中访问到的对象才会加锁
    c.优化1:等值查询,唯一索引加锁时,next-key lock退化为行锁;范围查询,不会退化
    d.优化2:非唯一索引上的等值查询,向右遍历时最后一个值不满足等值条件的时候,next-key lock退化为间隙锁
    e. bug:唯一索引上的范围查找会查到不满足条件的第一个值为止【例如 id<=15,并不是到15就结束了,而是会继续向后查找知道不为15为止】
  • 死锁和死锁检测

3.事务

  • 隔离级别
    读未提交:一个事务还未提交时,所做的变更可以被其他事务看到【读取行的最新值】
    读提交:一个事务提交后,所做的变更可以被其他事务看到【每个sql执行的时候创建的】
    可重复读:一个事务在执行过程中看到的数据都和该事务开始时看到的一致【事务开始时创建的视图】
    串行化:当出现读写锁冲突时,后一个事务需要等到前一个事务执行完才能执行【通过加锁实现】

4.主从

  • 如何保证主从一致
    从库执行主库的binlog,binlog格式设置为row,因为设置为statement会出现主从选择索引不一致的情况,就导致范围查找且limit操作的时候,结果不一致

    delete from t /*comment*/  where a>=4 and t_modified<='2018-11-10' limit 1;
    
  • 主备延迟的原因
    从库所在机器性能差
    备库压力大,例如:备库执行了一些分析查询等操作
    主库执行大事务:binlog是整个事务完成才写

  • 主从切换

    可靠性切换
    步骤二中两个库都是只读状态,会造成不可用,但是数据可靠
    在这里插入图片描述
    可用性切换
    立马切换过去,不等binlog同步是否有延迟,可能会造成数据不一致在这里插入图片描述

5.查大表会把内存打爆吗?

有两种方式:存内存的和使用net_buffer_length边读边发【写满了就清理】

6.join如何使用?

join中有两个角色,驱动表和被驱动表。

  • NLK:可以使用上索引的可以作为被驱动表,走索引,扫描次数会少。可以使用MRR算法优化到BKA

  • BNL:被驱动表使用不上索引的时候,小表作为驱动表,驱动表数据会加载到 join_buffer中,当加载不下时,会分段,段越少,扫描被驱动表的次数越少。可以优化到BKA

    两种优化方式都是借助buffer,提供批量数据,让被驱动表可以批量匹配
    在这里插入图片描述

7.临时表

线程自己可见,所以多个线程同时建临时表不用担心重名等问题。线程执行结束后,临时表会自动被删除,所以不用考虑资源回收等问题。

待查问题

  • 我们的数据量
    6000万数据,32张表,师生关系
    5400万数据,128张表,师生间发送的消息

  • 分表如何实现的

  • 优化器做了哪些优化,传送门
    查询条件简化
    外连接消除
    IN查询

  • 主从

  • mysql为什么选择RR

  • 可重复读为什么没有解决幻读
    关键点:快照读和当前读。在更新的时候是当前读

二、Redis篇 传送门

1. 五种基本数据结构

  • String
  • Hash:压缩列表(数据小)/hash表(数据大,从前者结构转换为后者结构),rehash采用渐进hash,同时保留两个哈希表,在字典中设置一个计数器hashidx,每次操作新增/查询/删除操作的时候,都会将hash值在hashidx上的rehash到新的哈希表上,直到旧的哈希表全部被rehash,这个计数hashidx会置为-1,旧hash表回收
    解决散列冲突:开放寻址法和链表法;开放寻址法需要提前分配内存,链表法是需要的时候再分配,所以链表法内存占用会更少
    使用场景:购物车、计数器(签到天数等)
  • List:顺序,可重复,压缩链表(数据小)/LinkedList/quickList(压缩链表和linkedList的结合)
    使用场景: 栈、队列
  • Set:整数集合/字典;无序,不重复,可做多个集合的交集、并集、差集,时间复杂度O(N)
    使用场景: 标签,共同爱好
  • Zset:压缩列表/跳跃表;有序,不重复,时间复杂度O(logN)
    使用场景: 点赞排名,考试排名

2. 项目中的使用场景

①:老师给学生的备注名
采用hash存储,设置1小时过期时间,老师设置学生备注的时候做删除动作
②:过去8小时产生的订课,取消课
采用zset存储,操作时间作为score
取数据使用范围取过去8小时到现在时间段内的数据

3.String是如何存储的?

在这里插入图片描述

4. incr和decr做接口防刷和次数限制

例如:每秒钟接口访问次数、每秒钟短信验证码访问次数
incr和decr命令内部分两步执行:

 key存在,则+1或-1,返回具体结果值
 key不存在,则设置初始值为0,然后执行+1或-1,返回具体结果值

通过给key设置过期时间,控制多长时间内访问次数

5. setnx和setex

6.redis如何保证和DB数据一致性

https://blog.csdn.net/wwd0501/article/details/106902856

7.雪崩和缓存击穿

雪崩:大量key同时过期,导致请求都去请求DB,增大DB压力。
------->解决方法:key的过期时间加一个随机时间,减少大批同时过期的情况
缓存穿透:redis也没有数据,DB也没有数据,白白走了很多次DB请求
------->解决方法:不存在的数据查询后也set到缓存,请求的时候请求到空数据直接返回,不请求数据库;布隆过滤器

8.持久化RDB和AOF

RDB: 快照,主进程fork子进程(fork会阻塞),子进程负责生成RDB文件。

存在的问题:生成期间不能保证数据准确性,生成需要时间,生成期间,做的更新会记录到RDB文件中,导致这破坏了某一时刻的数据

AOF: 命令执行时,同时写AOF缓存,之后同步缓存指令到磁盘,接近实时,
同步有三种:
a. 实时同步,性能较差
b. 每秒钟执行一次同步,在极端情况下会丢失1s的数据
c. 不设置同步时间,默认最多间隔30s执行同步
磁盘上AOF文件过大问题解决:AOF重写,将当前读到的key-value写一条命令set到新的AOF文件中,这样之前的多条指令就被整合成了一条,过程期间写入的指令记录到AOF重写缓冲区中,AOF重写完毕后会给主进程发一个信号,主进程收到后将阻塞,然后将AOF重写缓冲区的指令追加到新的AOF文件中;AOF重写缓冲区保证了数据不丢失。

9.redis集群

a. 主从复制:读写分离
b. 哨兵:主节点有问题时,可以自动选主
c. cluster(我们公司使用的这种模式):每个节点上存储的数据不一样,redis共16384个时间槽

10.如何证明redis是单线程的

三、java基础

1.hash、一致性hash、redis的hash槽

2.String、StringBuilder、StringBuffer

3.List、Map

https://zhuanlan.zhihu.com/p/78079598
如何判断有环:https://www.cnblogs.com/hhx626/p/7534615.html
指针追赶法和查表法

  • 用hashmap存储1万的数据,需要考虑什么?
    考虑初始化容量,避免频繁扩容。hashmap提供了一个构造函数初始化容量。但是这个初始化只是计算了负载因子和table容量,在第一次put的时候才根据table容量初始化,当达到负载因子*table容量的限制时就会触发resize()扩容
  public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
  
  public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

   static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
  • hashmap链表长度达到8会由二叉树转为红黑树
    https://www.jianshu.com/p/1286f9144475
    https://blog.csdn.net/baidu_37147070/article/details/98785367
    二叉树可能会由于插入顺序的影响产生树倾斜,所以树的高度决定了效率。hashmap由数组+链表结构组成,结构转换步骤:
    ① 在链表高度达到8的时候先会判断数组表容量是否超过64,未超过则扩容
    ②超过64则转换为红黑树,红黑树是一种近平衡的二叉树,有效减少树倾斜的情况,提高查询效率

  • ArrayList扩容原理是什么
    默认大小是10,若初始化时指定了大小,则会根据指定的大小进行初始化,之后每次add新元素时会先校验容量是否足够,当存满的时候会触发扩容,扩为当前容量的1.5倍

//①构造函数初始化
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
//②添加新元素
public boolean add(E e) {
        //添加新元素时校验容量是否足够,存满了就触发扩容,扩为当前容量的1.5倍
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

//③确认容量
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
//④扩容
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //扩为当前的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

4.单例模式

5.volatile

6.TCP和UDP

TCP三次握手保证数据安全传输到服务端,UDP是无连接的,只管发,不保证能正确的到达服务端

7.三大限流算法

8.多线程

9.如何构建高可用系统

大学java的期末复习资源,试题及答案 1、编译Java Application源程序文件将产生相应的字节码文件,这些字节码文件的扩展名为( )。 A.java B..class C.html D..exe 2、下面哪一项字符序列可以作为合法的标识符( )。 A.true B.null C.2018_year D.$2018year 3、下列选项中,( )不属于Java语言的简单数据类型。 A.整数型 B.数组 C.字符型 D.浮点型 4、对于int a[ ]=new int[3],下列叙述错误的是( )。 A. a.length的值是3 B. a[1]的值是1 C. a[0]的值是0 D. a[a.length-1]的值等于a[2]的值 5、用下列哪个代码替换程序标注的【代码】会导致编译错误? A.m-->0 B.m++>0 C.m = 0 D.m>100&&true; public class E { public static void main (String args[ ]) { int m=10,n=0; while(【代码】) { n++; } } } 6、以下关于继承的叙述正确的是( )。 A. 在Java中类不能多继承 B. 在Java中一个类只能实现一个接口 C. 在Java中一个类不能同时继承一个类和实现一个接口 D. 在Java中接口只能单一继承 7、对于下列Cat类,哪个叙述是错误的?( ) A.Cat类只有 2 个构造方法,而且没有无参数的构造方法 B.Cat类有 4 个构造方法 C.Cat (int m)和 Cat (double m)是互为重载的构造方法 D.int Cat (int m)和 void Cat (double m) 是互为重载的非构造方法 class Cat { Cat (int m){ } Cat (double m){ } int Cat (int m){ return 23; } void Cat (double m){ }} 8、下列叙述哪些是正确的( )。 A final 类可以有子类 B abstract类中只能有abstract方法 C abstract类中可以有非abstract方法,但该方法不可以用final修饰 D 不可以同时用final和abstract修饰一个方法 9、假设D类有如下定义,设d是D类的一个实例对象,下列语句调用错误的是 ( )。 class D{ int i; static String s; void method1(){} static void method2(){}} A.d.method1(); B.D.method2(); C.D.method1(); D.System.out.println(d.i); 10、将下列( )哪个代码替换下列程序中的【代码】不会导致编译错误。 A.public int f(){return 100+M;} B.int f(){return 100;} C.public double f(){return 2.6;}。 D.public abstract int f();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值