元宝数科备考

自我介绍 + 离职原因 + 你所负责的业务项目

  • ~ 面试常备问题,自我介绍往期面试经过都有就不单独写了;离职原因,主打一个真诚就好,只要不是因错被开除被优化正常说就行;
    ~ 主要负责基础信息的维护,提供并维护本系统的高效查询接口,日常业务项目的方案设计、排期、进度把控、代码开发;

如何能抗住1w的QPS,考虑下JVM或其他方面的东西

  • ~ 1w的Qps,对于数据库来说是不可能的,首先要考虑使用缓存;其次,当某些Qps到达数据库的时候,数据库要依靠合理的分库分表、索引、查询语句尽快的返回结果,防止阻塞过久的用户访问;代码层面考虑结束之后要考虑资源,流量大的服务是不是要考虑加机器,当机器出现断点等不可访问的时候,如何尽快的补齐。

垃圾回收器有了解嘛

  • ~ 最老的Serial / Serial Old;
  • ~ 支持多线程回收的Parallel / Parallel Old;
  • ~ 用户影响最小且也为多线程回收的ParNew / CMS;
  • ~ 横跨新生代老年代,可以自选垃圾回收时间的收集器G1;

CMS和G1的区别和优缺点

  • ~ CMS收集器:是一种以最短回收停顿时间为目标的收集器,基于并行的"标记-清除"实现,在回收过程中几乎不会暂停用户线程:
    ~ 1)初始标记:stop-the-world,仅标记GCroots能直接关联的对象;
    ~ 2)并发标记:和用户线程并行,通过 GCRoots Tracing 标记所有可达对象;
    ~ 3)重新标记:stop-the-world,对标记阶段用户线程运行产生的垃圾对象进行标记重标,更新逃逸对象;
    ~ 4)并发清理:和用户线程并行,清理在重复标记中被标记为可回收的对象;

  • ~ CMS的优点
    ~ 1)并发收集;
    ~ 2)低停顿,CMS可以让耗时的两个stop-the-world操作和用户线程并发执行,在较短时间执行完成;

  • ~ CMS的缺点
    ~ 1)CMS对CPU资源非常敏感,在并行阶段虽然不会导致用户线程停顿,但是会因为抢占了一部分CPU资源导致用户进程受阻;
    ~ 2)无法处理浮动垃圾:在执行清理步骤时,用户线程也会同时产生一部分可回收对象,但是这部分可回收对象只能在下次执行清理是才会被回收。如果在清理过程中预留给用户线程的内存不足就会出现 ConcurrentModeFailure,然后采用 Serial Old 进行回收;
    ~ 3)基于标记-清除算法的CMS会产生大量的内存碎片,当不足以提供整块连续的空间给新对象时又会触发FullGC;

  • ~ CMS的使用场景
    ~ 当垃圾回收需要短停顿时间,且在老年代GC频率不高的场景;

  • ~ G1收集器:G1收集器的内存结构完全区别于CMS,将堆内存划分成多个Region,来保证收集不必在全堆范围内进行,进而达到可控的停顿时间,可以指定收集操作在多长时间内完成;
    ~ 1)初始标记 stop-the-world:标记GC Roots关联的对象,开始Young GC,并修改NTAMS(Next Top at Mark Start)的值,让下一阶段用户线程并发运行时,能在Region中创建新对象;
    ~ 2)根区间扫描:标记所有幸存者区间的对象引用,扫描 Survivor到老年代的引用(必须在下一次Young GC 发生前结束扫描);
    ~ 3)并发标记:是从GC Roots开始堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行,该阶段可以被Young GC中断;
    ~ 4)最终标记 stop-the-world:是为了修正并发标记期间因用户线程继续工作而导致标记产生变动的那一部分,JVM将这段时间对象变化记录在线程Logs里面,最终标记阶段需要把Logs的数据合并到标记中;
    ~ 5)筛选回收:首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,回收没有存活对象的Region并加入可用Region队列;

  • ~ G1的优点
    ~ 1)并行与并发:G1充分发挥多核性能,使用多CPU来缩短Stop-The-world的时间,
    ~ 2)分代收集:G1能够自己管理不同分代内已创建对象和新对象的收集。
    ~ 3)空间整合:G1从整体基于 标记-整理 算法实现(多Region之间可以理解为 标记-复制 ),不会产生内存空间碎片。
    ~ 4)可预测的收集时间:它可以自定义停顿时间模型,可以指定一段时间内消耗在垃圾回收商的时间不大于预期设定值;

  • ~ G1的使用场景
    ~ 适用于大内存机器;

错标、漏标怎么办

  • ~ 基础概念,垃圾回收器标记时候使用的三色标记法:
    ~ 黑色:根对象,以及该对象与它的子对象都被扫描过;
    ~ 灰色:对象本身正在被扫描,其子对象还未被扫描;
    ~ 白色:未被扫描的对象,如果扫描完所有对象后仍为白色,则被判定根不可达;

  • ~ 错标:已经标记成黑色的,但是在用户线程执行过程中这个对象没用了,应该是白色 ===> Do nothing. 下次回收再说;

  • ~ 漏标:本来应该标记为黑色,但是某黑色对象 A 在自己被标记之后指向了白色对象 B(视为不可达),并且灰色对象 C 到白色对象 B 的直接或间接引用被删除了;
    ~ ===> CMS:当对象A为黑色,已有关联对象黑色B,对黑色A新增无关联白色对象C,此时会记录对象A,当重新标记开始时,设置A为灰色,这样就可以重新扫描,但是会导致B也被重复扫描一次;
    ~ ===> G1:【SATB算法】会对比重新标记和初始标记之间的快照,增加了内存成本但是不会过度扫描;

生产环境怎么处理JVM的异常

  • ~ 查看GCLog

如何监控JVM,JVM个一个小时飙高一次,如何排查

SQL优化,SQL已经用了索引但是扫描行数多,如何优化

  • ~ 1)首先查看虽然SQL用了索引但是实际情况有没有命中索引,explain查看执行计划有没有 using index,如果没有的话,说明MySQL的优化器默认索引的覆盖行数不够没有选择,考虑是不是修改一下查询条件,满足前缀原则;
  • ~ 2)如果已经命中了索引但是还是扫描行数多,看一下索引的筛选区分度是否足够,尽量避免给区分度不高的数据(比如说一个status只有两三种值的列)加列索引;
  • ~ 3)去掉多余不必要的单列索引,提升联合索引的优先级,因为在执行优化器眼里,如果前缀原则没有完全满足的情况下,单列索引的覆盖行数如果大于联合索引是不会使用联合索引的;
  • ~ 4)必要时,使用 force index 强制走索引,但是强制索引本身也会导致有些情况查询效率低下,需要谨慎使用;
  • ~ 5)SQL终究是一种走磁盘扫描的方案,如果最终也解决不了问题,在可以允许的查询范围内(比如说5s),可以考虑添加缓存机制,同时使用脚本异步刷新SQL结果;

Redis数据,使用hash做缓存可以吗?

  • ~ 可以,但是我们常用的数据一般是String,简单描述一下五种数据类型对应的应用场景吧:
    ~ 1)String:I.缓存对象,可以选择set整个Json数据,如果经常修改数据的话,可以选择使用mset多个属性;II.分布式锁,记得设置过期时间防止死锁;III.计数器,使用INCR实现文章阅读量、热度人数统计等数据的自增处理;IV.全局序列号,当需要大量新建数据时,数据库的Id生成未必来得及,可以考虑使用Redis来进行额外的Id生成(如果量大到Redis都扛不住,可以先一批一批的生成,先从已有批次的数据里获取,一批用完了不够再去申请下一批;
    ~ 2)Hash:I.购物车场景,对于热门商品,可以设定用户的唯一标识(userId)为RedisKey,指向一个hash数据结构<K,V> 其中,K为商品信息,V为商品数量,可以便捷的进行某一个用户的添加商品、增加数量、商品总数等操作;
    ~ 3)List

hash和String都可以用来存这样的结构数据,为什么不直接用String呢?

  • ~ 1)相对于Hash来说,每个用户的数据单独存储在一个Key里方便操作,也方便同步;
    ~ 2)多String的情况要比hash多存储一个Key的数据,一个数据量可能没多少,但是如果拓展到百万的用户量也是一种资源浪费;
    ~ 3)相对于2)来说,Hash也会因为本身是集合属性的问题,某个用户的数据过多会导致集群访问不均,且因为Redis的均衡原理是以Key为单位的,所以是不能调节的,容易出现大Key的问题;

Zset底层实现