大数据java面经一

一: sleep/interrupt/wait 的区别
interrupt 并不会中断线程啥的,而是改变线程里面线程中断位的一个标志;

sleep 方法是Thread的,而wait 方法是属于Object 类的。

sleep 方法交出cpu,但是状态依然被监控;而wiat 方法则进入等待队列,一点没有被监控到;

sleep 方法不会放弃锁,而wait 方法会

2;锁的大分类
乐观锁:读多写少;CAS

悲观锁:写多读少;synchronized 同步锁

自旋锁:空着自旋一会,等着获取锁,因为进入阻塞队列再唤醒的资源比自旋多。

3: synchronized 锁:

waitSet: 调用wait 方法阻塞的线程放在这个集合里

contentionList: 所有竞争锁的对象都放在这个队列

EntryList: 作为候选者的竞争锁的对象

onDeck: 即将获取到锁的线程,正在竞争锁的线程

Owner: 已经获取到锁的线程

!Owner: 已经释放锁的线程

4: ReentrantLock 可重入锁

4.1: 使用lock 加锁,使用unlock 解锁;而synchronized 是需要系统自动解锁的
4.2: 有公平锁和非公平锁之分,公平锁就是老老实实地等着,非公平锁就是不停地去抢锁,抢到了就占用
4.3: 可中断锁,可轮询锁。

6:AtomicInteger,AtomicBoolean 等,让这些基本对象的操作是具有原子性的,效率是ReentrantLock 的好几倍

7: 可重入锁,就是可以多次加锁,不会因为之前有锁而不获取锁了。

8: 线程上下文切换说白了就是CPU从寄存器和程序计数器中把其它线程之前的状态拿过来,把当前线程的那些相关的变量给保存起来;以供后续使用。就是个资源让出的结果嘛,要么当前线程被阻塞了,被挂起了,正常执行完了等

9: java 线程池工作原理

 有两个值要知道,corePoolSize 和maxPoolSize, 一个是当前可以容纳的可以运行的线程数大小,一个是最大线程数;如果我们调用execute方法,会先判断当前线程数大于corePoolSize 与否,小的话就立马创建线程运行任务,大了的话并且小于maximumPoolSize就会把它放到队列中,会创建非核心线程来运行这个任务;如果队列满了的话就会跑出 rejectedException;
 如果线程没事可做且超过了keepAliveTime 则会把干掉

10: 阻塞队列就是为线程池服务的,自动把要等待的那些线程放在阻塞队列中,有空位置你再跑出来吧,说白了就是我们日常食堂排队,等上一个打完饭我们才能上去。

11: CountDownLatch, 就是多线程里面的一个计数器,它会去计算某个线程要等多少个线程执行完毕之后他们才开始执行;
CyclicBarrier,回删栏,很像我们的跑道起跑线似的,等所有的线程选手到达了这个起跑线的时候大家就开始同时起跑执行任务;
Semaphore 信号量,就是定义一个值,多个线程竞争去获取,控制线程访问的个数;当超过阈值的时候想要获取则会被阻塞,相当于进入了阻塞队列;也有自己的方法去获取锁

12: java 中 volatile关键字的作用,
java 中出现volatile 就是为了打造一个更轻量级的锁,不要像synchronized 那样那么重,volatile 一般是用来修饰变量的,它保证修饰的变量修改了在所有线程中都是立马可见的的,说白了它就是在主内存中把这个变量修改,不是放在每个线程的cpu的高速缓存中读这个变量,(变量可见性)
对这个变量的操作都是原子性的,在这里理解就是基本只有一步操作的,原子性说的是要么全部执行,要么都不执行,某个操作过程,这里对变量加了一把小锁;

而且它是禁止指令重排序的,一般系统为例提高效率就会把指令重新排序,但是volatile 涉及的变量就不会有指令重排序

13: java 线程里面的调度算法,
抢占式调度(),谁能获取到资源系统就让cpu给资源调度,这个就是JVM里面的线程调度;
协同式调度,就像接力赛一样,一个线程运行完就通知系统让另一个线程接着干活,像接力赛一样,这个很容易阻塞进程;

14: 进程调度算法,包括先来先服务算法,短作业优先算法,高优先级调度算法;

15:什么是CAS 算法,就是比较并交换,CAS(V,E,N),v 就是这个变量,表示要更新的值,E 就是预期值,旧的值;而N 是新值;如果E和V一样了,那么就会把N赋值给V;否则说明有其它线程更改了值使得E值变了,所以就不会赋值,什么都不做,返回v值;AtomicInteger 就是用了这个算法;

CAS 会碰到一个ABA问题,这个问题的解决办法就是增加版本号。

16: AQS: 什么是AQS,AbstractQueueSynchronize,就是抽象队列同步器,它是很多锁的父类,比如ReentrantLock,CountDownLatch,Semaphore 等;
AQS 里面有两个关键点,一个是state,一个是阻塞队列;而且这个state 的值可以理解为有几把锁,阻塞队列就是里面有线程,轮番去访问这个state ;
所以这就分成了独占锁(ReentrantLock)和 Semaphore(共享锁),独占锁实现的方法是try_acquire 和try_release ; 而共享锁实现的关键方法是try_acquireshared 和try_releaseshared
所以独占锁就是要state 变为零其它线程才能获取锁,try_acquire 获取锁state 变成1,try_release 变成0 释放锁;如果是共享锁一下子state 可以变成N;后面每个字线程减掉一次state变成0 才继续后面的动作。

二:Java 基础

Throwable 是所有异常的父类,Error 和Exception 都是它的子类

2.1: Java 反射

2.1.1 : 动态语言,运行过程中可以改变结构,新的函数可以被添加,已有的函数可以被删除等,java 是半动态语言
2.1.2: 反射机制,在程序运行的过程中,可以调用类的任何属性和方法,也可以调用对象的任何属性和方法。
2.1.3: java中的反射的api, Class 对象,Method,Field,Contrustor 对象,Class.forName(“类的全路径名”) 和 p.getClass() 获取Class;

2.2: Java 注解

2.2.1: Annotation 对象,通过注解来获取Annotation 对象,然后通过这个对象来获取注解的元信息。
2.2.2: target 注解说明了作用的对象是谁,Retention 注解说明了这个注解的有效期;Inherited 说明了哪个类是被继承的;

2.3: Java 内部类
什么是java的内部类呢?就是java 类中又定义了一个类呗,既然可以定义方法和变量,也可以定义类嘛;按照定义的方式不同,可以分为静态内部类,成员内部类,局部内部类和匿名内部类;
2.3.1: 静态内部类,就是以static 申明一个类,就像HashMap 里面的Entry类一样,这个类可以访问外部类的所有的静态成员和方法, 而且可以自己定义静态成员,方法和变量;
2.3.2: 成员内部类,那就是不是静态的内部类,没有用static 申明的内部类;他不能定义静态方法和变量
2.3.3: 局部内部类,那就是在方法中使用的类,只需要在方法中用到
2.3.4: 匿名内部类,这个必须实现一个接口或者继承一个父类,通过new xxx 来实现。

2.4: Java 泛型,类型擦除,就是编译期间会有泛型,但是生成的class 文件已经把对应的类型具体替换了。

2.5: Java中的复制,直接赋值复制,浅复制(复制饮用但是不复制引用指向的对象,和直接复制差不多);深复制(复制对象以及它指向的所有对象,序列化就是这样的一种体现);

三:Spring

3.1: spring 的特点
3.1.1: 轻量级,就是可以用不到1M的文件里面就可以把各种东西配置齐,里面的应用对象不依赖于spring的类
3.1.2:控制反转,IOC,就是说某个对象用到的对象会被动地传过来,不需要对象自己去寻找这些对象
3.1.3: AOP,面向切面编程,意思就是把应用逻辑和系统服务可以分开
3.1.4: 容器化,就是可以自己管理并配置并决定对象的生命周期
3.1.5: 框架,可以通过简单的组合实现复杂的应用,利用一个xml 文件就可以实现。

3.2: spring 的核心组件
AOP(aspect)/ web/ core container(Bean,Core, Context)/ data Access(JDBC) /

3.3: Sring IOC, 全称是spring Invocation of Control, 就是控制反转的意思,把对象的管理都交给容器了,怎么实现的呢?

 3.3.1: 首先状态bean 的相关xml 文件,找到相关的bean,然后放进注册表
 3.3.2: 从注册表中取出来bean 并且实例化(BeanFactory)
 3.3.3: 实例化之后放进容器,然后供应用程序使用(ApplicationContext)。
 3.3.4: 注入bean的方法,构造器注入,setter 注入,静态工厂注入

3.4: Spring AOP;全称是 Spring Aspect Orinted Programming ,说白了,就是一刀下去,看看那些模块是大家都要用到的,但是和业务又没啥关联的业务逻辑抽出来,放到一个模块中去写;
这里很重要的一个概念就是在对方法进行拦截之后,然后进行相关的逻辑处理,之后再去通知相关的对象

3.5;jpa ,全称就是 java persistence api,一套持久化规范,spirng data jpa 就是spring orm 框架演变出来的,为了集成统一,省的和各种hibernates,mybatis 打交道
事务就是分为本地事务和分布式事务,
事务有两阶段提交,一个是准备阶段,一个是提交阶段。

3.6:

四:Netty与RPC

4.1: Netty 使用多路复用IO 模型,RPC,就是Remote Procudure Call ,远程服务调用;RMI,就是Remote Method Invocation, 就是远程方法调用。

五: 设计模式

5.1: 设计模式是什么呢?说白了就是前人在软件开发中的一些经验,然后传承下来,核心思想就是能复用代码就复用;提高代码的可扩展性和可维护性。

两个原则

5.1.1: 开闭原则,对新增代码开放,对修改代码关闭

5.1.2: 里氏替换原则,父类可以使用某个方法,子类也可以使用。

三种类型:
5.1.3: 创建型模式,这个的重点是关注如何创建对象,把对象的创建和使用分离开来。
5.1.4: 结构型模式,就是讲究如何使用各种方法把各种对象组合起来,以实现 更加灵活的结构和组合,虽然继承灵活,但是不够
5.1.5: 行为型模式,讲究的是确定算法和对象的职责,如何让两者更好地协调起来完成一个任务。

5.2: 创建型模式之生成器模式
这个是为什么呢?因为构建一个对象需要很多零件,所以我们就要把这些零件一一组合起来,最常见的就是Builder,把一系列的东西组合起来;

5.3: 创建型模式之工厂方法,定义一个接口创建类,但是由子类去决定使用哪个实现,创建一个什么样的类

5.4: 创建型模式之抽象工厂,提供一个创建一系列依赖对象的接口,而无需定义具体的类,都是定义的抽象的方法,这些抽象方法的返回类,具体实现逻辑由子类去实现

5.5: 创建型模式之原型模式,就是创建对象的时候根据现有的一个对象的原型来创建,说白了就是拷贝对象,类里面自己实现一个copy方法,不常用。

5.6: 创建型模式之单例模式,一类只有一个实例。

5.7: 结构型模式之适配器模式,就是把一个结果经过适配器转成另一个接口,方便用户使用,比如把Callable 换成Runnable,inputstream变成inputstreamReader等。

5.8: 结构型模式之桥接模式,将抽象部分与它的实现部分分离,使得都可以互相变化,解决子类爆炸的问题,比如生产不同类型的车如果都放在一个生产线上,会相当的复杂,所以就抽象出来,搞发动机的一条线,搞电机的一条线,搞车载系统的一条线,然后生产之后组装起来,桥接模式里面都是定义的接口和类的组合。

5.9: 结构型模式之组合模式,经常用于构建树形结构,相当于构建一棵树,把子节点和父节点组合起来统一整理。

5.10: 结构型模式之装饰器模式,装饰装饰,说白了不是核心功能,就是为了给某个类增加附加功能的,这个时候就是用到了装饰器模式,可以在运行期间给增加额外功能,比较容易添加。

5.11: 结构型模式之外观模式,哈哈哈,我觉得应该叫中介模式,就是说白了假如一个类的对象要和很多子系统打交道,而且它又不熟悉,怎么办呢?那就委托一个中介呗,让中介把这些事完成就行了,就是叫Facade

5.12: 行为模式之命令模式,说白了就是给一个请求命令,然后给到想要的结果,把请求封装成一个对象,编辑器的例子。

5.13: 行为模式之策略模式,策略模式说白了就是把一系列算法封装到一个对象里面,然后再根据客户传入的策略执行命令,比较常见的有购物卡打折,数组排序等。

5.14: 行为模式之中介模式,把多方会谈变成双方会谈,减少系统之间的耦合度。

六:网络

6.1: 网络通信的七层模式

 6.1.1: 物理层,物理物理,说白了就是和真实的世界做交互的,一般定义了网线的接口协议,光纤的接口协议等;还定义了传输的数据速率等,在这一层传输的就是比特位的数据,比特流,我们常说的数模转换和模数转换就在这一层。

 6.1.2: 数据链路层,这一层主要是设备和交换机打交道,把物理层传过来的数据加上mac地址进行解封装,常把这一层的数据叫做帧

 6.1.3:网络层,这一层总要和ip打上交道吧,这一层会把上一层传递过来的数据进行ip地址的封装与解封,这一层的数据就叫做数据包了。

 6.1.4: 传输层,定义了一些传输数据的协议和端口号,得到了数据之后总要相互之间进行传输了吧,传输协议是啥,端口又是啥呢?这个时候就需要我们进行定义了,协议比如TCP 协议,效率不高但可靠;UDP 协议效率高但是不怎么可靠,qq消息就是这个协议,通常把这一段的数据叫做段;

 6.1.5: 会话层,准备好了协议和端口号等东西就准备好洽谈会话了呗,这个时候洽谈会话的前提链路要通啊,这个会话层就是负责给双方建立通道的。

 6.1.6: 表示层,拿到了数据之后总要让人可以看得懂吧,所以就要把数据进行一个加解密,解压缩等操作,表示表示嘛,这就是表示层要做的工作。

 6.1.7: 应用层,主要就是终端的一些应用,用户可以看到的东西,比如ftp,qq,web 浏览器等

6.2: TCP/IP 协议,指的并不是TCP 和IP 两个协议,而是指的整个互联网的TCP/IP 协议家族;它一般是由四层组成,网络接口层,网络层,传输层,应用层。

  6.2.1: 网络接口层,这一层没有具体的定义,只是说主机和网络连接必须使用某些特定的协议。
  6.2.2: 网络层,最关键的一层,就是主机要把分组的顺序发往目的地,发往任何网络,不管顺序如何,分段如何,要精确到大目的地,互联网使用的是IP协议
  6.2.3: 传输层,使远端和目的段对等的主题可以进行会话,这一层定义了两个协议,TCP 和UDP 协议,一个是面向连接的可靠协议,有流量控制,多路复用等功能,一个是面向无连接的不可靠协议,没
  6.2.4: 应用层,包含了很多我们常见的高层应用协议,如ftp,smtp, telnet协议等。

6.3: TCP 三次握手和四次挥手:

 三次握手和四次挥手说白了就是建立连接和断开链接的过程 
 
 6.3.1: 三次握手之第一次,客户端向服务端发送请求报文,随即生成了一个数字x,这里我们叫做序列号seq,然后还在报文里面加上了一个标志位为SYN=1(就是一个同步标志);

 6.3.2: 三次握手之第二次,服务端收到请求后,也给客户端发送相应报文,报文里面依然包含了SYN=1 的同步位以及ACK=1的有效位,而且也生成了一个随机数字seq=y,不过还附带了一个内容,就是ack 字段,它的值是x+1, 表示我服务端已经收到你客户端的请求了;

 6.3.4: 第三次握手,客户端收到了服务端的请求后,也立马表忠心好的,老大,继续给服务端发送过去一个报文,这个时候的ACK=1 仍然没变;但是ack=y+1; seq=x+1; 这个时候的客户端就觉得客户端没有什么问题了,可以建立连接了,所以两者就开始通信了。

 为什么不进行两次握手或者四次握手呢?
 
 不进行两次握手的原因在于以防网络阻塞或其他原因导致报文延迟到达,因而产生了错误;如果一个请求延迟了,a又发送了一个请求,新的请求ok,延迟的请求又到达了B服务端,b服务端于是发送确认报文,但是a早就不管那个请求了,于是B就会在那一直等着耗费资源;

 四次握手也没必要,第三次双方都确认过了,知道没问题了,再来一次是多余的,没有完全可靠的通信,所以不需要反复进行确认。

 TCP 的半关闭状态决定要四次挥手

 6.3.4: 四次挥手之一:客户端没有数据再发了,想关闭链接了,这个时候就会向服务端发送中断报文,里面有个FIN码,就是关闭链接的标志,FIN=1, 然后随机生成一个数u,seq=u; 此时数据FIN_WAIT 状态

 6.3.5: 四次挥手之二:服务端发送确认报文,ACK=1 说明有效确认好了,ack=u+1, 是回复的报文,然后自己随机生成一个序列号为v, 组合起来把报文发送过去;

 6.3.6: 四次挥手之三:服务端也没有数据了,请求和客户端端开链接,所以也要发送一个FIN码,FIN=1;一个确认标志ACK=1; 随机生成一个序列号,seq=w;ack=u+1; 此时处于LAST_LOCK 状态;

 6.3.7: 四次挥手之四:客户端收到了服务端的中断相应的报文,也需要回复确认下,此时ACK=1;seq=u+1; ack=w+1 组成报文发送过去;但是这个时候客户端不会立马端开链接,还是得等下2MSL 时间去断开,防止这个报文的丢失。

6.4: http 原理

6.4.1: 简介,http 是属于应用层的协议,它是属于一个无状态的协议,无状态是指客户端和服务端不需要建立很久的链接,客户端和服务端通信之后,服务器响应,响应之后就会关闭TCP 链接。

6.4.2: 通信过程
1: 地址解析: 输入一个url之后,会进行地址解析,把协议,域名,端口以及对应的路径解析出来
2: 封装数据包,把上述的东西加上主机自己的信息封装成一个数据包
3: 建立TCP 链接,上述的数据包确认加上TCP 协议,进行三次握手
4: 像服务端发送数据
5: 服务端返回数据
6: 服务端关闭TCP 链接。

6.4.3: https 原理,https 说白了就是http 的安全版,在TCP 连接的过程中加入了ssl ,有些加解密的操作,如何实现的呢?

   6.4.3.1: 首先在建立TCP 链接的时候,ssl 客户端会给ssl 服务端发送一个请求,带上自己客户端的一些信息

   6.4.3.2: ssl 服务端收到以后会发过来报文确认,并且还发过来了密钥的相关信息,包括域名,公共密钥,以及签发机构

   6.4.3.3: ssl 客户端收到后会确认域名以及密钥是否是相应的机构颁发,如果是的话,就会生成对称密钥并且使用公共密钥加密,然后发送给服务端,服务端用她的公共密钥解密

   6.4.3.4: 然后大家通信都使用对称密钥加密的密文通信了。

七;大数据

1 : yarn 机制了解

 yarn 是一个hadoop 2.X 的工作调度器,保持工作正常运行;

 ResourceManager: 一个全局的资源管理器,所有的资源申请都需要经过它的同意,就是管着钱袋子的财政部长

 NodeManager: yarn 上实际的作业运行的物理节点,就是提供存粹的作业运算的节点,并时刻和RM 通信报告自己的情况;

 ApplicationMaster: 管理每一个运行程序的对象,管理作业的全局生命周期,有一个作业运行就有一个对应的ApplicationMaster; 和RM 通信申请资源,汇报情况;和NM 通信获取实际资源

 Container: 运行作业的实际容器,把NodeManager 的资源抽离出来一部分构建出的一个容器;

 yarn 的实际工作流程,以MapReduce 工作为例;

 1.1 : 客户端向RM 提交任务运行申请;
 1.2: RM 返回一个存放资源的路径 hdfs://xxx/staging 和applicationId;
 1.3: 客户端把任务的资源提交到刚刚的路径上,比如wc.jar,xx.xml,xx.jar 等文件;
 1.4: 客户端请求运行ApplicationMaster 
 1.5: RM 把请求初始化一个任务,初始化了appMaster, 把任务放到了任务队列中;
 1.6: RM 通知NM 去启动初始化一个Container 准备运行一个任务
 1.7: appMaster 通知Conatiner 把刚刚路径下的所有资源都下载到本地,准备运行任务
 1.8: appMaster 和RM以及NM 通信,让Container 准备运行任务
 1.9: 发送程序启动脚本,运行MapTask 任务
 1.10: appMaster 向RM 请求运行ReduceTask 任务
 1.11: NM 启动两个Container 运行ReduceTask任务,与mapTask 进行数据通信
 1.12: 任务运行完成之后AM 向RM 请求注销自己。

二:Hadoop 解决数据倾斜的办法

2.1: 在map 端提前进行Combine,减少数据分发的数量

2.2: 给map的key 加上前缀,增加一个随机分发

2.3: 增加reduce的并行度;

2.4: 实现自定义分区

三:Zookeeper

CAP 定理:就是Consistency(一致性), avalability(可用性),partition tolerance(分区容错性)
 要想达到一致性,就会失去可用性;要想可用性,就会失去一致性,分区容错性分布式系统一定要实现的,不能一个人挂了整个系统就起不来了吧;
 zookeeper 符合了cp,一致性和容错性

四:Flume

Flume 是一个分布式的可靠和高可用的日志收集,数据传输系统,它的核心是一个个事件,像水流一样流经不同的地方,分别有source, channel以及sink,而这三个组件都是在一个agent的进程里面,agent 是Flume的运行核心;说白了就是Flume的代理;每一个agent 都是运行在一个jvm里面

4.1: source,说白了就是对接不同的数据源,source 在监听对应的数据源里的数据,符合它定义的规则的事件就会被他捕捉到,然后采集过来;我们重点说下taildir source;
在之前有两种source, ExecSource 可以监听某个文件,但是agent 挂掉之后会重复消费;Spooling directory 是去监听目录下新的文件,不适合实时的数据消费监听;
taildirSource 是综合了两者的优点;可以实时监控一批文件,会实时记录文件的偏移量,知道消费到那里了;所以数据也不会重复消费;

   所以taildirSource 就是断点续传的榜样,flume 1.7 实现的;
   如果有重复数据,不处理,交给下游处理;
   而且它不支持遍历递归,要自定义;

4.2: Channel ,说白了就是一个流水通道的管子,这里也是可以理解为缓冲区,Channel 又分为File Channel,Memory Channel, Kafka Channel 

    4.2.1: File Channel 说白了就是把数据存放在了文件里面,优点是可靠性高,缺点是传输效率慢,要经过IO;最多能存储100万个event

    4.2.2: Memory Channel,把数据流到内存去; 然后再由sink 拉取消费,这个的优点是传输效率高,毕竟没有IO,但是可靠性不太行,容易丢数据;最多存储100个Event;

    4.2.3: Kafka Channel, 把数据发送到Kafka吧,如果下游是kafka的话,这个的效率大于 memory Channel + Kafka Sink

    4.2.4: 生产环境如何选择,下一级是kafka 选择Kafka Channel,如果是对数据准确性要求高的公司,选择File Channel; 如果要求效率的,memory Channel

4.3: Sink , 这个说白了就是数据的目的地了,数据最终需要传输到哪个地方,一般就是hdfs sink 用的比较多,不过要配置好自己的rollInterval 和rollSize, 确保不会生成太多小文件;

     source 到channel 是put 事务,channel 到sink 是take 事物。

4.4: Flume 拦截器,
      自定义interceptor 需要实现四个方法,初始化,intercept,intercept(处理多个事件)

4.5: Flume Channel Selector,可以根据规则将不同的数据发往不同的Channel,有Replicating channel selector 和 Multiplexing channel selector; 前者是发往所有的channel,后者是选择性的发送;

4.6: Flume 采用Ganglia 监控器,

五:Kafka

5.1: zookeeper 中的controller 是从Broker 中选举出来的,负责分区的leader 和follower 的管理。竞选的时候会去读取/Controller 下面的brokerid 的值,判断是否为-1;为-1 则竞选,不为-1 放弃竞选。
controller 负责管理集群的信息以及所有的元数据信息,还负责topic 分区以及副本的leader 以及 follower 选举。

partition 就是一个log 文件,leader 和follower 是针对分区的副本说的,一般分区中有一个leader,其余的是follower

index 存储的是key,value 对,key 是某个具体的offset,value 则是在log 文件中的偏移量;log 和index 文件是一一对应的

5.2: 所以如何根据offset 定义一条数据的位置呢?
首先,我们的partition 会被分成很多log 文件,为什么呢?当然是提高数据的查找效率,当log 文件大于1G的时候,就会产生新的log 文件,有一个log 文件生成就必定有一个index 文件一一对应,这两个是成双成对;然后每个log 文件的名字都是第一个offset 值,所以如果想定义到数据在哪个log 文件里面,首先根据二分查找看看是落在哪个segment。

    找到某个log 文件之后,就根据index 文件找出来偏移量,然后顺序查找就行;比如我们要找的offset 为6345678;然后有个log 文件是 6345145.log,下一个log 文件是8904567.log ,说明这个offset 对应的数据就在第一个log 文件中;
    这个log 文件对应的index 文件就是上面说的<key,value> 键值对,key 是抽选出来的一些offset-当前文件开头的offset+1,value 是他们在log 文件中的字节偏移量;

    所以只要把6345678 - 6345145 = 533;然后再次使用二分法查找,找到差不多533的位置,或者靠近的,然后就顺序往下读就可以了。
  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值