7.登虹公司(巨大收获)

1.异步化中为什么使用消息队列而不使用异步线程

  • 解耦与松耦合:
    • 消息队列:使服务提供者与服务消费者互不依赖,降低了系统的耦合性,并提高了系统的灵活性和可维护性,消费者出现问题不影响服务者生产消息。
    • 异步线程:也能实现异步处理,但是在代码层次需要显式处理线程间的同步、通信和错误处理,这回导致系统各部分之间有较强的依赖性。一个部分出现问题可能导致其他部分或者整个系统出现问题。
  • 横向扩展与分布式处理:
    • 消息队列:支持分布式环境,可在多台服务器形成集群,实现负载均衡和容错。跨网连接不同系统或服务,实现跨地域、跨组织的通信。
    • 异步线程:可在单台 服务器上实现并发处理,进行大规模的横向扩展和分布式处理,需要自行负责复杂的线程分配,状态同步和故障转移
  • 流量控制与缓冲:
    • 消息队列:消息队列本身提供了天然的流量控制功能。当生产者发送消息的速度超过消费者处理速度时未处理的消息会在队列中积累,起到临时缓冲的作用,避免消费者过载。同时,可以通过调整队列容量消费速率等参数来精细控制系统的吞吐量和响应时间
    • 异步线程:可以通过线程池等方式对异步线程进行一定的管理和调度,但线程池大小、线程调度策略等都需要手动配置和调整,且对瞬时大流量的应对能力相对较弱。当大量任务涌入时,若线程池已满,可能会直接导致任务被拒绝,或者迫使系统创建过多线程,引发资源竞争和性能下降
  • 可靠性和消息持久化:
    • 消息队列:许多消息队列产品支持消息持久化,确保在服务器故障或重启时消息不会丢失。此外,还提供确认机制重试策略死信队列等功能,确保消息的可靠投递和错误处理
    • 异步线程:单纯使用异步线程处理任务时,任务状态和数据通常仅存在于内存中,一旦进程或服务器中断,未完成的任务可能丢失。要实现类似消息队列的可靠性保障,需要额外编写复杂的状态管理、数据持久化和失败恢复逻辑
  • 异步流程编排与顺序保证:

    消息队列:某些高级消息队列支持消息的顺序保证、事务处理、 Saga 模式等特性,能够方便地实现复杂的异步流程编排和数据一致性要求

    异步线程:涉及多步骤、多系统协作的复杂异步流程,使用异步线程进行编排往往需要编写大量协调逻辑

 

2.线程池如何配置的,参数应该按照怎样的规格设置

线程池的重要参数有

  1. 核心线程(没有任务执行时也不会被销毁,这些线程会一直存活以处理新提交的任务,避免创建和销毁线程带来的开销)、
  2. 最大线程(根据配置的保活时间存活)、
  3. 保存时间
  4. 保存时间单位
  5. 工作对象
  6. 线程工厂(创建新线程的工程,设置线程的名称,优先级)
  7. 拒绝策略(丢弃任务抛拒绝执行异常、抛弃任务,丢弃队列最早的任务,调用提交任务的线程处理任务)

配置时考虑一下原则

  1. 任务特性:CPU密集型(CPU数+1)、IO密集型(CPU数*2)、任务执行时间、任务之间的依赖关系
  2. 系统资源:CPU核心数,内存大小,系统并发
  3. 响应速度要求:实时性要求高,可能需要增加线程数以减少任务等待时间
  4. 资源选择:无界队列可能导致资源耗尽,有界队列可以控制任务堆积,优先级队列可以优先处理重要任务

 

3.token泄漏有什么兜底方法,长短token

JWT是无状态的

 

4.使用分库分表,如何进行批量查询

   在数据库使用分库分表后进行批量查询时,由于数据分布在多个物理数据库或表中常规的单表或单库查询方法不再适用。要实现批量查询,需要采取一些特殊的策略和技术来处理分布式数据环境下的查询需求

  • 应用层合并查询
    • 方法描述:将相同的查询请求发送到每个相关的分库或分表各个数据库返回各自的查询结果;在应用层讲这些独立的结果集合并,进一步排序或分页
    • 适用场景:查询条件不涉及复杂的跨分片(分库分表)联接或聚合操作;数据量适中,应用层能够承受合并结果集的开销
    • 注意事项:确保应用层有足够的内存和计算能力处理可能得大规模结果集;处理分页注意合并后的数据顺序和边界问题
  • 使用中间件或代理层
    • 引入专门的数据库中间件或代理服务,如ShardingSphere、MyCat、Vitess等。中间件接收客户端的查询请求,根据分片规则透明地将查询分发到相应的分库分表。中间件负责收集各个分片的查询结果,并在内部进行必要的结果集合并和处理(如排序、分页)后返回给客户端
    • 使用场景:简化应用层逻辑,应用层负担;支持复杂分布式查询,如跨分片的join、group by,distinct
    • 注意事项:增加了系统复杂性,需要额外的运维和故障排查;中间件的性能和稳定性对于查询至关重要
  • 并行查询与结果集合并
    • 方法描述:利用多线程或异步编程模型,同时向多个分库分表发起查询请求;各个查询任务完成后,将结果集收集到一起进行合并和处理
    • 适用场景:查询条件简单,数据分布广泛,通过并行化提高查询效率
    • 注意事项:并行查询肯呢个导致瞬间数据库连接数剧增,需保证数据库连接池容量;结果集合需要注意线程安全和数据同步问题
  • 使用分布式查询引擎或搜索引擎
    • 方法描述:在分库分表上部署一个分布式查询引擎(Dremio)或全文搜索引擎(RS,Solar)
    • 适用场景:需要进行复杂数据分析或全文检索;查询性能要求高,额外基础设施
    • 注意事项:需要维护与底层数据的同步,可能存在一定数据延迟;学习和掌握新的查询语句和API

 

5.垂直分表、水平分表分别是什么,如何使用,数据隔离是什么

   垂直分表水平分表数据库设计中常见的数据拆分策略,用于应对数据增长性能瓶颈以及管理复杂度问题。两种策略是以不同的维度对数据进行分割,以适用不同的应用场景

(1)垂直分表

定义:将一个表中包含的字段按照某种逻辑进行拆分形成多个表,这些表共享相同的主键,但包含不同的列集合拆分依据是字段的使用频率数据类型访问模式或业务相关性

使用方法

  1. 字段使用频率:将不常用或者大字段(如文本、图片、文件等Blob类型)拆分到单独的表中,减少常用查询时不必要的I/O和内存占用。
  2. 按数据类型:将不同类型的数据(如数值、字符串、日期等)或属性(如基础信息、扩展信息、元数据等)拆分到不同的表中,便于管理和优化
  3. 按访问模式:经常一起访问的字段放在同一表中,将访问频率较低或关联性弱的字段拆分出去提高查询效率
  4. 按业务逻辑:根据业务模块或功能划分,将相关性强的字段归入同一表,使数据结构更符合业务流程。

(2)水平分表

定义:将一个表中的数据行按照某种规则切分到多个表中,这些表具有相同的结构(即包含相同的字段)。切分的依据可以是数据的范围、哈希值、特定字段的值等。

使用方法

  1. 按范围分:根据某个连续字段(如时间、编号)的区间将数据分配到不同的表中,如按年份或月份分表
  2. 按哈希分:使用特定字段(如用户ID)的哈希值对表数量取模,将数据均匀分散到多个表中,实现负载均衡。(自己项目的做法)
  3. 按特定字段分:根据业务逻辑,按照特定字段(如地区、用户类型)的值直接划分数据

(3)数据隔离

定义:在数据库操作中,通过各种机制确保不同事务之间在并发执行时互不影响保证数据的一致性和完整性。它主要关注的是在多用户环境下,如何避免因并发操作引发的数据不一致问题

 

6.消息队列的作用

  1. 流量 削峰
  2. 异步化
  3. 服务解耦
  4. 数据一致性(分布式事务)

 

7.Redis中的大key是什么,如何解决

   Redis中的大key指的是某个键对应的值(value)占用内存较大,从而可能对Redis的性能内存使用操作效率产生负面影响的数据结构。

大key符合以下特征

  • String类型:值大小不超过10KB;
  • 集合类型:(Hash、List、SET、Zset)

大key存在的问题

  1. 客户超时阻塞Redis单线程执行命令执行大key所需时间长导致客户端请求长时间不响应
  2. 内存分布不均:集群环境中,含有大key的节点可能占用过多内存,导致数据和查询负载不均衡。
  3. 影响备份和持久化:RDB或AOF备份时,大key可能导致备份文件增大,备份过程慢,影响Redis的正常写入。
  4. 网络传输效率:复制、迁移、Lua脚本操作场景中,大key值在网络传输时会占用较多的带宽和时间。

Redis大key解决策略

  1. 数据结构调整数据切分;使用更合适的数据结构(如果一个String存储的固定格式数据,可考虑使用节省空间的编码方式ProtocolsBuffers,或改用ZIPList特殊编码优化的集合类型);合理设置集合元素的过期时间
  2. 值压缩:如果value为文本或JSON可考虑使用压缩算法
  3. 控制集合元素数量主动限制集合元素大小:对List、SET、ZSet等几何类型的key,设定一个最大元素数量阈值,达到该阈值,执行清理策略(移除最老元素,按某种策略淘汰元素);定期清理
  4. 调整客户端操作策略避免一次性取出全部数据分批处理范围查询;对于原子性处理大key的复杂操作,使用Lua脚本减少网络往返和潜在的并发
  5. 监控预警使用Redis的OBJECT命令定期扫描并报告大key;设置Redis实例的内存使用上限报警,内存接近阈值报警,提示大key清理。

 

8.类加载机制阶段

    类加载----》链接(验证、准备、解析)----》初始化

 

9.动态代理与静态代理的区别

(1)代理类的创建时期

静态代理:

  • 静态代理的代理类在编译时就确定,即代理类的源代码由程序员编写或通过特定工具生成并编译成.class文件
  • 编译后代理类是一个独立的类实体,其结构、方法和实现都是固定的

动态代理:

  • 动态代理的代理类是在运行时动态生成的,通常通过反射或其他语言提供的动态代理API如(CGlib的MethodInterceptor和Enhancer或JDK的newProxyInstance和InvocationHandler)创建
  • 运行时根据需要(如接口信息、委托信息)动态生成代理类的字节码,并加载到JVM中成为可执行的类

(2)灵活性和通用性

静态代理:

  • 静态代理针对每个被代理的对象(或一组具有相同接口的对象)需要创建一个对应的代理类,这意味着为不同的接口或类服务需要编写多个代理类
  • 固定的代理类结构限制了其灵活性,如果接口方法有所变化,代理类可能也需要相应修改

动态代理:

  • 动态代理可以根据需要为任意实现了特定接口的对象生成代理,无需为每个被代理的对象预先编写代理类。
  • 动态代理可以集中处理接口中所有方法的调用通过一个统一的回调方法(如Java中的InvocationHandler.invoke()方法)进行转发和额外操作,因此在处理大量接口方法时更为灵活,只需修改回调方法的逻辑即可适应接口方法的变化

(3)使用场景

静态代理:

  • 适用于代理类结构稳定、接口方法较少且变化不频繁的情况。
  • 适用于需要长期稳定服务、代理逻辑固定且易于编译时确定的场景,如网站服务器管理、账户维护等。

动态代理:

  • 适用于代理逻辑需要动态调整接口方法数量较多需要频繁为不同接口生成代理的场景。
  • 适用于网络数据采集任务调度AOP(面向切面编程)RPC框架中的远程代理拦截器等需要高度灵活性和可扩展性的场景。

(4)代码复用性

静态代理:

  • 代理类与委托类之间的关系在编译时就已明确,代理类的复用性受限于其编写的特定场景和目的

动态代理:

  • 由于动态代理的生成逻辑是通用的,可以根据需要为任何符合要求的对象生成代理,因此代码复用性更强,尤其在处理大量相似代理需求时,可以减少重复编码工作

 

10.策略模式参数如何匹配,模板模式使用

 (1)策略模式:

  在策略模式中,通常有一个抽象策略(Strategy)接口或抽象类,它定义了一组公共方法,这些方法代表了某种算法或行为具体策略(Concrete Strategies)是实现了这个抽象策略的类,每个具体策略对应一个具体的算法实现。

参数匹配体现在

  • 策略接口参数:客户端代码通常不会直接创建具体策略对象,而是通过传递一个具体策略类的实例作为参数给需要使用策略的上下文(Context)对象。这样,上下文对象可以通过接受策略对象作为构造函数参数或setter方法参数,来灵活地切换或配置所使用的策略。

  • 策略方法参数:具体策略类中的方法可能会接收参数,这些参数通常与待执行算法所需的输入数据相对应。例如,如果策略是计算某种费用,那么方法可能接收费用计算所需的具体参数,如交易金额、税率等

使用:

  1. 定义策略接口:声明一个公共接口或抽象类,定义所有支持的算法或行为的公共操作
  2. 实现具体策略:为每一种具体算法创建一个实现策略接口的类
  3. 上下文使用策略:创建一个上下文类,它持有一个策略对象的引用,并通过调用策略对象的方法来执行具体的算法

(2)模板模式

参数匹配

  模板模式中的参数匹配主要发生在模板方法(Template Method)中定义的抽象操作(Primitive Operations)上。这些抽象操作通常是抽象方法或钩子方法(Hook Methods)由具体子类来实现或覆盖。参数匹配发生在这些子类方法的定义中,它们根据需要接收和处理参数

使用:

  1. 定义抽象类:包含一个或多个操作的框架方法(Template Method),这些方法通常调用一些抽象方法或钩子方法
  2. 实现具体子类:继承抽象类并实现其中的抽象方法和/或覆盖钩子方法
  3. 客户端调用:客户端代码创建具体子类的实例并调用模板方法

 

11.深分页

查100万数据中的90万数据

   深分页指在Web应用程序、API接口或者数据库查询中,为了访问大量数据集合中的后部页面(即远离第一页的深层次页面),而进行的分页操作。通常,随着分页深度的增加访问更远的页面会遇到一些特定的挑战和问题

深分页的常见问题

(1)性能问题

  1. 数据库查询效率降低扫描更多数据行才能找到数据,在未优化或使用索引情况下,查询性能显著下降。
  2. 带宽与服务器资源消耗:返回深层页面数据可能涉及传输大量数据,增加网络贷款以及服务器资源消耗。

(2)一致性问题

实时性难以保证:数据频繁变更场景下,深分页可能导致用户看到过时或不一致数据

(3)用户体验

  1. 导航困难
  2. 加载延迟

深分页优化策略

(1)性能优化

  1. 使用高效索引(使用索引)
  2. 懒加载或延迟加载(需要时加载)
  3. 数据缓存(热点数据缓存,即常用数据)
  4. 使用分页令牌
  5. 提供搜索、过滤和排序功能

 

12.本地缓存和分布式缓存的作用以及刷新机制,如何保证数据一致性

(1)本地缓存

  1. 本地缓存主要用于抵抗高并发流量不会发生远程IO操作,大部分请求会命中本地单机缓存
  2. 本地缓存采用主动刷新被动刷新,主动刷新是根据接口传入的版本号比较,被动刷新是数据过期从分布式缓存中获取同一时刻只有一个线程从分布式缓存中更新数据到本地缓存防止缓存击穿
  3. 初始容量为15,并发度为5,过期时间5秒。

(2)分布式缓存

  1. 分布式缓存主要用于协调和同步最近数据到本地缓存只有本地缓存失效才访问分布式缓存将分布式缓存更新到本地缓存中,且遵循同一时刻只有一个线程将数据更新到本地缓存,防止其他线程原地阻塞造成服务器和线程切换资源的浪费
  2. 分布式缓存采用主动刷新被动刷新主动刷新业务数据变更刷新主动刷新被动刷新数据过期去数据库中获取数据,同一时刻只允许一个线程从数据库中数据到分布式缓存,避免多个线程更新本地缓存。
  3. 由于多个线程会去抢占锁,因此一种重要机制当其他线程没有获取更新缓存的锁时立即返回避免其他线程一直原地阻塞等待,进而造成服务器和线程切换的资源浪费。

(3)数据一致性

  1. 先读本地缓存数据,存在(版本号为空或小于缓存版本号)则直接返回数据,不存在去查分布式缓存;
  2. 如果分布式缓存存在数据,则获取本地锁(同一时间只允许一个线程获取锁,其它未获取线程的锁直接返回,避免原地阻塞造成服务器和线程切换的资源浪费)将数据更新到本地缓存中,再返回数据;
  3. 若分布式缓存不存在数据,则获取分布式锁,如果未获取成功则重试,获取成功则保证只有一个线程从数据库中更新数据到分布式缓存,保存数据再分布式缓存中,然后返回数据。

(4)注意:刷新分布式缓存时,并不会同时刷新本地缓存数据本地缓存数据采用自身的缓存刷新机制

 

13.缓存设计原则

先读缓存,再读数据库;先写数据库再写缓存

posted @ 2024-04-23 16:55  求知律己  阅读(10)  评论(0编辑  收藏  举报