困状态不好的一次
- Redis 大key 热key原因 导致的问题,如何解决。热key说了本地缓存和分key还被追问了
- Mysql ACID各自咋保障的【锁和MVCC 管并发,保障隔离性。三者合力,达成一致性。】
- 介绍实习Redis架构
- redis集群如何路由 和 扩容缩容的?
- rpc和http区别 rpc是服务间 Https c/s架构一般
- rpc设计,我提到了负载均衡、序列化、通信协议、注册中心
- 追问: 负载均衡如何监控节点down机
- 补充:我没提到rpc可能存在链式调用情况,追问链式调用会发生什么问题 我:服务雪崩
- 服务雪崩如何解决 有用过什么框架嘛 sentinel
- 一个用户ID如何在rpc链式调用中传递呢
- 进程编程做过嘛 举例子
- 我说了僵尸进程 ,追问如何解决僵尸进程 我说kill - 9 编写代码及时wait掉子进程
- concurrenthashmap没有提到CAS 只说了sychronized
- 矩阵dp[i][j]=min(dp[i-1][j]+dp[i][j-1])+grid[i][j]追问如何优化空间复杂度
13. flux 为啥用这个
进程编程
锁(Lock)、信号量(Semaphore)、事件(Event)这些同步机制在进程编程和线程编程中都有,但是它们的作用范围不同。在线程编程中,这些同步机制用于同一进程内的线程之间同步;而在进程编程中,它们用于不同进程之间的同步,通常需要使用跨进程的同步机制(如命名信号量、命名事件等)或者使用在共享内存中创建的同步原语。对于进程间的同步,Java并没有内置的高级支持,通常需要借助操作系统级别的机制(如文件锁、套接字、信号量等)或者使用中间件。可以通过以下方式:
- 文件锁:FileLock可以用于进程间的同步,但它是基于文件的,不同进程可以通过对同一个文件加锁来实现同步。
- 套接字:通过网络套接字进行进程间通信和同步。
- 使用操作系统提供的信号量(如通过JNI调用系统信号量)或者使用消息队列等。
例如,使用文件锁进行进程间同步:
import java.io.RandomAccessFile; import java.nio.channels.FileLock; public class FileLockExample { public static void main(String[] args) throws Exception { RandomAccessFile file = new RandomAccessFile("lockfile.txt", "rw"); java.nio.channels.FileChannel channel = file.getChannel(); // 获取文件锁 FileLock lock = channel.lock(); try { // 执行临界区代码 System.out.println("进程获得了文件锁,正在执行操作..."); Thread.sleep(5000); } finally { lock.release(); } file.close(); } }
总结:
- 进程编程涉及创建和管理操作系统进程,通常用于需要隔离或运行外部程序的场景。
- 线程编程是在同一进程内创建多个执行流,共享内存空间,但需要同步机制来保证数据一致性。
- 锁、信号量、事件等同步机制在线程编程中常用,而进程间同步需要使用跨进程的机制。
进程编程与线程编程的区别
- 资源开销:进程是资源分配的基本单位,创建进程比创建线程开销大,因为每个进程都有独立的地址空间。
- 隔离性:进程之间相互隔离,一个进程的崩溃不会影响其他进程;线程共享同一进程的资源,一个线程的崩溃可能导致整个进程的崩溃。
- 通信方式:进程间通信(IPC)需要特定的机制,如管道、消息队列、共享内存等;线程间通信可以直接读写共享数据(但需要同步机制)。
- 上下文切换:进程上下文切换开销大,线程上下文切换开销小。
Redis分片集群伸缩:
关键命令redis-cli cluster reshard ip端口 给节点重新分哈希槽
add-node, del-node
数据都是跟着哈希槽走的,只是更改了哈希slot和redis节点的映射,将slot变更到新加入的redis节点上
Redis大key:
导致的问题:
- 内存不均:在集群模式下,可能导致某个数据分片内存占用远高于其他节点,影响集群稳定性。
- 阻塞操作:使用 DEL 删除大 Key 会阻塞 Redis 服务进程,导致其他命令超时。KEYS, HGETALL, LRANGE 等命令也会长时间阻塞。【使用 UNLINK 命令代替 DEL。UNLINK 会在后台异步删除,不会阻塞主线程。使用 MEMORY USAGE 命令查找大 Key,并使用 SCAN 类命令(如 HSCAN, SSCAN)渐进式处理。】
- 网络拥塞:一次查询大 Key 会占用大量带宽,影响其他请求。
- 数据迁移困难:在集群扩容或缩容时,大 Key 的迁移会非常耗时,容易导致迁移失败。
Redis热key:导致的问题:
- 流量倾斜:在集群模式下,大量请求会集中打到某一个数据分片上,导致该分片 CPU 和网络负载过高,成为系统瓶颈。
- 缓存击穿:如果热 Key 突然过期,海量请求会同时穿透到数据库,可能导致数据库瞬时压力过大而宕机。
Flux
Flux 是 Reactor 库的核心类,代表一个**异步的、能够发出 0 到 N 个元素的序列流**。核心是**发布-订阅**模型。.subscribe()订阅flux,采用streamobsever接收,具体是onNext和 onComplete,onComplete 是一个基于事件的通知,通过回调机制告诉订阅者流已经完成。sse.send分块把序列流传给前端。
我们使用 Reactor 的 Flux 时,订阅后通过 Subscriber(而不是 StreamObserver)来接收事件。Flux 是一个发布者,它发出数据流,我们通过订阅(subscribe)这个发布者来消费数据。在订阅时,我们提供一个 Subscriber(或者通过 lambda 表达式提供回调函数)来处理数据流中的元素、错误和完成信号。你提到 SSE(Server-Sent Events),确实可以通过 Flux 来发送服务器端事件流。在 Spring 中,我们可以使用 Flux 作为返回类型,并设置媒体类型为 "text/event-stream",然后 Spring 会将其转换为 SSE 流。每个元素都会被发送到前端,直到 onComplete 事件发生,前端会收到一个完成信号。
ConcurrentHashMap
- Segment(分段锁) 在Java 8之前,ConcurrentHashMap使用分段锁技术。它将整个哈希表分成多个段(Segment),每个段是一个独立的哈希表,拥有自己的锁。段的结构ConcurrentHashMap内部包含一个Segment数组,每个Segment是一个类似于HashMap的结构,即一个数组加链表。每个Segment继承自ReentrantLock,因此每个段都是一个可重入锁。
并发操作当进行put、remove等操作时,只需要锁定当前操作所在的段,其他段不会被锁定。
在Java 8及之后的版本中,ConcurrentHashMap放弃了分段锁,转而使用CAS操作和synchronized关键字来实现线程安全。CAS是一种无锁技术,它通过硬件指令来保证操作的原子性。
插入节点:当向一个空的桶中插入节点时,使用CAS来设置节点【比如数组的引用、节点的引用等。】,如果失败则重试或转为使用synchronized锁。
与synchronized的结合当多个线程竞争同一个桶时,ConcurrentHashMap会使用synchronized关键字来锁定桶中的第一个节点,从而保证线程安全。由于锁的粒度很小(只有一个桶),所以并发性能很好。
#找工作中的小确幸##秋招踩过的“雷”,希望你别再踩#