ITPub博客

首页 > 数据库 > 数据库开发技术 > 跳槽了,给大家整理一波面试题 | 附PDF下载

跳槽了,给大家整理一波面试题 | 附PDF下载

原创 数据库开发技术 作者:阿Q说代码 时间:2023-12-13 14:13:29 0 删除 编辑

哈喽大家好,我是阿Q!

最近好久没有输出原创文章了,究其原因是工作上出现了一些变动,顺势就跳了一波槽。

出去面了一圈后,虽说也顺利拿了不少offer,但是不知道是不是年底的缘故,面试机会比往常要少许多。所以劝一下今年有打算换工作的朋友,还是先苟到年底,拿了年终奖再走吧~

趁我还记得住这些面试题,在第一时间给大家整理一波,希望对大家有所帮助。老规矩,字数有点多,建议收藏。

回复【面试笔记】获取PDF版本!

java基础

ThreadLocal 的内存泄漏

ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,value 是强引用。当 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉,从而导致内存溢出。

解决方法:在线程使用完 ThreadLocal 后,手动调用 remove() 方法清理 ThreadLocal 中的数据,确保及时释放资源。

Synchronized 与 ReentrantLock 如何选择

相同点:两者都是“可重入锁“,即当前线程获取到锁对象之后,如果想继续获取锁对象还是可以继续获取的,只不过锁对象的计数器进行“+1”操作就可以了。不同点

  • ReentrantLock是基于API实现的,Synchronized是依赖于JVM实现的;
  • ReentrantLock可以响应中断,Synchronized是不可以的;
  • ReentrantLock可以指定是公平锁还是非公平锁,而Synchronized只能是非公平锁;
  • ReentrantLock的lock是同步非阻塞,采用的是乐观并发策略,Synchronized是同步阻塞的,使用的是悲观并发策略;
  • ReentrantLock借助Condition可以实现多路选择通知,Synchronized通过wait()和notify()/notifyAll()方法可以实现等待/通知机制(单路通知);

线程池的使用及场景

线程池是一个共享的线程资源池,用于管理和重用工作线程。其主要目的是减少创建和销毁线程的次数,以及减少线程的数量,从而提高系统性能。

线程池适用场景

  • 需要处理大量短时间的任务:线程池可以通过重用线程来减少线程创建和销毁的开销,在任务完成后将线程返回到线程池中,避免了频繁地创建和销毁线程,从而提高了效率。
  • 需要限制并发线程数量:线程池可以限制并发线程的数量,防止因为过多的线程导致系统负载过高等问题,从而保证系统的稳定性和可靠性。
  • 需要异步执行一些任务:线程池可以异步执行一些任务,从而提高程序的响应速度,使程序具有更好的用户体验。
  • 需要实现任务队列:线程池可以将任务放入队列中,并由工作线程逐个取出并执行,从而实现任务队列的功能。

不同场景下适用的类别

  • newFixedThreadPool:创建一个固定线程数量的线程池,该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  • newSingleThreadExecutor:创建一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
  • newCachedThreadPool:创建一个缓存线程池。线程池的线程数量不确定,如果有空闲线程可以复用,则会优先使用可复用的线程。如果所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用,默认60s之内未使用自动释放。极端情况下,这样会导致耗尽 cpu 和内存资源。
  • newScheduledThreadPool:创建一个可以执行延迟任务的线程池;可以实现循环或延迟任务,在对延迟任务和循环任务要求严格的时候,就需要考虑使用ScheduledExecutorService了。
  • newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
  • newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加,使用ForkJoinPool实现】;

注意JDK、Tomcat以及Undertow 下核心线程、最大线程、工作队列等数值及先后策略的不同!

设计模式在项目中的应用

先总结一下在Spring中用到的设计模式:

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配Controller。

再举两三个在工作中用到的设计模式例子:

  • 策略模式:在对接第三方系统时,采用策略模式来实现异步通知返回参数的统一处理。推荐文章
  • 模板方法模式:项目中的代码生成器基本都是采用了模板方法模式。通过定义一个抽象的代码生成算法,并允许子类通过实现特定的步骤来定制生成算法的不同部分。
  • 单例模式:项目开发过程中,缓存管理器、数据源管理器等都会使用单例模式来确保在整个应用程序中只有一个实例,并提供全局访问点。

Spring

Spring 的生命周期

  • 扫描包,通过注解扫描找到所有的Bean,生成 BeanDefinition
  • 在 doGetBean 方法中实例化 bean,通过构造方法反射实例化
  • populateBean 方法进行属性填充,遍历所有需要注入的属性,通过 inject 方法反射注入
  • 执行回调方法,例如 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 的方法
  • 执行所有后置处理器的 postProcessBeforeInitialization 方法
  • 执行bean生命周期回调中的 init-method ,若 bean 实现了 InitializingBean 接口,执行 afterPropertiesSet 方法
  • 执行所有后置处理器的 postProcessAfterInitialization 方法
  • 后续销毁时,如果实现了 DisposableBean,执行destroy接口
  • 如果定义了destroy-method,调用destroy方法

Spring 事务

事务管理方式

首先说一下 Spring 支持的两种方式的事务管理

  • 编程式事务 :在代码中硬编码(不推荐使用) : 通过 TransactionTemplate 或者 TransactionManager 手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。
  • 声明式事务 :在 XML 配置文件中配置或者直接基于注解(推荐使用) : 实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)

三大接口

然后说一下 Spring 事务管理中重要的接口

  • PlatformTransactionManager:(平台)事务管理器,Spring 事务策略的核心。
  • TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
  • TransactionStatus:事务运行状态。

PlatformTransactionManager 接口可以被看作是事务上层的管理者,而 TransactionDefinition 和 TransactionStatus 这两个接口可以看作是事务的描述。

PlatformTransactionManager 会根据 TransactionDefinition 的定义比如事务超时时间、隔离级别、传播行为等来进行事务管理 ,而 TransactionStatus 接口则提供了一些方法来获取事务相应的状态比如,是否新事务、是否可以回滚等等。

通过 PlatformTransactionManager 接口,Spring 为各个平台如:JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

其次可以说一下数据的属性

什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了 5 个方面:隔离级别、传播行为、回滚规则、是否只读、事务超时。

其中隔离级别和传播行为用的比较多

隔离级别

  • TransactionDefinition.ISOLATION_DEFAULT :使用后端数据库默认的隔离级别,MySQL 默认采用的 REPEATABLE_READ 隔离级别 Oracle 默认采用的 READ_COMMITTED 隔离级别
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED :最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务传播行为

  1. TransactionDefinition.PROPAGATION_REQUIRED 使用的最多的一个事务传播行为,我们平时经常使用的 @Transactional 注解默认使用就是这个事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
  3. TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,就在嵌套事务内执行——是指B事务嵌套在A事务中,两者不是单独的;如果当前没有事务,就执行与 TransactionDefinition.PROPAGATION_REQUIRED 类似的操作。
  4. TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)这个使用的很少。
  5. 若是错误的配置以下 3 种事务传播行为,事务将不会发生回滚:
  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

注意事项

最后说一下 @Transactional 的使用注意事项

  • @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
  • 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
  • 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败;
  • 被 @Transactional 注解的方法所在的类必须被 Spring 管理,否则不生效;
  • 底层使用的数据库必须支持事务机制,否则不生效;

Spring 如何实现多继承的效果

在 Spring 框架中,有几种方式可以实现多继承的效果:

  1. 接口继承:在 Spring 中,可以定义多个接口,并让一个类实现多个接口。接口可以定义一组方法的规范,一个类可以实现多个接口来获得这些方法的实现。通过接口继承,一个类可以具备多个不同接口的功能。
  2. 组合:组合是指一个类包含其他类作为其成员变量。在 Spring 中,可以通过组合的方式将多个类组合成一个复合类,从而实现多个类的功能组合。这样,复合类可以调用所包含类的方法,达到多继承的效果。
  3. AOP(面向切面编程):Spring 框架提供了面向切面编程的支持,通过切面可以在不修改原有类的情况下,为类动态地添加额外的功能。通过 AOP,可以将多个类的功能进行解耦和重用,实现类似于多继承的效果。

SpringBoot 的自动装配原理

@SpringBootApplication 注解由三个注解组成:@SpringBootConfiguration、@ComponentScan、@EnableAutoConfiguration。

基于 Spring 的 spi 扩展机制, @EnableAutoConfiguration 会读取 META-INF/spring.factories 文件,决定加载哪些类。

在引入的 SpringBoot Starter 中,在这个文件中引入了 @Configuration 配置类:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.test.MyAutoConfiguration

配置类中的 @Bean 会被自动加载。

MySQL

JOIN 与 IN 如何选择

查询效率

在查询效率方面,连表和IN子句都有各自的优缺点:

  1. 数据量大的情况下:
  • 当涉及大量数据时,JOIN 通常比 IN 更有效率。因为数据库优化器可以更好地对 JOIN 进行优化,执行计划更稳定。
  • IN 子查询在处理大数据量时可能会导致性能下降,特别是当子查询返回大量结果集时。
  • 存在索引的情况:
    • 如果表上有合适的索引,JOIN 通常会更快。因为数据库可以利用索引直接定位到需要的数据。
    • IN 子查询也可以利用索引,但是在某些情况下可能不如 JOIN 高效。
  • 子查询返回结果集较小时:
    • 当子查询返回的结果集非常小且能够被高效地缓存时,IN 子查询可能会与 JOIN 一样高效甚至更高效。

    总的来说,如果你拥有合适的索引并且在大数据量的情况下,倾向于使用 JOIN 而不是 IN 子查询。然而,在特定情况下,IN 子查询也可能会更有效率。最佳做法是根据具体情况对两种方法进行测试和比较,以确定哪种方法在特定场景下更高效。

    可读性

    在可读性方面,连表通常会比IN子句更易于理解和维护。

    因为连表可以将两个表中的数据进行关联,从而在查询结果中展现出来,而IN子句则需要使用列表或子查询来进行匹配,不易于直观地理解和维护。

    灵活性

    在灵活性方面,连表通常会比IN子句更具有灵活性。

    因为连表可以根据不同的匹配条件进行查询,可以实现比较复杂的查询逻辑,而IN子句则只能进行简单的列表或子查询匹配,不太适用于复杂的查询场景。

    MySQL 什么情况下需要调优

    1. 数据库出现性能瓶颈时。如果数据库遇到了读取和写入数据时的复杂操作或者面临大量并发访问时,可能会导致性能问题,需要通过优化来解决。
    2. 数据库应用程序需要处理大量数据。如果应用程序需要处理大量的数据,需要使用索引来加快查询速度。
    3. 查询请求的响应时间很长。如果查询将花费很长时间来执行操作,可能需要考虑数据库优化,以降低响应时间。
    4. 数据库访问负载增加。如果数据库访问负载增加,可能会影响服务器的响应能力和性能表现,因此需要优化。

    MySQL 调优

    说到MySQL调优,这个问题范围比较宽泛,既可以包含硬件和系统调优,比如使用SSD硬盘,提高I/O性能,使用RAID配置,以提高磁盘读写性能等,又可以包含安全方面的调优:制定备份和恢复策略,复制和高可用性等。但是我们平时在面试的时候基本都是在问如何提高sql的查询速度,接下来我们就从这个方向梳理一下思路。

    执行计划

    我们通常会使用 EXPLAIN 命令来获取SQL的执行计划信息。通过执行计划,我们可以了解到数据表的查询顺序、数据查询操作的操作类型、哪些索引可以被命中、哪些索引实际会命中、每个数据表有多少行记录被查询等信息,以便我们更好的调优。

    当我们执行 EXPLAIN 命令之后,我们主要关注type、key和 Extra 等几项信息:

    type 被称为连接类型(join type)或者访问类型(access type),它显示了 MySQL 如何访问表中的数据。访问类型会直接影响到查询语句的性能,性能从好到差依次为(特别重要,被问多次):

    • system:表中只有一行数据(系统表),这是 const 类型的特殊情况;
    • const:最多返回一条匹配的数据,在查询的最开始读取;常用于使用主键或唯一索引的所有字段作为查询条件。
    • eq_ref:当连表查询时,对于前面的每一行,从该表中读取一行数据;是除了 system 与 const 之外最好的 join 方式。
    • ref:对于前面的每一行,从该表中读取匹配索引值的所有数据行;
    • fulltext:通过 FULLTEXT 索引查找数据;
    • ref_or_null:与 ref 类似,额外加上 NULL 值查找;
    • index_merge:当查询条件使用了多个索引时,表示开启了 Index Merge 优化,此时执行计划中的 key 列列出了使用到的索引。
    • unique_subquery:替代以下情况时的 eq_ref:value IN (SELECT primary_key FROM single_table WHERE some_expr);
    • index_subquery:与 unique_subquery 类似,用于子查询中的非唯一索引:value IN (SELECT key_column FROM single_table WHERE some_expr);
    • range:使用索引查找范围值;
    • index:与 ALL 类型相同,只不过扫描的是索引;
    • ALL:全表扫描,通常表示存在性能问题。

    key 列表示 MySQL 实际使用到的索引。如果为 NULL,则表示未用到索引。

    Extra包含了 MySQL 解析查询的额外信息,通过这些信息,可以更准确的理解 MySQL 到底是如何执行查询的。常见的值如下:

    • Using filesort:在排序时使用了外部的索引排序,没有用到表内索引进行排序。
    • Using temporary:MySQL 需要创建临时表来存储查询的结果,常见于 ORDER BY 和 GROUP BY。
    • Using index:表明查询使用了覆盖索引,不用回表,查询效率非常高。
    • Using index condition:表示查询优化器选择使用了索引条件下推这个特性。
    • Using where:表明查询使用了 WHERE 子句进行条件过滤。一般在没有使用到索引的时候会出现。
    • Using join buffer (Block Nested Loop):连表查询的方式,表示当被驱动表的没有使用索引的时候,MySQL 会先将驱动表读出来放到 join buffer 中,再遍历被驱动表与驱动表进行查询。

    最后我们再说一下根据执行计划的结果进行SQL优化和索引优化的规则。

    SQL优化

    • 应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引;
    • 应尽量避免在 where 子句中对字段进行 null 值判断, 使用!=或<>;减少or来连接条件,使用union all代替;in 和 not in 也要慎用;减少表达式操作和函数操作;否则将导致引擎放弃使用索引而进行全表扫描。
    • 任何地方都不要使用 select * from t ,用具体的字段列表代替"*",不要返回用不到的任何字段。
    • 一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
    • 很多时候用 exists 代替 in 是一个好的选择。
    • 尽量减少多表联合查询,可以在表中冗余所需的字段。

    索引优化

    此类优化可以参考下文创建索引的规则来进行索引优化。

    Hash 索引和B+树索引的区别

    • 哈希索引适合等值查询,但是无法进行范围查询 ;
    • 哈希索引没法利用索引完成排序;
    • 哈希索引不支持多列联合索引的最左匹配规则;
    • 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题;

    创建索引规则

    • 限制每张表上的索引数量,建议单张表索引不超过 5 个;
    • 被频繁查询的字段建立索引;
    • 经常用于 WHERE、ORDER BY、GROUP BY、DISTINCT 后的的字段建议建立索引;
    • 上条中的字段列不要都建立索引,建议建立联合索引;
    • 多表 join 的关联列建议建立索引;
    • 禁止使用全文索引;
    • 被频繁更新的字段应该慎重建立索引;
    • 字符串类型的字段使用前缀索引代替普通索引;
    • 删除长期未使用的索引;

    Redis

    缓存穿透

    缓存穿透说简单点就是大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中 。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

    有哪些解决办法?

    最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。

    (1)缓存无效 key。

    如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,这种方式可以解决请求的 key 变化不频繁的情况。

    如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。

    (2)布隆过滤器

    把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。

    但是,需要注意的是布隆过滤器可能会存在误判的情况。布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。如果想减少误判率,可以适当增加 hash 函数。

    缓存击穿

    缓存击穿中,请求的 key 对应的是热点数据 ,该数据存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。这就可能会导致瞬时大量的请求直接打到了数据库上,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

    有哪些解决办法?

    • 设置热点数据永不过期或者过期时间比较长。
    • 针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
    • 请求数据库写数据到缓存之前,先获取互斥锁,保证只有一个请求会落到数据库上,减少数据库的压力。

    缓存雪崩

    缓存在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。另外,缓存服务宕机也会导致缓存雪崩现象,导致所有的请求都落到了数据库上。

    有哪些解决办法?

    针对 Redis 服务不可用的情况:

    • 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
    • 限流:避免同时处理大量的请求。

    针对热点缓存失效的情况:

    • 设置不同的失效时间比如随机设置缓存的失效时间。
    • 缓存永不失效(不太推荐,实用性太差)。
    • 设置二级缓存。

    Redis 宕机了怎么办?

    当听到这个面试题的时候我最先想到的是Redis的持久化,可以采用RDB或者AOF来实现,以及两种方式的备份策略和机器重启之后的数据恢复机制。

    当我回头将面试题抛给我的小伙伴时,他们纷纷表示面试官是在问单体架构的不完整性,我听完之后才恍然大悟。那么我们今天就简单介绍下Redis的哨兵机制和集群吧。

    哨兵机制

    Redis的哨兵机制是一种高可用性解决方案,由一个或多个Sentinel 实例组成的 Sentinel 系统可以监视任意多个主服务器以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。简单的说,哨兵就是带有自动故障转移功能的主从架构。

    其主要功能包括:

    1. 集群监控:负责监控主从服务器进程是否正常工作。
    2. 故障转移:如果master宕机,会自动从slave中选举出新的master,进行主从自动切换。
    3. 配置中心:如果发生了故障转移,sentinel负责通知客户端新的master地址。
    4. 消息通知:如果某个redis节点有故障,sentinel会发送报警消息给系统管理员。

    Redis集群

    Redis3.0 加入了 Redis 的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的 master 节点上面,从而解决了海量数据的存储问题。

    Redis集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一Redis 实例一样,不需要任何代理中间件,当客户端操作的 key 没有分配到该 node 上时,Redis 会返回转向指令,指向正确的 node。

    Redis 也内置了高可用机制,支持N个 master 节点,每个 master 节点都可以挂载多个 slave 节点,当 master 节点挂掉时,集群会提升它的某个 slave 节点作为新的 master 节点。

    Redis集群可以看成多个主从架构组合起来的,每一个主从架构可以看成一个节点(其中,只有 master 节点具有处理请求的能力,slave 节点主要是用于节点的高可用)

    Redis在项目中的使用场景

    • 使用 String 结构来存储 token、验证码等信息;
    • 使用 List 结构来查询最新的文章或者最新的动态;
    • 使用 Hash 结构来存储用户信息、商品信息、文章信息、购物车信息;
    • 使用 Set 结构根据差集可以做好友推荐、音乐推荐,根据交集可以做共同关注、共同好友,还可以利用随机函数来做抽奖系统;
    • 使用 ZSet 结构来开发直播间送礼物的排行榜、朋友圈的微信步数排行榜等;
    • 使用 Bitmap 结构来统计用户签到情况、还可以用来做数十亿数据的去重;
    • 使用 HyperLogLog 结构可以用来做热门网站每日访问 ip 数统计、热门帖子 uv 统计;
    • 使用 GEO 可以轻松实现两个位置距离的计算,获取指定位置附近的元素等功能;
    • 通过监听 redis 的过期 key 来实现订单自动关闭的功能;
    • 可以使用 Redis Cluster 基于 Redis 的 incr 命令实现分布式id;
    • 使用 Redis+Lua 脚本实现分布式锁;

    SpringCloud

    服务熔断

    在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。

    熔断器的原理很简单,如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费 CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

    Hystrix 断路器机制

    当 Hystrix Command 请求后端服务失败数量超过一定比例(默认 50%),断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务,断路器保持在开路状态段时间后(默认 5 秒),自动切换到半开路态(HALF-OPEN),这时会判断下一次请求的返回情况如果请求成功,断路器切回闭路状态(CLOSED),否则重新切换到开路状态(OPEN)。

    Eureka 和 Nacos 的对比


    Eureka Nacos
    开源社区 Netflix 阿里巴巴
    注册中心 是 是
    CAP AP AP + CP
    配置中心 否 是,支持动态刷新
    服务发现 定时拉取 定时拉取、订阅推送
    实例类型 临时实例 永久实例、临时实例(默认,由参数决定 ephemeral=true)
    健康检测 心跳模式 临时实例采用心跳检测模式,永久实例采用主动请求检测模式
    保护方式 如果15分钟之内,心跳发送失败的比例低于85%,就会触发Eureka服务端的自我保护机制。在该机制下,Eureka Server 不会剔除任何的微服务,等到正常后,再退出自我保护机制。 当域名健康实例占总服务实例的比例小于阈值时,无论实例是否健康,都会将这个实例返回给客户端。客户端可能访问到不健康的实例,请求失败,但不至于造成雪崩。牺牲了⼀些请求,保证了整个系统的可⽤。
    高可用机制 多副本之间的复制采用对等复制(peer to peer模式),每个副本都可以处理读写请求,通过彼此之间的数据复制使数据进行同步更新。但是数据同步可能会出现冲突。Eureka对此的解决方案是校验 lastDirtyTimestamp。 内部提供两种数据同步方案AP和CP,而且是混用的。实例是临时的默认用AP,AP使用 Distro 协议,采用对等复制模式;实例是永久的用CP,CP使用 Raft 协议作为默认的一致性协议,采用 Leader-Follower 同步方式保证数据一致性。

    回复【面试笔记】获取PDF版本!

    参考链接:

    • https://zhuanlan.zhihu.com/p/449929098
    • https://javaguide.cn/system-design/framework/spring/spring-transaction.html

    点个在看你最好看

    原文作者:阿Q说代码;如有侵权,请联系管理员删除。

    请登录后发表评论 登录
    全部评论

    注册时间:2019-01-02

    • 博文量
      95
    • 访问量
      40824