java中高级高频面试题整理

Java 高频八股文 精简纯手打

java基础篇

1.重写重载区别?

重载:在类中创建多个方法,方法名相同,参数及定义不同,返回值也可以不同
重写:子类对父类允许访问的方法实现进行重新编写,返回值和参数不可改变

2.说一下你理解的多态?

同一个行为具有多个不同表现形式或形态的能力
2.String、StringBuffer 和 StringBuilder 的区别
String:不可变
Buffer,Builder,可追加
Buffer线程安全,效率低
Builder线程不安全,效率高

3.== 和 equals 的区别 ?

==:基本类型比较的是值是否相同,引用类型比较堆内存地址是否相同
equals:引用类型默认情况下比较地址,也可重写equals方法用来比较值是否相同

4.HashMap

在 Java 1.8 中,如果链表的长度超过了 8且数组长度最小要达到64 ,那么链表将转化为红黑树;链表长度低于6,就把红黑树转回链表; 初始化 当创建一个 HashMap 实例时,它会初始化一个默认大小的数组(默认为16),每个数组元素是一个链表。

4.1HashMap底层实现原理?

通过hash()计算出key的hash值,然后调用putVal()执行put操作
putVal(hash(key), key, value, false, true);
1、判断数组是否为空,是则进行初始化

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

2、根据hash值求出数组下标(n-1&hash),并判断该下标是否有元素,没有则直接放入该下标

if ((p = tab[i = (n - 1) & hash]) == null)
     tab[i] = newNode(hash, key, value, null);

3、如果有值,则判断该下标的对象和要存储的对象是否相等,先判断hash值是否相等,再判断key是否相等,如果是同一个对象,则直接覆盖并返回

 		else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;

4、如果不是同一个对象则判断是否为树节点对象,是就直接添加

else if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

5、既不是同一个对象,又不是树节点则找到链表的尾部插入,判断链表长度是否需要树化。如果key相同则直接覆盖

 			else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }

6、最后判断hashmap的size是否达到阈值,进行扩容处理(元素个数超过16*0.75,把数组扩大一倍,然后重新计算每一个元素在数组中的位置,耗费性能)

		if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;

get() 方法来获取某个键对应的值时,HashMap 首先也会计算该键的 hashCode() 值,然后根据该值找到该键值对应在数组中的位置。如果该位置上没有元素,则返回 null;如果该位置上有元素,则遍历该位置对应的链表,找到与该键相等的键值对,然后返回该键值对的值。

final Node<K,V> getNode(Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & (hash = hash(key))]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

4.2:hashmap为什么采用两倍扩容?

以二次幂展开,容器的元素要么保持原来的索引,要么以二次幂的偏移量出现在新表中。也就是说hashmap采用2倍扩容,可以尽可能的减少元素位置的移动。

/**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    final Node<K,V>[] resize() {

4.3:hashmap负载因子为什么是0.75?

时间和空间的权衡 值越高空间开销小但是增加了查找开销

As a general rule, the default load factor (.75) offers a good
 * tradeoff between time and space costs.  Higher values decrease the
 * space overhead but increase the lookup cost

4.4:hashmap初始长度为什么是16?

服务于从Key映射到index的Hash算法,在性能和内存的使用上取平衡,实现一个尽量均匀分布的Hash函数,选取16,是通过位运算的方法进行求取的。

4.5:HashMap 和 Hashtable 的区别

HashMap 允许 key 和 value 为 null,Hashtable 不允许。
HashMap 的默认初始容量为 16,Hashtable 为 11。
HashMap 的扩容为原来的 2 倍,Hashtable 的扩容为原来的 2 倍加 1。
HashMap 是非线程安全的,Hashtable是线程安全的。
HashMap 的 hash 值重新计算过,Hashtable 直接使用 hashCode。
HashMap 去掉了 Hashtable 中的 contains 方法。
HashMap 继承自 AbstractMap 类,Hashtable 继承自 Dictionary 类。

4.6:为什么HashMap会产生死循环?

导致死循环的主要原因是扩容后,节点的顺序会反掉。多线程环境下建议采用ConcurrentHashMap替代。在JDK1.8中,HashMap改成了尾插法,解决了链表死循环的问题。

4.7:ConcurrentHashMap吗 为什么性能比HashTable高?

ConcurrentHashMap是线程安全的Map容器,JDK8之前,ConcurrentHashMap使用锁分段技术,将数据分成一段段存储,每个数据段配置一把锁,即segment类,这个类继承ReentrantLock来保证线程安全,JKD8的版本取消Segment这个分段锁数据结构,底层也是使用Node数组+链表+红黑树, 使用的 Synchronized 锁加 CAS 的机制。
hashtable类基本上所有的方法都是采用synchronized进行线程安全控制,高并发情况下效率就降低 ,ConcurrentHashMap是采用了分段锁的思想提高性能,锁粒度更细化

6.Java中实现多线程有几种方法?
继承Thread类,实现runnable,callable接口 使用线程池创建线程
7.Java中synchronized 和 ReentrantLock 有什么不同?
Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。
ReentrantLock是API层面的互斥锁,需要lock()和 unlock()方法配合try/finally语句块来完成。
Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码
指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线
程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就
减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被
另一个线程释放为止。
由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,
ReentrantLock类提供了一些高级功能,主要有以下3项:
1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于
Synchronized来说可以避免出现死锁的情况。
2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平
锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁
表现的性能不是很好。
3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象
8.线程池核心的参数?
一、corePoolSize 线程池核心线程大小
二、maximumPoolSize 线程池最大线程数量
三、keepAliveTime 空闲线程存活时间
四、unit 空闲线程存活时间单位
五、workQueue 工作队列
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
六、threadFactory 线程工厂
七、handler 拒绝策略
9.线程池拒绝策略?
1.AbortPolicy:默认策略,在需要拒绝任务时抛出RejectedExecutionException;
2.CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务,如果线程池已经关闭,任务将被丢弃;
3.DiscardPolicy:直接丢弃任务;
4.DiscardOldestPolicy:丢弃队列中等待时间最长的任务,并执行当前提交的任务,如果线程池已经关闭,任务将被丢弃。
10.数据库事务四个属性?
一、事务四大属性
分别是原子性、一致性、隔离性、持久性。
1、原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2、一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
3、隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
4、持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的
11.SQL怎么调优?
12.索引、组合索引、覆盖索引?
组合索引:对表上多个列进行索引,跟创建单个索引的方法一样。
遵循最左匹配原则,如果第一个字段遇到范围查询,就停止后面的匹配。
覆盖索引:需查询的字段建立索引,只需扫描一次索树,不需回表查询
https://www.cnblogs.com/s42-/p/13596212.html
覆盖索引
1 A: select id from user_table where name= ‘张三’
2 B: select password from user_table where name= ‘张三’
语句A: 因为 name索引树 的叶子结点上保存有 name和id的值 ,所以通过 name索引树 查找到id后,因此可以直接提供查询结果,不需要回表,也就是说,在这个查询里面,索引name 已经 “覆盖了” 我们的查询需求,我们称为 覆盖索引
语句B: name索引树 上 找到 name=‘张三’ 对应的主键id, 通过回表在主键索引树上找到满足条件的数据
因此我们可以得知,当sql语句的所求查询字段(select列)和查询条件字段(where子句)全都包含在一个索引中(联合索引),可以直接使用索引查询而不需要回表。这就是覆盖索引。
13.有没有做过数据库读写方案?
14.事务隔离级别有哪些?MySQL的默认隔离级别是?
Read uncommitted(未授权读取、读未提交):
Read committed(授权读取、读提交):
Repeatable read(可重复读取):
Serializable(序列化):
15.MySQL中有哪几种锁?
1、共享锁:共享锁锁定的资源可以被其他用户读取,但不能修改
2、独占锁:独占锁锁定的资源只允许锁定操作的程序使用,其他任何对他的操作都不会被接受,执行增删改命令时,自动会使用独占锁,知道事务结束后才会被释放
3、乐观锁:假设不会出现并发问题,每次取数据都认为不会有其他线程对数据进行修改,因此不会上锁,但是更新的时候会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制实现,在数据表中增加一个版本号,表示被修改的次数,当数据被修改时,版本号+1,当线程要更新数据时,读取版本号,提交更新的时候判断刚才读取到的版本号和当前版本号相等时才更新,否则重试,直到更新成功为止
4、悲观锁:假设最坏的情况,每次取数据都认为其他线程会修改,所以都会枷锁,synchronized的思想就是悲观锁
全局锁、表级锁和行锁
16.Spring Cloud和dubbo区别?
通信方式
Dubbo 使用的是 RPC 通信;Spring Cloud 使用的是 HTTP RestFul 方式。
注册中心
Dubbo 使用 ZooKeeper(官方推荐),还有 Redis、Multicast、Simple 注册中心,但不推荐。;
Spring Cloud 使用的是 Spring Cloud Netflflix Eureka。
监控
Dubbo 使用的是 Dubbo-monitor;Spring Cloud 使用的是 Spring Boot admin。
断路器
Dubbo 在断路器这方面还不完善,Spring Cloud 使用的是 Spring Cloud Netflflix Hystrix。
分布式配置、网关服务、服务跟踪、消息总线、批量任务等。
Dubbo 目前可以说还是空白,而 Spring Cloud 都有相应的组件来支撑。
17.微服务网关做什么的?
路由,限流,鉴权,
18.说说你对分布式事务的了解?
CAP
19.你知道哪些分布式事务解决方案?
2阶段提交2pc,三阶段提交3pc,tcc,本地消息表,消息事务,seata
20.分布式锁哪些实现方案?
21.Jdk1.8的新特性?
22.如何提高系统的并发能力?
系统拆分,缓存,mq,分库分表,读写分离,es
23.Springboot常用注解?
@SpringBootApplication@EnableAutoConfiguration@Configuration@Repository@Service
@RestController@Component@Bean@RequestParam@PathVariable@ComponentScan
24.SpringCloud有哪些组件?
1、Eureka实现服务治理;2、Ribbon主要提供客户侧的软件负载均衡算法 轮询随机重试权重;3、Hystrix断路器,保护系统,控制故障范围;4、gateway,api网关,路由,负载均衡等多种作用;5、Config配置管理。
25.Springboot启动流程?
运行SpringApplication.run(), 会new一个springapplication对象调用实例的run方法,查询加载所有的监听器,Springboot通知监听器,我马上要开始执行,创建Springboot将要使用的环境,初始化ApplicationContext,通过@EnableAutoConfiguration获取所有配置并加载到ApplicationContext调用方法对应用上下文进行设置和处理,以及refresh方法使ioc容器到达可用状态,执行SpringApplicationListener的finished方法,启动完毕

一、SpringBoot启动的时候,会构造一个SpringApplication的实例,构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:
1、把参数sources设置到SpringApplication属性中,这个sources可以是任何类型的参数.
2、判断是否是web程序,并设置到webEnvironment的boolean属性中.
3、创建并初始化ApplicationInitializer,设置到initializers属性中 。
4、创建并初始化ApplicationListener,设置到listeners属性中 。
5、初始化主类mainApplicatioClass。
二、SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:
1、构造一个StopWatch计时器,用来记录SpringBoot的启动时间 。
2、初始化监听器,获取SpringApplicationRunListeners并启动监听,用于监听run方法的执行。
3、创建并初始化ApplicationArguments,获取run方法传递的args参数。
4、创建并初始化ConfigurableEnvironment(环境配置)。封装main方法的参数,初始化参数,写入到 Environment中,发布 ApplicationEnvironmentPreparedEvent(环境事件),做一些绑定后返回Environment。
5、打印banner和版本。
6、构造Spring容器(ApplicationContext)上下文。先填充Environment环境和设置的参数,如果application有设置beanNameGenerator(bean)、resourceLoader(加载器)就将其注入到上下文中。调用初始化的切面,发布ApplicationContextInitializedEvent(上下文初始化)事件。
7、SpringApplicationRunListeners发布finish事件。
8、StopWatch计时器停止计时,日志打印总共启动的时间。
9、发布SpringBoot程序已启动事件(started())
10、调用ApplicationRunner和CommandLineRunner
11、最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了(running())
自动装配原理:
1.通过注解@SpringBootApplication=>@EnableAutoConfiguration=>@Import({AutoConfigurationImportSelector.class})实现自动装配
2.AutoConfigurationImportSelector类中重写了ImportSelector中selectImports方法,批量返回需要装配的配置类
3.通过Spring提供的SpringFactoriesLoader机制,扫描classpath下的META-INF/spring.factories文件,读取需要自动装配的配置类
4.依据条件筛选的方式,把不符合的配置类移除掉,最终完成自动装配

26.redis中五种常见数据结构?
Redis数据结构有:string缓存功能,计数器、list消息队列、hash购物车场景、set存储好友/粉丝、sorted set 排行榜、HyperLogLog可以用来统计网站的登陆人数以及其他指标、Geo主要用来存储地理位置信息、BloomFilter判断一个元素是否在某个集合中解决缓存穿透问题,黑名单校验
27.Redis哨兵模式
哨兵模式是Redis可用性的解决方案;它由一个或多个 sentinel 实例构成 sentinel 系统;该系统可
以监视任意多个主库以及这些主库所属的从库;当主库处于下线状态,自动将该主库所属的某个从
库升级为新的主库;客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址,然后再连接主节点进行数据交互。当主节点发生故障时,客户端会重新向 sentinel 索要主库地址,sentinel 会将最新的主库地址告诉客户端。通过这样客户端无须重启即可自动完成节点切换。
哨兵模式当中涉及多个选举流程采用的是 Raft 算法的领头选举方法的实现;
sentinel 会以每秒一次的频率向所有节点(其他sentinel、主节点、以及从节点)发送 ping 消
息,然后通过接收返回判断该节点是否下线;如果在配置指定 down-after-milliseconds 时间内,sentinel收到的都是无效回复, 则被判断为主观下线;
当一个 sentinel 节点将一个主节点判断为主观下线之后,为了确认这个主节点是否真的下线,它
会向其他sentinel 节点进行询问,如果收到一定数量的已下线回复,sentinel 会将主节点判定为
客观下线,并通过领头 sentinel 节点对主节点执行故障转移;
主节点被判定为客观下线后,开始领头 sentinel 选举,需要一半以上的 sentinel 支持,选举领头
sentinel后,开始执行对主节点故障转移;
从从节点中选举一个从节点作为新的主节点
通知其他从节点复制连接新的主节点
若故障主节点重新连接,将作为新的主节点的从节点
缺点:redis 采用异步复制的方式,意味着当主节点挂掉时,从节点可能没有收到全部的同步消息,这部
分未同步的消息将丢失。如果主从延迟特别大,那么丢失可能会特别多。sentinel 无法保证消息完
全不丢失,但是可以通过配置来尽量保证少丢失。
28.Redis集群模式
29.Redis哨兵模式和集群模式区别
哨兵模式每个实例是全量存储,浪费内存且有木桶效应,为了最大化利用内存,可以采用集群,即每台redis存储不同的内容,共有16384个spot,集群至少需要3主3从,集群是为了解决单机redis容量有限的问题
30.什么是缓存雪崩?
指当大量缓存同时过期或缓存服务宕机,所有请求的都直接访问数据库,造成数据库高负载,影响性能,甚至数据库宕机
1.不同的过期时间2.在缓存雪崩问题防治上面,一个比较典型的技术就是采用集群方式部署,使用集群可以避免服务单点故障。
31.什么是缓存击穿?
当某一key的缓存过期时大并发量的请求同时访问此key,瞬间击穿缓存服务器直接访问数据库,让数据库处于负载的情况。
1.热点数据不过期 2.加互斥锁当Redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则先阻塞
32.什么是缓存穿透?
缓存穿透是指缓存服务器中没有缓存数据,数据库中也没有符合条件的数据1.缓存空值2.布隆过滤器(Bloom Filter)
33.Mybatis #{}和${}的区别是什么?
34.自定义注解如何使用?
35.dubbo的工作原理
Container服务容器负责启动,加载以及运行Provider服务提供者
Provider服务提供者启动时,需要将自身暴露出去让远程服务器可以发现,同时向Registry注册中心注册自己提供的服务
Consumer服务消费者启动时,向Registry注册中心订阅所需要的服务
Registry注册中心返回服务提供者列表给消费者,同时如果发生变更,注册中心将基于长连接推送实时数据给消费者
服务消费者需要调用远程服务时,会从提供者的地址列表中,基于负载均衡算法选出一台提供者服务器进行调用,如果调用失败,会基于集群容错策略进行调用重试
服务消费者与提供者会在内存中统计调用次数和调用时间,然后通过定时任务将数据发送给Monitor监控中心
36.dubbo过滤器使用
37.springcloud 与springboot的关系
springboot可以单独使用,它不依赖于springcloud

  1. 而springcloud必然依赖于springboot,属于依赖关系。
  2. Springboot专注于快速方便的开发单个个体微服务。
  3. SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务。
    38.Linux 常用的命令?
    39.Springbatch有没有用过,介绍它的原理?
    40.spring核心是什么、优点是什么?
    41.spring线程池调优
    针对任务的不同特性 + 建议使用有界队列
    42.怎么实现线程安全?
    加锁 利用Synchronized或者ReenTrantLock来对不安全对象进行加锁
    Threadlocal来为每一个线程创造一个共享变量的副本来(副本之间是无关的)避免几个线程同时操作一个对象时发生线程安全问题
    43.单例模式的好处?
    1.提供了对唯一实例的受控访问;
    2;由于系统中内存只存在一个对象,因此可以节约系统的的资源,对于一些频繁的创建和销毁的对象单例模式无意可以提供系统的性能,
    3单例模式可以允许可变的数目的实例,使用单利模式进行扩展,使用控制单利对象相似的方法获取指定个数的实例,及解决了单利对象,共享过多,而有损性能的问题;
    主要缺点;
    1;由于单例模式,不是抽象的所以可扩展性比较差;
    2;单例类,职责过重,在一定程度上;违背了单一职责‘
    3;滥用单例将带来一些负面的问题,如为了节省资源将数据库连接池对象设计为单例模式,可能会导致共享连接池对象的程序过多未出而出现的连接池溢出,如果实例化对象长时间不用系统就会被认为垃圾对象被回收,这将导致对象状态丢失。
    44.谈谈你在项目中遇到印象深刻的问题,怎么解决的?
    45.常用的设计模式哪些,具体怎么实现的?
    46.Mybatis常用标签有哪些?
    select 标签insert、delete、update标签resultMap if标签foreach choose where set collection sql include
    47.AOP是怎么使用的?
    //2.定义aop切面
    @Aspect
    @Pointcut 切入点表达式
    @Before(“pointCut()”)
    //添加环绕通知 可以控制目标方法执行 要求添加参数
    @Around(“pointCut()”
    48.MQ如何保证分布式事务的最终一致性?
    rocketmq支持事务
    Rabbitmq:
    1、确认生产者一定要将数据投递到MQ服务器中(采用MQ消息确认机制)
    2、MQ消费者消息能够正确消费消息,采用手动ACK模式(注意重试幂等性问题)
    3、如何保证第一个事务先执行,采用补偿机制,在创建一个补单消费者进行监听,如果订单没有创建成功,进行补单。
    49.描述一下消息中间件Ack确认机制?
    ACK机制是消费者从RabbitMQ收到消息并处理完成后,反馈给RabbitMQ,RabbitMQ收到反馈后才将此消息从队列中删除。如果一个消费者在处理消息出现了网络不稳定、服务器异常等现象,那么就不会有ACK反馈,RabbitMQ会认为这个消息没有正常消费,会将消息重新放入队列中。
    50.如何保证redis的原子性?
    对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
    Redis的操作之所以是原子性的,是因为Redis是单线程的。
    Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
    51.多个命令在并发中也是原子性的吗?
    不一定, 将get和set改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.
    52.项目中是如何限流的?
    漏桶和令牌桶
    令牌桶算法:一个存放固定容量令牌的桶,按照固定速率(每秒/或者可以自定义时间)往桶里添加令牌,然后每次获取一个令牌,当桶里没有令牌可取时,则拒绝服务
    漏桶算法:水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出(拒绝服务),可以看出漏桶算法能强行限制数据的传输速率
    计数器
    53.如何解决超卖问题?
    mysql排它锁
    版本号
    Redis放队列单线程扣减库存
    redisson分布式锁
    54.如何解决高并发问题?
    集群,缓存,sql优化,读写分离,分库分表,限流,消息中间键异步处理
    55.使用 rabbitmq 的场景?
    56.rabbitmq防止消息重复消费?
    保证消息的幂等性,每个消息携带一个全局唯一的id
    57.rabbitmq延迟队列如何实现?
    将消息发送到延迟交换机,消息达到延迟时间会投递到对应的队列
    58.Mybatis-plus 与mybatis区别?
    MyBatis:一种操作数据库的框架,提供一种Mapper类,支持让你用java代码进行增删改查的数据库操作,省去了每次都要手写sql语句的麻烦。但是!有一个前提,你得先在xml中写好sql语句,也是很麻烦的。
    MP的存在就是为了稍稍弥补Mybatis的不足。在我们使用Mybatis时会发现,每当要写一个业务逻辑的时候都要在DAO层写一个方法,再对应一个SQL,即使是简单的条件查询、即使仅仅改变了一个条件都要在DAO层新增一个方法,针对这个问题,MP就提供了一个很好的解决方案,它可以让我们避免许多重复性的工作。
    59.Fegin怎么使用?
    60.Fegin和RestTemplate的区別?
    61.RestTemplate怎么实现负载均衡的?
    在使用了@LoadBalanced后,Spring容器在启动的时候会为被修饰过的RestTemplate添加拦截器,拦截器里会使用LoadBalanced相关的负载均衡接口来处理请求,通过这样一个间接的处理,会使原来的RestTemplate变得不是原来的RestTemplate了,具备了负载均衡的功能
    62.mybatis如何实现分页?
    Limit,pagehelper,rowbounds
    63.@Resource、@Autowired、@Qualifier区别
    1、@Autowired
    按照类型装配依赖对象(byType),默认情况下要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。
    2、@Resource
    默认按照名称装配 (byName),需要导入包javax.annotation.Resource
    @Resource有两个属性是比较重要的,分别为name和type
    1、Spring将@Resource注解的name属性解析为bean的名字,而type属性则 解析为bean的类型。
    2、 所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。
    3、如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
    @Resource装配顺序
    1、如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常  
    2、如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常  
    3、如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常  
    4、如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装 配;
    3、@Qualifier
    public class TestServiceImpl {
    @Autowired
    @Qualifier(“userDao”)
    private UserDao userDao;
    }
    1、@Autowired是根据类型进行自动装配的。
    2、 如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;
    3、 如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。
    4、这时我们可以使用@Qualifier配合@Autowired来解决这些问题。

64.MySQL B+树索引和哈希索引的区别
B+树索引是一种多路径的平衡搜索树,具有如下特点:
1.非叶子节点不保存数据,只保存索引值
2.叶子节点保存所有的索引值和数据
3.同级节点通过指针自小而大顺序链接
4.节点内的数据也是自小而大顺序存放
5.叶子节点拥有父节点的所有信息

优点
由于数据顺序存放,所以无论是区间还是顺序扫描都更快。
非叶子节点不存储数据,因此几乎都能放在内存中,搜索效率更高
单节点中可存储的数据更多,平均扫描I/O请求树更少
平均查询效率稳定(每次查询都从根结点到叶子结点,查询路径长度相同)
缺点
新增数据不是按顺序递增时,索引树需要重新排列,容易造成碎片和页分裂情况。
哈希索引
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快,具有如下特点:
1.哈希索引建立在哈希表的基础上
2.对于每个值,需要先计算出对应的哈希码(Hash Code),不同值的哈希码唯一
3.把哈希码保存在哈希表中,同时哈希表也保存指向对应每行记录的指针
优点
大量唯一等值查询时,哈希索引效率通常更高。
缺点
哈希索引对于范围查询和模糊匹配查询显得无能为力。
哈希索引不支持排序操作,对于多列联合索引的最左匹配规则也不支持。
哈希索引不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。

65.简单的谈一下SpringMVC的工作流程
用户发送请求至前端控制器DispatcherServlet
DispatcherServlet收到请求调用HandlerMapping处理器映射器。
处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
DispatcherServlet调用HandlerAdapter处理器适配器
HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
Controller执行完成返回ModelAndView
HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
DispatcherServlet将ModelAndView传给ViewReslover视图解析器
ViewReslover解析后返回具体View
DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
DispatcherServlet响应用户

66.IOC(控制反转)与DI(依赖注入)理解与区别?
IOC的意思是控制反转,DI的意思是依赖注入 ,前者以前创建对象的主动权和创建时机是由自己把控的,IOC则是把创建对象并给对象中的属性赋值交由spring工厂管理,从而达到控制反转的目的; 而后者则是通过依赖注入的手段让spring工厂来管理对象的创建和属性的赋值 。DI依赖注入:就是在构造某对象时,能将对象依赖的东西自动初始化进去。

67.Bean的作用域
Singleton Prototype Request、Session

68.一级缓存与二级缓存
一级缓存是SqlSession级别的缓存,Mybatis默认开启一级缓存
一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
一级缓存失效的四种情况
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存

二级缓存是mapper(namespace)级别的缓存
Mybatis默认没有开启二级缓存,需要在全局配置文件中开启二级缓存

  <setting name="cacheEnabled" value="true"/>

二级缓存(second level cache),全局作用域缓存。二级缓存默认不开启,需要手动配置。MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口。二级缓存在SqlSession 关闭或提交之后才会生效

多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
清空sqlSession缓存的六种方式 :
① session.clearCache( ) ;
② execute update(增删改) ;
③ session.close( );
④ xml配置 flushCache=“true”;
⑤ rollback;
⑥ commit。

69.MySQL优化
(1)尽量选择较小的列
(2)将where中用的比较频繁的字段建立索引
(3)select子句中避免使用‘*’
(4)避免在索引列上使用计算、not in 和<>等操作
(5)当只需要一行数据的时候使用limit 1
(6)保证单表数据不超过200W,适时分割表。针对查询较慢的语句,可以使用explain 来分析该语句具体的执行情况。
(7)避免改变索引列的类型。
(8)选择最有效的表名顺序,from字句中写在最后的表是基础表,将被最先处理,在from子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。
(9)避免在索引列上面进行计算。
(10)尽量缩小子查询的结果

数据库设计规范
(一)基础规范
1、表存储引擎必须使用InnoD,表字符集默认使用utf8,必要时候使用utf8mb4
解读:
(1)通用,无乱码风险,汉字3字节,英文1字节
(2)utf8mb4是utf8的超集,有存储4字节例如表情符号时,使用它
2、禁止使用存储过程,视图,触发器,Event
3、禁止在数据库中存储大文件,例如照片,可以将大文件存储在对象存储系统,数据库中存储路径
4、禁止在线上环境做数据库压力测试
5、测试,开发,线上数据库环境必须隔离
(二)命名规范
1、库名,表名,列名必须用小写,采用下划线分隔
2、库名,表名,列名必须见名知义,长度不要超过32字符
3、库备份必须以bak为前缀,以日期为后缀
4、从库必须以-s为后缀
5、备库必须以-ss为后缀
(三)表设计规范
1、单实例表个数必须控制在2000个以内
2、单表分表个数必须控制在1024个以内
3、表必须有主键,推荐使用UNSIGNED整数为主键
潜在坑:删除无主键的表,如果是row模式的主从架构,从库会挂住
4、禁止使用外键,如果要保证完整性,应由应用程式实现
5、建议将大字段,访问频度低的字段拆分到单独的表中存储,分离冷热数据
(四)列设计规范
1、根据业务区分使用tinyint/int/bigint,分别会占用1/4/8字节
2、根据业务区分使用char/varchar
3、根据业务区分使用datetime/timestamp
4、必须把字段定义为NOT NULL并设默认值
5、使用INT UNSIGNED存储IPv4,不要用char(15)
6、使用varchar(20)存储手机号,不要使用整数
7、使用TINYINT来代替ENUM
(五)索引规范
1、唯一索引使用uniq_[字段名]来命名
2、非唯一索引使用idx_[字段名]来命名
3、单张表索引数量建议控制在5个以内
4、组合索引字段数不建议超过5个
5、不建议在频繁更新的字段上建立索引
6、非必要不要进行JOIN查询,如果要进行JOIN查询,被JOIN的字段必须类型相同,并建立索引
7、理解组合索引最左前缀原则,避免重复建设索引,如果建立了(a,b,c),相当于建立了(a), (a,b), (a,b,c)
(六)SQL规范
1、禁止使用select *,只获取必要字段
2、insert必须指定字段,禁止使用insert into T values()
3、隐式类型转换会使索引失效,导致全表扫描
4、禁止在where条件列使用函数或者表达式
5、禁止负向查询以及%开头的模糊查询
6、禁止大表JOIN和子查询
7、同一个字段上的OR必须改写问IN,IN的值必须少于50个
8、应用程序必须捕获SQL异常

Redis中提供了8种内存淘汰策略:
volatile-lru:针对设置了过期时间的key,使用LRU算法进行淘汰
allkeys-lru:针对所有key使用LRU算法进行淘汰
volatile-lfu:针对设置了过期时间的key,使用LFU算法进行淘汰
allkeys-lfu:针对所有key使用LFU算法进行淘汰
volatile-random: 从设置了过期时间的key中随机删除
allkeys-random: 从所有key中随机删除
volatile-ttl:删除生存时间最近的一个键
noeviction(默认策略):不删除键,返回错误OOM,只能读取不能写入
通过修改 maxmemory-policy 配置来设置淘汰策略,默认noeviction

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘右今

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值