一致性hash
解决的问题
- 动态节点增减:在分布式系统中,当增加或删除节点时,传统的取模哈希算法会导致大量数据迁移,增加网络通信压力。一致性哈希算法限制了数据迁移仅在两个节点之间,避免全局的网络问题。
- 雪崩效应:传统哈希算法在节点数量变化时,可能导致数据倾斜,某些节点负载过重,而其他节点负载较轻。一致性哈希算法通过虚拟节点机制,均匀分布节点,减轻了这种问题,防止雪崩效应的发生。
- 高效资源定位:一致性哈希算法通过路由表提高资源定位效率,使得查询次数与节点数不再成线性关系,适用于大规模分布式系统。
原理
- 环形哈希环。首先,将所有节点映射到一个环形空间上,通常使用哈希值作为环的坐标。这个环被抽象为一个圆环,节点的哈希值映射到环上。
- 虚拟节点。为了均匀分布数据,每个节点会对应多个虚拟节点。
- 数据映射:当需要存储数据时,先对数据进行哈希计算,然后顺时针查找环上的第一个虚拟节点,该节点即为数据的目标节点。
- 节点添加:只会影响新增节点与前一个虚拟节点之间的数据映射,只会影响删除节点与前一个虚拟节点之间的数据映射。
- 总之,一致性哈希算法通过环形哈希环、虚拟节点和权重设置,实现了高效的数据分布和节点变化时的平滑迁移。
JVM内存区域
- 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) : 整个永久代有一个JVM本身设置的固定大小上限,无法进行调整,而元空间使用的是本地内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但比原来出现的几率会更小。
- 总的来说,JVM常量池中存储的是符号引用或字面量值,而不是对象本身。在运行时,这些引用会被解析为实际的对象或方法引用,同时JVM会确保合法性和一致性,以防止越权访问。
- 主要是因为永久代(方法区实现)的 GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。
类的生命周期
- 加载
- 通过全类名获取定义此类的二进制字节流
- 将字节流所代表的静态存储结构转换为方法区的运行时数据结构
- 在内存中生成一个代表该类的Class对象,作为方法区这些数据的访问入口。
- 链接
- 验证
- 文件格式验证、元数据验证、字节码验证、符号引用验证
- 准备
- 为类变更分配内存并设置类变更初始值的阶段
- 解析
- 虚拟机将常量池内的符号引用替换为直接引用的过程。
- 验证
- 初始化
- 执行初始化
<clinit>()
方法的过程,是类加载的最后一步,这一步JVM才开始真正执行类中的定义的Java程序代码。
- 执行初始化
- 使用
- 卸载
事务的一致性,和分布式的一致性
- 事务一致性是指在数据库中,事务的执行不会破坏数据的完整性和约束条件。当一个事务执行成功后,数据库的状态应该是从一个一致的状态转变为另一个一致的状态,
- 分布式系统中的一致性是指在多个节点之间保持数据的一致性,即使在面对网络故障、节点故障或并发操作时也能保持数据的正确性。
MySQL解决幻影读
- 间隙锁 (Gap Locking)。对查询范围内的间隙进行锁定,从而防止其他的事务在这个范围内插入新的数据。在事务中使用SELECT … FOR UPDATE语句,它会在读取数据的同时对查询的范围内的间隙进行锁定。
- 一致性非锁定读。你可以使用SELECT … LOCK IN SHARE MODE语句或者SELECT … READ UNCOMMITTED语句来进行一致性非锁定读。这样一来,事务可以在读取数据的同时,其他事务仍然可以对相同的数据进行插入或修改操作,但是读取到的数据仍然是一致的
模板方法的钩子
钩子方法是一种特殊的方法,它被生命在抽象类中,但通常只有空的或者默认的方法实现。钩子方法的存在,可以让子类有能力对算法的的不同点进行挂钩。要不要挂钩由子类自行决定。
在抽象类中定义一个模板方法,这个模板方法中包含了一个算法的骨架,而算法的某些步骤则被设计为抽象方法和钩子方法。
Full GC线程数
Full GC的线程数量取决于垃圾回收器的类型和配置。例如,对于并行垃圾回收器(Parallel GC),Full GC的线程数量通常等于可用的CPU核心数。然而,对于串行垃圾回收器(Serial GC),Full GC只会使用一个线程。
横向越权和纵向越权解决思路
横向越权指的是攻击者尝试访问与他拥有相同(级别或角色)权限的用户的资源。
纵向越权指的是一个低级别(低权限)攻击者尝试访问高级别(高权限)用户的资源。
横向越权(水平越权)
- 建立用户和可操作资源的绑定关系,用户对任何资源进行操作时,通过该绑定关系确保该资源是属于该用户所有的。
- 对请求中的关键参数进行间接映射,避免使用原始关键参数名,比如使用索引1代替id值123等。
纵向越权
- 使用基于角色访问控制机制来防止纵向越权攻击,即预先定义不同的权限角色,为每个角色分配不同的权限,每个用户都属于特定的角色,即拥有固定的权限,当用户执行某个动作或产生某种行为时,通过用户所在的角色判定该动作或者行为是否允许。
spring AOP失效的场景
- 私有方法调用:AOP无法增强私有方法。
- 静态方法调用:AOP无法增强静态方法。
- final方法调用:AOP无法增强final方法。
- 类内部自调用:如果在同一个类中的一个方法调用另一个方法,那么被调用的方法上的AOP可能会失效。这是因为这种内部调用实际上是在目标对象上进行的,而不是在AOP代理对象上进行的。
- 内部类方法调用:AOP无法增强内部类的方法。
Mysql的最左匹配原则
MySQL的最左匹配原则是指在使用联合索引进行查询时,MySQL会从联合索引的最左边开始匹配
这个原则可以从联合索引的结构来解释。在InnoDB中,联合索引只有先确定了前一个(左侧的值)后,才能确定下一个值。如果有范围查询的话,那么联合索引中使用范围查询的字段后的索引在该条SQL中都不会起作用
在创建联合索引时,MySQL会首先对最左边的字段进行排序,然后在保证第一个字段有序的情况下,再对第二个字段进行排序,以此类推。
因此,当我们进行查询时,MySQL可以通过最左边的字段快速定位到具有相同 a
值的数据,然后在这些数据中,再通过 b
字段快速定位到具有相同 b
值的数据,最后再通过 c
字段找到最终的结果。
I/O多路复用
- 非阻塞IO是一种调用方式,每次发起系统调用,只能检查一个文件描述符是否就绪,当文件描述符很多时,系统调用成本较高。
- IO多路复用一种机制,允许一个线程同时处理多个IO请求。通过一个系统调用,可以检查多个fd的状态,避免了频繁的用户态和内核态的切换,减少了系统调用的开销。
消费之Rebalance机制
rebalance机制的目的
- 当一个消费组在消费一个Topic时,kafka为了保证消息不被重复消费或遗漏,将每个分区唯一地分配给消费者。
- 但是,如果某个消费者宕机或者有新的消费者加入,分区分配可能变得不公平,导致某些消费者负载过重,而其他消费者则没有负载。
Kafka会触发Rebalance机制时机:
- 消费组成员发生变更,例如有新的消费者加入或有消费者宕机。
- 消费者无法在指定的时间内完成消息的消费。
- 订阅的Topic发生变化。
- 订阅的Topic的分区发生变化。
ES写入的原理
- 客户端发写请求的时,可以发往任意节点。这个节点就会变成coordinating node协调节点。
- 计算的点文档要写入的分片;计算时就采用hash取模的方式来计算。
- 协调节点就会进行路由,将请求转发给对应的primary sharding所在的datanode。
- datanode节点上的primary sharding处理请求,写入数据到索引库,并将数据同步到对应的replica sharing。
- 等primary sharding和replica sharding都保存好了文档之后,返回客户端相应,
ES查询数据的工作原理
- 客户端发请求给任意节点,这个节点就成为协调节点
- 协调节点将查询请求广播到每一个数据节点,这些数据节点的分片就会处理查询请求。
- 每个分片进行查询,将符合条件的数据放到一个队列中,并将数据的文档ID、节点信息、分片信息都返回给协调节点。
- 由协调节点将所有的结果进行汇总,并排序。
- 协调节点向包含这些文档ID的分片发送get请求,对应的分片将文档数据返回给协调节点,最后协调节点数据整合返回给客户端。
全连接队列满了会影响半连接队列吗
- SYN queue 队列长度由 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,默认为2048。
-
Accept queue 队列长度由 /proc/sys/net/core/somaxconn 和使用listen函数时传入的参数,二者取最小值。默认为128。 TCP三次握手第一步的时候如果全连接队列满了会影响第一步drop 半连接的发生。大概流程的如下:
tcp_v4_do_rcv->tcp_rcv_state_process->tcp_v4_conn_request //如果accept backlog队列已满,且未超时的request socket的数量大于1,则丢弃当前请求 if(sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_yong(sk)>1) goto drop;
一个进程用kill 杀不死
ps -aux 看看STAT一栏,如果是Z,那就是zombie状态的僵尸进程
ps -ef | grep 僵尸进程id , 可以找到父进程id,然后kill父进程即可 |
一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过wait() 和waitpid()获取子进程信息后才会释放,如果子进程退出,而父进程并没有调用wait()或waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称为僵尸进程。
磁盘满了
df - h 查看磁盘使用的情况,查看对应的日志,按天切割,crontab定时,定期删除7天以前的日志。
是否有很多大于100M的大文件 : find / -size +100M | xargs ls -lh |
或者有很多文件,du -h > fs_du.log ,看看各个目录占用的磁盘空间带下
线上CPU100%,怎么排查问题
- top -c, 就可以显示进程列表,然后输入P,按照cpu使用率排序
- top -Hp 43987,就是输入那个进程id就好了,然后输入P,按照cpu使用率排序
- 定位哪段代码导致的cpu过高,将线程pid转成16进制,
-
jstack 43987 grep ‘0x41e8’ -C5 –color
-
ConcurrentHashMap中TreeBin与TreeNode的区别
ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。
Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
static class Segment<K,V> extends ReentrantLock implements Serializable {
}
ConcurrentHashMap 取消了 Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。
在Java1.8的实现中,TreeBin
并不是红黑树的存储节点,TreeBin
通过root
属性维护红黑树的根结点,因为红黑树在旋转的时候,根结点可能会被它原来的子节点替换掉,在这个时间点,如果有其他线程要写这棵红黑树就会发生线程不安全问题,所以在ConcurrentHashMap中TreeBin
通过waiter
属性维护当前使用这棵红黑树的线程,来防止其他线程的进入。
所以在Java8中TreeBin
并没有指向下一节点的引用,而是使用TreeNode
来存储红黑树节点
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;
volatile TreeNode<K,V> first;
volatile Thread waiter;
volatile int lockState;
// values for lockState
static final int WRITER = 1; // set while holding write lock
static final int WAITER = 2; // set when waiting for write lock
static final int READER = 4; // increment value for setting read lock
...
}
Spring AOP 中 JDK 和 CGLib 动态代理哪个更快?
1、JDK动态代理具体实现原理:
- 通过实现InvocationHandler接口创建自己的调用处理器;
- 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
- 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
2、CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
3、性能对比
-
CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少;
-
但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多;
-
因此,对于Singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
#{}和${}的区别是什么?
-
#{}是sql的参数占位符,Mybatis把其替换成?,#{itme.name}的取值方式为使用反射从参数对象中获取item对象的name属性值。
-
${}是properties文件中的变量占位符,属于静态文本替换。
HashMap负载因子为什么是0.75?
Ideally, under random hashCodes, the frequency of
* nodes in bins follows a Poisson distribution
* (http://en.wikipedia.org/wiki/Poisson_distribution) with a
* parameter of about 0.5 on average for the default resizing
* threshold of 0.75, although with a large variance because of
* resizing granularity. Ignoring variance, the expected
* occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
* factorial(k)). The first values are:
*
* 0: 0.60653066
* 1: 0.30326533
* 2: 0.07581633
* 3: 0.01263606
* 4: 0.00157952
* 5: 0.00015795
* 6: 0.00001316
* 7: 0.00000094
* 8: 0.00000006
* more: less than 1 in ten million
在理想情况下,使用随机哈希吗,节点出现的频率在hash桶中遵循泊松分布,同时给出了桶中元素的个数和概率的对照表。 从上表可以看出当桶中元素到达8个的时候,概率已经变得非常小,也就是说用0.75作为负载因子,每个碰撞位置的链表长度超过8个是几乎不可能的。hash容器指定初始容量尽量为2的幂次方。HashMap负载因子为0.75是空间和时间成本的一种折中。
count(*) 和 count(1)和count(列名)区别
- count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL
- count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL
- count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。
redis和zk分布式的对比
-
redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能
-
zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小
-
另外一点就是,如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁
-
redis分布式锁比较麻烦,遍历上锁,计算时间等等,zk的分布式锁语义清晰实现简单
ThreadLocal
- 每个Thread维护着一个ThreadLocalMap的引用
- ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
- ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
- ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
- 在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。
- ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
死锁怎么检测
必要条件:不可剥夺、互斥、请求保持和循环等待。
防止:破坏必要条件
避免:银行家算法
死锁检测算法:
- 首先针对每种资源类型只有一个实例的情况。
- 构建资源分配图,采用深度优先遍历算法确定是否存在环路
- 第二种情况是每种资源类型还有多个实例的情况
- 构建向量矩阵,利用向量矩阵算法模拟资源分配。
美团一面
-
类的三大特性
-
继承和接口的区别
-
Kafka的结构,高可用模型
-
kafka的partition选举算法
-
怎么保证顺序消费
-
volatile原理
-
怎么保证a++的线程安全性,有多少种方案 (synchronized、 ReentrantLock、AtomicInteger )
-
synchronize原理
-
ApplicationContext的内部结构
-
如何解决循环依赖,(setter注入,而不是构造方法注入)
-
Spring用到哪些设计模式
-
数据库存储引擎,Myisam 和InnoDB的区别
-
索引,索引失效
-
线程池怎么创建、线程池的执行过程、参数、拒绝策略
-
事务的特性(ACID),C是怎么保证的
快手一面
- kafka优点,为什么用kafka
- kafka为什么吞吐量高
- kafka怎么保证数据不丢失
- 限流算法,固定窗口,url+userId
- 接口归一化设计
- Java的基础数据类型,
- int几个字节,int数据范围,为什么,float的底层表示,0.2的表示
- 非受检异常和受检异常
- LinkedHashMap结构
- HashMap的put操作,原先的头插法的有什么问题,
- key的hashcode的扰动,低16位和高16位进行异或
- 头插法为什么换成尾插法
- 什么时候转成红黑树,什么是红黑树
- ConcurrentHashMap,put的过程,resize的过程
- Redis的数据对象
- SDS的优点
- zset = ziplist + skiplist ,Set的底层结构,整数集合,什么时候用压缩列表
- 跳表查询
- 归并排序是什么,链表的归并排序
快手二面
- 线程池,线程池为什么要有最大线程池和工作队列,为什么要两者,怎么考虑设计
- 抛硬币的先手赢的概率,1/2
- Trie
- BST
腾讯一面
-
为什么是16384
-
半连接队列,全连接队列
-
redis rdb aof
-
IO模型
-
那么全连接队列满了会影响半连接队列吗?
猿辅导二面
线程创建过程,