学习解答

197 阅读40分钟

学习解答

1.一致性hash

对ip进行取模,分布到虚拟节点,可以减少因为增加减少节点导致的大量数据重hash,

对地址进行多次计算, 减少数据倾斜问题,分布到不同的节点。

2.mysql的架构逻辑

三层架构

服务器层、核心服务功能层,存储引擎层

服务器层:提供连接,认证,安全的处理 核心服务: 查询的解析,分析,优化,缓存以及时间日期等内部函数的实现,以及跨存储引擎层的视图,触发器的实现 存储引擎:数据的存储与提取, 不同引擎实现不同,innodb,myasim

3.MySQL 的锁策略有什么?

表锁是MySQL中最基本的锁策略,并且是开销最小的策略。表锁会锁定整张表,一个用户在对表进行写操作前需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。

只有没有写锁时,其他读取的用户才能获取读锁,读锁之间不相互阻塞。

行锁可以最大程度地支持并发,同时也带来了最大开销。InnoDB 和 XtraDB 以及一些其他存储引擎实现了行锁。行锁只在存储引擎层实现,而服务器层没有实现。

  1. sql事务是什么

执行一组sql,要么成功,要么失败

5.mysql如何处理死锁问题

Innodb将持有最少行排他琐进行回滚

6.ACID

原子性 事务要么成功要么失败 一致性 数据库有总是从一个状态到另一个状态 隔离性 事务之间隔离,不会读取到其他事务未提交的记录 持久性 提交成功永久保存

7.事务的隔离级别

read uncommited

事务可以读到其他事务未提交的记录

read commited

事务不可以读到其他事务未提交的记录

repeatable read

mysql默认隔离级别, 不会重现重复读的问题,多次读取的不一样

serialbale

事务串行化执行,效率低,

8.查询执行流程是什么

1.客户端发送查询请求到服务端 2.服务端从缓存中查询是否命中缓存,命中则返回 3.没有命中,sql 解析 优化执行记录 4.调用存储引擎的api,查询 5.返回结果给客户端

9.varchar 和 char的区别

varchar 适合存长度不固定的字符串 char适合存储字符串固定的

10.datetime 和timestamp的区别

datetime可以存储1000--9999的时间 timestamp存储1970-2038,并且依赖时区

JAVA

  1. hashmap 死循环

hashmap在JDK1.7中会出现多线程扩容时出现死循环。原因是扩容使用得是头插法。

如:

A T1 T2 B T1.next T2.next C

T1扩容完成:

C B T2.next A T2

B--->A

T2准备扩容时出现: A---> B B --> A 死循环

2.SDS

simple dynamic string

free + len + char[]

  1. 获取len的时间复杂度是O(1)

  2. 防止缓冲区溢出

  3. 减少内存重分配

  4. 可以保存二进制和文本是(原因是不以空字符\0为判断结束)

  5. 不够会扩大一倍,当超过1M每次会扩1M

  6. 零拷贝

零拷贝减少了:内核空间到用户空间的拷贝

普通io: 磁盘--> 内核空间 --> 用户空间 --> 内核空间 -> socket

零拷贝: 磁盘--> 内核空间 -> socket

利用nmap复制: 复制到内核空间, 利用nmap内核空间和用户空间共享内存,减少了两次复制。

  1. ArrayList扩容

无参构造默认为0 也可以指定初始化数组的大小 add初始化为10,再次扩容为1.5倍 addAll 第一次数组的大小为math(10,添加的元素个数),再次扩容为1.5倍,不够扩容到想要的大小

  1. 线程池的核心参数

    corePoolsize : 核心线程数 maxPoolsize : 最大的线程数 BlockingQueue: ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue,意义是保留住当前想继续入队的任务 keppActiveTime : 空闲线程存活的时间 unit : 线程存活时间单位 ThreadFactory rejectHandler : 四种拒绝策略: 丢弃任务抛异常、丢弃任务不抛异常、丢弃队列最前面的任务、由调用线程处理该任务

    corePoolsize --> workQueue --> maxPoolsize --> rejectHandler

    当核心线程未满,线程池提交了一个任务,为什么不复用已有的线程而是创建新的线程呢? 1. 预热,当核心线程未满,认为线程池初始化还未完成,继续创建线程直到满足核心线程数 2. 复用之前的线程会存在额外的开销(锁开销)

    5.1 业务中用到线程池吗 有, 1.系统翻版后,去安全那里查这个系统的依赖信息,系统会有多个模块,这里用的线程池。这里拒绝策略用的是,丢弃,不跑异常,我们有抓住这个异常,后面提交任务统一处理 2. 流水线传输过来依赖树,要异步调用安全传输解析后的依赖树 总结:一个子系统有多个模块,对多个模块做业务处理

6.Spring注入两个同样类型的bean

在bean要注释名称

@comPonent("fooFormat") public class FooFormat implements Format{

}

@comPonent("booFormat") public class BooFormat implements Format{

}

public class fooService{ @Autowired @Qualifirt("fooFormat") private Format format; }

  1. 分布式中的脑裂

脑裂: 分布式系统由于网络或其他原因,出现了两个leader/master

zk处理方法: 过半选举原则,以及epoch标号,表示当前属于那个leader统治,每位leader的标号递增。

blog.csdn.net/wo541075754…

zk部署节点,部署奇数个节点

8. AQS了解?里面有那些属性?AQS有哪几种模式?你对AQS还有那些理解?

AQS: abstract queue sychronized 抽象的队列式的同步器

ReentrantLock、countDownLatch、Semaphore都是AQS的实现。


https://juejin.cn/post/6844904146127044622

// CAS: compare and set
volatile int state;  共享资源,通过CAS的操作来保证并发修改的正确性
// 独占锁的线程
Thread exclusiveOwnerThread

// first input first output
FIFO 等待队列

tryAcquire:独占的获取锁
tryRelease:独占的释放锁
tryAcquireShared: 共享获取锁
tryReleasedShared: 共享释放锁



可重入锁的实现:当获取state不等于0,以及持有锁的线程等于当前线程则state加1,释放的时候state减1,直到为0ReentrantLock: 可重入的非/公平锁(根据传入的参数),默认非公平锁

公平锁: 当AQS队列中有数据,新的抢占锁的线程直接加入到队列,按照队列的先后顺序获取锁
非公平锁: 不管AQS队列是否有数据,新的抢占所的线程,先尝试获取锁,没有获取到后再加入队列


AQS有两种模式: 一种独占模式 一种共享模式;ReentrantLock就是独占模式,因为一次只有一个线程可以竞争到锁,而像Semaphore就是共享模式,一次可以多个线程获取到资源


读写锁的互斥规则ReentrantReadWriteLock: 读读共享、读写互斥、写写互斥

9. jvm内存模型 那个区域不会发生oom

JMM: 线程本地内存,主内存 volatile的主要作用就是变量的操作每次都会刷新到主内存,使各个线程都可以看到最新的值

程序计数器不会发生oom: 程序计数器: 存储下一个需要执行命令的地址


jvm内存划分:
    方法区 (包含常量池)
    堆
    程序计数器
    本地方法栈
    虚拟机栈

gc算法:
     引用计数器
     可达性分析:可达性根据gc roots
     gc roots: 
        常量池
        本地方法栈
        虚拟机栈

JVM可调整的参数
    xms: 最小堆的大小,初始化堆的大小
    xmx: 最大堆的大小


标记-清除算法: 会产生内存碎片
复制算法: 会有一半的年轻代不能使用
标记整理:  标记不可达对象,清理后,整理内存

10. 深拷贝、浅拷贝

包装类型是浅拷贝

clone方法实现是浅拷贝,当对象有基本属性和对象属性,使用clone复制的时候,对象属性指向堆上的同一个地址。因此也是浅拷贝。

String复制也是浅拷贝,但由于他是不可变,修改后的行为表现为深拷贝。

对象要完全实现深拷贝,使用序列反序列化的方法来进行深拷贝。

对象的几种创建方法: 1.使用new关键词创建 2.反射 class.newInstance 3.clone 4.反序列化

  1. redis key的删除策略

    1.被动删除 2.主动删除 3.当使用内存达到maxMemory时根据配置淘汰规则 3.1 TTL 3.2 LRU 3.2.1 只对设置了过期时间的key进行lru算法删除 3.2.2 对所有key进行lru算法删除 3.3 LFU 3.3.1 只对设置了过期时间的key进行LFU算法删除 3.3.2 对所有key进行lFU算法删除 3.4 random 3.4.1 随机删除设置了过期时间的key 3.4.2 随机删除key 3.5 noeviction 永不过期,对于写请求返回异常

    被动删除: 当读写key的时候检查是否过期,过期删除,无法处理冷数据 主动删除: redis有1s10次的定时任务,检查key是否过期,过期则删除 1.有ttl时间的key 2.删除已经过期的 3.上述中100个key中有25个被删除了,则重复步骤1

    LRU: least recently use LRU淘汰最长时间没有使用的 LFU: least frequently use LFU淘汰使用次数最少的

    默认的是noeviction

  2. threadlocal内存泄漏

    threadlocal不正确使用会产生内存泄漏,原因: threadLocalMap中entry是弱引用,entry存储key,value,当引用 被设置为null,会导致value暂时无法被回收。

    ThreadLocal threadlocal = new ThreadLocal(); threadlocal.set(new Object()); // 内存泄漏 (会导致value无法被回收) threadlocal = null; //正确的处理 threadlocal.remove();

    threadLocal ---强引用--> ThreadlocalMap对象 ----弱引用--> entry ----强引用---> value

    threadlocal = null;此时value还强引用new Object()导致无法释放,产生了内存泄漏。key引用的entry由于是弱引用,是可以被清除的

    四种引用: 1. 强引用 2. 软引用 3. 弱引用 4. 虚引用

  3. 跳跃表

blog.csdn.net/helloworld_…

跳表: 复杂的有序链表,每个节点可能又多层指针,第一层的指针指向下一个节点,越高层的指针跳跃的指向下下节点。插入、删除只需要修改相邻节点的指针。

与平衡数和哈希表的比较: 跳表和平衡树都支持范围查询,平衡树插入、删除节点可能会重建子树。范围查询跳表查询到最小值后,只需要在第一层继续向前执行就可以找到最大值, 平衡树找到最小值后,需要按照中序遍历找到最大值,如果不对平衡树改造,实现起来比比较麻烦 占用内存跳表比平衡树灵活,平衡树每个节点有2个指针,跳表的每个节点的平均指针是1/(1-p),取决于p的大小,redis p = 1/4 ,平均每个节点1.33个指针

哈希表不支持范围查询

跳表结构示例:

1---------------------->5
1--------->3------------5
1---->2---->3----->4---->5

14. 阻塞线程方法

yeild: 当前线程放弃时间片,可能会再次获得 join: 在当前线程执行另一线程的join方法,线程进行阻塞,直到另一线程结束执行,这个线程才能就绪 sleep : 当前线程阻塞,不得到时间片 wait\nofity: 实例在当前线程A执行wait,A线程阻塞,实例在线程B执行nofityAll, 线程A不再阻塞

LockSupport: park和unpark不会出现死锁问题,park和unpark会对使用的线程保持一个标记permit, park阻塞,unpart释放

www.jianshu.com/p/f1f2cd289…

  1. 线程通信 blog.csdn.net/weixin_4621…

定义: 多个线程在操作共享资源时,互相告知自己的状态以避免资源争夺

通信方式: 1. 共享内存 volatile 2. 消息传递 wait\nofity,join 3. 管道流

以上方式都具有侵入性,如果是第三方框架,那么没法控制用户代码,需要使用修改字节码(ASM,javassist),修改线程池类的字节码,添加相应的线程间通信代码。

16. 如何统一处理异常

@ExceptionHandler

17.泛型的extend和super

blog.csdn.net/jeffleo/art…

extend 是上界 super 是下界

public class Apple extend Fruit{} public class Balana extend Fruit{}; public class B extend Apple{};

List<? extend Fruit> 上界,频繁读取数据,适合上界,因为读取的元素都可以用上界定义的类型来接收,父类可以指向子类 List<? super Fruit> 下界,既然元素是fruit的基类,那往里存的粒度比fruit小的都可以。

上界不能添加元素,可以读, 下界不能读取,可以添加

List<? extends Fruit> list = new ArrayList<>(); list = apples; list = balanas; list.get(0); list.add(new Apple()); // 编译失败

List<? super Apple> list1 = new ArrayList<>(); list1 = apples; list1 = fruits; list1 = balanas;// 编译失败 list1.add(new Apple());

//上界: 只能查询,不能添加 public void test(List<? extends Fruit> fruits) { for (Fruit fruit : fruits) { fruit.call(); } fruits.add(new Apple()); // 编译报错 }

//下界:只能添加不能查询 public void test(List<? super Fruit> fruits) { // 元素只能是obejct for (Object o : fruits) {

}
fruits.add(new Apple());
fruits.add(new B());
fruits.add(new Fruit());// 编译报错

}

extend

使用场景Collections:

public static void copy(List<? super T> dest, List<? extends T> src)

dest: 只能添加 src: 只能读取

使用泛型上下界的原因是: 类的继承关系可以做到对子类型的兼容,但是泛型由于类型擦出的原因,没有做到对子类的兼容。所以要引入上下界。

18.分布式锁

三种分布式锁的实现方式: 1.db 2.redis 3.zk

db分为悲观锁和乐观锁

悲观锁
 select * from table  for update 
 如果没有查到则insert 获取到锁

 乐观锁
    version
    每次获取锁,判断version是否一样


redis
    setnx expire
    set ex px nx
    会存在过期时间太短,业务逻辑还未执行完,锁被释放了

    redisson 有一个守护进程,定时判断,锁是否存在,存在则延长锁的到期时间

    redisson + redlock
     5台不同步的master,获得锁分别从这个5台获取,当大于等于3台获取到,则认为获取到锁

zk 持久节点 + 临时顺序节点

当客户端尝试获取锁,没有持久节点创建,同时创建临时顺序节点,当临时顺序节点是最小的节点,则获取到锁,
否则监听,当自己持有的临时顺序节点是最小的则获取到

19. zk paxos算法

每位议员会同意比自己当前记录大的提议号,过半同意则广播

zk只有一个leader,所有的提议有leader发起。

zid: epoch标记当前leader和计数递增的议案号

20. Synchronized

monitor entry
monitor exist

偏向锁(只有一个线程获取资源,无锁竞争) ---------> cas自旋锁(轻量级锁) -----一定时间还未获取锁--------------> 重量级锁 (挂起进程)

markWord

sychronized实现:重量级锁通过monittor entry和monitor exist来加锁代码,偏向锁和轻量级锁通过mark word 对onwer字段cas

  1. B+树

    www.cnblogs.com/ttaylor/p/1…

    天然具有排序性

    B+,b树(b-) 多路平衡树

    B+ 只有叶子节点保存数据,非叶子节点只保存索引值, 所以相比b-树层级更少、查询更快(磁盘存储结构,寻道时间)、 叶子节点的数据从小到大,从左到右,左边节点保存右边节点的指针,构成有序链表适合范围查询 b- 叶子、非叶子节点都保存数据

  2. Future 实现阻塞等待获取结果的原理

    原理: state + 自旋等待

    FutureTask继承Runnable和Future, runnable是生产者、Future是消费者

    get()方法获取state如果大于complete状态则返回结果,否则自旋阻塞等待

    java中Future的使用: www.cnblogs.com/flydean/p/1… Future 实现阻塞等待获取结果的原理: www.cnblogs.com/piaobodeyun…

    public class FutureTest { private ExecutorService executorService = Executors.newSingleThreadExecutor(); public Future call(Integer input) { return executorService.submit(() -> { System.out.println("call....." + input); Thread.sleep(3000); return input * 10; }); }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTest futureTest = new FutureTest();
        Future futureTask1 =  futureTest.call(100);
    
        while (!futureTask1.isDone()) {
            System.out.println("wait task ing....");
            Thread.sleep(1000);
        }
        Integer res = (Integer) futureTask1.get();
        System.out.println("res:" + res);
    }
    

    }

    输出结果: wait task ing.... call.....100 wait task ing.... wait task ing.... res:1000

  3. Spring中的aop切面,如果定义了多个增强执行顺序是什么样的

实现ordered或使用注解@order来定义执行顺序,越小的先执行。

如下: aspect1, aspect2(从小到大)

aspect1 start ----> aspect2 start ----> aspect2 end ---> aspect1 end

  1. DDD是否了解? 其中有哪些术语?

    DDD: 领域驱动设计, 充血模型, 业务方法定义在实体对象中, 业务更加聚合,不再零零散散的分布在表对应的对象中

    DTO: data transfer object VO: view object

    DDD的四层经典模型:

    1. 展示层: controller层,无业务具体逻辑
    2. 应用服务层,提供简单的查询
    3. 领域服务层:核心业务处理
    4. 基础设施层: 持久化存储
  2. 说说单一职责

    控制类的粒度大小

    一个类只负责一个职责,不能因为其他职责的修改,导致另外的职责

    www.cnblogs.com/study-every…

  3. ThreadLocal连环问题

    1.ThreadLocal是什么,项目中有用到过吗 本地线程变量、用来存储不同线程的变量,key为当前线程,value为值 用到过,保存config改变之前的值,做判断检查

    2.ThreadLocal的结构是怎么样的 ThreadLocalMap 的有entry存储key,value, key就是threadlocal的引用

    3.使用ThreadLocal需要注意什么问题 防止内存泄漏,不要 ThreadLocal t = null来设置,要使用 t.remove()来移除变量,value是强引用

    4.为什么key被设计为弱引用 弱引用,在下次GCd的时候回收,防止变量长时间未使用导致内存泄漏

    5.那为什么value不设置为弱引用,ThreadLocal不是持有这个value的强引用,它还会被回收? value设置为弱引用,会导致取值的时候可能为Null.

    假如value被设计成弱引用,那么很有可能当你需要取这个value值的时候,取出来的值是一个null。
    你使用ThreadLocal的目的就是要把这个value存储到当前线程中,并且希望在你需要的时候直接从当前线程中拿出来,那么意味着你的value除了当前线程对它持有强引用外,
    理论上来说,不应该再有其他强引用,否则你也不会把value存储进当前线程。
    但是一旦你把本应该强引用的value设计成了弱引用,那么只要jvm执行一次gc操作,你的value就直接被回收掉了。
    当你需要从当前线程中取值的时候,最终得到的就是null

    key的管理有threadlocal设置为弱引用自己管理,value强引用交由开发者remove

    6.为什么出现内存泄漏,你是怎么发现内存泄漏的 线程被重复执行,value没有正确清理,导致vlaue膨胀,产生了内存泄漏

    7.怎么避免出现脏数据 线程池复用线程,会出现脏数据,读取到之前设置的值,避免方式: 每次使用完毕remove掉,以及使用前set新值

    blog.csdn.net/foxExceptio… www.zhihu.com/question/39…

    8.ThreadLocal怎么解决hash冲突

    开放地址法或者也叫线性探测法解决哈希冲突,该方法一次探测下一个地址,直到有空的地址后插入,若整个空间都找不到空余的地址,则产生溢出
    
    假设当前table长度为16,也就是说如果计算出来keyhash值为14,如果table[14]上已经有值,并且其key与当前key不一致,那么就发生了hash冲突,这个时候将141得到15,取table[15]进行判断,这个时候如果还是冲突会回到0,取table[0],以此类推,直到可以插入
    
    https://blog.csdn.net/xiaoxiaodaxiake/article/details/107732928
    
  4. 几种IO模型,NIO和多路复用的区别

    BIO: blocking io 阻塞io NIO 非阻塞io 多路复用io SIO 信号驱动io AIO 异步io

    IO过程: 等数据 + 数据搬迁

    如果要提高io效率,则要将等的时间降低

    IO: 一直阻塞等待 NIO(non-blocking io): 轮询不同socket的read,直到有数据读到

    java new io(nio ,多路复用io): select模型: 循环遍历, 底层数组只有1024个,O(n)浪费了cpu时间 poll模型: 遍历循环所有连接,底层链表,无限制

    epoll: 回调,O(1)
    

    NIO的时间的复杂度是O(n), 多路复用的复杂度是O(1)

    epoll会返回就绪的文件描述符数量,select和poll不会,需要遍历

    blog.csdn.net/ZWE7616175/… blog.csdn.net/u014453898/… blog.csdn.net/haogenmin/a…

28.什么情况下需要重写hashcode

重写equals一定要重写hashcode,否则会导致hmap,set等使用异常。

默认equals是判断的对象地址,实际上业务逻辑上判断对象相等,用的是其中的联合属性来判断。

定义: 当两个对象相等时,其hashcode一定是一致的。 反之,hashcode相等,对象不一定相等。
       这里对象不相等但hashcode相等的情况时,存储map可能会出现哈希碰撞

       当hash碰撞hashmap使用的是拉链法用链表+红黑树来存储节点

29. 聚簇索引和非聚簇索引

聚簇索引:  数据和索引存储到一起, innodb存储主键,一张表只有一个聚簇聚簇索引

非聚簇索引: 将数据和索引分开存储

聚簇索引不需要回表,非聚簇索引需要回表

30 mysql约束 约束主要完成对数据的校验

not null
unique
primary key 

31. 如何处理热key问题

1.热key发现:
    1.1 提前预估业务是否会有热key
    1.2key主动发现,统计key在某段时间的请求频率


2.处理热key
    2.1 应用实例本地缓存hashmap,ehcache
    2.2 备份缓存,加入随机数,使key存在于多台机器

处理目标: 使流量均匀的分发到多台机,不要把一台机器打死了

https://www.cnblogs.com/rjzheng/p/10874537.html

32. spring如何同时注入多态bean

使用数组、集合List、set、map

public class  TestService@autowired
    private BeanService[] beanServiceArray;
}

public class  TestService@autowired
    private List<BeanService> beanServiceList;

}

public class  TestService@autowired
    private Set<BeanService> beanServiceSet;

}

public class  TestService// key 必须为String 存储bean的名称
    @autowired
    private Map<String,BeanService> beanServiceMap;

}


https://www.jianshu.com/p/ea9f5ef067c5


@Primary 指定同类型主要的bean
@Qualifier  指定具体bean的名称,通过名称注入解决冲突
@Order 指定注册同类型的bean的权重(或顺序),值越小,权重越大

33. RabbitMQ如何保证消息不丢失?

1.生产者
2.MQ
3.消费者

生产者:
    异步,开启confirm机制,MQ收到消息会回调通知
    同步,开启RabbitMQ事务,不推荐性能低

MQ:
    开启持久化,mq消息默认保存在内存
    设置集群镜像模式
    消息补偿机制

消费者
    手动ack确认


MQ集群:
    1. 普通集群 多节点保存元数据和队列结构,一条消息只在A,B,C节点保存一份,节点间相互通信,当A节点没有会和B节点通信获取消息
    2. 镜像集群  每个实例都可以相互通信,每个节点都保存了元数据及节点结构以及节点消息


https://www.cnblogs.com/cnndevelop/p/12091348.html

https://www.cnblogs.com/linwenbin/p/13382753.html

34. ApplicationEvent

https://juejin.cn/post/7078481193133408270

事件驱动


事件 + 监听器 观察者模式

```java
    @Component
    public class EventListenerServie{
        
        // 监听到应用启动
        @EventListener(ApplicationReadyEvent.class){

        }
    }
```

35. rabbitmq实现订单30分钟超时关闭

订单消息投送到aa队列设置TTL30分钟,不设置消费者。 30分钟后投送到死信队列,消费死信队列,检查订单是否已经完成支付,未完成关闭订单。

https://www.cnblogs.com/equals/p/15788538.html


死信队列:
    1. 被拒绝的消息
    2. TTL过期未消费
    3. 达到最大队列


https://blog.csdn.net/qq_43654581/article/details/121552694

36. rabbitmq的工作模式

1. 简单模式  生产者直接投递到队列,一个消费者
2. 工作模式 生产者直接投递到队列,轮询多个消费者只有一个可以消费
3. 发布订阅模式 投递消息到交换机exchange可能到多个队列,所有消费者都可以消费
4. 路由模式  投递到exchange,根据routing key投递到匹配的队列,不同类型的消息可以由不同的消费者去消费
5. topic模式 相比路由模式,绑定routing key的时候可以使用通配符

工作模式: 只能投递到一个队列只有一个消费者;投递到一个队列有多个消费者轮询; 可以投递到多个队列,所有消费者都可以消费;根据routing key投递匹配到多个队列,不同列行的消息由不同的消费者去消费
            绑定routing key的时候可以使用通配符

37. 消息大量堆积应该怎么处理

原因:
    1.网络故障
    2.消费方未进行ack确认


 处理:
 1. 检查消费方故障,使其正常消费
 2. 编写临时程序将堆积的消息发送到容量更大的集群,增加消费者快速消费
 3. 堆积消息处理完毕后,停止临时程序,恢复正常消费

38. rabbitmq的集群模式

1. 普通集群 多节点保存元数据和队列结构,一条消息只在A,B,C节点保存一份,节点间相互通信,当A节点没有会和B节点通信获取消息
2. 镜像集群  每个实例都可以相互通信,每个节点都保存了元数据及节点结构以及节点消息

39. 网络七层模型

物理层、数据链路层、网络层、数据传输层、会话层、表示层、应用层
        源macipport
        目标mac    目标ip  目标port 

物理层: 每台机器由唯一的mac地址
数据链路层: 两台机器通过mac地址进行通信
网络层: 多机器、经交换机、路由器通过ip通信
数据传输层: 端到端的端口通信
会话层: 会话的建立、管理、终止
表示层: 数据的表示、安全、压缩
应用层:  httpssmtp


传输层: 可靠的传输层协议TCP,不可靠的传输层协议UDP;

滑动窗口: 控制流量速率

三次握手    
    A: SYN
    B  ACK SYN
    A ACK

四次挥手
    A FIN
    B ACK
    ...一段时间B处理完剩余数据
    B FIN
    A ACK

    MLS:  maximum segment leftime  报文最大生存时间

40. CHL同步队列是怎么实现非公平和公平的?

CHL本身是公平的队列,当使用公平锁,新的线程加入,当队列不为空,直接加入队列,否则尝试获取锁;当时用非公平锁,先尝试获取锁,失败,则加入到队列尾


CHL同步队列: FIFO双向队列。队列的节点保存:线程Thread、等待状态waitStatus,前驱节点pre,后继节点next

线程没有获得锁通过CAS加入到队列中的尾部 compareAndSetTail。

同步队列: 线程安全的数据共享区,从上面线程没有获得锁,通过cas加入到队列尾部,就知道CHL是线程安全的同步队列


https://juejin.cn/post/6961025073797365773

41. ReentrantLock和sychronized的区别

1.实现上: sychronized是java的关键字,由jvm实现,Reentrantlock是API层面提供的 

2.使用上: sychronzied由编译器保证锁的加锁和释放; Reentrantlock需要手工声明来加锁和释放锁,忘记释放会造成死锁。

3.性能: sychronized优化后引入偏向锁、轻量级锁(自旋锁)后两者性能差不多,官方建议如果使用场景一致,建议使用sychronized

4.公平非公平锁: sychronzied是非公平锁, ReentrantLock默认非公平锁,可以设置为公平锁,构造方法传true。

5.功能区别上: 
    ReentantLock支持
        1.等待可中断   持有锁的线程长期不释放的时候,正在等待的锁可以选择放弃等待 lockInterruptibly方法

        2.公平锁 Reentantlock可以实现公平锁

        3.锁定多个条件 Reentantlock提供了一个条件类condition,用来实现多个对象分组唤醒线程;sychronized只能要么随机唤醒一个线程要么全部唤醒

6.两者的实现:
    sychronized实现:重量级锁通过monittor entry和monitor exist来加锁代码,偏向锁和轻量级锁通过mark word 对onwer字段cas
    ReentrantLock: 通过AQS实现, state, CHL,当前持有锁的线程


7.如何选择: 
    当要使用Reentrantlock的三个特性: 等待可中断、公平锁、多条件选择ReentrantLock,否则使用sychronized


8.ReentrantLock如何正确解锁
    ReentrantLock lokck = new ReentrantLock();
    boolean locked = false;
    try {
        lock.lock();
        locked = true;
    } finally{
        if (locked) {
            lock.unlock();
        }
    }

如果上面没有locked使用这个判断,在解锁有可能获取锁的时候抛异常,IllegalMonitorStateException异常。

https://blog.csdn.net/zxd8080666/article/details/83214089

42. JVM 里 new 对象时,堆会发生抢占吗?怎么去设计JVM的堆的线程安全

会,假设jvm虚拟机,每一次new对象时,指针都会向右移动一个对象size的距离,一个线程正在给A对象分配内存,指针还没有来得及修改,另一个对象B内配内存的线程,引用这之前的指针指向,
就发生了抢占,被称为指针碰撞。

jvm通过线程本地分配缓存(Thread local allocation buffer),在新生代eden开辟了一小块线程私有的区域TLAB

43. redis的数据结构

String
List
Set
Zset
Hash

String:
    int:  存储数字
    embstr: 简单字符串,
    Raw: SDS

Zset:
    SkipList


https://www.cnblogs.com/wind-snow/p/11172832.html


44. redis缓存同步问题

更新、删除缓存

1.更新db,更新redis 2.更新redis 更新db

3.删除缓存、更新DB 延时双删

4.更新DB, 删除缓存

mysql与redis数据同步问题: 1. DUF 触发器 2.bin log日志

  1. AVL树和红黑树的区别

    AVL平衡二叉树

    AVL是严格的平衡二叉树,左右子树不超过1,

    AVL: 适合插入和删除次数少的,但查找多的情况

    红黑树: 确保没有一条路径会比其他路径长出两倍 节点非红即黑, 根节点是黑色, 每个叶子节点都是黑色, 如果一个节点是红的,那么他的两个子节点都是黑色, 对任意节点,其到叶子节点的每条路径都包含相同数目的黑节点

    blog.csdn.net/u010899985/…

    AVL树比红黑树保持更加严格的平衡

46. 你设计数据库遵循的范式

遵循第三范式

第一范式:列不可再分,每一列的属性都是不可再分的属性值,

第二范式: 属性完全依赖于主键,每一行可以被唯一区分出来

第三范式:属性不依赖其他非主属性,属性直接依赖于主键

    如:student表(学号、姓名、年龄、性别、所在院校、院校地址、院校电话) 就存在院校地址、院校电话依赖于所在院校。
    因此设计应该拆出来 student(学号、姓名、年龄、性别、所在院校) school(所在院校、院校地址、院校电话)

https://blog.csdn.net/qq_40899182/article/details/81706253

47. 并发大的情况下怎么保证数据一致性

先要问,这个问题具体的场景?

如果是并发扣款,那么用CAS乐观锁扣款


也可能会涉及到 volatile,sychronized,reentrantlock

volatile会涉及JMM的模型: 线程本地内存、主内存,volatile可以是本地内存的值刷新到主内存,i++使用volatile不一定线程安全,只能是保证可见性

sychronized: 无锁时偏向锁,有锁时cas轻量级锁,其他线程枪锁没有抢到升级到重量级锁

Reentrantlock: AQS的实现,FIFO, state, AQS独占模式、共享模式; 公平锁,非公平锁


偏向锁: 实际无竞争,只有一个线程在获取锁,在对象的mark word通过cas记录onwer为当前线程,onwer的值一定是从null到有值的
轻量级锁: 自旋的获取锁,不需要进行线程切换,适合持有锁时间短,竞争不激烈,也是通过cas设置onwer的值,多个线程交替短时间内持有锁
重量级锁: 当自旋一段时间,无法获取锁,轻量升级重量级锁,重量级锁通过monitor监视器实现

48. TCP四次挥手中最后一次为什么要等待2MLS才关闭呢

MSL: maximum segment lifetime 报文最大生存时间

最后一次客户端client告诉服务端server可以断开后,需要等待2MSL,防止报文丢失,如果报文丢失服务端server会在2MSL的时间内重发第三次的报文。

  1. TCP/IP是如何解决解决丢包的

    1.数据分片 2.超时重传 3.ack确认 4.滑动窗口 5.失序处理 6.重复处理 7.数据校验

    blog.csdn.net/qq_30108237…

  2. zab

    zxid(64bits): epoch(32bits) + orderid(32bits)

    www.cnblogs.com/Jacian/p/14…

    zab消息广播的2pc提交 1.二阶段提交中,需要所有参与者反馈ack后再发送commit请求,要么所有参与者 成功,要么失败,这样会产生严重的阻塞问题。 2.在zab协议中,leader等待半数以上的follower成功反馈ack既可。

    zab协议两种模式:崩溃恢复和消息广播

    zab保证数据一致性: 1.已经在leader服务器上提交的事务最终被所有服务器提交 2.丢失只在leader服务器上被提出(未提交)的事务 综上所述:能够确保提交已经被leader提交的事务proposal,同时丢弃已经被跳过 的事务proposal,即:

    1.新选举出来的leader不能包含未提交的proposal
    2.新选举的leader节点中含有最大的zxid
    

    zab运行时状态 looking: leader选举状态,正在寻找leader following: 当前节点是follower,与leader服务器保持同步状态 leading: 当前节点是leader,作为主进程领导状态

    zk中的三种角色: leader: leader follower: 追随者 observer: 观察者,不参与选举也不响应提议,不需要数据持久化到磁盘,每次重启从leader同步整个数据

    www.jianshu.com/p/fe6f80773…

  3. select(*) select(1) select(primary key) select(col)

    结论性能: select(*) >= select(1) > select(primary key) > select(col)

    myAism引擎会把表的总数存在磁盘,当不带条件select(*)查询,直接返回总数

    select(1) : 遍历正常表,取出每一行,到server,直接加1

    select(primary key): 遍历整张表,把每一行的主键取出来,返回给server,判读主键是否非空,不为空加1

    seelct(col): 遍历整张表,把每一行带取出来,判断col是否为null,不为null,加1

    select(*) : 并不把所有字段取出来,mysql针对他专门做了优化,

  4. redis哨兵选举

    https://blog.csdn.net/LiaoHongHB/article/details/109092165
    
    1. 故障节点主观下线
    2. 故障节点客观下线
    3. 哨兵集群选择新的leader
    4. sentinel leader决定选择新master节点

    1.故障节点主观下线
    当一个sentinel在心跳时间内,没有收到master/slave节点的心跳回复,则认为主观下线

    1. 故障节点客观下线

      sentinel会询问其他sentinel节点,主观下线的节点是否可以收到心跳回复,如果quorum个sentinel没有收到 master/slave节点的回复,则认为客观下线

    2. 哨兵集群选举新的sentinel的leader

      sentinel发起leader选举,当其中一个sentinel获得了quorum或(节点数/2 + 1)的投票,则此sentinel升级为leader

    3. sentinel leader决定选择新的master节点

      由sentinel leader在从节点中选择一个作为master节点。

      1. 过滤故障节点
      2. slave-priority
      3. 复制偏移量最大的节点
      4. runid

      过滤故障节点 -----> 如果存在slave-priority最大从节点选择为master -----> 如果没有,复制偏移量最大的节点为master节点 ----> 没有, runid最小的节点升级为master节点

      slave-priority: 值越低,优先级越高

      runid:redis每次启动生成随机的runid作为redis的标识

    quorum: 节点 / 2 +1

  5. @autowired和@resources的区别

    blog.csdn.net/qq_45590494…

    autowired: spring提供的,按照默认type类型注入,可以配合@qualifier按照name注入

    resources: J2EE规范,默认按照name注入,可以指定name或type

  6. 延时消息的几种实现方案

    1. 数据落db,定时任务扫描db, 数据量大不性能堪忧

    2.redis redis zset + list + 定时任务 zset的score按照时间戳存储,定时扫描zset查到小于当前时间到期的,丢到List消费

    1. rabbitMq 最大生存时间 + 死信队列 设置消息的最大生存时间,投送到没有消费者的队列,过期未消费,路由到死信队列,死信队列消费者消费

    2. rocketMQ 开源版的延时消息 开源版仅支持18种时间机制,不能很好的实现自定义时间

      业务上可以用多个固定的延时队列来实现

    3. rocketMQ商业版的延时消息 商业版支持自定义的延时消息

  7. 类的初始化顺序

    静态成员和static块-----> 普通成员和非static块 -----> 构造函数

    父子类:

    父类静态成员和static块 -----> 子静态成员和static块 ---> 父类普通成员和非static块 ---> 父类构造函数 ---> 子类普通成员和非static块 ---> 子类构造函数

    加载过程: 变量先初始化为0 --> 构造方法 ---> 变量赋值

    blog.csdn.net/u013565163/… blog.csdn.net/xyajia/arti…

  8. tcp怎么保证的不乱序、不丢失

    TCP数据包的头格式有两个概念: 1. sequence number是数据包的序号,用来解决网络包乱序的问题 2. acknowledgement number就是ACK,用来确认收到,用来解决不丢包的问题

    blog.csdn.net/qq_28764223…

  9. 关闭线程池

    1.shutdownNow : 线程池拒绝接受新提交的任务,同时立马关闭线程池,线程池里的任务不再执行。 2.shutdown : 线程池拒绝接受新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。

    shutdownNow可能导致报错,需要捕获异常; 确保线程池里没有永久阻塞等待的线程,shutdown执行才会关闭线程池

    shutdown和shutdownNow是异步的,并不会立即马上关闭线程池。

    www.cnblogs.com/qingquanzi/…

  10. TCP报文结构

TCP首部组成: 源端口、目标端口 序号 sequence number 确认号 acknowledgment number 数据偏移 offset 保留 reserved tcp flag:
URG ACK PSH RST SYN FIN

窗口 windows size 
检验和 checksum
紧急指针 urgent pointer
TCP选项  TCP options

www.jianshu.com/p/421dd948a…

  1. 索引失效的几种情况

    1. like %xxx %号开头
    2. 查询条件有or
    3. 复合索引未使用左列字段
    4. 需要类型转换
    5. where 索引列有运算
    6. where索引列使用了函数
    7. 如果Mysql觉得全表扫描更快时(数据量较小)

    www.cnblogs.com/liehen2046/…

60.哨兵模式下slave到master的选举算法

实例有sdown和odown两种失败状态。
    sdown: 主观宕机,如果一个sentinal ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就认为主观宕机了。
    odown: 客观宕机,如果quorum数量的哨兵都觉得一个master宕机了,那就是客观宕机

选举算法:
    1. 跟master断开连接的时常
    2. slave的优先级
    3. 复制offset
    4. run id

61. 负载均衡算法 1.轮询法 2.随机法 3.源地址hash 4.加权轮询法 5.加权随机法 6.最小连接数

  1. spring bean的实例化顺序以及扩展点

    1.实例化就是调用类的构造器进行对象创建的过程,比如:new Object();就实例化了一个Obejct对象; 2.填充属性是指注入bean的依赖或者给属性赋值; 3.Aware接口是Spring中的“觉醒”接口,是Spring容器通过回调向bean注入相关对象的接口; 4.初始化是指完成bean的创建和依赖注入后进行的一个回调,可以利用这个回调进行一些自定义的工作,实现初始化的方式有三种,分别是实现InitializingBean接口、使用@PostConstruct注解和xml中通过init-method属性指定初始化方法; 5.可用状态是指bean已经准备就绪、可以被应用程序使用了,此时bean会一直存在于Spring容器中; 6.销毁是指这个bean从Spring容器中消除,这个操作往往伴随着Spring容器的销毁。实现销毁方法的方式有3中,分别为实现DisposableBean接口、使用@PreDestroy注解和xml中通过destroy-method属性指定。

    四个扩展点: EP1 --> 实例化 --> EP2 --> 填充属性 —> 执行aware接口 --> EP3 --> 初始化 --> EP4 --> 可用状态 --> 销毁

    实现BeanPostProcessor接口或者继承InstantiationAwareBeanPostProcessorAdapter实现四个方法,处理扩展点.

    原文链接:blog.csdn.net/u013565163/…

  2. 磁盘中的块(block)和innodb中的页(page)

    系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘快中的数据会被一次性读取出来,而不是需要什么取什么。

    Innodb存储引擎中有页(page)的概念,页是其磁盘管理的最小单位。Innodb存储引擎中默认每页的大小为16KB,可通过参数innodb_page_size将页的 大小设置为4k,8k,16k。在mysql中可以通过命令查看页的大小: show variables like 'innodb_page_size'

  3. redis在RDB持久化FOrk子进程时,copy-on-write是什么?

    进程的内存有: 虚拟空间 ---指向--> 物理内存。

    当fork是标记父进程的物理内存为read-only,子进程的虚拟空间指向父进程的物理内存,两个内存共享同一个物理内存。 当父子进程修改相应段的行为发生时,,再为子进程相应的段分配物理空间。

    copy-on-write的好处是: 1.减少分配和复制大量资源时带来的瞬间延时 2.可以减少不必要的资源分配

  4. 线程池的四种拒绝策略

    默认是这个AbortPolicy

    hreadPoolExecutor: AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 DiscardPolicy:丢弃任务,但是不抛出异常。 DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

  5. 线程池corePoolSize怎么确定

    看任务是IO密集型还是CPU密集型,IO密集型任务会存在等数据,因此核心池可以相对设置大一些,如果是CPU密集型,则核心池可以设置小一些,减少线程间的切换,加快单个任务。

    www.jianshu.com/p/2a80237c3…

    1.看任务类型是IO密集还是计算密集型,如果是计算密集型,核心线程树就应该设置为CPU的核数,可以确保每个任务都有充足的CPU资源 2.任务响应时间,如果任务要求实时型比较高,核心数尽可能多一些,防止任务因为阻塞而等待 3.系统资源

    注意核心线程数过多会导致系统的负载加重,影响性能;如果太少,可能导致任务无法及时处理

  6. linux授予用户权限及添加组

    chmod 777 1.txt

    777:三段 owner group other user

    groupadd [groupname] 创建用户组

    usermod -G [groupname] [username] 把用户添加进组

    cat /etc/group 查看用户组

    useradd [username] 增加用户

  7. Redis的两种持久化RDS和AOF

    RDB可以配置在多少秒内多少key改变才会触发RDB持久化,RDB文件是二进制,人类不可读。

    AOF支持三种写入时机: 1.每写一次的数据都要写入 2.由OS决定时机 3.每秒写入

    命令追加到aof buffer中,下一次进入事件循环循环后,再将buffer写到磁盘上,AOF极端情况会丢失一条命令,

  8. 如何设计才可以让系统从未分库分表动态切换到分库分表上? 双写,新库,老库双写 分为5步: 同步、双写、校验、切读、切写

    同步: 首先通过Binlog监听工具(如Canal)获取MySQL的全量日志及增量日志,并且不断写入新库MongoDB:
    双写: 其实就是在程序中写入MySQL时,也将这份数据转换格式并同时写入MongoDB
    校验: 同步及双写两个步骤,此时MySQL和MongoDB应该已经保持线上实时同步了,但是为了确保双写的数据不出问题,需要通过定时任务来对MySQL及MongoDB的数据一致性做抽样校验,当校验到数据不一致时,以MySQL的数据为准覆盖MongoDB中的对应数据。
    切读: 当抽样一段时间(一周),如果数据100%一致,则读的流量则切到新库
    切写: 也就是关闭双写,只写新库MongoDB。
    
    注意:双写这里有一个开关,如果双写开启,则不再同步binlog日志
    

    blog.csdn.net/pbrlovejava…

  9. binlog以及canal监听工具

    binlog记录数据库增删改,记录DDL(create、alter、drop)和DML语句(除了数据查询语句select),不记录查询的二进制日志,使用mysqlbinlog解析查看。

    DDL: Data Definition Language DML: Data Manipulation Language

    binlog 是用作人工恢复数据,redolog 是 MySQL 自己使用,用于保证在数据库崩溃时的事务持久性。

    binlog日志分类 二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件 二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML(除了数据查询语句select)语句事件。

    canal blog.csdn.net/lovedingd/a… blog.csdn.net/ww265107102…

    canal译意为水道/管道/沟渠, 是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB)。
    
    canal主要是通过伪装成mysql的slave来向master拉取数据。
    
  10. 实现分布式锁和解决锁续期问题

     假设我们给锁设置的过期时间太短,业务还没执行完成,锁就过期了,这块应该如何处理呢?
    
        redisson 是 Redis 的一个客户端,它能给 Redis 分布式锁实现过期时间自动续期。
    
    redisson 如何实现过期时间自动续期?
    
        redisson 加锁成功后,会单独创建一个线程来监视这个锁。假设我们锁的过期时间是3s,这个线程在1s的时候就会查看当前的锁是否释放,如果没有释放,将过期时间再次设置成3s。
        这个相当于是一个定时任务,每隔过期时间的1/3就会来确认一次锁是否释放,没有释放就续期。这个是从源码查看的
    
    https://blog.csdn.net/jack1liu/article/details/96270927
    
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.8.2</version>
    </dependency>
    
    Config config = new Config();
    config.useClusterServers()
        .setScanInterval(2000)// Cluster state scan interval in milliseconds
        ..addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001")
        .addNodeAddress("redis://127.0.0.1:7002");
    
    RedissonClient redisson  = Redisson.create(config);
    RLock  lock = redisson.getLock("anyLock");
    lock.lock();
    // your business code
    lock.unlock();
    
  11. mybaits插件,实现读写分离

    Myabtis插件,可以根据实现逻辑数据源来实现,读写分离

    通过拦截

    www.manongjc.com/detail/28-b…

  12. redis的缓存雪崩、击穿、穿透是什么,怎么处理

    击穿:缓存不存在、DB存在 穿透:缓存、db都不存在

    击穿: 热key平滑过期、缓存预热启动加载冷数据、定时刷新值,已经过期重新加载进缓存 穿透: 对不存在的key设置空值并返回;使用布隆过滤器判断key是不是不存在;限制封锁恶意IP的请求; 雪崩: 大量流量打到了DB,把DB打死了;redis使用哨兵或者集群避免单机故障;设置热key永不过期;设置key错峰、平滑过期;服务引入本地缓存map、ehcache;服务降级