学而思索面经分享
学而思索面经分享
面试整理(3 年工作经验)
第一家 跨境电商 自研
1.做一个自我介绍
2.上一个公司为什么离职?
3.场景题,跨境电商,一台 jvm,一个进程,单体应用,只有一台 mysql 数据库,我像做
一个秒杀活动,秒杀的商品 1000 份,
要求:1.不能加锁 2.不能把所有请求打到 mysql 上面去,用户 10 多 w,QPS 有 1w 个,怎
么解决超卖问题,不能明面上加锁 (不知道什么好答案能让面试官满意)
1.预先加载商品库存到内存中:在秒杀活动开始之前,将商品的库存数量提前加载到内存中,以
减少对数据库的频繁查询。可以使用缓存技术(如 Redis)将商品库存信息存储在内存中,并在
更新库存时保持一致性。
2.使用乐观锁机制:在数据库中的商品表中增加一个版本号字段(version),每次更新库存时
需要同时比较版本号。当发起秒杀请求时,先读取当前版本号,然后执行更新操作时,再次校验
版本号是否一致,如果一致才进行库存更新。
3.使用队列进行异步处理:将秒杀请求放入消息队列中进行异步处理,避免直接打到数据库上
去。消费者从队列中获取请求,处理库存更新操作,并返回秒杀结果给用户。
4.实现限流控制:通过在应用层面设置限流策略,控制用户请求的并发量,防止过多的请求同时
访问系统,导致系统负载过高。可以使用令牌桶算法或漏桶算法来进行限流控制。
5.支付的时候,让前端做一个答题,滑动验证,减少并发的数量级。
4.滑动窗口的限流算法,桶(漏桶限流算法),令牌桶算法的区别
1.滑动窗口的限流算法:
滑动窗口算法通过维护一个固定大小的时间窗口,在这个时间窗口内对请求进行计数。只有在时
间窗口内请求的计数未超过设定的阈值时,请求才会被允许通过,否则请求将被拒绝。
该算法简单易懂,适用于对请求进行简单的时间窗口内的限流控制。
2.桶(漏桶)限流算法:
桶(漏桶)限流算法是一种固定容量的桶,按照固定速率出水。对于请求来说,如果桶中有空
间,则请求被加入到桶中;如果桶已满,则请求被拒绝。
该算法可以平滑处理突发请求,控制请求的发送速率,适合对请求进行速率限制。
漏桶算法有一个缺点:当系统在短时间内有大流量进来,漏桶算法处理不了。
3.令牌桶算法:
令牌桶算法是另一种固定容量的桶,但与桶(漏桶)限流算法不同,令牌桶算法以固定的速率往
桶中添加令牌。每当有一个请求到达时,需要消耗一个令牌,只有在有足够令牌时,请求才会被
允许通过。
该算法可以精确控制请求的发送速率,并且可以应对突发流量。
令牌桶算法的几种情况:
请求速度大于令牌的生成速度:那么桶中的令牌会被取完,后续再进来的请求,由于拿不到令牌
会被限流。
请求速度等于令牌的生成速度:那么此时系统处于平稳状态。
请求速度小于令牌的生成速度:那么此时系统的访问量远远低于系统的并发能力,请求可以被正
常处理。
令牌桶算法,由于只有一个桶的存在,可以处理短时间大流量的场景,这是令牌桶和漏桶的一个
区别。
5.了解 ThreadLocal 不
ThreadLocal 是 Java 中的一个类,它提供了线程局部变量的功能。简单来说,
ThreadLocal 可以让每个线程都拥有自己独立的变量副本,互不干扰。这在多线程并发编
程中非常有用,特别是在需要在每个线程中保持一些上下文信息或状态时。
6.ThreadLocal 的原理
底层是一个 Map,key 是 ThreadLocal 的线程 id,value 是对应线程的局部变量值
7.spring 里面的@Transaction 在什么情况下会失效?
1.未被 Spring 托管的类:如果一个类没有被 Spring 托管(即没有被 Spring 管理),那么在
该类中使用 @Transactional 注解将不会生效。因为 Spring 无法拦截和代理未被 Spring 管理
的类中的方法。
2.异常捕获:如果在事务方法内部捕获了异常并处理了而没有抛出,那么事务可能不会回滚。通
常情况下,Spring 的事务管理是基于抛出异常来触发事务回滚的,如果异常被捕获并处理了,
事务可能不会回滚。
3.在同一个类中调用另一个带有 @Transactional 注解的方法:当在同一个类中的一个带有
@Transactional 注解的方法调用另一个带有 @Transactional 注解的方法时,事务可能会失
效。这是因为 Spring 默认使用基于代理的 AOP 来实现事务管理,对于同一个类中的方法调用
并不会经过代理,导致事务注解不生效。
4.线程问题:如果在一个未被 Spring 托管的线程中调用带有 @Transactional 注解的方法,事
务也可能失效。因为 Spring 事务管理是基于当前线程的事务上下文进行管理的,如果线程不受
Spring 管理,事务可能无法正确传播。
5.事务传播机制设置错误:如果在使用 @Transactional 注解时设置了错误的事务传播机制,可
能会导致事务失效。比如某个方法使用了 REQUIRED 的传播机制,但实际情况下应该使用
REQUIRES_NEW 才能正确管理事务
8.Spring 中的动态代理是什么?
在 Spring 中,动态代理是一种 AOP(面向切面编程)的实现方式,它允许在运行时为目标对象
生成代理对象,从而实现对目标对象方法的拦截和增强。动态代理通常用于实现横切关注点
(cross-cutting concerns),比如事务管理、日志记录、性能监控等功能。
在 Spring 中,动态代理主要有两种实现方式:
基于 JDK 动态代理:JDK 动态代理是 Java 提供的一种动态代理机制,它要求被代理的类必须实
现一个接口。Spring 使用 JDK 动态代理来生成代理对象,当业务类实现了接口时,Spring 就
可以通过 JDK 动态代理生成实现这些接口的代理对象。在代理对象的方法调用前后,可以插入相
应的逻辑进行增强处理。
基于 CGLIB 动态代理:CGLIB 是一个强大的、高性能的代码生成库,它可以在运行时动态生
成指定类的子类,并重写其中的方法实现代理功能。相比于 JDK 动态代理,CGLIB 可以代理没
有实现接口的类,而且通常情况下 CGLIB 动态代理生成的代理对象效率更高。
Spring 在使用动态代理时会根据被代理对象是否实现了接口来选择使用 JDK 动态代理还是
CGLIB 动态代理。如果目标对象实现了接口,Spring 会使用 JDK 动态代理;如果目标对象没
有实现接口,Spring 会使用 CGLIB 动态代理。
通过动态代理,Spring 能够在不修改原有代码的情况下,通过增加针对特定方法的拦截器
(Interceptor)等手段,实现诸如日志记录、性能监控、事务管理等横切关注点的功能,从而
提高了系统的可维护性和扩展性。
9.poxy 和 cglib 的区别是什么?
CGLIB 动态代理和 JDK 动态代理(也称为 Proxy 动态代理)是 Spring AOP 中两种常用的代
理实现方式,它们之间有一些区别:
基于类和接口:
JDK 动态代理只能代理实现了接口的类,它通过反射机制来创建一个接口的实现类作为代理。
CGLIB 动态代理则可以代理没有实现接口的类,它通过生成目标类的子类来实现代理。
性能:
JDK 动态代理在创建代理对象时性能相对较低,因为它需要通过反射调用来实现代理。
CGLIB 动态代理在创建代理对象时性能较高,因为它是通过生成字节码的方式来实现代理,避
免了反射调用。
代码侵入性:
JDK 动态代理要求目标对象必须实现接口,这就要求目标类必须修改以实现接口,可能会导致代
码侵入性增加。
CGLIB 动态代理不要求目标对象实现接口,可以直接代理目标类,减少了代码侵入性。
性能优化:
JDK 动态代理由于使用反射,无法对 final 方法进行代理,而 CGLIB 可以代理 final 方法。
JDK 动态代理在调用被代理方法时,会经过一层方法调用,而 CGLIB 则直接调用目标对象的方
法。
总体而言,JDK 动态代理适合代理接口的情况,对于没有实现接口的类,则需要使用 CGLIB 动
态代理。在性能要求较高或需要代理 final 方法的情况下,CGLIB 动态代理更为合适。在选择代
理方式时,可以根据具体需求和场景来决定使用 JDK 动态代理还是 CGLIB 动态代理。
宝,国外平台的特有标识),如何保证在不更改太多的代码,达到实现效果?(不确定是
面试官想要的答案不)
1.我的思路是通过面向接口编程,调用支付后,先经过某个接口(公共支付),
找出传参属于哪一种支付类型,再到对应得实现类里面去处理对应的逻辑。
2.也可以使用到工厂设计模式,支付方式定义一个工厂类,
其它各个渠道得,去实现这个工厂类,让它进行处理对应的逻辑。
11.SpringBoot 的全局异常处理做过没得?
可以自己去定义和创建对应的异常处理类。给异常统一抛出一个返回类
聚合 payment-spring-boot-starter
下单的时候,传入对应的回调地址,支付成功后,会找到对应的接口地址进行处理逻辑
14.使用过 springSecurity 或 jwt 没得,研究过 springSecurity 框架没得?
下面是关于 Spring Security 的一些常见概念和用法:
身份验证(Authentication):Spring Security 提供了多种身份验证方式,包括基于表单、
基于 HTTP 基本认证、基于 LDAP 等。你可以选择适合你的应用程序的身份验证方式,并配置相
应的认证提供者。
授权(Authorization):Spring Security 支持基于角色或权限的访问控制。你可以使用注
解、表达式或配置文件来定义访问规则,并确保只有具有适当权限的用户能够访问受保护的资
源。
Session 管理:Spring Security 提供了对会话管理的支持,可以管理用户的认证状态以及处理
并发登录和注销等场景。
记住我(Remember Me):Spring Security 允许用户在登录后选择“记住我”选项,以便在下
次访问时自动登录。
CSRF 保护:Spring Security 默认为应用程序提供了 CSRF(跨站请求伪造)保护,以防止
恶意网站利用用户的身份进行攻击。
集成其他认证机制:Spring Security 可以与其他认证机制(如 OAuth、LDAP)进行集成,
以满足特定的应用程序需求。
此外,如果你熟悉 JWT(JSON Web Token)的概念,可以结合 Spring Security 来实现基
于 JWT 的身份验证和授权。使用 JWT 可以实现无状态的身份验证,避免服务器端存储用户状态
信息。
总的来说,Spring Security 提供了一套完整且灵活的认证和授权解决方案,可以帮助你构建安
全的应用程序。如果你有具体的问题或需要更深入的讨论,我将很乐意为你提供帮助。
15.AQS 和 CAS 的一些东西。在多线程中,我想全部线程完成后,在进行下一步操作,应
该引入什么组件
在多线程中等待所有线程完成后再进行下一步操作,可以使用 CountDownLatch(倒计时门
闩)组件来实现。
CountDownLatch 是 Java 并发包中的一个工具类,它可以让一个或多个线程等待其他线程的
完成。你可以在主线程中创建一个 CountDownLatch 对象,并设置它的初始计数为线程数量。
每个子线程在完成任务之后,调用 countDown() 方法来减少计数器的值。当所有子线程都调用
了 countDown() 后,计数器变为 0,主线程就会被唤醒继续执行后续操作。
16.lock 锁和 Synchronized 的区别
功能角度看:都是 Java 中用来解决线程安全问题的工具。
特性来看:Synchronized 是 Java 中的同步关键字, lock 是 juc 包中提供的接口 这个接口有
很多实现类,其中包括 ReentrantLock 的重入锁
Synchronized 可以通过两个方式来控制锁的力度:
1.Synchronized 关键字修饰在方法层面
2.Synchronized 修饰在代码块上
并且我们可以通过 Synchronized 加锁对象的生命周期来控制锁的范围
锁对象: (静态对象)or (类对象) 全局锁
(普通实例) 锁的范围取决于这个实例的生命周期
Lock 锁的粒度是通过它提供的 lock()和 unlock()方法来决定的
包裹在这两个方法之间的代码能保证线程的安全性,而锁的作用域,取决于 Lock 实例的生命周
期
灵活性: Lock 锁相对于 synchronized 更加灵活,Lock 能够决定什么时候加锁,什么时候释
放锁,只需要调用 lock(),unlock()两个方法就行了
lock 还提供了非阻塞的竞争锁方法 tryLock(),这个方法通过返回 true/false 来告诉当前线
程是否已经有其它线程正在使用锁。
synchronized 锁是关键字,无法实现非阻塞竞争锁的方法。synchronized 锁的释放是被动
的,相当于 synchronized 同步代码块执行完后,或者代码出现异常的时候才会释放,
Lock 锁提供了公平锁和非公平锁的机制
公平锁:线程竞争锁资源时,如果已经有其它线程正在排队等待锁释放,那么当前竞争锁资源的
线程无法插队。
非公平锁:不管是否有线程在排队等待锁,它都会尝试去竞争一次锁。
synchronized 锁只提供了非公平锁的实现
性能上看:synchronized 和 Lock 锁差别不大
区别:
synchronized 引入了偏向锁,轻量级锁,重量级锁以及锁升级的方式来优化加锁的性能。
Lock 锁中,实际用到了自旋锁的方式来实现性能优化
gpt:
灵活性: Lock 锁相对于 synchronized 更加灵活。使用 Lock 锁时,你可以显式地获取锁和释
放锁,可以在不同的代码块中获取和释放锁,而 synchronized 是隐式地获取和释放锁,只能在
方法或代码块中使用。
可中断性: Lock 锁提供了 tryLock() 方法,可以尝试获取锁,如果获取不到锁,可以根据需求
进行其他处理,而 synchronized 在获取不到锁时,线程会一直阻塞等待,无法中断。
公平性: Lock 锁可以实现公平锁(Fair Lock)的机制,即按照线程请求锁的顺序来获取锁,
而 synchronized 是非公平锁,无法控制线程获取锁的顺序。
性能: 在低竞争的情况下,synchronized 的性能通常比 Lock 锁更好,因为 synchronized 内
置了很多优化机制,而 Lock 锁需要显式地调用方法进行加锁和解锁操作。
可重入性: synchronized 是可重入的,同一个线程可以多次获取同一个锁,而 Lock 锁需要显
式地调用 lock() 方法来获取锁,不支持重入。
综上所述,Lock 锁相对于 synchronized 更加灵活、可中断和可控制锁的公平性,但使用
Lock 锁需要更多的代码来管理锁的获取和释放。在高竞争和需要更高级的线程同步机制时,
Lock 锁的优势更加明显;而在简单的同步场景下,synchronized 是更常用和方便的选择。
第二家 类似电商 (只问了重要问题) 自研
1.问我分布式锁是怎么处理的?
我说的是用 redission ,大量请求进来,只有一个获取锁,其它的线程会进行等待并返回
也可以使用 zookeeper
2.redission 的实现原理
基于 Redis 命令协议: Redisson 通过使用 Redis 提供的命令协议来与 Redis 服务器进行通
信。它使用了 Redis 的数据结构和命令,如哈希表、列表、集合等来实现分布式的 Java 对象、
锁、队列等功能。
分布式对象和服务: Redisson 提供了分布式的 Java 对象(如 Map、Set、List 等)和服务
(如锁、信号量、队列等),它们通过 Redis 的数据结构来实现分布式存储和操作,从而为
Java 应用程序提供了分布式和并发的支持。
实现原语: Redisson 使用了 Redis 提供的一些原语来实现其功能,比如分布式锁基于 Redis
的原子性操作来实现,分布式队列和集合则利用 Redis 的列表和集合数据结构来实现。
事件监听: Redisson 支持对 Redis 数据变化的监听,它可以通过 Redis 的发布/订阅机制来实
现对数据变化的实时监控,并通知相关的 Java 应用程序。
多线程并发控制: 在实现分布式锁、信号量等功能时,Redisson 使用了一些多线程并发控制的
技术,确保在分布式环境下的线程安全和并发控制。
3.redission 锁,锁 10 秒钟得不得行,有什么问题没得?
使用 Redisson 锁来实现一个 10 秒的锁是可行的,但需要注意以下几点:
锁的有效时间:在使用 Redisson 锁时,可以设置锁的有效时间。如果需要一个 10 秒的锁,可
以在获取锁的时候设置锁的有效时间为 10 秒,确保在一段时间后锁会自动释放。
锁的续期:Redisson 在获取锁成功后,会自动进行锁的续期操作,确保在业务执行时间较长时
不会因为锁超时而被释放。可以根据需要调整续期的参数配置,以确保锁在业务执行期间一直有
效。
业务逻辑时间:在设置锁的有效时间时,需要考虑到业务逻辑的执行时间。确保业务逻辑在锁的
有效时间内可以完成,避免锁提前释放导致并发问题。
监控和日志:建议在使用 Redisson 锁时增加监控和日志功能,定时检查锁的情况,及时发现异
常情况并处理。
总的来说,使用 Redisson 锁实现一个 10 秒的锁是没有问题的,只需要合理设置锁的有效时
间、考虑业务执行时间、续期策略等因素,确保锁可以在需要的时间内生效并释放。如果在具体
实现过程中遇到问题,可以进一步检查锁的设置和业务逻辑,以及查看日志来定位问题所在。
4.分布式事物晓得不(存在数据短期不一致的问题) 解决方案?
Himily 框架,seata,RocketMq
5.seata 了解不?
Seata 是一个开源的分布式事务解决方案,旨在解决微服务架构下的分布式事务问题。Seata 提
供了分布式事务的支持,包括全局事务管理、分布式事务协调、分布式事务一致性等功能。
以下是 Seata 的一些主要特点和组件:
全局事务管理器(Global Transaction Manager): Seata 提供了全局事务管理器来管理全
局事务的生命周期,包括事务的发起、提交、回滚等操作。
分支事务(Branch Transaction): 在分布式事务中,每个参与者(如数据库、消息队列等)
都会有一个对应的分支事务,Seata 负责协调各个分支事务的状态,确保全局事务的一致性。
分布式事务协调器(Transaction Coordinator): Seata 使用分布式事务协调器来协调各个
参与者的分支事务,保证全局事务的 ACID 特性。
支持多种存储模式: Seata 支持多种存储模式,包括关系型数据库(如 MySQL)、NoSQL 数
据库(如 Redis)等,用于存储事务相关的元数据和状态信息。
高可用性和容错机制: Seata 提供了高可用性和容错机制,确保在各种异常情况下也能够保证分
布式事务的一致性和可靠性。
兼容多种框架: Seata 可以与各种主流的容器和框架集成,包括 Spring Cloud、Dubbo、
gRPC 等,为不同类型的微服务应用提供分布式事务支持。
总的来说,Seata 是一个功能强大的分布式事务解决方案,可以帮助开发人员在微服务架构下实
现分布式事务的管理和协调,保证全局事务的一致性和可靠性。通过 Seata,开发人员可以更轻
松地处理分布式事务带来的挑战,提升系统的可靠性和稳定性。
seata 的原理晓得不,怎么去做的
Seata 的原理主要围绕着全局事务管理和分布式事务协调展开,下面是 Seata 实现分布式事务
的基本原理:
全局事务发起: 当一个业务操作需要跨多个微服务进行数据更新时,应用程序会向 Seata 的全
局事务管理器发起全局事务,并生成一个全局事务 ID。
分支事务注册: 每个参与全局事务的微服务都会创建一个本地事务,并将本地事务信息注册到
Seata 的分布式事务协调器中,同时与全局事务关联。
全局事务提交/回滚: 在业务操作结束后,应用程序会根据操作结果决定提交或回滚全局事务。
如果需要提交,全局事务管理器会通知各个参与者提交本地事务;如果需要回滚,将通知各个参
与者回滚本地事务。
分布式事务协调: Seata 的分布式事务协调器负责协调各个参与者的本地事务状态,确保所有分
支事务都能按照全局事务的最终结果进行提交或回滚,保证全局事务的一致性。
事务日志记录: Seata 使用日志来记录全局事务和分支事务的执行过程和状态,以便在发生故障
或异常情况时进行事务恢复和补偿。
分布式锁和并发控制: Seata 在协调分布式事务时会使用分布式锁和一些并发控制机制,确保事
务操作的原子性、一致性、隔离性和持久性。
总的来说,Seata 的原理基于全局事务管理和分布式事务协调,在全局事务管理器和分布式事务
协调器的支持下,实现了分布式事务的一致性和可靠性。通过对各个参与者的本地事务进行协调
和控制,Seata 能够有效地解决微服务架构下的分布式事务问题,确保系统数据的一致性和完整
性。
7.redis 用的多不
1.高性能: Redis 是基于内存的数据库,读写速度非常快,适合用作缓存系统。它支持丰富的数
据结构(如字符串、哈希、列表、集合、有序集合等),提供了高效的数据操作方法。
2.持久化: Redis 支持数据持久化,可以将内存中的数据定期写入磁盘,保证数据不会丢失。同
时,Redis 提供了多种持久化方式,包括 RDB(快照)和 AOF(日志),可以根据需求选择合
适的方式。
3.分布式支持: Redis 提供了主从复制(Replication)和哨兵(Sentinel)机制,支持构建高
可用、高可靠的分布式系统。此外,Redis Cluster 提供了分布式的数据存储和自动分片功能。
4.丰富的功能: Redis 不仅仅是一个简单的键值存储系统,还提供了丰富的功能,如事务支持、
发布订阅、Lua 脚本、管道操作等,使得开发人员可以更灵活地利用 Redis 构建各种应用。
5.社区支持和生态系统: Redis 拥有庞大的开发者社区和活跃的维护团队,提供了丰富的文档、
教程和工具,便于开发人员学习和使用。此外,许多第三方库和工具与 Redis 集成,扩展了
Redis 的功能和用途。
8.redis 的数据结构
字符串(String): 最基本的数据结构,可以存储字符串、整数或浮点数。字符串类型支持各种
操作,如设置值、获取值、增减等。
哈希(Hash): 类似于关联数组,存储字段和值的映射关系。哈希类型适合存储对象、用户信
息等复杂数据。
列表(List): 有序、可重复的字符串列表。列表类型支持在两端进行插入、删除、修剪等操
作,可以用作栈、队列等数据结构。
集合(Set): 无序、不重复的字符串集合。集合类型支持添加、删除、判断成员等操作,还提
供了交集、并集、差集等集合运算。
有序集合(Sorted Set): 有序、不重复的字符串集合,每个成员关联一个分数,根据分数进
行排序。有序集合类型支持按分数范围获取成员,以及计算排名、排行榜等操作。
位图(Bitmap): 由二进制位组成的数据结构,支持位操作。位图类型适合存储某种状态、权
限等标记。
除了以上主要数据结构,Redis 还提供了一些辅助数据结构和功能,如地理位置
(Geospatial)、超级日志(HyperLogLog)和发布订阅(Pub/Sub)等。此外,Redis 还
支持事务、Lua 脚本和管道操作,提供了更高级的数据操作和批量处理能力。
9.高并发,redis 单线程为什么能支持高并发
异步非阻塞 I/O: Redis 使用了异步非阻塞的 I/O 模型,通过事件驱动机制实现了高效的 I/O 处
理。在大多数情况下,Redis 的操作是基于内存的,读写速度非常快,可以迅速响应请求,避免
了因 I/O 阻塞而导致的性能问题。
纯内存操作: Redis 主要是基于内存进行操作的,内存访问速度比磁盘访问速度要快很多。由于
数据都存储在内存中,Redis 可以快速地读取和写入数据,提高了处理速度。
单线程避免上下文切换开销: Redis 采用单线程模型,避免了多线程之间频繁的上下文切换开
销。单线程虽然在某些情况下会成为性能瓶颈,但对于 CPU 密集型的操作来说,单线程反而可
以更好地利用 CPU 资源,避免了线程间的竞争和同步开销。
多路复用技术: Redis 使用了多路复用技术(Multiplexing),可以同时监听多个客户端连
接,通过一个线程就可以处理多个请求。这样可以减少线程开销和资源占用,提高并发处理能
力。
简单且高效的数据结构: Redis 提供了简单而高效的数据结构,如字符串、哈希表、列表等,这
些数据结构的设计和实现使得 Redis 在处理数据时更加高效和灵活。
虽然 Redis 是单线程的,但通过合理的设计和优化,结合异步 I/O、高效的数据结构和多路复用
技术,使得 Redis 能够在高并发场景下表现出色,成为许多应用程序的首选缓存和数据存储解决
方案。
10.redis 是基于内存的话,为什么我不使用内存?
内存成本高昂: 内存相比磁盘存储来说更昂贵,尤其是大规模存储数据时成本会很高。如果你的
数据量很大,而且可以容忍一定的读写延迟,那么可能需要考虑其他基于磁盘存储的数据库。
持久化和数据安全: 内存中的数据是易失的,一旦发生断电或异常情况,数据可能会丢失。为了
保证数据的持久性和安全性,Redis 提供了持久化机制,可以将数据定期保存到磁盘上,或者通
过日志方式来保障数据的安全。
缓存 vs 数据库: Redis 通常被用作缓存系统,用来加速数据访问和减轻后端数据库的压力。但
如果你需要长期存储大量数据,并且对实时性要求不高,可能需要考虑其他数据库系统作为主要
数据存储。
灾备和扩展性考虑: 内存有限,无法存储无限量的数据。在设计架构时,需要考虑数据的备份、
灾备和扩展性问题,确保系统可以应对数据量的增长和故障情况。
11.常用设计模式
单例模式(Singleton Pattern): 确保一个类只有一个实例,并提供一个全局访问点。
工厂模式(Factory Pattern): 通过工厂类来创建对象,而不直接使用 new 关键字实例化对
象。
观察者模式(Observer Pattern): 定义对象间的一对多依赖关系,当一个对象状态改变时,
所有依赖它的对象都会收到通知并自动更新。
策略模式(Strategy Pattern): 定义一系列算法,将每个算法封装起来,并使它们可以互相
替换,让算法的变化独立于使用算法的客户。
适配器模式(Adapter Pattern): 将一个类的接口转换成客户希望的另外一个接口,使得原本
由于接口不兼容而不能一起工作的那些类可以一起工作。
装饰者模式(Decorator Pattern): 动态地给一个对象添加一些额外的职责,就扩展功能而
言,装饰模式比生成子类更为灵活。
命令模式(Command Pattern): 将请求封装成对象,以便使用不同的请求、队列或者日志来
参数化其他对象。
模板方法模式(Template Method Pattern): 定义一个操作中的算法的骨架,而将一些步骤
延迟到子类中。
建造者模式(Builder Pattern): 将一个复杂对象的构建与它的表示分离,使得同样的构建过
程可以创建不同的表示。
享元模式(Flyweight Pattern): 运用共享技术有效地支持大量细粒度的对象。
12.单例模式创建的条件,怎么创建一个单例对象
私有构造函数: 单例类的构造函数需要是私有的,这样外部就无法直接通过 new 关键字来实例
化对象。
静态成员变量: 单例类需要拥有一个私有静态成员变量,用于保存唯一的实例对象。
静态方法: 单例类通常会提供一个静态方法,用于获取单例对象。这个方法会负责创建实例并确
保只有一个实例被创建
第三家 外包
纯八股文,记录的题目不多
1.acid 的组成是什么?
原子性(Atomicity): 事务被视为不可分割的最小工作单元,要么全部执行成功,要么全部失
败回滚。在事务中的所有操作要么全部提交成功,要么全部失败,不存在部分提交的情况。
一致性(Consistency): 事务开始前和结束后,数据库的完整性约束没有被破坏。即事务执行
前后,数据库从一个一致性状态变为另一个一致性状态,不会破坏数据的完整性、约束条件和业
务规则。
隔离性(Isolation): 多个事务并发执行时,每个事务都应该与其他事务相互隔离,互不干
扰。隔离性可以防止并发执行的事务之间产生意外的结果,保证事务之间的独立性。
持久性(Durability): 一旦事务提交,其结果应该永久保存在数据库中,即使发生系统故障,
也不能丢失已提交的数据。系统保证事务的提交能够持久保存,即使系统崩溃或重启也不会丢失
已提交的数据。
2.jwt 的组成成分
JWT(JSON Web Token)是一种用于在网络应用间安全传输信息的开放标准(RFC
7519)。一个 JWT 通常由三部分组成:头部(Header)、载荷(Payload)和签名
(Signature)。
头部(Header): JWT 的头部通常由两部分信息组成:令牌的类型(如 JWT)和所使用的签
名算法(如 HMAC SHA256 或 RSA)。头部使用 Base64 编码后得到一个字符串。
载荷(Payload): 载荷包含了 JWT 的具体内容,也就是存放需要传输的数据。载荷可以包含
一些预定义的声明(如 iss(签发者)、exp(过期时间)等)和自定义的声明。载荷同样使用
Base64 编码后得到一个字符串。
签名(Signature): 签名由三部分组成:编码后的头部、编码后的载荷以及一个秘钥(只有服
务端知道)。签名通过将这三部分使用特定的算法进行加密生成。签名用于验证消息的完整性和
真实性,以防止篡改。
JWT 的形式一般为 头部.载荷.签名。这三个部分之间使用点号 . 进行分隔。最终生成的 JWT 是一
个长字符串,可以通过 HTTP 请求的头部、URL 参数或者在请求体中进行传输。接收方可以通
过验证签名来验证 JWT 的真实性和完整性,并从载荷中获取所需的信息。
需要注意的是,虽然 JWT 可以被解码,但签名是用于验证数据完整性的,不能被篡改。因此,在
使用 JWT 时,需要保证秘钥的安全性,以防止非法篡改 JWT 内容。
3.redis 的常用数据类型
字符串(String): 最基本的数据结构,可以存储字符串、整数或浮点数。字符串类型支持各种
操作,如设置值、获取值、增减等。
哈希(Hash): 类似于关联数组,存储字段和值的映射关系。哈希类型适合存储对象、用户信
息等复杂数据。
列表(List): 有序、可重复的字符串列表。列表类型支持在两端进行插入、删除、修剪等操
作,可以用作栈、队列等数据结构。
集合(Set): 无序、不重复的字符串集合。集合类型支持添加、删除、判断成员等操作,还提
供了交集、并集、差集等集合运算。
有序集合(Sorted Set): 有序、不重复的字符串集合,每个成员关联一个分数,根据分数进
行排序。有序集合类型支持按分数范围获取成员,以及计算排名、排行榜等操作。
位图(Bitmap): 由二进制位组成的数据结构,支持位操作。位图类型适合存储某种状态、权
限等标记。
除了以上主要数据结构,Redis 还提供了一些辅助数据结构和功能,如地理位置
(Geospatial)、超级日志(HyperLogLog)和发布订阅(Pub/Sub)等。此外,Redis 还
支持事务、Lua 脚本和管道操作,提供了更高级的数据操作和批量处理能力。
4.springboot 的常用注解
@SpringBootApplication: 这个注解用于标记主类,表示这是一个 Spring Boot 应用程序的
入口。它包含了@ComponentScan、@EnableAutoConfiguration 和@Configuration 注
解。
@RestController: 这个注解用于标记一个类,表示这个类是一个 RESTful 风格的控制器。配
合@RequestMapping 等注解,可以定义处理 HTTP 请求的方法。
@RequestMapping: 这个注解用于标记一个方法或类,指定处理特定 HTTP 请求的 URL 路
径。可以通过设置 method 属性来指定请求的方法类型。
@Autowired: 这个注解用于自动注入依赖对象,可以在需要使用的地方直接使用该注解标注字
段、构造函数或者 setter 方法。
@GetMapping、@PostMapping 等: 这些注解用于标记处理 HTTP GET、POST 等请求的
方法,简化了@RequestMapping 的书写。
@PathVariable: 这个注解用于获取 URL 路径上的参数值,并将其绑定到方法的参数上。
@RequestBody、@ResponseBody: 这两个注解分别用于将 HTTP 请求的内容(如 JSON)
绑定到方法的参数上,或者将方法的返回值转换为 HTTP 响应体(如 JSON)。
@Configuration: 这个注解用于标记一个类,表示这是一个配置类。在 Spring Boot 中,通
常使用@Configuration 注解来定义一些 Bean。
@Component: 这个注解用于标记一个类,表示这个类是一个可被组件扫描和自动装配的
Bean。
以上只是 Spring Boot 中的一些常用注解,还有更多的注解可以用于不同的场景和需求。根据具
体情况,可以选择适合的注解来实现业务逻辑和功能。
5.spring 的源码了解哪些
6.redis 储存目录数据,是怎么进行存储的
第四家 自研 物联网相关
1.问了 nacos 集群
2.redis 宕机了,如何恢复数据?
检查持久化配置: 首先,确保 Redis 的持久化配置已经正确设置。在 redis.conf 配置文件中,
你可以找到两个与持久化相关的配置项:
save:该配置项定义了在指定的时间内,有多少次写操作发生时,Redis 会自动执行一次快照
(RDB)持久化操作。确保该配置项被正确设置,以防止数据丢失。
appendonly:如果你使用了 AOF 日志持久化方式,确保该配置项设置为 yes,以便将所有写操
作追加到 AOF 日志文件中。
启动 Redis 服务: 重新启动 Redis 服务,可以使用以下命令启动 Redis:
redis-server /path/to/redis.conf
检查快照文件: 如果你使用了 RDB 持久化方式,在 Redis 启动后,它会尝试加载最新的快照文
件(通常是 dump.rdb)。Redis 会自动检查并加载该文件,以将数据从磁盘恢复到内存中。
检查 AOF 日志文件: 如果你使用了 AOF 持久化方式,Redis 会在启动时重放 AOF 日志中的命
令,以将数据恢复到内存中。在 Redis 启动后,它会自动检查 AOF 文件并进行重放。你可以在
Redis 配置文件中设置 AOF 的重写策略以减小 AOF 文件的大小。
3.索引的类型
单列索引(Single-column index): 最常见的索引类型,它基于单个表列的数值进行排序和
检索。可以大大提高查询效率,尤其是针对该列的等值查找或范围查找。
复合索引(Composite index): 由多个列组成的索引,可以用于优化多列条件的查询。复合
索引可以覆盖多个列,提高多列条件查询的性能。
唯一索引(Unique index): 确保索引列中的所有值都是唯一的。当对一个列创建唯一索引
后,该列将不允许有重复的值出现。
全文索引(Full-text index): 用于对文本数据进行全文搜索的索引。全文索引可以快速定位
包含特定关键词或短语的文档。
空间索引(Spatial index): 用于在地理信息系统(GIS)中存储和查询空间数据的索引。空
间索引可以加速对空间数据进行范围查询、最近邻搜索等操作。
哈希索引(Hash index): 使用哈希函数将索引列的值映射到哈希表中的位置。哈希索引适用
于等值查找,但不适用于范围查询。
聚集索引(Clustered index)和非聚集索引(Non-clustered index): 这两种索引类型通常
用于关系型数据库中。聚集索引决定了数据在磁盘上的物理存储顺序,而非聚集索引则是独立于
数据存储顺序的。
4.左连接,内连接的区分。
左连接(Left Join): 左连接返回左表中的所有记录,以及符合连接条件的右表中的匹配记录。
如果右表中没有匹配记录,则对应的结果列将包含 NULL 值。左连接使用 LEFT JOIN 或
LEFT OUTER JOIN 关键字进行定义。
例如,假设有两个表 A 和 B,通过左连接可以检索出 A 表的所有记录,以及与 B 表匹配的记录。
如果 B 表中没有与 A 表匹配的记录,那么对应的 B 表列将为 NULL。
内连接(Inner Join): 内连接只返回符合连接条件的左表和右表中的匹配记录。只有当左表和
右表在连接条件上有匹配时,才会将其包含在查询结果中。内连接使用 INNER JOIN 关键字进
行定义。
例如,对于两个表 A 和 B,通过内连接可以检索出同时存在于 A 表和 B 表中的匹配记录。
5.分布式事物是如何保证的。(未完善)
两阶段提交协议(Two-Phase Commit,2PC): 2PC 是一种分布式事务协议,由协调者和参
与者组成。在这个协议中,协调者负责协调事务的提交或回滚。它包含两个阶段:准备阶段和提
交阶段。在准备阶段,协调者会询问所有参与者是否可以提交事务。如果所有参与者都准备好,
则进入提交阶段,否则回滚事务。2PC 提供了一致性的保证,但在某些情况下可能存在单点故障
和阻塞问题
补偿事务(Compensating Transaction): 补偿事务是一种用于处理分布式事务失败的方
法。当某个参与者无法完成事务时,它可以执行相应的补偿操作来撤销之前的操作。通过补偿事
务,可以回滚或修复已经提交的部分事务,从而保持数据的一致性。
6.HTTP 协议是什么,状态码知道多少
HTTP(HyperText Transfer Protocol)是一种用于传输超文本数据(例如 HTML)的应用
层协议。它是 Web 上数据传输的基础,通常用于客户端和服务器之间的通信。HTTP 协议定义
了客户端和服务器之间交换信息的规则,包括请求和响应的格式、方法、状态码等。
HTTP 协议中的状态码是服务器对客户端请求处理结果的标识,用于指示请求的状态。常见的
HTTP 状态码包括:
1xx(信息性状态码):表示请求已经接收,继续处理。
2xx(成功状态码):表示请求被成功接收、理解、接受或处理。
200 OK:请求成功。
201 Created:请求已创建新资源。
204 No Content:服务器成功处理请求,但不返回任何内容。
3xx(重定向状态码):表示需要进行进一步操作以完成请求。
301 Moved Permanently:永久重定向。
302 Found:临时重定向。
304 Not Modified:资源未修改,可以使用缓存。
4xx(客户端错误状态码):表示客户端请求存在错误。
400 Bad Request:请求无法被服务器理解。
401 Unauthorized:未授权访问。
404 Not Found:请求资源不存在。
5xx(服务器错误状态码):表示服务器处理请求时发生错误。
500 Internal Server Error:服务器内部错误。
503 Service Unavailable:服务器暂时不可用。
7.Redis 的常用场景
缓存: Redis 最常见的使用场景就是作为缓存层,将频繁读取的数据存储在 Redis 中,以提高
读取性能。由于 Redis 使用内存作为存储介质,读写速度非常快,适合存储热点数据。
会话管理: Redis 提供了持久化的功能,可以将用户会话数据存储在 Redis 中,并实现会话共
享。这样可以方便地实现分布式系统中的会话管理,增加系统的可扩展性和灵活性。
分布式锁: Redis 支持原子操作和事务,可以利用这些特性实现分布式锁。通过将锁状态存储在
Redis 的数据结构中,可以保证在分布式环境下的并发控制,避免资源冲突和竞态条件。
排行榜和计数器: Redis 的有序集合(Sorted Set)和计数器功能非常适合实现排行榜和计数
器等功能。可以将用户的得分或点击次数存储在有序集合中,并通过 Redis 提供的丰富命令进行
排名和统计操作。
消息队列: Redis 的发布订阅功能可以实现简单的消息队列系统。生产者可以将消息发布到指定
的频道,而消费者可以订阅感兴趣的频道并接收消息,实现异步消息传递和解耦。
分布式缓存: Redis 可以通过分布式部署来构建高可用和高性能的缓存集群。多个 Redis 节点
可以组成一个集群,提供数据的复制和自动故障转移,增加系统的可靠性和扩展性。
8.了不了解分库分表
一般答 mycat,ShardingSphere。担心 mycat 答不上来,直接说用 ShardingSphere 进行分
库分表
分库分表是指将数据库按照一定规则分成多个数据库(分库)和表(分表)来存储数据,旨在提
高数据库的性能、扩展性和可用性。这种数据库架构设计常用于大型互联网应用或需要处理大量
数据的系统中。
在传统的单一数据库架构下,随着数据量的增大和访问压力的增加,单一数据库往往会面临性能
瓶颈、扩展困难等问题。通过分库分表可以有效解决这些问题,实现水平扩展,提高系统的稳定
性和性能。
分库分表的主要思想包括:
分库(Sharding): 将数据库按照某种规则(如按用户 ID、按地域等)划分成多个独立的数据
库实例,每个数据库实例存储部分数据。这样可以将数据分散存储在不同的数据库中,减轻单一
数据库的压力。
分表(Partitioning): 在每个数据库中,可以将数据表按照一定规则(如按时间范围、按数据
量等)分成多个子表,每个子表存储部分数据。这样可以降低单个数据表的数据量,提高查询性
能。
分库分表的优点包括:
提高性能: 将数据分散存储在多个数据库和表中,减少单一数据库的负载,提高读写性能。
提高扩展性: 可以根据业务需求动态添加数据库节点和表,实现水平扩展,方便系统的扩展和升
级。
提高可用性: 分库分表可以降低单点故障的风险,一旦某个数据库或表发生故障,不会影响整个
系统的运行。
然而,分库分表也会带来一些挑战,如跨库关联查询、事务处理、数据迁移等问题需要额外考虑
和处理。因此,在实施分库分表时,需要综合考虑系统需求、数据特点和技术复杂度,设计合适
的方案来实现数据的分布存储和管理。
9.nginx 的作用
Web 服务器: Nginx 可以作为静态资源(如 HTML、CSS、JavaScript 文件)的 Web 服务
器,处理客户端的 HTTP 请求并返回相应的内容。其高性能和低内存消耗使其成为处理高流量网
站的理想选择。
反向代理服务器: Nginx 可以用作反向代理服务器,接收客户端的请求并将其转发给其他服务
器(如应用服务器)来处理。反向代理有助于隐藏后端服务器的真实 IP 地址,并提供负载均衡
和缓存功能。
负载均衡器: Nginx 可以实现负载均衡,将客户端请求分发到多个后端服务器上,以提高系统
的性能、可用性和稳定性。通过负载均衡,可以有效地分担服务器的压力,避免单点故障。
HTTP 缓存: Nginx 可以缓存静态内容或动态内容,减轻后端服务器的负载并提高网站性能。
通过设置缓存规则,Nginx 可以缓存响应并在未来的请求中直接返回缓存的内容,加快页面加载
速度。
安全防护: Nginx 提供了诸如访问控制、限速、SSL/TLS 加密、反爬虫等安全功能,帮助保护
网站免受恶意攻击和非法访问。
10.灰产是什么
灰产(Grey Hat)是指介于合法产业和非法产业之间的一类经济活动。它常常存在于法律规定
不明确或存在漏洞的领域,具有一定的灰色地带,难以明确归类为完全合法或非法。
灰产通常是指那些在法律允许的范围内,但在道德和行业规范上存在争议的商业行为。这种行为
常常利用法律的漏洞和模糊性来获得利益,但可能会对消费者、竞争者或整个市场造成一定程度
的负面影响。
一些常见的灰产活动包括:
网络欺诈: 包括虚假广告、诈骗网站、钓鱼网站等,在法律未明确规定的情况下,通过误导和欺
骗手段获取利益。
SEO 黑帽技术: 使用一些违反搜索引擎准则的技术手段,如关键词堆砌、隐藏链接等,提高网
站在搜索结果中的排名,获取更多流量和曝光。
软件盗版和破解: 以非法手段获取商业软件、游戏等知识产权作品,并进行破解、复制和分发,
非法获利。
操纵网络评论和评价: 利用批量注册账号、刷评等手段,操控产品或服务的评论和评价,影响消
费者的购买决策。
总的来说,系统的灰产:
上游收集资料 ,中游交给软件开发进行研发,下游负责提现