跳到主要内容

0422-面试

  • redis 缓存击穿、穿透、雪崩

    • 击穿:单一 key 失效,并发请求打到数据库上
      • 均匀设置过期时间
    • 穿透:单一 key 不存在,请求打到数据库上
      • 检查 key 是否否和要求
      • 缓存空值或默认值
      • 使用布隆过滤器先判断下数据是否存在
        • 布隆过滤器的原理是:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了
    • 雪崩:某一时间,大量的 key 失效,请求打到数据库上。
      • 均匀设置过期时间
      • 如果 key 不在 redis 里面,就加一个互斥锁,保证统一时间只有一个请求来构建缓存,互斥锁要加超时时间。
      • 不够缓存设置有效期,后台定时更新 redis 的缓存
      • 服务熔断,请求限流
      • 构建高可用 redis 集群
  • 出现了死锁如何解决?

    • 保持案发现场
  • SM3、SM4 的区别,以及流程?

    • SM3 类似 MD5
    • SM4 对称加密
    • 加密流程:对 msg base64 编码,对 base64 编码进行 SM3 摘要的得到 sm3 字符串, 对 base64 编码进行 SM4 摘要的得到 sm4 字符串
    • 解密流程:对 sm4 字符串解密,得到 base64 编码,对 base64 编码进行 sm3 摘要,与请求中的 sm3 字符串进行比较
  • HTTP

    • RESTful 风格
      • PUT
      • POST
      • GET
      • DELETE
    • Accept 的值
      • text/html
      • application/json
      • application/octet-stream 下载文件
  • Innodb 和 myisam 的区别

    • InnoDB 支持事务,行级锁、表锁;查询全表行数需要遍历全表;适合大量增删改的场景(数据安全);
    • MyISAM 不支持事务,支持表锁,不支持行级锁;查询全表行数快;适合大量查询的场景;
  • 索引

    • 索引的类型
      • 主键索引
      • 唯一索引
      • 普通索引
      • 组合索引
      • 全文索引
    • 数据结构:B+树
    • 最左匹配原则:复合索引,匹配从最左边开始匹配,
    • 回表:根据非主键索引找不到所有要查询的列,最后还是要去主键索引上查找数据。
    • 覆盖索引:仅通过索引就能查询到想要查询的数据,不用回表。
    • 索引下推:把 where 中的一些过滤条件放到引擎层来过滤。
  • JVM 调优的案例

    • 分析 GC 日志,导出 dump 文件
    • 修改堆内存大小
    • 设置在 OOM 时导出 dump 文件
    • 如果频繁创建对象,应该适当的增加年轻代的空间
    • 如果有较多的持久对象,应该适当的增加老年代空间
  • 导入导出优化(数据量很大)

    • 分页查询出要导出的数据
      • 分页时,如果数据量很大的话,使用子查询来代替 limit 进行分页。
      • 1000W 数据,每页大小 100000 ,导出需要 12 分钟左右。
      • 优化 select count(*) from table_name, 让这个查询走普通索引,而不是主键索引。查找时,会把索引的数据加载到内存,主键索引树上存放所有的数据(占用内存大),普通索引树上存放的是索引列的数据。
      • 设置 fetchSize 减轻对内存的压力。
    • 使用 SXSSFWorkBook 对象
    • 重复使用样式对象 CellStyle ,不要每次填充 cell 时都创建一个新的样式对象
    • 导出实测
      • 使用 limit 来进行分页 1000w ,每页 10000 超过 1 小时
      • 使用子查询 + limit 分页, 1000w ,每页 10w -Xmx1g ;导出会有内存溢出
      • 使用子查询 + limit 分页, 1000w ,每页 10w, -Xmx1g ,每从数据库中读取到 1000 条记录,向 sheet 中写入一次;耗时:761700 ms 约等于 12 分半
      • 使用子查询 + limit 分页, 1000w ,每页 10w , -Xmx1g ,每个 sheet 100w 行,每个 sheet 页使用一个线程;耗时:333506 ms 约等于 5 分半
      • 使用子查询 + limit 分页, 1000w ,每页 10w , -Xmx1g ,每个 sheet 100w 行,每个 sheet 页使用一个线程,设置 fetchSize 为 1000 ;耗时:408296ms 约等于 6 分 48 秒
      • 使用子查询 + limit 分页, 1000w ,每页 10w , -Xmx1g ,每个 sheet 100w 行,每个 sheet 页使用一个线程,设置 fetchSize 为 10000 ;耗时:耗时:350596ms 约等于 5 分 50 秒
      • 使用子查询 + limit 分页, 1000w ,每页 10w , -Xmx4g ,每个 sheet 100w 行,每个 sheet 页使用一个线程,每个线程一个数据库连接, workBook 的 rowAccessWindowSize 设置为 10 w,List 的 size 为 10w, ;耗时:耗时:266845 ms 约等于 4 分 26 秒
  • HashMap 、HashTable 的区别

    • HashMap 线程不安全;底层数据结构:数据+链表/红黑树,链表的长度大于 8 时,转为红黑树;key 允许为空;
    • HashTable 线程安全;key 不允许为空;
    • 集合的种类
      • List
      • Set
      • Map
    • set 和 list 的区别
      • Set 无序,不重复
      • List 有序,可重复
  • 使用到的 Spring 组件

    • Spring
    • Spring MVC
    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
  • js 数据类型

    • 基本数据类型
      • undefined
      • null
      • boolean
      • string
      • number
      • symbol ES6 新增
    • 引用数据类型
      • object
      • array
      • function
  • ===== 的区别

    • == 比较前会先将对象类型转成一致,再比较内容。
    • === 先比较类型是否一样,一样的话,再比较内容。
  • 用到的 Spring 注解

    • spring 实现异步的几种方式
      • @Async 注解
      • 线程池
    • IoC 是如何实现的?

如何保证数据库与缓存的一致性?

两种方案

  1. 先更新数据库
    • 更新数据库后,删除缓存,如果删除缓存失败,将 key 传递给消息队列,让消费者来删除缓存。
    • 更新数据库,订阅数据库变更日志,将数据变更的 key 传递给消息队列,让消费者来删除缓存。
  2. 先删除缓存
    • 先删除缓存,在更新数据库会有两个问题
      1. 删除缓存成功,在更新数据库成功之前,有另外的请求构建的缓存,导致缓存错误
      2. 删除缓存成功,更新数据库也成功,但是在从库数据更新之前,有另外的请求构建的缓存,导致缓存错误
    • 解决方案:延时双删
    • 在更新数据库成功之后,延时删除缓存。
      • 延时时间的要求
        • 大于另外线程请求数据库+构建缓存的时间
        • 大于主从复制的时间

延时双删的伪代码

#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)