面试补充知识点
HashMap 的底层原理以及如何扩容?
- HashMap 由 数组 + 链表 + 红黑树 构成
- 初始大小为 16,通过哈希策略将数据分布到不同的桶中
- 扩容机制:
- 当数组长度小于 64 时,扩容并且会 Rehash
- 当数组长度大于等于 64 且链表长度超过 8 时,链表转为 红黑树,查询复杂度从 O(n) 变为 O(logN)
- 扩容时数组大小翻倍(*2),装载因子默认为 0.75,可自定义
Redis 的穿透、击穿、雪崩
缓存穿透
- 现象:客户端访问不存在的数据,缓存和数据库均未命中,大量请求直达数据库,导致负载过大甚至宕机
- 原因:
- 业务层误删缓存和数据库的数据
- 恶意请求不存在的数据
- 解决方案:
- 未命中时将空值写入缓存,下次请求直接返回空值
- 使用 布隆过滤器拦截不存在的请求
缓存击穿
- 现象:某个热点数据在缓存失效的瞬间,大量并发请求直达数据库,导致服务崩溃
- 解决方案:
- 对热点数据设置 永不过期
- 使用 互斥锁,一个线程重建缓存,其他线程等待
缓存雪崩
- 现象:大量缓存同时过期,或 Redis 节点故障,导致所有请求直达数据库,造成数据库宕机
- 解决方案:
- 避免同时过期,设置 随机过期时间
- 启用 降级和熔断措施
- 热点数据设置 永不过期
- 使用 Redis 集群,单点宕机时仍有节点可用
创建线程的方法?
- 继承Thread类,重写run()方法;
- 实现Runnable接口,实现run()方法;
- 实现Callable接口,实现call方法;
- 线程池创建。
Spring/SpringBoot怎么理解
- 其实Spring就是一个管理类的容器,我们通过注解将我们定义好的类添加到Spring容器里,在我们需要的时候就是用注解从Spring容器里拿出之前定义好的类,可以理解为Spring就是一个Map结构,K是类名,V是类,取出来类后就可以调用类的方法了。
- 存进银行的注解:@Component,@Component可以细化为这三个:@Service@Controller@Repository
- 从银行取出来的注解:@Resource@Autowired
Redis怎么理解
- redis很像一个公共的变量,如果是分模块部署的,想要获取公共的东西,就需要去发http请求获取内存里的内容。
单点登录和鉴权
- 可以使用SpringSecurity进行鉴权的判断,当用户第一次登陆的时候,就去数据库查询该用户的权限并封装成token,这样之后每一个请求都会去token判断用户的类型进而判断是否放行。
消息队列
- 其实消息队列就是一个Map,然后用List存取很多个Map,当生产数据的时候,就把他存进List,消费数据的时候,就是用get(),然后remove掉;
Docker
- docker是一个容器,我们之前部署上线的时候是把项目生成一个jar包,然后上Linux服务器启动这个jar包,但是我们可能还需要redis,mysql等一系列的东西,如果有多台服务器,安装就很麻烦;
- 于是我们使用docker把jar包、mysql、redis、nginx、jdk17全部打包成一个镜像,然后把这个镜像上传到linux上,这样linux只需要安装一个docker,然后docker run就可以运行了。
- 但是镜像过多,所以出现了k8s去管理镜像。
索引失效的场景有哪些?
- 使用左或者左右模糊匹配的时候,
like %超 /like %超% - 对索引使用函数:where length(name)=6,这样就没有走原来定义好的name了
- 对索引进行表达式计算:where id+1=10
- 联合索引非最左
- where的or条件前语句是索引列后语句不是where id = 1 or age = 2;id是索引但是age不是就不会走。
线程池参数该如何设置?
- 如果是IO密集型,线程的大部分时间都花在了IO上,就会导致CPU利用率低,所以我们需要多开线程让cpu运作起来,一般是2*核心数
- 如果是计算密集型,大部分线程都在处理计算任务,不会频繁进行上下文切换,所以设置为核心数+1即可
hashCode和equals方法有什么关系?
- 如果两个对象相等,那他们equals和hashCode的返回值都相等
- 但如果两个对象的hashCode返回值相同,两个对象不一定相同
- 所以重写equals方法的时候一定要重写hashCode方法,以保证在使用hash等数据结构的时候能正常工作
如何使用Spring实现事务?
- 事务四大原则ACID:原子、隔离、持久、一致
- 使用注解@Transactional
- @Transactional失效的场景有哪些?
- 方法不是public:Spring默认基于代理,只有public方法才会被代理拦截
- 方法内部调用,不会经过代理对象
- final/static方法
- 异常没有抛出
- 多线程调用,因为新线程不会继承老线程的上下文
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Explainfuture's Blog!
