面经分享--京东一面
一、自我介绍,说一下实习经历
二、说一下TCP和UDP的一些区别
tcp和udp是网络传输中OSI七层模型中传输层的协议,主要区别有tcp是需要传输数据的双方或者多方进行连接的,而udp不需要进行连接,同时tcp是基于字节流进行传输的,而udp是基于数据报进行传输,同时tcp拥有许多机制,例如超时重传,滑动窗口,拥塞控制等机制来保证传输数据的可靠性以及流量的控制,使得传输效率更搞笑,而udp没有这些机制,所以tcp适合于保证数据要可靠的场景下,udp适合用于对实时性要求较高的情况下,例如视频通话等情况。
三、详细说说tcp和udp他们的一些连接过程吗
udp协议在进行数据传输的时候就是不需要进行连接的,应用层将数据交给UDP之后,udp制作两件事也就是给数据加上一个udp的头部,然后把封装好的udp数据报交给网络层。tcp连接的过程就是3次握手的过程,客户端向服务端发送请求报文也就是syn报文之后,状态变为syn_sent状态。服务端收到syn报文之后,返回一个ack+syn报文表示已经收到了客户端的请求,状态变为syn_rcvd状态。并也想要和客户端建立连接,之后客户端收到了就发送一个ack返回给服务端,双方之后就变为established状态,至此双方成功连接上了,之后便开始数据传输。
四、你说你熟悉Redis,那你详细说说Redis的一些数据结构
redis是基于key-value这样键值对的非关系型数据库嘛,他的key是string类型的,value可以是string类型也是使用最多的类型嘛,主要用于存储一些token啊,验证码等内容,list主要用于存储消息队列这种情况,hash类型主要用于存储单个商品的具体信息,比如价格,数量啊这种一一对应的情况,set类型主要用于存储一些要去重的场景,zset类型主要用于要进行排序的情况,还有一些位图啊bitmap,geo这样的经纬度位置类型。
五、那你具体说说这些底层的数据结构
string类型其实底层有3种实现方式,int用于存整数,小数这样和数字有关的,str和raw主要用于存储字符串,区别只是raw用于存储长字符串,embstr用于存储短字符串,list主要也是有3种实现方式,链表,压缩列表,quicklist,quicklist主要就是链表和压缩列表的集合了,整体还是链表,只是链表的每一个节点都是一个压缩列表。hash的底层主要是hashtable也就是最简单的一种哈希表,还有ziplist压缩列表。set底层主要是intset和hashtable,intset主要保存整数集合,hashtable也是用于存储字符串。zset的底层是skiplist和ziplist,skiplist主要用于存储大数据的时候,能够快速地查找到具体的数据。
六、那你说的zset种的压缩列表和跳表,他们的底层是如何实现的,你有看过源码吗
skiplist其实就是跳表+哈希表的双层结构,就是用空间换时间,利用多级索引,速度是logn。最底层是完整的有序数据链表(Level 0),上层是稀疏的 “索引层”,越往上索引越稀疏,最终形成类似 “金字塔” 的结构。查询时从顶层索引快速 “跳跃” 定位,最后落到底层链表获取数据。
七、Redis的主从存储,集群模式下的数据同步是如何做的
主从模式下其实一般情况是主节点负责写,从节点负责读,每次主节点进行写入操作之后,就会采用全量复制或者批量复制的方式进行数据的同步,全量复制也就是当一个新的从节点全部地将主节点的数据复制过去,主要是通过replid和offset。集群模式的数据同步主要是槽位元数据一致性” 和 “槽迁移时的数据同步”。
八、内存淘汰策略有了解吗,有哪些特点
主要看当前的这个key是属于哪种情况吧,最简单的就是不再允许写入新的key,如果存在过期时间,有随机删除,也有lru最近使用频率最少的删除掉,有lfu使用频率最少的,也有ttl使用时间最短的删除掉。不存在过期时间:随机删除,使用频率最少的,使用次数最少的
九、刚刚提到了IO多路复用简单介绍一下原理
IO多路复用就是在IO的时候一个线程不再仅仅只处理一个IO请求了,可以同时处理多个IO的请求,同时会有一个监听器(操作系统内核提供的)像select,poll,epoll这样的监听器,去监听这样的IO操作,如果发生了就绪情况,就会发生具体的IO操作
具体流程可以分为3步:1.应用程序将需要监听的多个FD,以及要监听的事件类型注册到内核的监听器中,2.当某个FD出现了我们预期的情况,监听器就会收集这些。3.针对这些就绪的FD执行具体的IO操作
总结:IO 多路复用的核心是让操作系统内核的监听器(如 epoll)代替应用线程监控多个 IO 描述符,当某个描述符就绪(比如可读)时,内核再通知应用线程处理。相比传统的‘一个线程对应一个 IO’,它避免了大量线程的阻塞和上下文切换,而 epoll 通过红黑树和就绪链表实现了 O (1) 效率,是 Redis 等高并发组件的核心底层支撑。
十、Mysql的底层索引原理介绍一下
MySQL 索引的本质是帮助引擎快速定位数据的数据结构,核心目标是减少磁盘 IO 次数。不同存储引擎的索引实现差异很大,最常用的 InnoDB 和 MyISAM 均基于 B + 树,但底层逻辑、数据与索引的关联方式完全不同。Mysql它的存储引擎主要是分为InnoDB和Memory还有MyISAM,底层索引主要是使用了一种B+树的结构,叶子节点存储具体的数据和主键索引,而非叶子节点就去存储索引信息,当我们查数据的时候,先去判断非叶子节点上是否有需要的数据,没有就根据存储的主键索引信息去叶子节点上面查询,这个过程也就叫做回表,使得树的高度降低了许多,这样就使得即使是1000w的数据,只需要3/4次IO就能查找到数据。同时叶子节点之间还使用双向链表进行了连接,在对于范围查询的时候能够拥有更快的效率。
十一、B树的B+树的区别
B+树最大的有点就是叶子节点存储数据和主键索引信息,非叶子节点存储索引信息,这样使得索引和具体的数据进行了分离,而B树没有进行这样的操作,使得B+树和B树在相同的IO次数下,B+树查询到的信息更多,同时B+树还可以进行范围查询。
十二、Mysql它的事务是如何实现的
事务的特性就是ACID嘛,A原子性(所有操作要么一起成功,要么一起失败)主要是通过undo log这种日志来实现的,也就是说当我们执行一条操作的时候,这时候undo log日志就会记录这条操作的反操作,如果事务进行了回滚,就从undo log日志上面执行这条反操作。C一致性是由其他3个特性共同保持的。I(隔离性)(各个事务之间相互不会干扰),主要是通过加锁的方式,当我们开启一个事务之后,会对相应的数据行进行加锁,这样就保证了该条数据不会被其他事务影响以及MVCC实现。D(持久性)主要是通过redo log来实现的,当我们的机器宕机之后,会从redo log里面会恢复数据。先写 redo log,再写数据页(刷盘)。宕机后,先重放 redo log 恢复已提交的修改,再通过 undo log 回滚未提交的事务,保证已提交的数据不会丢失。默认配置下事务提交时 redo log 立即刷盘,确保最高安全性。
十三、你平时实习的时候有用到过Mysql事务这些,遇到过一些问题吗
十四、说说你用过的设计模式,怎么用的
我用过的主要是责任链模式和策略模式,
十五、你怎么想着用策略模式和责任链模式呢
策略模式也就是说:我们定义了一个接口或者抽象类,去规定了这一行为,比如状态的扭转这一行为,然后具体的实现内容比如人员从未被抽取到已被抽取,活动从未结束到已结束这个过程,每个类具体实现的内容都不一样,但是他们做的都是一件事情就是状态的扭转。避免了大量if-else和switch判断不同逻辑。举例:假设电商平台支持 “支付宝”“微信支付”“银行卡支付” 三种方式,每种支付的流程不同,但客户端只需调用 “支付” 动作。
责任链模式:创建了一条链,这条链上面有许多个接收者也就是方法,当参数传递过来的时候,就去看那个接收者能够去处理这个参数。举例:假设订单折扣审批规则:折扣≤10% 由组长审批,10%<折扣≤30% 由经理审批,折扣> 30% 由总监审批。
十六、JVM内存模型
1.程序计数器:私有的,记录下一条指令执行的地址,最小的一块区域
2.虚拟机栈:私有的,与方法的执行有关,当方法执行的时候虚拟机栈会开辟一块栈帧,里面包含一些参数信息啊,返回地址,动态链接等信息,同时还有有局部变量表,将这些变量拿到操作数栈里面进行操作,然后返回结果,当方法执行完毕之后,这个栈帧也就销毁了
3.本地方法栈:私有的,与底层的NATIVE方法相关,与底层JVM实现有关
4.堆:共有的,最大的一块区域,主要存储一些对象和变量,通常也是垃圾回收发生的主要区域,分成2块区域,一块新生代,里面又分为Eden,S0,S1区,一块老年代
5.方法区:共有的,主要存储一些常量和一些静态变量,用于存放编译期生成的各种字面量和符号引用。
十七、简单说一下垃圾回收
其实垃圾回收也就是程序运行之后,JVM去自行判断是否需要对一些数据进行清除,然后腾出空间。主要是有好几种垃圾回收器吧,最经典的就是串行垃圾回收器,当发生垃圾回收的时候,其他所有操作都被阻塞了,现在都不怎么使用了,然后就并行化垃圾回收器,多线程并行回收,注重吞吐量(吞吐量 = 运行用户代码时间 / 总时间);STW 时间较 Serial GC 短。CMS回收器:老年代并发回收(与用户线程并行),STW 时间短;内存碎片多,CPU 消耗高。G1回收器:兼顾吞吐量和响应时间;可预测 STW 时间;自动平衡新生代和老年代回收。
十八、AQS了解吗
AQS就是javajuc里面的一个基类吧,很多东西都是根据这个AQS实现的,例如信号量,cyclicBarrier这些,主要是存在一个state表示同步状态,还有一个CLH的队列,state就是一个变量通过volatile和CAS实现,state标识锁的状态,0表示未占用,大于0表示被持有,通过CAS来修改state的值来保证原子性,如果线程获取锁失败,就把线程挂到CLH队列中,然后挂起,等前驱节点释放了,再唤醒。