页面启动中 . . .

QA-Review


对个人的Resume进行复盘,总结可能会出现的问题以及对应的回答(简单粗略版本)~

1. 数据库方面

  • SQL常见的索引类型有哪些?

常见的索引类型有B-tree索引、哈希索引和全文索引

  1. B-tree索引:最常用的索引类型,适用于范围的查找和排序操作
    • B 树的所有节点既存放键(key) 也存放数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key;
    • B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点;
  2. 哈希索引:使用哈希函数将键映射到存储位置,适用于等值查询;
  3. 全文索引:适用于全文的搜索,比如模糊搜索、关键词匹配和相关性排序等;
  • 索引失效的场景有哪些?
  1. 对索引列进行函操作:如对索引列进行函数运算、计算操作、类型转换、使用的顺序未遵循最左匹配原则等;(此时会全表扫描,由于最初保存的是原始值)
  2. 使用不等于(!=)或不包含(NOT IN、<>)的条件。
  3. 使用模糊查询:%开头的LIKE查询。
  4. 使用OR关键字,在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效;
  5. 使用in不当,当IN的取值范围较大时会导致索引失效,走全表扫描;
  6. order By使用不当
  7. 在联合索引中未使用索引的第一个列。
  8. select* :使用SELECT*,获取了不需要的数据,则首先通过辅助索引过滤数据,然后再通过聚集索引获取所有的列,这就多了一次b+树查询,速度必然会慢很多,减少使用select * 就是降低回表带来的损耗。如果此时使用select * 放一些无用的列,只会白白的占用缓冲空间。浪费本可以提高性能的机会。
  • mysql一般怎么去优化语句
  1. 使用合适的索引:索引是提高查询效率的重要手段。通过为经常用于过滤、连接和排序的列创建索引,可以大大加快查询速度。但是索引也会增加写操作的开销,因此需要权衡考虑哪些列需要创建索引。
  2. 优化查询语句:编写高效的查询语句也是提升性能的关键。避免在查询中使用不必要的列、多余的表连接或子查询,尽量简化查询语句的结构。
  3. 避免全表扫描:当查询涉及的数据量较大时,可能导致全表扫描,影响性能。可以通过合理设计索引、使用合适的查询条件以及分页等方式来避免全表扫描。
  4. 调整数据库参数:MySQL有很多配置参数可以调整,如缓冲区大小、并发连接数等。
  5. 定期维护和优化表结构:可以通过删除冗余数据、重建表等方式提高性能。
  6. 使用缓存:适当使用缓存可以减少对数据库的访问,从而提升性能。如非关系数据库redis等
  • mysql中的事务操作?
  1. 隐式事务:自动开启事务的操作,比如提交、回滚等事务
  2. 显示事务:需要开发者自己手动开启
  3. savepoint关键字:回滚部分的数据,指定其中的部分
  4. 只读事务
  • 事务操作可以保证一组SQL语句要么全部成功提交,要么全部失败回滚,确保数据的一致性和完整性。在开发中,可以根据实际需求选择合适的事务操作方式。
  • mysql中的隔离级别
  • 事务隔离级别主要指的是多个事务之间数据可见性以及数据正确性的问题。分为下面的4级
  1. 读未提交
    • 读未提交是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。
  2. 读已提交
    • 一个事务可能会出现不可重复读的问题。简单说就是一个事务中,多次读到同一数据,但是此时恰好被另外的事务修改数据,导致读取的数据不一致。
  3. 可重复读
    • 一个事务可能会遇到幻读(Phantom Read)的问题。
  4. 可串行化
    • Serializable 是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
    • 缺点是效率收到影响,由于是串行执行,性能降低。
  • 如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用 InnoDB,默认的隔离级别是Repeatable Read(可重复读)
  • MySQL默认什么级别、怎么实现的?
  1. 默认的是REPEATABLE-READ可重读,我们可以通过SELECT @@transaction_isolation命令查看。缺点是不可以防止幻读的问题

  2. InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:

    • 快照读:由 MVCC 机制来保证不出现幻读。
    • 当前读:使用 Next-Key Lock 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。

    因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ 并不会有任何性能损失。

  • Redis的用处?能用来做什么?
  1. 缓存(最主要):redis将数据缓存到内存中去,加快访问的速度。同时由于丰富的数据结构,在缓存领域得到广泛的应用;
  2. 分布式锁:Redis提供了原子性的操作,可以用来实现分布式锁。使用redis自带的SETNX命令来获取锁,在缓存领域得到广泛的使用;
  3. 计数器和排行榜:Sorted-set有序集合实现排行榜,INCR和DECR命令实现原子性的计数器操作,统计。
  4. 发布订阅、消息队列:使用Redis的List或Pub/Sub方式实现的消息队列是简单的消息传递机制,并不具备一些高级特性,如消息持久化、消息确认机制等
  5. 分布式缓存:集群模式等,将数据分散在多个节点中去,提高性能和可用性。
  • redis可能出现的生产问题有哪些?
  1. 缓存穿透:大量的请求key不合理,或者说是根本就不存在数据库或redis中,请求直接到达数据库。
    • 布隆过滤器:使用布隆过滤器对请求进行过滤,如果请求的数据在布隆过滤器中不存在,则直接返回空结果,避免请求穿透到数据库。
    • 缓存空对象:当数据库中不存在某个数据时,可以在缓存中设置一个空对象作为占位符,这样下次请求同样的数据时,就可以命中缓存,并且不再访问数据库。
  2. 缓存击穿(热key问题):请求的热点key(也就是系统频繁访问的数据),存在于数据库中,却不存在于缓存中(热点数据的缓存数据失效)
    • 过期时间:设置热点数据较长的过期时间。
    • 分片:将热key分散到多个Redis节点上,使每个节点的负载更均衡。
    • 设置互斥锁:请求数据库写数据到缓存之前,先设置互斥锁,保证只有一个请求会落到数据库中,减少数据库的压力。
  3. 缓存雪崩:redis缓存大面积失效,导致请求的数据直接落到数据库上,对数据库造成巨大压力。
    • 设置不同的失效时间比如随机设置缓存的失效时间。
    • 缓存永不失效(不太推荐,实用性太差)。
    • 缓存预热,也就是在程序启动后或运行过程中,主动将热点数据加载到缓存中。定时、消息队列、异步缓存预热等等
  • 怎么保证数据库的数据和redis数据一致性呢,说说考虑的原因
  1. 上个问题的缓存redis中的生产问题解决方案
  2. 读写数据的一致
    • 旁路缓存模式:读取数据先从缓存中查询,不存在则从数据库中读取,然后将读取到的数据存入缓存中;写操作,先更新数据库,再更新缓存;
    • 同步读写穿透:读取数据时先从缓存中查询,如果不存在,则从数据库中读取,同时将数据加载到缓存中。写操作,先查cache。不存在直接更新db,存在先更新cache,后写入数据库;
    • 异步缓存写入:先将更新操作写入到缓存中,然后异步地批量将缓存中的数据写入到数据库中。这种方式虽然可能会导致短暂的数据不一致,但可以提高写入性能和吞吐量。
  3. 主从同步
    • Redis支持主从同步的情况,将主节点的数据自动同步,保证多个节点数据的一致性。
  4. Redis和数据库的数据定期同步
    • 可以设置定时任务,定期将Redis中的数据同步到数据库中,从而保证Redis和数据库的数据一致性。
    • 可以通过Redis提供的持久化方式(如aof、rdb)将数据同步到磁盘中,再通过读取磁盘中的数据来实现Redis和数据库之间的数据同步。

2. JUC

  • volatile关键字相关?
  1. volatile关键字保证变量的可见性,但是不能保证原子性。而反之的synchronized关键字两者都能保证。
  2. 在Java中,volatile关键字可以保证变量的可见性,还有一个重要的作用是防止JVM的指令重排序;
  • 手写单例模式?解释双重检验锁方式实现单例模式的原理
  • 基于双重检验锁方式实现的单例模式代码:

    public class Singleton {
        private volatile static Singleton instance; // 使用volatile关键字保证线程安全
    
        private Singleton() {} // 将构造方法私有化,确保外部无法创建实例
    
        public static Singleton getInstance() {
            if (instance == null) { // 第一次检查,如果实例不存在则进入同步块
                synchronized (Singleton.class) {
                    if (instance == null) { // 第二次检查,防止多个线程同时进入同步块后重复创建实例
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
  • 上述代码中,双重检验锁模式通过在getInstance()方法中添加两个if语句,来确保只有第一个获取锁的线程才能创建实例。其中,第一个if语句用于在实例未创建时进行第一次检查,如果发现实例不存在,则进入同步块。在同步块内部,再次检查实例是否为空,如果为空,则创建实例并将其赋值给instance变量。

  • 要注意的是,在实现双重检验锁模式时,需要使用volatile关键字来保证线程安全。volatile关键字可以保证instance变量的可见性和禁止指令重排序,从而确保多个线程可以正确地处理instance变量。

  1. instance 分配内存空间
  2. 初始化 instance
  3. instance 指向分配的内存地址

后续再补充~


3. 项目介绍


文章作者: XKJ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 XKJ !
  目录