大数据学习总结+JAVA学习总结+大数据面试+java面试+大数据java异常总结

大数据 同时被 2 个专栏收录
6 篇文章 0 订阅
4 篇文章 0 订阅

文章目录

一、java模块面试问题

1.动态代理

利用反射机制在运行时创建代理类。

1.基于JDK的动态代理:是基于接口的动态代理 ,也就是要继承以接口

2.基于CGILB的动态代理:可以代理普通的java类

​ cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“ 父 类 方 法 名 父类方法名 ”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

​ 这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

​ C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

​ jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

​ 在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者添加后续操作,但是不干预实现类的正常业务,把一些基本业务和主要的业务逻辑分离。我们一般所熟知的Spring的AOP原理就是基于动态代理实现的。

2.浏览器禁用Cookie后的Session处理

\1. 实现购物车, 可以基于Cookie, 也可以基于Session, 若服务器性能较差, 可以考虑基于Cookie实现购物车

\2. 解决方案: URL重写

把用户可能点的每一个超链接后面,都跟上用户的sessionid号

  1. sendRedirect方法后的URL地址进行重写
response.encodeRedircetURL(url);
  1. 表单或超链接的URL地址进行重写
response.encodeURL(url);

3.主从复制的流程[重要]

1.创建一个注解用来 标注写操作,在server中写的地方加该注解

2.创建一个可以存储数据(读还是写),有一个线程空间,有存储数据和取数据的方法

3.用AOP去读读取注解,用环绕增强,记得抛出一个自定义异常,读取方法的注解,判断,把状态放入到线程空间中

4.创建一个数据源路由类继承DataSource ,在该类中获取线程空间中的数据,定义一个方法返回要是使用的数据源,如果是读,可以加一些轮询,随机之列的策略

5.A、在重写的数据源类里面,创建所有的数据源对象交给工厂,写获取数据源的方法,返回一个路由类,里面存储了要使用的数据源,把返回值路由类,交给工厂管理

B、自定义SqlSessionFactory覆盖spring工厂的 :写返回SqlSessionFactory,的方法,参数是路由类,在方法里面对创建SqlSessionFactory设置数据源,例如给实体类起别名,加载映射文件,等,然后把该对象返回出去,交给工厂管理

C、自定义SqlSessionTemplate覆盖spring工厂的,

D、自定义事务管理器PlatformTransactionManager覆盖spring工厂的

5.redis的数据类型[重要]

  1. String :字符串 string是redis中最基本的数据类型 每个key最大可以存512MB

  2. Hash :哈希 是一个键值对集合,是一个String类型的field和value的映射表 特别适合存储对象

  3. List: 列表 是一个简单的字符串列表,按照插入顺序排序,也可以把一个元素插入到头部或者尾部

  4. Set: 元素集合 集合的元素是不可重复的 集合是通过哈希表实现的,所有添加删除的复杂度都是o(1)

  5. Zset: 和set一样也是一个String的集合,且元元素不也重复。每个元素关联一个double类型的分数,通过分数对集合中的元素进行从小到大的排序,zset的成员是惟一的但是分数是可以重复的。

6.Junit测试的注解

  • 在Junit测试中还有一些注解,需要知道
    1. @After:在@Test标记的方法执行之后执行
    1. @BeforeClass:在类中的static方法执行之前
    1. @AfterClass:在类中的static方法执行之后

7、缓存问题

扩展缓存穿透/击穿/雪崩

  • 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

    (1) 原因

    查一个不存在的数据,由于数据不存在,每次查询都会去数据库查询。如果有人利用这个漏洞去伪造大量请求,很可能导致DB承受不了大量请求而挂掉。

    (2) 解决方法

    事前预防:对所有请求进行参数校验,拒绝非法请求(布隆过滤器)

    事后预防:当查询到一个空值时,仍然把这个空结果缓存,但是设置一个很短的过期时间

  • 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

    (1) 原因

    在热点key失效的瞬间,海量的请求方法数据库,导致数据库崩溃。

    (2) 解决方法

    互斥锁:

    ​ *是在缓存KEY过期去更新数据的时候,先让程序去取锁,只有取到锁的程序才有资格去更新缓存KEY,其他没有获得锁的线程先休眠片刻再去获取最新的缓存数据。通过这种方式,同一时刻只有一个线程去读取数据库,这样也就避免了大量数据库请求对于数据库的冲击。

    永不过期:将缓存设置为永不过期,通过定时任务去同步缓存与数据库的数据。

  • 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

(1) 原因

多个key同时过期,或者缓存层失效,大量请求直接进入数据层,导致数据库不堪重负而挂掉

(2) 解决方法

· 在key的原有失效时间上加上一个随机时间,来降低key的过期时间的重复率,这样可以减小缓存雪崩的几率。

· redis高可用集群

· 限流降级 在缓存失效后,通过加锁或队列控制读数据库写缓存的操作,比如对某个key加锁,只允许一个线程读数据库写缓存,其他线程等待

缓存穿透解决方案

一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

有很多种方法可以有效地解决缓存穿透问题最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

/伪代码
public object GetProductListNew() {
    int cacheTime = 30;
    Sts
    String cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) {
        return cacheValue;
    }

    cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) {
        return cacheValue;
    } else {
        //数据库查询不到,为空
        cacheValue = GetProductListFromDB();
        if (cacheValue == null) {
            //如果发现为空,设置个默认值,也缓存起来
            cacheValue = string.Empty;
        }
        CacheHelper.Add(cacheKey, cacheValue, cacheTime);
        return cacheValue;
    }
}

缓存击穿解决方案

key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。

使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

public String get(key) {
      String value = redis.get(key);
      if (value == null) { //代表缓存值过期
          //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
      if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
               value = db.get(key);
                      redis.set(key, value, expire_secs);
                      redis.del(key_mutex);
              } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                      sleep(50);
                      get(key);  //重试
              }
          } else {
              return value;      
          }
 }

memcache代码:

if (memcache.get(key) == null) {  
    // 3 min timeout to avoid mutex holder crash  
    if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  
        value = db.get(key);  
        memcache.set(key, value);  
        memcache.delete(key_mutex);  
    } else {  
        sleep(50);  
        retry();  
    }  
}

缓存雪崩解决方案

与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。

用高可用增加稳定性

缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

加锁排队,伪代码如下:

//伪代码
public object GetProductListNew() {
    int cacheTime = 30;
    String cacheKey = "product_list";
    String lockKey = cacheKey;

    String cacheValue = CacheHelper.get(cacheKey);
    if (cacheValue != null) {
        return cacheValue;
    } else {
        synchronized(lockKey) {
            cacheValue = CacheHelper.get(cacheKey);
            if (cacheValue != null) {
                return cacheValue;
            } else {
              //这里一般是sql查询数据
                cacheValue = GetProductListFromDB(); 
                CacheHelper.Add(cacheKey, cacheValue, cacheTime);
            }
        }
        return cacheValue;
    }
}

加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法!

注意:加锁排队的解决方式分布式环境的并发问题,有可能还要解决分布式锁的问题;线程还会被阻塞,用户体验很差!因此,在真正的高并发场景下很少使用!

随机值伪代码:

//伪代码
public object GetProductListNew() {
    int cacheTime = 30;
    String cacheKey = "product_list";
    //缓存标记
    String cacheSign = cacheKey + "_sign";

    String sign = CacheHelper.Get(cacheSign);
    //获取缓存值
    String cacheValue = CacheHelper.Get(cacheKey);
    if (sign != null) {
        return cacheValue; //未过期,直接返回
    } else {
        CacheHelper.Add(cacheSign, "1", cacheTime);
        ThreadPool.QueueUserWorkItem((arg) -> {
      //这里一般是 sql查询数据
            cacheValue = GetProductListFromDB(); 
          //日期设缓存时间的2倍,用于脏读
          CacheHelper.Add(cacheKey, cacheValue, cacheTime * 2);                 
        });
        return cacheValue;
    }
} 

解释说明:

  • 缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存;
  • 缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。

关于缓存崩溃的解决方法,这里提出了三种方案:使用锁或队列、设置过期标志更新缓存、为key设置不同的缓存失效时间,还有一种被称为“二级缓存”的解决方法。

小结

针对业务系统,永远都是具体情况具体分析,没有最好,只有最合适。

于缓存其它问题,缓存满了和数据丢失等问题,大伙可自行学习。最后也提一下三个词LRU、RDB、AOF,通常我们采用LRU策略处理溢出,Redis的RDB和AOF持久化策略来保证一定情况下的数据安全

二、项目中java模块的难点异常[重要]

前后端分离,前台和后台的交互问题

1.MultipartiFile图片从前台用Restfull到后台问题

注册/添加:添加图片,后台要的是一个MultipartFile,restultfull不能直接把MultipartFile传过去,他要传一个FileSystemResource对象,我们可以先转换成字节数组multipartFile.getBytes()在创建ByteArrayResource对象参数是字节数组,把该对象传输过去就行

但后来发现可以直接multipartFile.getResource(),就得到了一子Resource对象

2.批量删除参数问题

除了页面要关闭参数深度序列化问题

前台链接后台,前台传给后台的数据也要进行处理把前台传来的数组给变成1,5,6,8的格式就是不要中括号

LinkedMultiValueMap<String, Object> request = new LinkedMultiValueMap<>();
//这里要这样写才行
for (Integer id : ids) {
    //这里不能用set
    request.add("ids",id);
}

    restTemplate.postForObject("http://localhost:8087/www.userModel/user/deleteUserByIds", request, User.class);

要对数据进行处理

总结:当我们不知道前台给给后台的数据是什么格式,就可以看页面F12的控制台看发送请求后的参数,按照该格式进行参数处理

3.前台后台的请求方式post,get,del

不同方式要的调用的方法不一样,要传的参数也不一样

4.后台给页面响应string(返回值时String)的时候就需要进行处理

//主页面显示登录的用户名
//当ajax响应的是String,要这样写
@RequestMapping(value = "/getNowUser",produces = "text/plain;charset=utf-8")
@ResponseBody

这里如果不处理,就响应到页面的不是json串,是test文本,页面解析就会出错、乱码

5.导出到excel表格的时候JSON反序列化问题

​ 导出到excel的poi要的是java对象,从后台发来的是经过JSON处理的JSON串,有经过restfull处理变成java类型的的里面数据还跟,前台的类型不一致,例如后台响应来是的是List 格式的

前台得到的是一个List,这时候就需要把List里面的User进行反序列化

第一种用FastJSON的方式进行反序列化

 //取数据
 List list=restTemplate.postForObject("http://localhost:8087/www.userModel/user/queryUserByPage", request, List.class);

//这里不转导出的是空
 List<User> userList=new ArrayList<>();
 for (Object o : list) {
     //把java对象转成JSON字符串
     //把json字符串转换成json对象
     JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(o));
     //把json对象转换成java对象
     User user = jsonObject.toJavaObject(User.class);
     String[] photos = user.getPhoto().split("&");
     user.setPhoto("http://192.168.247.111/"+photos[0]);
     userList.add(user);
     System.out.println("###########"+user.getPhoto());
 }

第二种转换方式,

    String s = JSON.toJSONStringWithDateFormat(list, "yyyy-MM-dd");
    List<User> users = JSON.parseArray(s, User.class);

第三中写法:用的Stringboo它自带的JackSON

6.埋点,统计用户输入特征的时候

这里用的jq自定义插件写一个统计输入框用户输入时间的插件

项目中在注册和登录的时候使用,测试的时候登录和注册的代码片段在一个html文件里,这时候,这个插件就不行了,要把两个面分开,他会统计页面的输入框,在一个文件里面,他的class都一样,例如两个Name,他就不能对输入框进行统计;

把统计的用户输入特征封装到cookie里面,设置一个key在提交表单的时候传到后台

在后台是在拦截器里面获取cookie的,通过key获取,这里可以获取ip 之类的

这里要两个拦截器,一个用在获取cookie的一个用来轻质登陆的,要记得在拦截器配置类里面设置优先级,把第一个拦截器优先级设置最高,也可以在拦截器配置类里面把他写到最前面

7.后台上传图片到FastDFS

后台上传图片到FastDFS的时候要文件的后缀,这里要对文件进行拆分,要打印下看你传的后缀是Jpg还是 . jpg

8.ajax跨域的问题

用代理实现,用restfull实现,微服务SpringCloud用Feign组件使用,可以把请求地址都封装到一个接口里,在controller里面调用接口里面的方法

9、后台用response直接响应字符串到浏览器乱码

除了设置响应的编码集

response.setCharacterEncoding("utf-8");

但是页面的中文仍是乱码的时候就要考虑浏览器这边的编解码方式是不是utf-8 ,我们不管是不是在响应的时候直接指定浏览器的编解码方式

response.setContentType("text/html;charset=UTF-8");

10、自定义JQ插件的时候this问题

我们在自定义插件的时候一般用$(this)来获取使用该插件的标签对象

​ 但是当我们在插件里面往后台用ajax获取数据,得到响应后在对$(this)操作时,这时候就操作的已经不是当前对象了我们要先在外面保存一些this,当前标签 var $this=this;

$.fn.extend({
    //从session中获取数据,在页面中显示
    sessionUser:function(option){
         //里面是要使用该插件的可以使用的参数
        var defaultOption={
            url:"userWeb/getNowUser"
        }
        console.log("获取登录用户")
        //如果插件使用这穿了参数就替换掉上面的默认的,
        //没有传就使用默认的
        option=$.extend(defaultOption,option)
        //load方法是一个jQuery.ajax()的变种,它把后台响应回来的数据直接填充到了dom里面
        //$(this).load(option.url)
        var $this=this;   //=======================这里========================
        jQuery.get(option.url,function(data){
            $($this).html(data+",欢迎登录!")//===================这里========================
        });
    }
 }

11、自定义拦截器问题

当我们自定义了多个拦截器的时候,尽量把要先使用的拦截器在拦截器配置类里面设置放行和过滤的时候放在前面,也可以对拦截器设置优先级,

但我们有的页面明明已经放行的时候但还时进了拦截器,就要看看是否需要把该页面需要的静态资源例如css、js

文件、自定义插件等 ,

要是还不行可以考虑重写addResourceHandler 方法把静态资源所在路径添加进来

注:在把单体项目改成微服务SpringCloud的时候如果页面有问题有可能就是拦截器的问题,看看拦截啥了把他放行了就行

如果拦截器只用来过滤某个请求只用在.addPathPatterns("/**")方法里面写上要进入拦截器的地址就行,例如在我们获取登录数据,做风险评估的是时候只需要拦截登录的请求就行,没必要所有的设置所有的都进入拦截器,然后后面又放行一大堆东西

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器:不登录不能访问拦截器
*/
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    //方法返回true表示放行,否则不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
            String requestURI = request.getRequestURI();
            System.out.println("uri:"+requestURI);
            String username = (String) request.getSession().getAttribute("username");
            if(username!=null){
            return true;
            }else{
            //重定向到登录页面
            response.sendRedirect(request.getContextPath()+"/login.jsp");
            return false;
            }
        }
}
import com.baizhi.web.interceptor.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class MyWebMvcConfiguration extends WebMvcConfigurationSupport {
    @Autowired
    private AuthInterceptor authInterceptor;
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns(
            "/usr/code",
            "/usr/registerUser",
            "/usr/userLogin",
            "/**/*.html",
            "/**/*.js",
            "/**/*.css",
            "/**/*.png",
            "/**/*.jpg",
            "/**/*.gif"
        );
        }
总结:
1. 拦截器的使用。需要注意添加了拦截器,静态资源需要重新配置
2. 解决问题的思路
通过日志:日志级别调整为debug,查看日志信息
对比。比如上课讲的添加了拦截器和没有添加拦截器分别怎么请求的
上网搜,需要筛选
又一个拦截器
拦截器-->再定义一个拦截器,用来获取到用户的输入特征(每一个输入框在输入内容的时候用多长时间)、获取到
用户的设备信息、获取到用户的IP地址(由IP地址就可以获取到经纬度,由经纬度就可以定位到具体的城市)
/**
* 在springboot2.0之后,拦截器添加了之后,会把静态资源所在的路径覆盖掉
* 通过这个方法,把静态资源所在的路径添加进来
* 如果没有这个方法,在请求静态资源的时候,
* springmvc会把静态资源的请求映射到处理器方法(会拿静态资源请求地址找对应的处理器方法)
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/").addResourc
    eLocations("classpath:/static/").addResourceLocations("classpath:/public/").addResourceLoca
    tions("/");
    super.addResourceHandlers(registry);
    }
}

12.reids二级缓存

​ 在写自定二级缓存类的时候,在进行增删改操作要删除该表的该sql的缓存数据,时这里不能直接用flushDB清空,要直接用key删除redisTemplate.delete(id); 这里时大key的值

13.Mysql主从复制

​ 在 配置mysql主从复制的时候要在从机设置一个日志标记的时候,我在配了一个从机的时候 进行了测试,在表中填家了数据,导致主机的日志标记改变,而我在配置其他的从机的时候,日志标记用的不是开始的标记,这就导致,从机的主机的数据不一致,而且没报错,提示配置成功

14.用AOP实现读写分离

我在写AOP类的时候用的环绕增强,在里面判断读操作还是写错做,配完后发现事务没有生效,仔细梳理流程后发现,在写环绕增强的时候,直接把方法的异常用try/catch处理了,这就导致事务管理器没有检测到异常,从而没有进行事务回滚操作,在处理异常的时候有在catch里面在手动抛出一个异常就好了new Throwable();

15.读写分离自定义数据源问题

这里的参数时交给spring工厂进行的自动装配,要i注意

​ 当参数类型和spring工厂中只有一个该类行的对象,可以按照类型进行自动装配

​ 当多个参数类型一致个数一样,工厂中有多个该类型的对象时,会自动按照名字进行自动装配

​ 当参数只有一个,工厂中有很多该类型的对象,而需要的对象的名字,有跟该参数的名字不一致的时候就需 要手动指定一下@Qualifier(“myRoutingDataSource”)DataSource dataSource

最后要记得用自定义的覆盖工厂中的

自定义SqlSessionFactory覆盖spring工厂的
自定义SqlSessionTemplate覆盖spring工厂的
自定义事务管理器PlatformTransactionManager覆盖spring工厂的

16.读写分离自定义路由类和AOP类的问题

自定义DataSource类负责决定用主从三个数据库数据源的哪一个的路由类的注意

这里用的是AtomicInteger(自动递增从0开始,程序不结束一直递增到最大值在从负数开始)

用来回自动递增实现两个从机的轮询访问

这里要注意自动递增用的是Int,他是有范围的我们从0开始,当他递增到最大值的时候就会从负数开始递增

我们就需要判断一下是负数重新计时

  private final AtomicInteger atomicInteger=new AtomicInteger(0);


//通过轮询策略选择slave1或者slave2
 //获取atomicInteger中的值并且让他做自增
 int andIncrement = atomicInteger.getAndIncrement();
 if (andIncrement<0){//如果为负数
     //如果里面获取的数据小于0,就重新从0开始
     andIncrement=0;
     atomicInteger.set(0);
 }
int index= andIncrement%allSlave.size();//0和1来回循环
 return allSlave.get(index);//把操作返回出去

记得在自定义切面判断是读操作还是写操作的时候,记得要把该改操作封装的对放到象线程空间 ThreadLocal

里面才能在路由类里面的线程重取出来

17、读写分离,固定字符串比较多

当项目出现很多的用来进行标记的字符串,比较常用,有不想被改变的值

我们自定义了一个常量里类把这些字符串定义成常量,可以在项目中随便使用,例如用来标记MySql主机和从机的常量类

package com.baizhi.um.config.mysqldatabase;
//常量类,一些固定常用的的不希望被改变的值
public class MyConstans {
    public static final String Master="master";
    public static final String SLAVE1="slave1";
    public static final String SLAVE2="slave2";
}

18、lombok的注意

@NoArgsConstructor注解是指不要构造参数,只有一个默认的
@AllArgsConstructor注解,是指所有参数都有构造参数,但是用了该注解就没有的无参构造
要加@RequiredArgsConstructor才会有无参构造

19、在使用easyUI框架的时候

展示数据datagrid需要table标签最好别设置大小,让他自适应,不然数据网格的底部分页栏在结合布局layout的时候可能会显示不出来

导入easyUI的js、css等时候要注意顺序

在布局的时候使用代码片段,不能要有头部head、body等标签,直接写页面代码就行

三、微服务SpringCloud的异常[重要]

1、Eureka,启动报错可以正常使用

启动完Eureka后, 启动各项服务, 进行发现与注册, 各项服务均能看到正常的注册, 然而Eureka还是会一直不停的尝试连接8761端口服务, 这是为什么呢?

查阅相关文件, 了解到 8761 是Eureka的默认路径, 当Eureka的启动配置里没有对它的url地址指定时, 会默认寻找这个地址, 源码里的默认url如下:

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka

这里在指定了,集群中其他Eureka的地址就不会了

在配置了Zuul网关后,注意访问路径

2.使用了配置服务器,从gitreee中获取配置信息

​ 其中的UserModel中除了application.yml还需要一个bootstrap.yml两个配置文件,我在Application.yml配置把该项目注册到注册中心

application.yml

#eureka的配置
spring:
  application:
#    name: userServer #在注册中心中显示的名字
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8082/EurekaServer/eureka  #eureka服务端的地址
  instance:
    prefer-ip-address: true #在eureka使用的时候,通过IP访问

management:
  endpoints:
    web:
      exposure:
        include: '*' #配置所有的endpoints在web中暴露,可以在页面访问到

bootstrap.yml

spring:
  application:
    name: jdbc #配置文件的前缀
  cloud:
    config:  #ConfigServer的地址端口
      uri: http://localhost:8073/configServer
      profile: dev #要使用的环境 ,就是配置文件中环境的名字
      label: master
  #RabbitMQ的地址,自动刷新
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

两个配置文件中都配置了一个spring.application.name,在启动项目的时候造成了冲突,在获取数据库配置信息的时候一直没获取到,报错,把他们删掉一个就好了

bootstrap.yml先与application.yml加载,当配置中key相同时后加载的就会覆盖先加载的,eureka中注册注册的名字把要读的配置文件的名字覆盖了,在读配置文件的时候自然就报错了,两种解决方法,该配置文件的名字和该注册中心的名字

3.微服务总结面试

​ SpringCloud主要又各个模块组成

​ (1)Eureka:注册中心,一个Eureka集群叫region,里面可用的叫Zone,我们把微服务注册到注册中心中由Eureka统一管理,注册到Eureka的微服务就可以叫Eureka的客户端,他没隔30秒会向Eureka心态,汇报自身节点的状态,当90秒Eureka没有接收到客户端的心跳就会把该客户端注销掉,也就是删除注册信息

注册中心的功能:自我保护机制,当短时间内有大量客户机下线时会自动开启自我保护机制不在注销客户机的信息,而是报错起来,健康检测机制:有的时候客户机没问题能继续心跳,但里面的服务出现了问题,他仍然会采取措施。

(2)springboot项目状态监控工具:actuator:可以把注册中心的服务的节点暴露出来,在浏览器直接访问

(3)Ribbon:负载均衡,服务使用者不在直接到Eureka中发现服务,而是请求Rinnon,他会到注册中心中发现服务,并且会对访问的服务进行访问策略控制,不让所有请求访问某个服务的一个机器,有随机策略,轮询策略、自定义策略,在一个相同的微服务有备机的时候使用

(4)Feign:是一个使用http发送请求掉用其他项目的模块,跟RestFull的功能一样,他可以把所有的请求链接封装到一个接口里面,在controller使用的时候直接调用接口里面的方法就行

(5)Hystrix:断路器,保护集群用过的,平常处于关闭状态,当微服务出现问题的时候会处于打开状态,这时候他就不会让其他请求访问,这个时候每过一段时间会处于半开状态,放行一条请求去探路,如果正常访问就关闭断路器,如果有问题就打开状态, 还有一个就是回退机制,当检测到微服务出现问题的时候会进入到一个事先定义好的一个方法里面执行其代码

(6)Zuul:路由/网关,非常重要,继承了Ribbon/Actuator/hystrix的功能,还可以进行配置请求过滤,当拦截器用好处,不用在每个微服务里面配置拦截器(强制登录/权限控制),只用在Zuul服务器里面定义拦截器就行,他有很多种状态就是在什么时候拦截例如在请求注册中心前,请求微服务前,等等

其他的微服务框架Double/alibaba

四、大数据模块的面试题[重要]

1.spark批处理的问题

1、spark 的standalone模式和yarn模式的区别?Local模式?

1、spark 的standalone模式使用的是spark自身的集群管理器,yarn模式是将spark作业运行在yarn上。

2、standalone对于多用户多application支持的不好,只能支持fifo模式进行资源调度,先来的任务先执行,后来的任务就后执行。也可以通过配置,让先来的任务不占用所有资源,给后来的任务留点资源。

yarn模式对于多用户多任务支持比较好,支持多种资源调度模式,fifo等等。

Standalone是spark自带的默认的资源调度器分为Master和work,

Yarn使用的是hadoop生态的资源调度器分为ResourceManager和nodeManager,

三种模式?

a. Spark-shell
b. Local模式
c. Standalone模式
Spark可以通过部署与Yarn的架构类似的框架来提供自己的集群模式,该集群模式的架构设计与HDFS和Yarn大相径庭,都是由一个主节点多个从节点组成,在Spark 的Standalone模式中,主,即为master;从,即为worker.
d. Spark on Yarn 模式
将Spark应用程序跑在Yarn集群之上,通过Yarn资源调度将executor启动在container中,从而完成driver端分发给executor的各个任务。将Spark作业跑在Yarn上,首先需要启动Yarn集群,然后通过spark-shell或spark-submit的方式将作业提交到Yarn上运行。

2.spark中生产环境为什禁止使用collect行动算子

生产环境禁止使用collect,只是在我们测试阶段使用

由于collect会讲worker(executor)里面执行完的数据拉去到driver端打印

1.大量的网络操作

2.造成driver端的数据阻塞设置崩溃

3.collect返回结果是数组,讲所有执行结果都存在这个数组中,导致OOM

3.sparkRDD的Stage划分和其划分依据(简历技术)

Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。

DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的 依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖, 由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分tage的依据

宽依赖:父依赖对应多个子依赖,窄依赖:父依赖对应一个子依赖

4.RDD的Application、Job、Stage和Task 的区别他们代表着什么 [重要]

RDD任务切分中间分为:Application、Job、Stage和Task

1)Application:初始化一个SparkContext即生成一个Application

2)Job:一个Action算子就会生成一个Job

3)Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。

4)Task:Stage是一个TaskSet,将Stage划分的结果发送到不同的Executor执行即为一个Task。

注意:Application->Job->Stage-> Task每一层都是1对n的关系。

application > job > stage > task (他们的关系都是一对多)

sc = new SparkCotext() 就形成了一个application

rdd.行动算子 就形成了一个job

worker启动executer本质执行就是task任务

5.Spark的优化使用广播变量

如果你的算子函数中,使用到了特别大的数据,那么,这个时候,推荐将该数据进行广播。这样的话,就不至于将 一个大数据拷贝到每一个task上去。而是给每个节点拷贝一份,然后节点上的task共享该数据。这样的话,就可以 减少大数据在节点上的内存消耗。并且可以减少数据到节点的网络传输消耗。

官网:使用广播变量,每个Executor的内存中,只驻留一份变量副本, 而不是对每个 task 都传输一次大变量,省了很多的网络传输, 对性能提升具有很大帮助, 而且会通过高效的广播算法来减少传输代价

//定义一个set集合
val set = Set("hello","hehe")
//通过set集合 创建广播变量
val broadCast: Broadcast[Set[String]] = sc.broadcast(set)
//从hfds读取文件,创建RDD
val rdd1: RDD[String] = sc.textFile("hdfs://spark1:9000/a.txt")
//对读取的单词执行flatMap操作,并且进一步筛选,最终保留set集合中规范的数据
rdd1.flatMap(_.split(" ")).filter(v=>{
//通过广播变量获取set集合
broadCast.value.contains(v)
}).collect().foreach(println)

总而言之: 广播变量的好处不是每一个task一份变量副本,而是变成每个节点的executor才一份副本,这样的话就可以变量产生的副本大大减少

6.了解广播变量的实现原理和BT种子下载的原理

BT的概念:

BitTorrent协议(简称BT,俗称比特洪流、BT下载)是用在对等网络中文件分享的网络协议程序。和点对点(point-to-point)的协议程序不同,它是用户群对用户群(peer-to-peer),而且用户越多,下载同一文件的人越多,下载该档案的速度越快。且下载后,继续维持上传的状态,就可以“分享”,成为其用户端节点下载的种子文件(.torrent),同时上传及下载。
BT的流程:

关键的几个点

下载者要下载文件内容,需要先得到相应的种子文件,然后使用BT客户端软件进行下载。

提供下载的文件虚拟分成大小相等的块, 并把每个块的索引信息和Hash验证码写入种子文件中

有一个 Tracker 负责维护元信息, 所有的客户端都可以通过 Tracker 找到每个快离自己最近的其他下载者

下载时,BT客户端首先解析种子文件得到Tracker地址,然后连接Tracker服务器。Tracker服务器回应下载者的请求,提供下载者其他下载者(包括发布者)的IP。下载者再连接其他下载者,根据种子文件,两者分别告知对方自己已经有的块,然后交换对方所没有的数据。此时不需要其他服务器参与,分散了单个线路上的数据流量,因此减轻了服务器负担。

下载者每得到一个块,需要算出下载块的Hash验证码与种子文件中的对比,如果一样则说明块正确,不一样则需要重新下载这个块。这种规定是为了解决下载内容准确性的问题。

Spark广播变量的流程:

针对以上的几个点, spark 是怎么做的, 我们看下:

TorrentBroadcast 底层使用的是 BlockManager, 下载每个数据块先要去 master 去获取 Block 所在的位置 (location)。

在把大变量写到广播变量的时候, 通过 ChunkedByteBufferOutputStream把输入的数据分成多个小块, zipWithIndex 中, 为每个小块加一个唯一标识, 形如 broadcast_broadcastId_pieceId。 作为BlockId, 存储在 BlockManager 中。 而且对每个小的数据块加上一个校验码。

BlockManagerMaster 作为 tracker 维护所有 Block块的元信息, 知道每个数据块所在的 executor和存储级别。 Broadcast 变量中维护属于自己的所有小块的 BlockId

通过 value 方法读取 Boradcast 变量的时候, 取出所有小块的 BlockId, 对于每个 BlockId, 通过BlockManagerMaster 获取了该BlockId的位置的集合, 随机化,位置集合被打乱, 优先找同主机的地址(这样可以走回环),然后从随机的地址集合按顺序取地址一个一个尝试去获取数据,因为随机化了地址,那么executor不只会从Driver去获取数据。分散了driver 上的压力。

取到 Block piece 后, 使用校验码进行校验,看看数据块有没有损坏, 如果没有损坏, 然后按照顺序拼在一起。

总结:

比较一下流程是不是差不多, 基本贯穿了 BitTorrent 的思想原理

开始的时候, 大家都是通过 driver 拿数据, 但是一旦其他 executor 上有了数据块之后, 所有的 executor 都是有机会通过别的 executor 来获取数据块, 这样就分散了 driver 的压力。 套用一句话, 下载的 executor 越多, 下载的越快

为什么只能 broadcast 只读的变量:
这就涉及一致性的问题,如果变量可以被更新,那么一旦变量被某个节点更新,其他节点要不要一块更新?如果多个节点同时在更新,更新顺序是什么?怎么做同步? 仔细想一下, 每个都很头疼, spark 目前就索性搞成了只读的。 因为分布式强一致性真的很蛋疼

6.目前主流的流式处理框架

Kafka Streaming : kafka框架自己做的流处理框架 (小众)

Storm : 相对早期,上手比较难

Spark Streaming : 底层基于RDD构建一个流式处理框架 spark 1.6

Structure Streaming : 基层基于Spark SQL构建一个流式处理框架 spark 2.0

Flink : 和spark没有关系,不过也是apache下的顶级项目,应用于流式处理 ,目前比较流行

7.SparkRDD是啥都有哪些特性?RDD是如何容错的?

弹性分布式数据集
带有分区的分布式数据集,可以实现数据集中数据的并行运算
分区数据会在不同的工作节点执行(移动计算)

使用缓存是为了解决依赖过长的问题

目前Saprk的容错策略根据RDD依赖关系重新计算、RDD做Cache(缓存)、RDD做Checkpoint手段完成RDD计算的故障容错

了解:

  • A list of partitions- RDD都有分区
  • A function for computing each split - 按照分区实现并行计算
  • A list of dependencies on other RDDs - RDD存在相互依赖关系,-linage血统 shuffle|narrow
  • Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned) - 给k-v RDD 指定 分区策略
  • Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file) - 更具数据块的位置,优化的计算,采取本地计算原则。

8.什么是宽窄依赖为什么要设计区分宽窄依赖?

宽窄依赖 Stage(阶段) 血统 形成依赖的关系就是血统

DAG有向无环图 通过算子操作(依赖)划分的stage,就是有向无环图

窄:独生子女 父分区(partition)的数据处理后可以只需要输出一个子分区partition中 map fliter

宽: 超生 父分区(partition)的数据处理后可能会输出到多个子分区中 reduceBykey groupBy

划分Stage,每当出现一个宽依赖时,就会产生一个新的Stage 这就是为什么提交一个job,可能会包含多个Stage的原因 假设一个job包含两个Stage, 那么stage阶段内的计算是完全并行,互不影响的

9.spark streaming批次时间/窗口时间/滑动时间的作用?

批次时间:每到批次时间,对该时间区间内的数据进行处理,

窗口时间:对该时间内的所有批次的数据进行处理、计算控制计算最近多少个批次的数据,批次时间的倍数

滑动时间:多长时间进行一次窗口操作,批次时间的倍数。

10.什么是DataFrame、DataSet和RDD以及他们的区别、性能上比RDD要高的原因

什么是DataFrame

与RDD类似,DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以 外,还记录数据的结构信息,即schema。从API易用性的角度上看,DataFrame API提供的是一套高层的关系操 作,比函数式的RDD API要更加友好,门槛更低。

​ RDD[Person]虽然以Person为类型参数,但Spark框架本身 不了解Person类的内部结构。而右侧的DataFrame却提供了详细的结构信息,使得Spark SQL可以清楚地知道该数 据集中包含哪些列,每列的名称和类型各是什么。DataFrame是为数据提供了Schema的视图。可以把它当做数据 库中的一张表来对待,DataFrame也是懒执行的。

​ 性能上比RDD要高,主要原因:优化的执行计划:查询计划通过 Spark catalyst optimiser进行优化。

​ 主要想突出spark SQL底层catalyst的牛逼 底层按照最优的方式生成RDD,也会优于我们写的RDD

什么是DataSet

​ 1)是Dataframe API的一个扩展,是Spark最新的数据抽象

​ 2)用户友好的API风格,既具有类型安全检查也具有Dataframe的查询优化特性。

​ 3)样例类被用来在Dataset中定义数据的结构信息样例类中每个属性的名称直接映射到DataSet中的字段名称。

​ 5)DataSet是强类型的。比如可以有DataSet[Car],DataSet[Person].

​ 6)DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是 否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错,而DataSet不仅仅知道字段,而且 知道字段类型,所以有更严格的错误检查。就跟JSON对象和类对象之间的类比。

RDD 、DataFrame、DataSet的区别

​ 共同点:支持分布式计算 DataFrame和DataSet底层依赖于RDD 他们拥有很多相同的算子操作

​ 不同:RDD不支持SQL语法

DataFrame是一个特殊的DataSet

DataFrame里面元素的类型只能是Row, DataSet更零活一些 

​ 答:RDD是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合,RDD是Spark Core的底层核心,Spark则是这个抽象方法的实现
​ DataSet是分布式的数据集合。DataSet是在Spark1.6中添加的新的接口。它集中了RDD的优点(强类型和可以用强大lambda函数)以及Spark SQL优化的执行引擎。DataSet可以通过JVM的对象进行构建,可以用函数式的转换(map/flatmap/filter)进行多种操作
​ DataSet(dataset中每行数据是个Object)包含了DataFrame的功能,Spark2.0中两者统一,DataFrame表示为DataSet[Row],即DataSet的子集

11.什么是parquet 、CSV数据类型

Parquet是一种流行的列式存储格式,可以高效地存储具有嵌套字段的记录。Parquet格式经常在Hadoop生态圈 中被使用,它也支持Spark SQL的全部数据类型。Spark SQL 提供了直接读取和存储 Parquet 格式文件的方法。

csv是一个txt文本文件,只是可以使用excel软件打开 csv的应用场景:poi技术将数据导出到excel表格中,如果数据量达到十万+,此时将数据导成csv格式

12.什么是StructuredStreaming

spark streaming (spark 1.6 引入 使用批处理模拟流式计算) DStream (离散流)

structured streaming (结构化流 spark2.0引入)

structured streaming是构建在spark sql之上的流式计算模型

**Structured Streaming 基于 Spark SQl 引擎, 是一个具有弹性和容错的流式处理引擎. **

使用 Structure Streaming 处理流式计算的方式和使用批处理计算静态数据(表中的数据)的方式是一样的.

13.SparkStreaming和StrutcturedStreaming的区别和EventTime

spark streaming 基于 processing time(spark接受到数据的时间)将数据落入winsdow structured streaming是基于event time将数据落入window

event time:数据产生的时间,而不是数据接受或者处理的时间

基于event time的好处:对于延迟数据可以落入他本该的统计窗口中

对于一些“过分”延迟的数据,就可以丢弃了,当然通过学习我们会发现,丢弃的不是数据,而是窗口

可以提高数据的实时性,准确性

14.Structured Streaming 的核心思想,Programming Model(编程模型) ?

Structured Streaming 的核心思想是:把持续不断的流式数据当做一个不断追加的表.

这使得新的流式处理模型同批处理模型非常相像. 我们可以表示我们的流式计算类似于作用在静态数据表上的 标准批处理查询, spark 在一个无界表上以增量查询的方式来运行.

15.Structured Streaming基于 event-time 的窗口操作

在 Structured Streaming 中, 可以按照事件发生时的时间对数据进行聚合操作, 即基于 event-time 进行操作.

备注:spark streaming(DSteram,离散化流)并不基于event time操作,而是基于processing time

在这种机制下, 即不必考虑 Spark 陆续接收事件的顺序是否与事件发生的顺序一致, 也不必考虑事件到达 Spark 的时间与事件发生时间的关系.

因此, 它在提高数据处理精度的同时, 大大减少了开发者的工作量.

我们现在想计算 10 分钟内的单词, 每 5 分钟更新一次, 也就是说在 10 分钟窗口 12:00 - 12:10, 12:05 - 12:15, 12:10 - 12:20等之间收到的单词量. 注意, 12:00 - 12:10 表示数据在 12:00 之后 12:10 之前到达.

现在,考虑一下在 12:07 收到的单词。单词应该增加对应于两个窗口12:00 - 12:10和12:05 - 12:15的计数。 因此,计数将由分组键(即单词)和窗口(可以从事件时间计算)索引。

16.Structured Streaming基于 Watermark 处理延迟数据lateData

在数据分析系统中, Structured Streaming 可以持续的按照 event-time 聚合数据, 然而在此过程中并不能 保证数据按照时间的先后依次到达. 例如: 当前接收的某一条数据的 event-time 可能远远早于之前已经处理 过的 event-time. 在发生这种情况时, 往往需要结合业务需求对延迟数据进行过滤

现在考虑如果事件延迟到达会有哪些影响. 假如, 一个单词在 12:04(event-time) 产生, 在 12:11 到达应用. 应 用应该使用 12:04 来在窗口(12:00 - 12:10)中更新计数, 而不是使用 12:11. 这些情况在我们基于窗口的聚合中 是自然发生的, 因为结构化流可以长时间维持部分聚合的中间状态

但是, 如果这个查询运行数天, 系统很有必要限制内存中累积的中间状态的数量. 这意味着系统需要知道何时从内存 状态中删除旧聚合, 因为应用不再接受该聚合的后期数据

为了实现这个需求, 从 spark2.1, 引入了 watermark(水印), 使用引擎可以自动的跟踪当前的事件时间, 并据此尝试 删除旧状态.

通过指定 event-time 列和预估事件的延迟时间上限来定义一个查询的 watermark. 针对一个以时间 T 结束的窗口, 引擎会保留状态和允许延迟时间直到(max event time seen by the engine - late threshold > T). 换句话说, 延迟时 间在上限内的被聚合, 延迟时间超出上限的开始被丢弃.

可以通过withWatermark() 来定义watermark

watermark 计算: watermark = MaxEventTime - Threshhod

水位线=一个窗口的最大迟到时间-阈值,把该水位线的上一个窗口的数据抛弃,也就是这个时候上一个窗口的数据再来的时候就不再对这个窗口的数据进行计算了

MaxEventTime :当前窗口最大的数据的时间戳

Threshhod:阈值,自定义的

watermark :水平线,水印低于该时间的上一个窗口的数据将会抛弃而且watermark只能逐渐增加不能减少

总结:

Structured Streaming 引入 Watermark 机制, 主要是为了解决以下两个问题:

  1. 处理聚合中的延迟数据

  2. 减少内存中维护的聚合状态

    注意:引⼊入watermarker以后,⽤用户只能使⽤用 update 、 append 模式,系统才会删除过期数据。

    在update模式下,水位线没有沒过窗口的end time之前,如果有数据落入到该窗口,该窗口会重复触发

    在Append模式下,水位线没有沒过窗口的end time之前,如果有数据落入到该窗口,该窗口不会触发,只 会默默的计算,只有当水位线沒过窗口end time的时候,才会做出最终输出。

17.hadoop和spark都是并行计算,那么他们有什么相同和区别?

​ 两者都使用mr模型来进行并行计算,hadoop的一个作业称为job,job里面分为map task和reduce task,每个task都是在自己的进程中运行的,当task结束时,进程也会结束。

​ Spark用户提交的任务称为application,一个application对应一个SparkContext,app中存在多个 job,没触发一个action操作就会产生一个job。

​ 这些job可以并行或者串行执行,每个job有多个stage,stage是shuffle过程中DAGSchaduler通过RDD 之间的依赖关系划分job而来的,每个stage里面有多个task,组成taskset有TaskSchaduler分发到各个 executor中执行,executor的生命周期是和application一样的,即使没有job运行也是存在的,所以 task可以快速启动读取内存进行计算的

​ Hadoop的job只有map和reduce操作,表达能力比较欠缺而且在mr过程中会重复的读写hdfs,造成大 量的io操作,多个job需要自己管理关系。

​ Spark的迭代计算都是在内存中进行的,API中提供了大量的RDD操作join,groupby等,而且通过DAG 图可以实现良好的容错。 ​

18.简单说一下hadoop和spark的shuffle过程

​ Hadoop:map端保存分片数据,通过网络收集到reduce端。

​ Spark:spark的shuffle实在DAGSchedular划分Stage的时候产生的,TaskSchedular要分发Stage到各 个worker的executor。减少shuffle可以提高性能。

19.spark有哪几种join

​ 答:join,left-outer-join,right-outer-join

20.spark jdbc(mysql)读取并发度优化

​ 答:根据数据的特性,进行适当的分区操作,高并发度可以大幅度提高读取以及处理数据的速度,但是如果设置过高(大量的partition同时读取)也可能会将数据源数据库弄挂

21.spark运行流程、源码架构

1)创建SparkContext

2)初始化SparkContext,申请资源

3)创建DAGScheduler和TaskScheduler、SchedulerBackend(和计算节点通信),系统会将计算资源反向注册到Drvier

4)调用DAGScheduler对任务做Stage划分(RDD 宽窄依赖关系)

5)提交的stage,每个Stage会根据RDD的区分区,计算出任务的并行度,封装成Taskset提交给服务后端

6)Driver在真个计算过程中调度和协调各个阶段任务执行。

​ spark 提交模式 deploy-mode 参数 client和cluster分别表示什么含义。

​ 如果是在Client模式运行,Driver是运行在本地提交任务的节点上,单一进程运行。如果是Cluster模式Driver运行在Worker节点上,处于一种并行计算的形式。

22.如何解决Spark在shuffle过程中出现了内存溢出该怎么办?

1)第一种情况:分区数太小,导致每个任务处理的分区数据太大,注意在shuffle之前增加RDD分区数。

2)第二种:分区数很多,但是由于分布不均匀,导致了数据倾斜,也会引发OOM,需要重新设计key,然后调用shuffle()

会将数据在分区中重新洗牌。
3)当计算超大集群规模大的数据,可以尝试先对数据做预处理|预分区,降低数据计算规模。

2.分布式锁的问题

概述

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁

为什么要使用分布式锁

需要对某一个共享变量进行多线程同步访问的时候 例如:

  • 成员变量 A 存在 JVM1、JVM2、JVM3 三个 JVM 内存中(Jvm1-3分别在三台机器上)
  • 成员变量 A 同时都会在 JVM 分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的
  • 不是同时发过来,三个请求分别操作三个不同 JVM 内存区域的数据,变量 A 之间不存在共享,也不具有可见性,处理的结果也是不对的
    注:该成员变量 A 是一个有状态的对象

如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题,这就是分布式锁要解决的问题

分布式锁应该具备哪些条件

  • 在分布式系统环境下,一个方法在同一时间只能被一个机器的0一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
  • 具备锁失效机制,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

分布式锁的实现有哪些

  • Memcached(是一套分布式的高速缓存系统 与redis相似 ,开源基于内存):利用 Memcached 的 add 命令。此命令是原子性操作,只有在 key 不存在的情况下,才能 add 成功,也就意味着线程得到了锁。

  • Redis:和 Memcached 的方式类似,利用 Redis 的 setnx 命令。此命令同样是原子性操作,只有在 key 不存在的情况下,才能 set 成功。

    **缺点:在这种场景(主从结构)中存在明显的竞态: **

    客户端A从master获取到锁,

    在master将锁同步到slave之前,master宕掉了。

    slave节点被晋级为master节点,

    客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。安全失效!

  • Zookeeper:利用 Zookeeper 的顺序临时节点,来实现分布式锁和等待队列。Zookeeper 设计的初衷,就是为了实现分布式锁服务的。【重要】

    具体实现:

    1.获取锁

    首先,在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时,需要在ParentLock这个节点下面创建一个临时顺序节点 Lock1

    之后,Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。

    这时候,如果再有一个客户端 Client2 前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock2。

    Client2查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock2是不是顺序最靠前的一个,结果发现节点Lock2并不是最小的。

    于是,Client2向排序仅比它靠前的节点Lock1注册Watcher,用于监听Lock1节点是否存在。这意味着Client2抢锁失败,进入了等待状态。

    这时候,如果又有一个客户端Client3前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock3。

    Client3查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock3是不是顺序最靠前的一个,结果同样发现节点Lock3并不是最小的。

    于是,Client3向排序仅比它靠前的节点Lock2注册Watcher,用于监听Lock2节点是否存在。这意味着Client3同样抢锁失败,进入了等待状态。

    这样一来,Client1得到了锁,Client2监听了Lock1,Client3监听了Lock2。这恰恰形成了一个等待队列,很像是Java当中ReentrantLock所依赖的

    2.释放锁

    释放锁分为两种情况:

    1.任务完成,客户端显示释放

    当任务完成时,Client1会显示调用删除节点Lock1的指令。

    2.任务执行过程中,客户端崩溃

    获得锁的Client1在任务执行过程中,如果Duang的一声崩溃,则会断开与Zookeeper服务端的链接。根据临时节点的特性,相关联的节点Lock1会随之自动删除。

    由于Client2一直监听着Lock1的存在状态,当Lock1节点被删除,Client2会立刻收到通知。这时候Client2会再次查询ParentLock下面的所有节点,确认自己创建的节点Lock2是不是目前最小的节点。如果是最小,则Client2顺理成章获得了锁。

    同理,如果Client2也因为任务完成或者节点崩溃而删除了节点Lock2,那么Client3就会接到通知。

    最终,Client3成功得到了锁。

    缺点:

    性能可能并没有缓存服务那么高。因为每次在创建锁和释放锁的过程中,都要动态创建、销毁瞬时节点来实现锁功能。ZK中创建和删除节点只能通过Leader服务器来执行,然后将数据同不到所有的Follower机器上。
    
    其实,使用Zookeeper也有可能带来并发问题,只是并不常见而已。考虑这样的情况,由于网络抖动,客户端可ZK集群的session连接断了,那么zk以为客户端挂了,就会删除临时节点,这时候其他客户端就可以获取到分布式锁了。就可能产生并发问题。这个问题不常见是因为zk有重试机制,一旦zk集群检测不到客户端的心跳,就会重试,Curator客户端支持多种重试策略。多次重试之后还不行的话才会删除临时节点。(所以,选择一个合适的重试策略也比较重要,要在锁的粒度和并发之间找一个平衡。)
    

    问题:

    client1奔溃了,断开连接了,但这时还没释放锁,就是你说的第二种情况,这个client1对应的线程还在跑,client2当client1崩掉时会立即获取锁,可能会操作同一个成员变量a,这会有问题吧 ?(面试问的)

    不会的。 你client1 如果在zk服务宕机钱创建了节点,client2 自然会监听;如果没成功,client2就直接获取到锁了

    故障场景:

    1.如果是client1拿到锁,进行业务处理时,断开zk连接,锁没释放前,client2是没办法获取到锁,就不会进行对应业务处理; 2.如果是client1还没拿到锁前就断开zk连接,那么client1不会进行业务处理,client2直接拿到锁,进行业务处理; 不同场景做不同补偿机制,例如1,释放锁失败,需要事务回滚;2就是重试即可

  • Chubby:Google 公司实现的粗粒度分布式锁服务,底层利用了 Paxos 一致性算法。

  • 使用数据库实现容易理解,比较繁琐有兴趣可以看看https://blog.csdn.net/wuzhiwei549/article/details/80692278

    挑一个实现方式具体看看,其他的实现方式了解

    使用Redis开发分布式锁和使用Zookeeper开发分布式锁的区别是 什么?[面试题]

    Redis setnx 开发分布式锁,无法保证线程顺序,有可能让某些线程 一直处于等待状态(自己动手 实现) Zookeeper 开发分布式锁,主要借助Zookeeper的临时顺序节点(可以使用Curator-framework 实现)

3.Kafka的问题

1.工作原理,要搞清楚里面的概念

首先 Producer 产生 Record 发送给指定的Kafka Topic(Topic实质是有多个分区构成,每一个 分区都会相应的复制分区),在真正存放到Kafka集群时会进行计算 key.hashCode%topicPartitionNums等于要存放的分区序号。

Leader 分区中的数据会自动同步到 Follower 分区中, ZooKeeper 会实时监控服务健康信息,一 旦发生故障,会立即进行故障转移操作(将一个Follower复制分区自动升级为Leader主分区)

Kafka一个分区实际上是一个有序的Record的 Queue (符合队列的数据结构,先进先出), 分区中 新增的数据,会添加到队列的末尾,在处理时,会从队列的头部开始消费数据。 Queue 在标识读 写操作位置时,会使用一个 offset (读的offset <= 写的offset)

最后 Consumer 会订阅一个Kafka Topic,一旦Topic中有新的数据产生,Conumser立即拉取最新 的记录,进行相应的业务处理。

2.kafka 的数据同步过程

Producer 在 发 布 消 息 到 某 个 Partition 时 , 先 通 过ZooKeeper 找到该 Partition 的 Leader 【 get /brokers/topics//partitions/2/state】,然后无论该Topic 的 Replication Factor 为多少(也即该 Partition 有多 少个 Replica(副本)),Producer 只将该消息发送到该 Partition 的Leader。Leader 会 将该消息写入其本地 Log。每个 Follower都从 Leader pull 数据。这种方式上,Follower 存储的数据顺 序与 Leader 保持一致。Follower 在收到该消息并写入其Log 后,向 Leader 发送 ACK。一旦 Leader 收到了 ISR 中的所有 Replica 的 ACK,该消息就被认为已经 commit 了,Leader 将增加 HW(HighWatermark)并且向 Producer 发送ACK。

这里会分两种情况:

第一种是 leader 处理完 producer 请求之后,follower 发送一个 fetch 请求过来、

第二种是follower 阻塞在 leader 指定时间之内,leader 副本收到producer 的请求

3.Kafka组件[重要]:

Topic :消息根据Topic进行归类

Producer:发送消息者

Consumer:消息接受者

broker:每个kafka实例(server)

Zookeeper:依赖集群保存meta信息。

1)Producer :消息生产者,就是向kafka broker发消息的客户端;

2)Consumer :消息消费者,从kafka broker拉取消息的客户端;

3)Consumer Group (CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数 据,一个分区只能由一个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是 逻辑上的一个订阅者。(consumer Group:消费者组,至少要有一个消费者,消费者组中一个消费者对应一个分区,会提高消息的读取效率,某个分区只能被消费者组中的某一个消费者消费(在同一时刻)所以在设计消费者组里面的消费者的数量不要超过分区的数量)

4)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。 5)Topic :可以理解为一个队列,生产者和消费者面向的都是一个topic; kafka1 kafka2 kafka3 zk zk zk kafka kafka kafka

6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多 个partition,每个partition是一个有序的队列;

7)Replica:副本,为保证集群中的某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能够继 续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个follower。

8)leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是leader。

9)follower:每个分区多个副本中的“从”,实时从leader中同步数据,保持和leader数据的同步。leader发生故障 时,某个follower会成为新的follower。

4.kafka和rabbitmq的区别

一、语言不同**

RabbitMQ是由内在高并发的erlanng语言开发,用在实时的对可靠性要求比较高的消息传递上。 kafka是采用Scala语言开发,它主要用于处理活跃的流式数据,大数据量的数据处理上

二、结构不同

RabbitMQ采用AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是一个进程间传 递异步消息的网络协议 。

RabbitMQ的broker由Exchange,Binding,queue组成

kafka采用mq结构:broker 有part 分区的概念

三、Broker与Consume交互方式不同

RabbitMQ 采用push的方式

kafka采用pull的方式

四、在集群负载均衡方面,

rabbitMQ的负载均衡需要单独的loadbalancer进行支持。

kafka采用zookeeper对集群中的broker、consumer进行管理

五、使用场景

rabbitMQ支持对消息的可靠的传递,支持事务,不支持批量的操作;基于存储的可靠性的要求存储可 以采用内存或者硬盘。 金融场景中经常使用

kafka具有高的吞吐量,内部采用消息的批量处理,zero-copy机制,数据的存储和获取是本地磁盘顺序 批量操作,具有O(1)的复杂度(与分区上的存储大小无关),消息处理的效率很高。(大数据)

5.消息队列的两种模式

(1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除) 消息生产者生产消息发送到Queue中, 然后消息消费者从Queue中取出并且消费消息。 消息被消费以后,queue中不再有存储,所以消息消费者不可能 消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

(2)发布/订阅模式(一对多,消费者消费数据之后不会清除消息) 消息生产者(发布)将消息发布到topic中, 同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。

6.Kafka工作流程及文件存储机制【简历写的技术】

Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的

kafka的工作流程

创建一个topic,需要指定分区和副本,分区理论上是无限多的,分布在broker上,副本的数量不能超过 broker的数量(副本数量=leader个数 + follower个数)。

produer向topic中发送数据,数据存储在topic的partition中,首先写入leader这个分区副本,然后将数据 同步到follower中

consumer会从topic的partition中读取数据,也是从leader这个分区副本中读, consumer会记录[offset偏移量],offset代表当前consumer在分区中读取到哪个位置

自己的理解:

kafka的工作流程:
创建一个topic,需要指定分区和副本,分区理论上数无限多的,分布在不同的broker上,
副本的数量不能超过broker的数量(replication=leader个数+follower个数),否则就没有意义了。
producer向topic中发送数据,数据实际会存在topic的partition中,首先写入leader这个
分区副本,然后将数据同步到follower中
consumer会从topic的partition中(根据offset偏移量上一次读到的位置)读取数据,也是从leader这个分区副本中读,只有leader挂了才会从replication中读
consumer会记录offset偏移量,offset代表当前consumer在分区中读到哪个位置,也就是当数据读完了你把consumer在启动不会在读取之前的数据了(这个数据是不会删除的)。

7.kafka底层数据的存储:

自己总结的:

/opt/kafka/logs里面有分区的文件夹,
数据存在里面的一个.log文件中(不断往里面追加数据)。
还有一个.index文件,是索引文件,快速的到数据文件中找到数据。
数据文件:这个文件默认大小是1G(可以修改),当达到1G会形成一个新的日志文件
还会重新生成一个index文件,一个数据日志文件对应一个index文件
这两个文件加起来叫segment(片段)
底层是怎么把偏移量和数据联系起来?(index和log是怎么联系起来的)
RandomAccessFile:io类,随机访问文件,特点:指定文件中的某个位置开始读取数据。
里面有一个seek()的方法,指定文件偏移量(起始偏移位置)的位置开始读取数据
index就是存储数据文件log中数据的起始偏移量
这样设计的目的:通过index存储的offset(对应有一个数据在数据文件中的起始位置,一一对应的)可以快速的到数据日志文件中的数据。
找数据的流程:consumer---->index中的(key[offset]–value[数据的起始位置]到—>下一key的value计算出数据在数据文件中的位置)—>数据文件中的数据

8.Kafka生产者 数据到topic的分区策略【简历写的技术】

原因:提高扩展和并发
三种情况:在发送数据的时候:指定分区编号,指定key,两个都不指定

1)分区的原因

(1)方便在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组 成,因此整个集群就可以适应任意大小的数据了;

(2)可以提高并发,因为可以以Partition为单位读写了。

2)分区的原则,

三种情况-在发送数据的时候:指定分区编号(1),指定key(2),两个都不指定(3) 我们需要将producer发送的数据封装成一个ProducerRecord对象。

(1)指明 partition 的情况下,直接将指明的值直接作为 partiton 值;

(2)没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值;

(3)既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自 增),将这个值与 topic 可用的 partition 总数取余得到 partition 值,也就是常说的 round-robin 算法。

9、Kafka生产数据到topic数据可靠性保证 ack应答机制

​ 为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后, 都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否 则重新发送数据。

ack应答机制 ( 0 1 -1)

对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等ISR中的 follower全部接收成功。

所以Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。

acks参数配置: acks:

0:producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返 回,当broker故障时有可能丢失数据; 延迟最低,效率最高,数据不安全

1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故 障,那么将会丢失数据

-1(all):producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack。但是如果在 follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复

自己的理解:

Kafka生产者之数据可靠性保证:

ack应答:topic写入数据后给producer的应答 设置acks的参数的值 producer在没有接受到ack应答会重新发送消息,这个机制是自动的不需要任何干涉。

0:不等待ack应答,延迟低,效率最高,但会丢失数据。

1:leader写入数据,就会给producer应答,相对0数据安全,只要leader不出故障就不会有问题,leader出问题 数据就会丢失。

-1:leader写入数据,folloer成功同步数据,leader才会给出ack应答,效率最慢, 数据安全性比较高。不过如果 在ack应答之前leader挂了,aplication上任后不会继续ack应答, producer接受不到ack应答会重新发送消息,导 致数据重复。

10.幂等性

幂等性:一次和多次操作的结果一样

对于某些比较重要的消息,我们需要保证exactly once语义,即保证每条消息被发送且仅被发送一次

在0.11版本(包含)之后,Kafka引入了幂等性机制(idempotent),配合acks = -1时的at least once语义,实现了 producer到broker的exactly once语义。

总结:就是0.11以后acks应答为-1的时候连小概率的数据重复都不会发生了,功能默认是关闭的

idempotent + at least once = exactly once

使用时,只需将enable.idempotence属性设置为true,kafka自动将acks属性设为-1

数据库的和kafka的幂等性:

对于一个事务做1次操作和n次操作,结果是一样

数据库的【幂等性】:查询一定是幂等操作,增删改可能是幂等性操作,也可能不是

实际开发:希望xx功能可以保证幂等性操作

其实幂等性可以理解为表单重复提交,导致重复注册和重复下订单

kafka的幂等性:保证消息有且只有一条(不会丢失,也不会重复)

11.Kafka消费者 消费数据的 消费方式

consumer获取消费数据的方式
consumer不断的主动的到topic中拉取数据pull,可以自己控制消费的速率
topic有消息会主动发给consumer,push,主动权在topic中,consumer控制不了处理效率

consumer采用pull(拉)模式从broker中读取数据。

push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。它的目标是尽可能以最 快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而

pull模式则可以根据consumer的消费能力以适当的速率消费消息。

pull模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka的消 费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再 返回,这段时长即为timeout

也就是pull在有数据的时候不断从topic中拉取数据,topic没有数据的时候会延迟获取数据,给pull设置一个时间

12、Kafka一个消费者组有多个消费者之间消费数据的分区分配策略

一个consumer group中有多个consumer,一个 topic有多个partition,所以必然会涉及到partition的分配问题, 即确定哪个partition由哪个consumer来消费

Kafka有两种分配策略,一是roundrobin,一是range

总结:

1、在consumer比分区少的按照一下两种,

1)roundrobin:轮询的方式 :以轮询的方式将topic分配给不同的消费者

2)range:计算 :统计除法计算,将分区分配给消费者(将连续的几个分区一次性分给一个消费者)

​ 例如有5topic,前三个分给一个消费者,后面两个分给一个消费者

2.分区和consumer一样的就一个分区对应以consumer

3.consumer 比分区多有分区闲置一般不会这样设置

13、Kafka消费者 之offset(保证重启后不重新消费)的维护

​ 由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费, 所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费

Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,从0.9版本开始,consumer默认将offset保存 在Kafka一个内置的topic中,该topic为__consumer_offsets。 (有意的在淡化zk)

也就是在新建consumer指定一个新的consumer Group组的时候会读取之前读过的所有数据, 指定一个之前的消费者组的时候重新读,而是接着读 ,新建消费者组就重新读数据

14、Kafka 高效读写数据并且保证数据正确的原因和零拷贝

Kafka的producer生产数据,要写入到log文件中,写的过程是一直追加到文件末端,为顺序写。官网有数据表 明,同样的磁盘,顺序写能到到600M/s,而随机写只有100k/s。这与磁盘的机械机构有关,顺序写之所以快,是 因为其省去了大量磁头寻址的时间。

写入性能:分区、磁盘顺序写入、Memory Map File(内核空间内存)

写出性能:分区、Zero Copy

总的来说Kafka快的原因:

1、partition顺序读写,充分利用磁盘特性,这是基础;

2、Producer生产的数据持久化到broker,采用mmap文件映射,实现顺序的快速写入;

3、Customer从broker读取数据,采用sendfile零拷贝 ,将磁盘文件读到OS内核缓冲区后,直接转到socket buffer进行网络发送。

1、顺序读写

磁盘顺序读或写的速度400M/s,能够发挥磁盘最大的速度。 随机读写,磁盘速度慢的时候十几到几百K/s。这就看出了差距。 kafka将来自Producer的数据,顺序追加在partition,partition就是一个文件,以此实现顺序写入。 Consumer从broker读取数据时,因为自带了偏移量,接着上次读取的位置继续读,以此实现顺序读。 顺序读写,是kafka利用磁盘特性的一个重要体现。

2、零拷贝 sendfile(in,out)

数据直接在内核完成输入和输出,不需要拷贝到用户空间再写出去。 kafka数据写入磁盘前,数据先写到进程的内存空间。

3、mmap文件映射

虚拟映射只支持文件;

在进程 的非堆内存开辟一块内存空间,和OS内核空间的一块内存进行映射, kafka数据写入、是写入这块内存空间,但实际这块内存和OS内核内存有映射,也就是相当于写在内核 内存空间了,且这块内核空间、内核直接能够访问到,直接落入磁盘。

数据写入进程空间----》该空间会和系统内核内存映射也就是数据直写入系统内核空间中----》系统内核内存空间可以直接写入磁盘中

使用mmap+write方式替换原来的传统IO方式,就是利用了虚拟内存的特性

重要就是简单版的零拷贝:

整体流程的核心区别就是,把数据读取到内核缓冲区后,应用程序进行写入操作时,直接是把内核的 Read Buffer(内核缓冲区)的数据 复制到 Socket Buffer(内核缓冲区) 以便进行写入,这次内核之间的复制也是需要CPU参与的

kafka的零拷贝详解

zero copy技术就是减少不必要的内核缓冲区跟用户缓冲区间的拷贝,从而减少CPU的开销 和内核态 切换开销,达到性能的提升

传统IO的流程

1、第一次:将磁盘文件,读取到操作系统内核缓冲区;

2、第二次:将内核缓冲区的数据,copy到application应用程序的buffer;

3、第三步:将application应用程序buffer中的数据,copy到socket网络发送缓冲区(属于操作系统内核 的缓冲区);

4、第四次:将socket buffer的数据,copy到网卡,由网卡进行网络传输

磁盘文件----》系统内存缓冲区-----》应用程序缓冲区-----》网络发送端口缓冲区------》用网卡发送传输(例如Kafka的把数据发送到到topic里面)

传统方式,读取磁盘文件并进行网络发送,经过的四次数据copy是非常繁琐的。实际IO读写,需要进行 IO中断,需要CPU响应中断(带来上下文切换),尽管后来引入DMA来接管CPU的中断请求,但四次copy 是存在“不必要的拷贝”的。

kafak使用的方式,传统和Kafka传输数据的区别

重新思考传统IO方式,会注意到实际上并不需要第二个和第三个数据副本。应用程序除了缓存数据并将 其传输回套接字缓冲区之外什么都不做。相反,数据可以直接从读缓冲区传输到套接字缓冲区

套接字是一种通信机制(通信的两方的一种约定),凭借这种机制,不同主机之间的进程可以进行通信。我们可以用套接字中的相关函数来完成通信过程

socket缓冲区:每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

15、kafka 的数据同步过程

Producer 在 发 布 消 息 到 某 个 Partition 时 , 先 通 过ZooKeeper 找到该 Partition 的 Leader 【 get /brokers/topics//partitions/2/state】,然后无论该Topic 的 Replication Factor 为多少(也即该 Partition 有多 少个 Replica(副本)),Producer 只将该消息发送到该 Partition 的Leader。Leader 会 将该消息写入其本地 Log。每个 Follower都从 Leader pull 数据。这种方式上,Follower 存储的数据顺 序与 Leader 保持一致。Follower 在收到该消息并写入其Log 后,向 Leader 发送 ACK。一旦 Leader 收到了 ISR 中的所有 Replica 的 ACK,该消息就被认为已经 commit 了,Leader 将增加 HW(HighWatermark)并且向 Producer 发送ACK。

详细见面试题二

16、Zookeeper在Kafka中的作用

Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线,所有topic的分区副本分配和 leader选举等工作。

Controller的管理工作都是依赖于Zookeeper的。

0.8之前消息的偏移量也存在zk里面

Kafka目前有意的简化zk在kafka中的作用, 目前存储的比较重要的信息

  1. 可以使用的broker服务器 2. topic分区

zookeeper的节点和作用:

/brokers/ids:记录可以正常使用的broker节点的id,此处使用的是临时节点,某个broker挂掉会自动消失

/brokers/topics:记录当前可用的topic,topic分区

SpringBoot整合了Kafka

17.kafka的事务和数据库事务区别

数据库事务回顾

数据库事务:多个操作是一个原子操作,不可分割,同时成功或者同时失败。

事务问题: 脏读、不重复读、幻影读

事务隔离级别(解决事务问题):读未提交、读提交、可重复读、序列化读

kafka事务指的是在一个事务内多个Record(记录/数据)发送或者处理是一个原子操作,不可分割,同时成功,或者 同时失败。

kafka事务的隔离级别:读未提交(默认: read_uncommitted )、读提交( read_commmitted ) 仅生产者事务

Kafka生产者在一个事务内生产的Record是一个不可分割的整体,要么同时写入Kafka集群,或者某个 出错,回滚撤销所有的写操作

消费生产并存事务(重点)

​ 特点:消费kafka的A主题进行业务操作,将操作的结果写入到B主题中。如果开启 consumetransfer-produce 事务,读A和写B在一个事务环境中,不可分割,要么同时成功,要么同时失 败

4.定时器、crom表达式[了解]

springboot整合定时器

1.在启动上增加@EnableScheduling注解

2.在某个方法增加 @Scheduled(cron = “0/1 * * * * ?”)

作用:启动springboot项目,被@Scheduled注解标注的方法就会定时执行

@Scheduled(cron = “cron表达式”)

cron表达式对应7个位置:

秒 分 时 日期 月份 星期 年(可选)

秒(Seconds) 0~59的整数

分(Minutes) 0~59的整数

小时(Hours) 0~23的整数

日期(DayofMonth) 1~31的整数(但是你需要考虑你月的天数)

月份(Month) 1~12的整数

星期(DayofWeek) 1~7的整数

年(可选,留空)(Year) 1970~2099

应用:

1.日期和星期会冲突,只能同时指定一个 ,可以写成?代表不管

5 5 5 10 6 ? 2019

2.年份省略,代表每年都会执行

​ 5 5 5 10 6 ?

  1. *代表对应位置匹配任意时间

5 5 10 6 ? 5点5分后每秒都会执行

  1. /:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25, 45等分别触发一次

0/5 * * * * ? 每隔5秒触发一次

  1. -:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

    0-5 * * * * ?

5.Flume问题

1.简单介绍一下Flume和为为什么使用

​ Flume是一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume基 于流式架构,灵活简单。

Flume优点 :

① 可以和任意存储进程集成,可以选择的输入源很多

② 输入的数据速率大于写入目的存储的速率,flume会进行缓冲,减小hdfs的压力。

③ flume中的事务基于channel,使用了两个事务模型(sender + receiver),确保消息被可靠发送。

数据安全: Flume使用两个独立的事务分别负责从source到channel,以及从channel到sink的事件传递。一旦事务中所 有的数据全部成功提交到cha

2.Flume的组件和其作用

Agent :

​ Agent是一个JVM进程,它以事件的形式将数据从源头送至目的地

​ Agent主要有3个部分组成,Source、Channel、Sink。可以使用多个agent组成集群达到不同的目的

Source :

​ Source是负责接收数据到Flume Agent的组件。Source组件可以处理各种类型、各种格式的日志数据, 包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、 syslog、http、legacy

Channel :

​ Channel是位于Source和Sink之间的缓冲区。因此,Channel允许Source和Sink运作在不同的速率上。 Channel是线程安全的,可以同时处理几个Source的写入操作和几个Sink的读取操作。

Flume自带两种Channel:Memory Channel和File Channel。

​ Memory Channel是内存中的队列。Memory Channel在不需要关心数据丢失的情景下适用。如果需要 关心数据丢失,那么Memory Channel就不应该使用,因为程序死亡、机器宕机或者重启都会导致数据 丢失。

​ File Channel将所有事件写到磁盘。因此在程序关闭或机器宕机的情况下不会丢失数据

主要用来解决速率不一致的问题

Sink :

​ Sink不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者 被发送到另一个Flume Agent

​ Sink是完全事务性的。在从Channel批量删除数据之前,每个Sink用Channel启动一个事务。批量事件 一旦成功写出到存储系统或下一个Flume Agent,Sink就利用Channel提交事务。事务一旦被提交,该 Channel从自己的内部缓冲区删除事件。

Sink组件目的地包括hdfskafka、logger、avro、thrift、ipc、file、null、HBase、solr、 自定义

Event :

​ 传输单元,Flume数据传输的基本单元,以事件的形式将数据从源头送至目的地。 Event由可选的 header和载有数据的一个byte array 构成。Header是容纳了key-value字符串对的HashMap。

传输的数据 单位,分为数据头里面存储了数据的一下信息是一个Map例如IP等,数据体就是数据

3.Flume常用案例和注意

1.监控端口数据案例 :

​ 数据source源是端口netcat

2.实时读取本地文件到HDFS案例 :

​ 实时采集一个文件里面的数据,source类型是exec,sink类型是hdfs

​ Flume要想将数据输出到HDFS,必须持有Hadoop相关jar包

​ 输出到hdfs创建文件默认使用的是数据头的时间,测试数据没有头,就需要把默认的改成本地时间

3.实时读取目录文件到HDFS案例 :

​ 实时监控一个文件夹里面的所有文件把里面的数据写入hdfs

​ source类型是spooldir,sink类型是hdfs

​ 说明: 在使用Spooling Directory Source时

​ 1) 不要在监控目录中创建并持续修改文件

​ 2) 上传完成的文件会以.COMPLETED结尾

​ 3) 被监控文件夹每500毫秒扫描一次文件变动

4.单数据源多出口案例(选择器)

​ 只有一个source,有多个channel和多个sink(这里涉及了选择器的问题后面有详解),一个文件中的数据传输到不同的存储设备上

​ source是exec,sink是一个avro,就是把数据传到下一flume的agent,下一flume的source就是avro了

注:Avro是由Hadoop创始人Doug Cutting创建的一种语言无关的数据序列化和RPC框架。

注:RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服 务,而不需要了解底层网络技术的协议。

提示:输出的本地目录必须是已经存在的目录,如果该目录不存在,并不会创建新的目录。

5.数据源多出口案例(Sink组)

单Source、Channel多Sink(负载均衡) ,这里需要定义一Sink组,下面有解释,可以输出到不同的地方

6.多数据源汇总案例

​ 多Source汇总数据到单Flume ,也就是多个agent传输到一个agent的source

7.Taildir Source多目录断点续传

​ AILDIR类型的source在读取文件完成后,会接续读取此文件,查看此文件是否有最新的文件内容,如果有最新的 文件内容会对此文件的新的内容进行读取

备注:flume 1.7.0推出了taildirSource组件

他和aildir的的区别就是,当我flume出现故障恢复的时候不会重新读取文件内容而是接着故障前的内容继续读

6.一次 RPC 调用流程

一次 RPC 调用流程如下:

​ 服务消费者(Client 客户端)通过本地调用的方式调用服务。 客户端存根(Client Stub)接收 到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。

​ 客户端存根(Client Stub)找到 远程的服务地址,并且将消息通过网络发送给服务端。

​ 服务端存根(Server Stub)收到消息后进行解码(反序列化操 作)。

​ 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理 服务端(Server)本地服务业务处理。 处 理结果返回给服务端存根(Server Stub)。

​ 服务端存根(Server Stub)序列化结果。 服务端存根(Server Stub)将结果 通过网络发送至消费方。

​ 客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。 服务消费方得到最终结 果

7.Flume的拦截器

​ flume通过使用Interceptors(拦截器)实现修改和过滤事件的功能。举个栗子,一个网站每天产生海量数 据,但是可能会有很多数据是不完整的(缺少重要字段),或冗余的,如果不对这些数据进行特殊处理,那 么会降低系统的效率。这时候拦截器就派上用场了。

拦截器一般针对Event的Header进行处理

event:

event是flume中处理消息的基本单元,由零个或者多个header和正文body组成。

Header 是 key/value 形式的,可以用来制造路由决策或携带其他结构化信息(如事件的时间戳或事件来源的服 务器主机名)。你可以把它想象成和 HTTP 头一样提供相同的功能——通过该方法来传输正文之外的额外信 息

Body是一个字节数组,包含了实际的内容。

flume提供的不同source会给其生成的event添加不同的header

Flume内置拦截器:

1.Timestamp Interceptor 时间戳拦截器:

​ Timestamp Interceptor :拦截器就是可以往event的header中插入关键词为timestamp的时间戳。

  1. host拦截器 :

    该拦截器可以往event的header中插入关键词默认为host的主机名或者ip地址(注意是agent运行的机器的主 机名或者ip地址)

3.Regex Filtering Interceptor拦截器 :正则过滤拦截器

​ Regex Filtering Interceptor拦截器用于过滤事件,筛选出与配置的正则表达式相匹配的事件。可以用于包含 事件和排除事件。常用于数据清洗,通过正则表达式把数据过滤出来。

#全部是数字的数据

a1.sources.r1.interceptors.i1.regex = 1*$

#排除符合正则表达式的数据

a1.sources.r1.interceptors.i1.excludeEvents = true

Regex Filtering 实战开发引用场景:排除错误日志

对应的正则表达式 :a1.sources.r1.interceptors.i3.regex = ^((?!error).)*$

多个拦截器可以同时使用 拦截器:作用于Source,按照设定的顺序对event装饰或者过滤

8.Flume的选择器:

​ agent中source只有一个,而channel有很多就需要数据进入哪个channel

​ 在event进入到Channel之前,可以使用通道选择器 使指定的Event进入到指定的Channel中

Flume内置两种选择器,replicating(复制选择器) 和 multiplexing(多路选择器),如果Source配置中没有指定选择器,那么会自动使用复制 Channel选择器。

复制Channel选择器 (replicating ) 特点:数据同步给多个Channel

​ a1.sources.r1.selector.type = replicating

多路复用Channel选择器 (multiplexing) :数据分流到指定Channel ,根据数据头的key决定进入哪个channel

​ a1.sources.r1.selector.type = multiplexing

​ a1.sources.r1.selector.header = state #数据头的key是state的数据

​ a1.sources.r1.selector.mapping.app1 = c1

​ a1.sources.r1.selector.mapping.app2 = c2

9.自定义拦截器,有啥用?

​ 概述:在实际的开发中,一台服务器产生的日志类型可能有很多种,不同类型的日志可能需要发送到不同的分析系 统。此时会用到 Flume 拓扑结构中的 Multiplexing 结构,Multiplexing的原理是,根据 event 中 Header 的某个 key 的值,将不同的 event 发送到不同的 Channel中,所以我们需要自定义一个 Interceptor,为不同类型的 event 的 Header 中的 key 赋予不同的值。

实现Interceptor 接口重新intercept 方法,将项目打成jar包,上传到flume安装目录的lib目录下

编写agent,在job目录下的interceptors目录下创建,命名为my.conf

a1.sources.r1.interceptors.i1.type = com.baizhi.interceptors.MyInterceptor$Builder #权限定名

10.flume的source、sink、channel的作用?

① Source组件是专门用来收集数据的,可以处理各种类型、各种格式的日志数据 包括avro、exec、spooling directory、netcat、tail dir

② Channel组件对采集到的数据进行缓存,可以存放在Memory或File中 ,解决source和sink速率不一致问题

③ Sink组件是用于把数据发送到目的地的组件 目的地包括Hdfs、Logger、avro、file、kafka

11.flume的channel selector选择器作用?

channel selectors 可以让不同的项目日志通过不同的channel到不同的sink中去

官方文档上channel selector有两种类型:Replicating channel selector (default) 和 multiplexing channel selector 。

这两种selector的区别是:replicating会将source过来的events发送所有channel,而multiplexing可以选择该发往 哪些channle

12、flume的参数调优? 【简历写的技术】

1.source

​ ****增加source的个数,****可以加大source读取文件的能力。例如:当某一个目录产生的文件过多时需要将这个文件 目录拆分成多个文件目录,同时配置好多个source以保证source有足够的能力获取到新产生的数据。

​ batchSize参数决定source一次批量运输到channle的event条数,适当调大该参数可以提高source搬运event 到channle时的性能

2.channle

​ type选择memory时性能好,但是可能导致丢失数据。type选择file容错性更好,但是性能稍差。 使用file Channel时dataDirs配置多个不同盘下的目录可以提高性能。

​ Capacity 参数决定Channel可容纳最大的event条数。transactionCapacity 参数决定每次Source往channel里面写 的最大event条数和每次Sink从channel里面读的最大event条数。transactionCapacity需要大于Source和Sink的 batchSize参数。

capacity >= transactionCapacity >= (source和sink的batchSize)

3.sink

增加Sink的个数可以增加Sink消费event的能力。Sink也不是越多越好够用就行,过多的Sink会占用系统资源,造成系统 资源不必要的浪费

​ batchSize参数决定Sink一次批量从Channel读取的event条数,适当调大这个参数可以提高Sink从Channel搬出event的 性能。

13flume的事务机制

​ Flume使用两个独立的事务分别负责从Soucrce到Channel,以及从Channel到Sink的事件传递。比如spooling directory source 为文件的每一行创建一个事件,一旦事务中所有的事件全部传递到Channel且提交成功,那么 Soucrce就将该文件标记为完成。同理,事务以类似的方式处理从Channel到Sink的传递过程,如果因为某种原因使得事 件无法记录,那么事务将会回滚。且所有的事件都会保持到Channel中,等待重新传递。

Put事务流程 source---->channel

doPut:将批数据先写入临时缓冲区putList

doCommit:检查channel内存队列是否足够合并。

doRollback:channel内存队列空间不足,回滚数据

Take事务,channel---->sink

doTake:先将数据取到临时缓冲区takeList

doCommit:如果数据全部发送成功,则清除临时缓冲区takeList

doRollback:数据发送过程中如果出现异常,rollback将临时缓冲区takeList中的数据归还给channel内存队列。

14.flume采集数据会丢失吗?

​ 不会,Channel存储可以存储在File中,数据传输自身有事务 ,可以说上面的事务

15、Flume上传文件会生成大量小文件的问题

给对应的sink增加如下配置
#是否按照时间滚动文件夹
a2.sinks.k2.hdfs.round = true
#多少时间单位创建一个新的文件夹
a2.sinks.k2.hdfs.roundValue = 10
#重新定义时间单位
a2.sinks.k2.hdfs.roundUnit = minute
#多久生成一个新的文件 0代表禁用
a2.sinks.k2.hdfs.rollInterval = 0
#设置每个文件的滚动大小 1048576 = 1M
a2.sinks.k2.hdfs.rollSize = 1048576
#文件的滚动与 Event 数量无关
a2.sinks.k2.hdfs.rollCount = 0

特别注意:如果测试使用的是hadoop伪分布式环境,需要将hadoop下的hdfs-site.xml复制到flume的conf目录下

已经配置三个属性后的问题,也就测试的时候时候使用伪分布式的问题
hadoop分布式环境 副本数 3 flume内部默认的值,不会存在小问题
伪分布式环境 副本数 1 hdfs-site.xml

由于flume底层上传文件默认副本数是3,而如果在伪分布式环境下,我们设置的副本数是1,但是flume不知道,
导致flume底层源码在处理时,导致rollInterval/rollSize/rollCount不生效

4.解决方案
将hadoop下的hdfs-site.xml复制到flume的conf目录,不用做额外配置,flume会自动识别

16、了解shell脚本,死循环,向a.log追加内容

​ sleep起到睡眠的作用

#!/bin/bash

i=0
while ((1))
do
echo “hello world,$i” >> /opt/a.log
#echo hello
let i++
sleep 0.01
done

上边只能做到滚动生成文件,通过下边的配置可以滚动生成文件目录
hdfs.round = true
hdfs.roundValue = 10
hdfs.roundUnit = minute

hdfs.round false Should the timestamp be rounded down (if true, affects all time based escape sequences except %t)
hdfs.roundValue 1 Rounded down to the highest multiple of this (in the unit configured using hdfs.roundUnit), less than current time.
hdfs.roundUnit second The unit of the round down value - second, minute or hour.

17.Flume传输数据的流程:

source:

1 接收事件

2 处理事件

channel处理器:

3 将事件传递给拦截器链

4 将每个事件给Channel选择器

Channel选择器:

5 返回写入事件Channel列表

6 根据Channel选择器的选择结果,将事件写入相应Channel。

多个channel缓存数据

sink处理器:

7 Sink处理器选择其中一个Sink去获取Channel数据,并将获取数据写入到下一个阶段。

sink输出数据

source----接收数据----处理数据-------》channel处理器------经过拦截器链—决定进入哪个channel----》channel--------》sink处理器--------配置里面写的有哪个channel的数据进入哪个sink------》sink--------》目的地

6.Linux的问题

1.md5sum 是啥?计算机硬件特点

​ 1.数字签名-数字指纹。 2. 可以验证文件是否被修改。

​ 特点:每个文件只要内容不变,就有一个唯一的不确定数字指纹(数字签名)

文件内容的唯有标记,只要文件的内容不发生改变,MD5sum码就一致
一般用来版本的校验,看下载的软件和官方的是否一致

CPU  逻辑运算
硬盘  储存空间大,便宜。永久储存,读取比较慢
内存  读取速度快,贵, 空间小  不能永久存储数据
网卡  用来计算机之间的通讯。连接计算机,上网

2.查看文件的命令,滚动查看日志,介绍几个常用linux的命令

查看文件中的全部 :cat 文件名

以分页的方式浏览文件信息: more 文件名

滚动显示文件的最后10行信息 :tail -f 文件名

常用命令:ls/ll/cd/pwd/ip addr/systemctl status|stop|start firewalld hostname /vi tar -zxvf

kill -9 进程id 查找sshd进程:ps -aux | grep sshd rpm ivh xxx.rpm yum install -y 软件名

查看系统进程 ps -aux vim /etc/sysconfig/networkscripts/ifcfg-ens33 hostnamectl set-hostname 主机名

vim /etc/hosts

Linux基础命令:

ls、mkdir、rm|rmdir、cp|scp、mv、touch、more|cat|head|tail、vi 、chmod、du|df、ps、top、grep
> 必须记住
ps - aux | grep 进程名
kill -9 `ps -aux | grep 进程 | awk ‘{print $1}'`
kill  -s TERM 进程号 优雅退出
系统:/etc/profile
用户:~/.bashrc
主机名:/etc/sysconfig/network
网卡:/etc/sysconfig/network-scripts/ifcfg-eth0
ip主机名映射:/etc/hosts
普通用户切换超级管理员:su
常见的软件安装:tar -zxvf|-zcvf 文件名、rpm -ivh|-Uvh rpm包、yum 联网、编译安装(./configure|make |make install)
              tomcat|maven|hadoop        jdk|mysql            tree|get|gcc     Nginx|Redis|FastDFS
SSH:Linux系统之间秘钥认证一种形式,用户无需输入密码即可登录目标主机
ssh-keygen -t rsa|dsa -P '' -f ./ssh/id_rsa  - 产生公私钥
ssh-copyid 目标主机
service 服务名 start|stop|restart|status
chkconfig 服务名 on|off

3.Linux中的文件管理原理,数据和元数据的区别?

1.linux会把文件的真正数据保存在数据空间中

2.会为每一个文件生成一个元数据信息保存inode(文件的元数据、描述信息)

3.linux删除文件只需要删除文件名,就意味着文件找不到了

元数据存储的是一个数据的信息:大小创建时间,文件位置,创建用户等等,他会指向一个数据
元数据就是描述数据的数据,一般不会太大,大小基本是固定的
元数据删除后就相当于把数据删除了

4.啥事SSH,啥是免密登录咋实现的流程是啥,有啥用?

作用: 1. 远程ip连接(登录到另一个linux系统中) 2. 远程在对应的linux中执行命令。

远程登录linux :ssh 远程linux的ip或者映射域名

退出远程ssh连接: exit

免密登录:

ssh登录远程linux,免去输入密码的麻烦。

设置了免密登录两台计算机通信的流程:

准备:

1.生成一对密钥,公钥和私钥,ssh-keygen

2.把公钥交给要免密登录的机器保存起来 ssh-copy-id 保存公钥的ip

免密后的流程: A登录B

1.A发起连接请求ssh

2.B接收到后随机生成一个字符串,使用A给的公钥进行加密

3.把加密后的字符串发送给A

4.A接收到后对加密后的数据进行解密,然后把解密后的结果发送给B

5.B接收到后进行校验,校验成功,返回验证结果可以通信

在Hadoop的高可用集群就用到了

远程拷贝:scp -r 本地的文件 root@远程linuxip:/远程linux的文件路径

5.虚拟机的一些了解NAT和桥接两种网络方式区别?

NAT 
虚拟机分配的ip网段和主机不一样,可以上网,
主机所在的局域网不能和虚拟机通信,
虚拟机组成的局域网可以通信

桥接
虚拟机分配的ip网段和主机一样,可以和外部局域网通讯,可以上网

7.Hadoop生态圈和大数据的问题

1.什么是大数据 ?特点是啥?5V指的啥? 应用业务方向

大数据:

Big Data,数据体量非常大,TB级,日增长在GB级。

B-KB-MB-GB-TB-PB-EB-ZB 彼此之间差1024倍

大数据技术 :

数据体量大道一定程度,导致传统开发技术无法处理,需要使用新的分布式技术处理,叫做大数据技术。

这套技术可以使计算突破单体计算机的硬件配置的限制。

大数据特点 :5V:

1.体量大 Volume

MySQL 20G 上限 数据体量达到TB和PB级别,或日增长数据量在GB级别。

例如大型电商网站、金融行业的日志数据、订单数据

  1. 多样性 Variety

    是指不同的数据类型

    结构化:二维表

    半结构化:JSON XML

    非结构化:文本文件、图片、音频、视频、地理位置

  2. 快速 Velocity

    处理数据速度快,时效性要求高。 1小时 1天

  3. 价值密度低 Value

    数据中存在大量无关紧要的数据,不同的业务需求,需要的业务数据又不一样,需要从海量数据中清洗除需要 的关键性指标。

    Value -价值是指将数据转化为价值。通过将访问的大数据转换为价值,企业可以创造收入

5.Veracity

​ 是指可用数据的不确定性。由于大量数据带来不完整性和不一致性,因此产生了 准确性。

注意: 这是大数据访谈中提出的基本和重要问题之一。如果您看到面试官有兴趣了解更多信息,您可 以选择详细解释五个V. 但是,如果您被问及“大数据”这一术语,甚至可以提及这些名称。

应用场景:

应用场景:规律分析、系统预警、定向广告推送、猜你喜欢。。 行业:互联网、工业预警、智慧农业、大数据医疗、金融风控 本质: 海量数据===>有价值数据==>价值

工作方向 • 大数据运维工程师 • 大数据开发工程师(实时计算、数据仓库、ETL、基本挖掘) • 大数据分析(算法)

大数据核心问题 :

1.海量数据存储 (突破单机存储限制)

  1. 海量数据计算 统计 分析 处理(突破单机计算能力)

2.大数据分析如何有助于增加业务收入?

答:大数据分析对企业来说非常重要。它可以帮助企业将自己与众不同并增加收入。通过预测分析,大 数据分析为企业提供定制的建议和建议。此外,大数据分析使企业能够根据客户需求和偏好推出新产 品。这些因素使企业获得更多收入,因此公司正在使用大数据分析。通过实施大数据分析,公司可能会 收入大幅增加5-20%的收入。一些使用大数据分析来增加收入的受欢迎公司是 - 沃尔玛,LinkedIn, Facebook,Twitter,美国银行等。

3.解释部署大数据解决方案时应遵循的步骤摄取/存储/计算。

1.数据摄取

​ 部署大数据解决方案的第一步是数据提取,即从各种来源提取数据。数据源可以是像Salesforce这样的 CRM,像SAP这样的企业资源规划系统,像MySQL这样的RDBMS或任何其他日志文件,文档,社交媒 体源等。数据可以通过批处理作业或实时流来提取。然后将提取的数据存储在HDFS中。

2.数据存储

​ 在数据摄取之后,下一步是存储提取的数据。数据存储在HDFS或NoSQL数据库(即HBase)中。HDFS 存储适用于顺序访问,而HBase适用于随机读/写访问。

3.数据处理

​ 部署大数据解决方案的最后一步是数据处理。数据通过Spark,MapReduce,Pig等处理框架之一进行 处理。

4.大数据和Hadoop的关联:

​ 答: 大数据和Hadoop几乎是同义词。随着大数据的兴起,专门从事大数据操作的Hadoop框架也开始 流行起来。专业人员可以使用该框架来分析大数据并帮助企业做出决策。

​ 注意: 这个问题通常在大数据访谈中提出。 可以进一步去回答这个问题,并试图解释的Hadoop的主 要组成部分。

5.简单介绍一些Hadoop

​ Hadoop是一个由Apache基金会所开发的分布式系统基础架构。

Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。

​ HDFS为海量的数据提供了存储,而MapReduce则为海量的数据提供了计算

  1. HDFS–解决大数据存储问题 2. MapReduce–解决大数据计算问题

8.HDFS问题

1.hdfs是啥?和他的核心架构和作用NN/DN/SN[重要]

:解决海量数据存储问题。

架构NameNode DataNode SecondNode

Namenode:主节点

​ 1.管理hdfs集群中所有的从节点(ip地址,block分布信息。)i;

​ 2.管理hdfs中文件的元数据信息,保存在内存中(文件元数据 checksum );

​ 3.管理从节点中 block块的checksum( 判断DN是否活跃,判断block的checksum,验证DN数据完整性);

​ 4.接收客户端访问(操作文件上传下载,删除 );

Datanode:从节点:

​ 1.保存切分后的数据及备份;

​ 2.定时心跳,向namenode汇报自己的状态信息;

​ 3.定期发送block块的checksum;

SecondaryNameNode:

​ 负责持久化文件的合并,将主节点中内存里的数据进行持久化,定期将日志文件EditsLog中的数据,合并成FSImage(快照)的数据状态

Block:数据块

​ 默认128M

​ 数据块,文件切分后的最小单元

​ 过大:单个block的传输时间过长,更容易出现数据重传的情况(降低传输效率)

​ 过小:导致Block的数量过多,导致namenode的元数据过多,导致内存空间过早用完,导致效率极低

Replication:副本

​ HDFS对每个block的备份,保证blok的数据安全

​ 数据块block的副本,备份block,防止单体datanode宕机损坏导致数据丢失

checksum 数字指纹:

​ 校验和类似指纹数字签名。 确保数据的可靠性,hadoop内部在判断文件是否一致或者变化,就对比文件的签名即可

2.CheckPoint:editslog和FSImage的合并/持久化机制SNN作用?

CheckPoint:editslog和FSImage的合并

​ 1. SecondaryNameNode向NameNode发起合并请求;

​ 2. NameNode将当前的Editslog文件保存改名edits,并新建EditsLog继续持久化工作;

​ 3.将改名后的edits文件和本地的FSImage(旧)发送给SecondaryNameNode;

​ 4.SecondaryNameNode负责将FSImage(旧)+edits文件合并成FSImage(新);

​ 5.再将新的FSImage(新)发送给NameNode保存;

​ FSImage:全本快照的文件 ,备份慢,恢复块

​ editslog:写入日志命令的文件 备份快,恢复慢

为什莫使用SecondaryNameNode

NameNode数据存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数 据的FsImage。这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效 率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。因此,引入 Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加 到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据。但是,如果一 旦长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过 长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因 此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并

Checkpoin时机

  1. 每隔1小时触发一次checkPoint

  2. 每100w次操作,触发一次checkpoint

    每1分钟检查一次,操作次数

3.HDFS的上传和下载的流程:

HDFS文件上传:

​ 1.客户端发送上传请求;

​ 2.NN判断文件夹是否存在以及权限;

​ 3.返回确认信息;

​ 4.上传文件信息(文件的元数据信息);

​ 5.NN接收文件信息,计算出block信息;

​ 6.客户端按照block信息就近上传到一个DN(就近原则);

​ 7.上传其余block;

​ 8.NN协调DN对block进行备份replication;

HDFS文件下载:

​ 1.客户端发送下载请求;

​ 2.namenode接收请求,检测文件是否存在以及权限;

​ 3.返回确认结果,文件的block块以及对应的datanode信息和checksum;

​ 4.收到block信息,datanode信息以及checksum;

​ 5.根据block对应的datanode的地址,就近下载block中的数据(就近原则);

​ 6.对下载的block进行验证,下载的checksum是否和ND传递过来的checksum一致;

​ 7.下载所有的block;

​ 8.客户端对block进行合并;

4.hdfs集群启动失败怎么处理?格式化的命令?

场景: 格式化或者启动hadoop失败。

说明: hadoop/data文件夹

作用: 保存datanode和namenode持久化的数据。

时机:

  1. 格式化hdfs namenode -format 会初始化该目录下的文件。
  2. hdfs运行期间产生的数据,会操作该目录中的数据。

必要操作:删除格式化或者启动数据保存的文件目录。

9.MapReduce的问题

1.什么是MR,干啥用的?job/map/reduce是啥?

​ MapReduce是hadoop体系下的一种计算模型(计算框架|编程框架),主要是用来对存储在hdfs上的数据进行统计,分析 的

  1. Job(一个大型任务)[Application] : 一组MapReduce又统称为一个Job作业

  2. Map(拆分后的小任务) : 局部计算

  3. Reduce(整合任务) :对局部计算结果进行汇总计算。

2.介绍一下MR的Yarm是什么?特点?主要组成RM和NM?

​ 作用(包工队) 资源调度,任务监控 主要用来整合hadoop集群中的资源(CPU 内存),进行统一调度 同时监控任务的执行情况 总结: 联合多个服务器节点的硬件,共同完成一个计算。突破单机服务器的计算能力。

ResourceManager:(包工头)

​ 整个hadoop集群中的资源管理器,也是yarn架构中的主节点;

​ 负责监控和分配集群中的资源

NodeManager:(干活的)

​ yarn集群计算资源的提供者,也是yarn架构中的从节点;

​ 负责监控本节点的资源,向ResourceManager汇报资源现状;

MapReduce特点:

​ 1. 易于编程:只需要使用hadoop接口进行编程,即可实现多台计算机分布式计算和分布式存储。

​ 2. 高扩展性:存储空间不足或者计算能力不足,则可以添加计算机完成。

​ 3. 容错性高:如果某个节点宕机,hadoop可以自动切换讲计算任务转移到其他节点上完成,不会影响计算结果。

​ 4. 应用场景:PB级别以上海量数据的离线处理,无法实时处理和流失动态处理。

3.hadoop序列化 和java序列化和排序

​ mapreduce执行过程中,被处理的key-value数据,需要在网络中传输,就需要对象转化为字节,字节转化为对 象,这就是序列化和反序列化过程; key和value都要经过序列化传输

  1. Java序列化(序列化数据+对象描述信息)

序列化会包含java的继承关系,验证信息,验证信息。(重量级) 不便于在网络中传输。

  1. Hadoop序列化(仅关注数据序列化)

空间紧凑 传输快速,网络开销小。 对象重用(反序列化的时候,只创建一个)

结论: mapreduce中所有key-value都要支持序列化。

自定义序列化类型

自定义一个类实现WritableComparable

  1. 可以被hadoop序列化传输。 2. 可以支持排序。

排序

1.简介

Shuffle期间,MapReduce会对map输出的数据,对key进行排序。

2…时机:

​ 1.map输出之后,shuffle过程中。

​ 2.map输出之后的map端。

3.规则:

1. key如果是Text类型按照字典顺序,进行字符串排序。
2. key如果是IntWritable LongWritable 则按照数字大小进行升序排序。 

5.MapTask局部计算并行度(优化) MapTask并行度,是不是越大越好?

MapTask并行度,是不是越大越好?

  1. MapTask的并行度的产生

    1. inputformat根据配置信息,获得hdfs中文件的split大小和位置。
    2. 每个split就会启动一个MapTask,进行处理。
  2. 总结MapTask并行度决定机制

    split的个数。

  3. 概念:

    block:hdfs文件的最小单元。

    split:文件切分信息,虚拟的文件切片。

  4. 默认:blocksize的大小就是split的大小128M,也就是一个MapTask执行的任务。

    这样能够减少多个节点的MapTask之间的网络IO。

  5. 切片操作是针对1个文件,多个文件的切片不会合并。

6.ReduceTask汇总并行度(优化)

提高ReduceTask的数量,提高Reduce的并行度,提高效率

  1. 增加ReduceTask的并行度(数量) ,可以启动多个程序处理map的汇总结果,可以提高效率

  2. 每个ReduceTask输出结果,都会单独的输出到1个文件。

ReduceTask的数量是可以在程序中手动指定

​ 默认数量为: 1个

​ Reduce 可以通过: job.setNumReduceTasks(数字); 0 就是没有 数字是几就是几个

4.MapReduce数据流转机制、工作原理 【重要】

Spill溢写 :

  1. map输出的结果会存入环形缓冲区(从start下标开始写,写到80%,则启动溢写程序。环形缓冲区继续写入)
  2. 当环形缓冲区中的数据,达到80%,则开始溢写。(每次写够80%,就开始溢写。)
  3. 如果设置了分区,则对数据进行分区
  4. 然后对分区后的数据各自做排序
  5. 如果设置combiner,则执行map端的reduce合并处理
  6. 将本次溢写的数据写入到本地的磁盘上。
  7. 循环2~6,将多个文件溢写到磁盘上。
  8. 将各个分区中,多次溢写的文件,再进行一次合并排序,然后将合并后的数据写入到对应的磁盘的分区上。

全工作流程

MapTask过程

  1. 创建InputFormat,读取数据

    ① 获得文件split

    ② 读取split范围内的数据,k-v。

  2. Mapper.map()方法处理,InputFormat读取到k-v, 循环读取文件中k-v,每次读取,调用一次mapper.map();

while(读下一条){
	mapper.map(k,v);
}	

​ map执行结果context.write(ko,vo)

  1. mapper输出结果

    ① 获得ko-vo获得分区号。(Partitioner.getpartion())

    ② 将ko-vo写出到环形缓冲区中

  2. 一旦环形缓冲区中数据达到溢写条件(80%,写完了)

    ① 读取环形缓冲区中的数据

    ② 根据分区号,分区排序、(Combiner)

    ③ 将处理结果溢写到磁盘中文件中。

    ④ 每次达到溢写条件(80%,写完了),①~③,在mapTask本地磁盘形成分区文件。

    ⑤ 最后在本地完成一次分区内多个溢写文件 归并排序,产生1个文件(maptask处理结果)。

ReduceTask过程:

  1. 根据分区号,启动ReduceTask,下载多个MapTask处理结果中的对应分区文件

    ​ MapTaskA(分区0)----ReduceTask0 MapTaskB(分区0)----ReduceTask0

  2. 将当前分区中,来自不同MapTask的分区文件,归并排序。(为了reduce的merge操作效率)

    产生1个大的本分区的文件,且内容key有序。

  3. merge操作,将有序的结果,合并key的value。

  4. 循环调用reducer的reduce方法,处理汇总的数据

while(xxx){
    reduce.reduce(key,values);
    context.write(k,v)
}
  1. ReduceTask调用OutputFormat将结果写入到hdfs文件中。

Shuffle:

map阶段

  1. mapper输出结果

    ① 获得ko-vo获得分区号。(Partitioner.getpartion())

    ② 将ko-vo写出到环形缓冲区中

2、一旦环形缓冲区中数据达到溢写条件(80%,写完了)

​ ① 读取环形缓冲区中的数据

​ ② 根据分区号,分区排序、(Combiner)

​ ③ 将处理结果溢写到磁盘中文件中。

​ ④ 每次达到溢写条件(80%,写完了),①~③,在mapTask本地磁盘形成分区文件。

​ ⑤ 最后在本地完成一次分区内多个溢写文件 归并排序,产生1个文件(maptask处理结果)。

reduce阶段

  1. 根据分区号,启动ReduceTask,下载多个MapTask处理结果中的对应分区文件

    ​ MapTaskA(分区0)----ReduceTask0 MapTaskB(分区0)----ReduceTask0

  2. 将当前分区中,来自不同MapTask的分区文件,归并排序。(为了reduce的merge操作效率)

    产生1个大的本分区的文件,且内容key有序。

  3. merge操作,将有序的结果,合并key的value。

split切片规则

​ 前提:splitSize=128M

​ 1.判断文件是否大于128M的1.1倍

​ 2.如果大于则按照128M切一次

​ 3.如果剩余的还大于128的1.1倍,就在切一次,知道结束

总结:

​ 1、MapTask:FileInputFormat:对文件进行了切分,读取文件 循环调用Mapper.map()对数据进行局部计算 把计算后的结果,进行分区计算,写入到环形缓冲区总 当写入数据达到80%或者数据写完触发溢写机制

对数据进行溢写,根据分区编号把数据进行整合排序,每次溢写 会形成一个文件在把文件根据分区编号进行整合排序 

  如果有需要可以对数据进行合并后写入到磁盘文件中 

2、ReduceTask阶段:根据分区编号启动对应的ReduceTask对相应分区的

数据进行下载,把下载的当前分区的多块数据进行排序合并 ReducerTask循环调用Reducer.reduce()对数据进行处理计算 通过FileOutputFormat把数据写入到HDFS中,一个分区一个文件

3、Shuffle:map处理的数据计算分区编号写入到环形缓冲区中 当写入数据达到80%或者数据写完触发溢写机制 对数据进行溢写,根据分区编号把数据进行整合排序, 每次溢写会形成一个文件在把文件根据分区编号进行整合排序

​ 如果有需要可以对数据进行整合后写入到磁盘文件中 当所有MapTask执行完之后会触发ReducTask ReduceTask会根据分区编号启动对应的ReduceTask对相应分区的

​ 数据进行下载,把下载的当前分区的多块数据进行排序合并

5.MR程序运行的时候会有什么比较常见的问题?

​ 比如说作业中大部分都完成了,但是总有几个reduce一直在运行

​ 这是因为这几个reduce中的处理的数据要远远大于其他的reduce,可能是对键值对任务划分的不均匀 造成的数据倾斜

​ 解决的方法可以在分区的时候重新定义分区规则对于value数据很多的key可以进行拆分、均匀打散等处 理,或者是在map端的combiner中进行数据预处理的操作

10、Zookeeper问题

1.简单介绍一下Zookeeper,特点?角色?

​ Apache ZooKeeper是Apache软件基金会的一个软件项目,它为大型[分布式计算]提供[开源]的分布式配置 服务、同步服务和命名注册。

简言:ZK就是一个管理多个服务的 集群管理者 + 文件系统

核心解决问题:

​ 1.同步配置集群配置信息(文件系统)

​ 2.监听服务器的上下线,同时可以做出反应。(监听器)

  1. 存数据,所有zk节点同步数据。
  2. 监听器,服务器和数据变化,事件发生可以做出反应(执行代码)
  3. zk集群本身 稳定性 数据可靠性 非常高。

特点 :

  1. 1主(Leader)多从(Follower),一旦故障就能立刻选出新的主机,继续提供服务。 自身稳定性No单点故障
  2. 集群中只要有半数以上zk节点存活,zk集群就能正常使用。 服务节点可靠性
  3. 选主和操作,使用 投票机制 。 服务的高效性、可靠性、操作的可靠性
  4. 任何一台zk主机添加的数据,都会同步到所有所有zk主机。 全局数据同步
  5. zk数据更新要么全部成功,要么全部失败。 事务性,数据一致性

集群角色 :

​ leader(主机)是 zookeeper集群的核心 :

​ 1.zookeeper集群工作的核心。

​ 2.事务操作的老大(增删改操作得让他点头)

​ follower(从机)

​ 1.承担非事务性操作,读操作。

​ 2.参与leader选举的投票

​ observer(观察者,了解)

​ 1.承担非事务性操作,读操作

​ 2.不能参与leader选举的投票

zookeeper以后的客户端实际上是zk要管理的集群服务器(软件程序)。

zookeeper以后保存数据:服务器地址、状态、配置信息。(非常少量的数据)

2.Zookeeper投票选主流程:

主机不固定,动态选举,保证主机的可靠性,选举过程中主从之间有通信

​ 假设有5台服务器,编号1,2,3,4,5。

​ 服务器1启动,给自己投票,接受在已经启动的机器队列中接受投票。得1票。

​ 服务器2启动,给自己投票,接受在已经启动的机器队列中接受投票。得2票,但此时投票数没有大于半数,所以两个服 务器的状态依然是(选举状态)

​ 服务器3启动,给自己投票,接受在已经启动的机器队列中接受投票,得3票,由于服务器3的编号最大所以服务器3胜 出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。

​ 服务器4启动,已经有老大,服务器4只能成为小弟。

​ 服务器5启动,后面的逻辑同服务器4成为小弟

3.Zookeeper的节点类型和监听机制

  1. 持久化节点 节点只要不删除,会一致存在。

  2. 顺序编号节点 每次对相同节点,重复创建,会自动对znode名称进行编号

  3. 临时节点 客户端断开,则节点消失。

    永久性节点:节点一直存在不会消失

    永久性有序节点:每次创建该节点的时候名字会自增

    临时性节点:创建节点的客户端关闭就会小时,用来监控服务器的上下线

    临时性有序节点:创建的临时节点会自动自增

监听通知

  1. 监听节点值得修改(set)和删除(delete)变化
  2. 监听某个节点及其子节点的增加、删除。

监听节点的流程【重要】:

​ 1.客户端发送监听命令(监听节点/和监听事件)到zkServer

​ 2.zkServer转发给zKServer(Leader)

​ 3.zKServer(Leader)把该事件绑定到要监听的节点上,也就是注册到事件列表中

​ 4.当有客户端对所监听的节点进行操作(删除/修改)

​ 5.zKServer(Leader)在zk事件列表中查找节点,并查找是否绑定了对应的事件

​ 6.如果发现该节点有对应的事件的注册信息,会通知该监听事件的客户端

4.客户端操作zookeeper的步骤添加/删除加节点/写操作

​ 1.客户端发送写操作命令到zkServer(任意)

​ 2.zkServer将操作写操作转发给zKServer(Leader)

​ 3.zKServer(Leader)将写操作广播给所有zkServer节点

​ 4.每个zKServer写成功后,返回给zKServer(Leader)

​ 5.如果集群中超过半数的zKServer写成功,则zKServer(Leader)返回给客户端写成功

5.简介ZAB

​ ZAB协议是为Zookeeper专门设计的一种支持崩溃恢复的消息广播协议。ZAB协议只允许有一个主进 程接受客户事务请求并处理,即leader。当leader收到请求后,将请求事务转化为事务proposal,由于 leader会为每一个follower创建一个队列,将该事务放入响应队列,保证事务的顺序性。之后会在队列 中顺序向其他节点广播该提案,follower收到后会将其以事务的形式写入到本地日志中,并向leader发 送反馈ack确认。leader会等待其他followe的回复,当收到一半以上的follower响应时,leader会向其 他节点发送commit消息,同时提交该提案。 (可以说zk的写机制上面的4)

​ ZAB有两种模式,分别为故障恢复模式以及消息广播。当系统启动或者leader服务器出现故障等现象 时,进入故障恢复模式。将会开启新的一轮选举,选举产生的leader会与过半的follower进行同步,使 数据一致。

​ 当同步结束后,退出恢复模式,进入消息广播模式。当一台遵从ZAB协议的服务器启动后,如果检测 到有leader在广播消息,会自动进入恢复模式,当其完成与leader的同步之后,进入消息广播模式;如 果集群中的非leader节点收到客户端请求,非leader节点会先将请求发送到leader服务器。

​ 故障恢复发时候,ZAB协议两个需要保证的地方,第一就是ZAB协议需要保证已经被leader提交的事 务最终被所有的机器提交;第二就是需要保证丢弃那些只在leader上提交的事务。为了保证以上两点, 选举时如果选择ZXID最大的节点可以解决上述问题。

​ leader重新选举的条件是当leader宕机或发生故障,集群中少于一半的节点与当前leader保持连接。

6.Zookeeper如何保证数据的一致性?

1)顺序一致性: 来自客户端的更新将严格按照客户端发送的顺序处理;

2)原子性 :更新或者成功或者失败,不存在部分成功或者部分失败的场景;

3)单一视图: 无论客户端连接到哪个服务器,看到的都是一样的视图;

4)可靠性: 一旦一个更新生效,它将一直保留,直到再次更新;

5)实时性: 在特定的一段时间内,任何系统的改变都能被客户端看到,或者被监听到。

7.叙述ZAB集群数据同步的过程

准备:了解 什么是ZooKeeper中ZXID

​ 致使ZooKeeper节点状态改变的每一个操作都将使节点接收到一个Zxid格式的时间戳,并且这个时间戳全局有序。也就是说,每个对节点的改变都将产生一个唯一的Zxid。如果Zxid1的值小于Zxid2的值,那么Zxid1所对应的事件发生在Zxid2所对应的事件之前。实际上,ZooKeeper的每个节点维护者两个Zxid值,为别为:cZxid、mZxid。

(1)cZxid: 是节点的创建时间所对应的Zxid格式时间戳

(2)mZxid:是节点的修改时间所对应的Zxid格式时间戳。

实现中Zxid是一个64为的数字,它高32位是epoch用来标识Leader关系是否改变,每次一个Leader被选出来,它都会有一个新的epoch。低32位是个递增计数。

什么是epoch?

ZooKeeper中ZXID是一个长度64位的数字,其中低32位是按照数字递增,即每次客户端发起一个proposal,低32位的数字简单加1。高32位是leader周期的epoch编号。

ZAB集群数据同步的过程:

第一阶段(准leader 生成初始化事务集合)

​ 所有follower 向leader 发送自己最后接收的事务的epoch;

​ leader 选取最大的epoch,加1得到e1,将e1 发送给follower;

​ follower 收到leader 发送的epoch 值之后,与自己的epoch 值作比较,若小于,则将自己的epoch 更新为e1,并向准leader 发送反馈ACK信息(epoch 信息、历史事务集合);

​ leader 接收到ACK 消息之后,会在所有历史事务集合中选择其中一个历史事务集合作为初始化事务 集合,该事务集合满足ZXID最大;

第二阶段(数据同步)

​ leader 将epoch 与 初始化事务集合发送给集群中过半的follower;每个follower 会分配到一个队 列,leader 会将那些没有被各个follower 同步的事务以proposal 形式发送给各个follower,并在后面 追加commit 消息,表示事务已被提交;

​ follower 接收后,会执行初始化事务集合中的事务(执行过跳过,未执行执行),反馈给leader 表明 自己已经处理

​ leader 收到后过半反馈后,发送commit 消息

​ follower 接收到commit 消息后,提交事

11、Hadoop的高可用集群

目前hadoop分布式集群的问题

  1. editslog无法做到100%持久化,如果namenode宕机会有少量数据就丢失了。
  2. 一旦NameNode宕机,整个hadoop集群还是会停止服务。
  3. 如果启动2个NameNode,缺乏自动切换,就算namenode自动切换,客户端访问的ip地址也无法

1.主要组件:

ZKFC:
故障切换,当namenode主机挂掉,远程连接挂机的killall,强制杀死。启动NameNode(备机)
Journalnode
共享Editslog,同步多份。
NameNode(主备)
NameNode主:hdfs集群的主机。保存元数据、管理DN、协调集群。
NameNode从:做editslog和fsimage合并的操作。(checkpoint Secondarynamenode职责)
DataNode
保存block数据 replication副本 心跳汇报自身资源 数据的checksum。

2.总结:

1.zkFC:自动故障切换hadoop根据zoozookeeper写的,监控主机的状态,主机挂掉后启动备机,在启动备机的同时为了防止“脑裂"会杀死之前的主机

2.备机在主机的可用的情况下,会充当SecondaryNameNode去进行快照持久化

3.journalNode:依赖与zookeeper用来存储主机的命令日志备份 和zookeeper一样journalNode里的数据会同步到其他的JournalNode里面,这样在主机出现重大故障的时候命令日志备份不会丢失

4.namespace:虚拟IP里面有主机nn1和备机nn2两个IP当主机正常的时候nn1可用,nn2不可用。两个IP只有一个可用,客户端访问的时候会访问这个namespace

12、HBase的问题【重要】

1.简单介绍HBase为什么使用?特点?

​ HBase是构建在HDFS之上的分布式、面向列的存储系统,在需要实时读写、随机访问的超大规模数据集是, 可以使用HBase

为什么需要HBase :

​ 海量数据存储 一个表百亿行 百万列;(MySQL实战最大值500万行,30列)

​ 实时查询 1秒内查询得到结果。

HBase特点 :

  1. 容量大

    HBase单表百亿行,百万列。

  2. 面向列

    HBase存储是面向列,可以再数据存在以后动态增加新列和数据,并支持列数据的独立操作。

  3. 多版本

    HBase每个数据,可以同时保存多个版本,按照时间去标记。

  4. 稀疏性

    ​ HBase每条数据的增删,并不是要操作所有的列,的列可以动态增加,可以存在大量空白单元格,不会占用磁盘空间,这对于海量数据来讲,非常重要。

  5. 扩展性

    底层使用HDFS,存储能力可以横向扩展。

  6. 高可靠性

    底层使用HDFS,拥有replication的数据高可靠性。

  7. 高性能

    表数据达到一定规模,“自动分区”,具备主键索引,缓存机制,使得HBase海量数据查询能达到毫秒级

2.HBase的数据相关概念 ,行键列族的概念,物理模型,表的设计原则?

​ 1.namespace 命名空间

​ hbase管理表的结构,在HDFS中对应一个文件夹。

​ 2.table 表

​ hbase管理数据的结构,在HDFS中对应一个文件。

​ 3.column family 列族

​ 表中数据的列,要属于某个列族,所有的列的访问格式(列族:列名)

​ 创建表时指定的,为列的集合,每个列族作为一个文件单独存储,存储的数据都是字节数组,其中数据可以有很多,通过时间戳来区分

​ 4.rowkey 主键

​ 用来标记和检索数据的主键key。

​ 是hbase表自带的,每个行键对应一条数据

​ 5.cell 单元格

​ 由row key+column family+column+version 唯一确定的一条数据

​ timestamp 时间戳

​ 6.时间戳,每个单元格可以保存多个值,每个值有对应的时间戳,每个cell中,不同版本的数据倒叙排序,排在最前面的是最新数据

​ 物理模型:整个hbase表会拆分成多个region,每个region记录着行键的起始点保存在不同的节点上,查询时就是对各个节点的并行查询,当region很大时使用.META表存储各个region的起始点,-ROOT又可以存储.META的起始点。

​ Rowkey的设计原则:各个列族数据平衡,长度原则、相邻原则,创建表的时候设置表放入regionserver缓存中,避免自动增长和时间,使用字节数组代替string,最大长度64kb,最好16字节以内,按天分表,两个字节散列,四个字节存储时分毫秒。

​ 列族的设计原则:尽可能少(按照列族进行存储,按照region进行读取,不必要的io操作),经常和不经常使用的两类数据放入不同列族中,列族名字尽可能短。

3.HBase架构相关概念 【重要】

**HBase采用Master/Slave架构搭建集群,它隶属于Hadoop生态系统,由一下类型节点组成: HMaster 节点、HRegionServer 节点、 ZooKeeper 集群,而在底层,它将数据存储于HDFS中,因而涉及到HDFS的NameNode、DataNode等,总体结构如下: **

HMaster

  1. 管理HRegionServer,实现其负载均衡。
  2. 管理和分配HRegion,比如在HRegion Split时分配新的HRegion;
  3. 在HRegionServer退出时迁移其内的HRegion到其他HRegionServer上。
  4. 实现DDL操作(Data Definition Language,namespace和table的增删改,column familiy的增删改等)。
  5. 管理namespace和table的元数据(实际存储在HDFS上)。
  6. 权限控制(ACL)。

HRagionServer

​ HRegionServer(和DataNode同一节点)

​ 1.存储表数据部分

​ 2.put delete get scan等针对数据的操作

​ 3.定时向Master报告自身节点的状态

​ 4. 管理表的数据的Table的数据

  • 存放和管理本地HRegion。
  • 读写HDFS,管理Table中的数据。
  • Client直接通过HRegionServer读写数据(从HMaster中获取元数据,找到RowKey所在的HRegion/HRegionServer后)。

Zookeeper

​ 1.解决HMaster的单点故障问题/实现HMaster主从节点的failover

​ 2.存放HMaster管理的HRegionServer的状态信息,并通知HMaster

​ 3.存放HMaster管理的表的元数据信息 表名、列名、key区间等。

HRegion

​ 表的横向切片的物理表现,大表的子表,有(startkey endkey),多行数据。

​ 为了减小单表操作的大小,提高读写效率。

Store

  1. 表的纵向切分的物理表现,按照列族作为切分。
  2. 按照列族查询,仅需要检索一定范围内的数据,减少全表扫描。

重要:

HMaster
​ 1:集群的管理者。
​ 2:监控并管理HRegionServer状态,并HRegionServer负载均衡。
​ Region2–Region1
​ 3: HMaster管理表的元数据
​ namespace名字 表名字 : zookeeper存储。
​ 接受DDL指令。
​ 补充:如果HMaster挂掉,增删和DDL语句无法执行。
Zookeeper
​ 1: 存储表的元数据:表:RegionServer节点。
​ 2:存储管理监控HMaster RS节点数据。(临时节点)
​ 3:解决单点故障问题。
HRegionServer
​ 1:保存数据,存在的形式Region
​ 2:监控自身节点状态,动态向主机汇报。
​ 3:接受数据操作DML语句。
​ 如果集群HMaster挂掉,允许你get scan HRegionServer中的数据

Region:
1:有HRegionServer维护的表。
2:每个Region维护startkey~endkey范围内数据。
3:Region数据量达到128M(10G),拆分。[合久必分]

Store:
1: 在每个Region中,有多个列族。每个列族的数据就是一个Store(n行1列族);
2:如果RS 40%,Memstore(1h),Region数据量128M,导致store的刷写。
内存中Store数据持久化到-----HDFS文件系统中–StoreFile
StoreFile:
1:将大量小的Storefile合并 [分久必合]
2:局部合并:
部分相邻的store file文件合并大文件。
3:全局合并:
将1个列族对应store刷写到hdfs对应文件夹下,所有storefile进行合并,1个全局大文件。
频率:默认7天一次。
注意:
① 将所有的小文件合并,将过期的版本的数据清除掉。
② 整个过程比较消耗时间。
手动: HBase服务器闲置。
major_compact “namespace:表”

4.HBase读写数据操作原理

读数据:

客户端向zookeeper发送请求,zk会返回meta(存储有表的元数据信息)表的位置,客户端向该HRegionServer发送请求同时携带要查询的rowkey,HRegionServer会根据meta表计算出该rowkey所在的Region所在的HRegionServer所在的位置,得到ip后客户端向该HRegionServer发送请求,先到存在内存中的Region中读,读不到,到缓存(查询频率高的数据)中读,最后到硬盘中读,因为硬盘中的数据是有序的所以比较块

写数据:

客户端向zookeeper发送请求,zk会返回meta(存储有表的元数据信息)表的位置,客户端向该HRegionServer发送请求同时携带要写入的rowkey,HRegionServer会根据meta表计算出该rowkey所在的Region所在的HRegionServer所在的位置,得到ip后客户端向该HRegionServer发送请求,先把数据写入数据的命令写入一个日志中,同时把数据写入内存中的Region,stort定期刷写到硬盘中,在定期局部合并全局合并

具体的flush刷写和合并后面

总结:

数据读取流程
语法:get 表 rowkey
1. 先访问zookeeper,获得meta所在Regionserver地址
2. 访问Meta所在的RS。
目的:找到要访问的Region在哪儿?
表–n条region–找到rowkey所在Region—Region所在的RS
3. 访问对应RS的Region:
RegionServer在内部检索数据
4. 先从Region内存(Memstore),查找数据,如果查到返回结果。
5. 如果没有查到,就查找BlockCache(缓存 LRU),如果查找到,就返回。
6. 如果没有,则HBase从HDFS的磁盘中查找,StoreFile里面查找。
添加(修改 删除):
语法:put 表 rowkey 列族 值
1. 先访问zookeeper,获得meta所在Regionserver地址
2. 访问Meta所在的RS。
目的:找到要访问的Region在哪儿?
表–n条region–找到rowkey所在Region—Region所在的RS
3. 访问对应RS的Region:
RegionServer在内部检索数据位置
4. 将写操作记录日志 (HLog) (WAL)
5. 将数据写入内存(Region:memstore写缓存 内存)

**5.Region Split 分区默认分区和自定义分区好处?**Region 如何预建分区?

​ 提高Region的负载和读写效率。 Region一拆为二,并分布在不同的RegionServer上。

默认分区机制 :

​ Region中数据超过128M、512M、1152M… Region数量2*hbase.hregion.memstore.flush.size … 10G、10G

问题 :

​ 默认分区容易导致数据倾斜,硬件资源无法利用。(数据热点问题,大量的客户端访问,落在部分节点 上,导致忙的忙死,闲的闲死。)

​ 也就是我们插入数据一般都是顺序插入这样最先分区的region的数据就可少,因为他达到128就分了,后面的就会很大,并且前面分的region一般就不在里面存了

解决:自定义分区,预分区

​ 预分区的目的主要是在创建表的时候指定分区数,提前规划表有多个分区,以及每个分区的区间范围,这样在存储的时候 rowkey 按照分区的区间存储,可以避免 region 热点问题。

为什么 :

​ 增加读写效率。(多个region分布在不同的RegionServer中,可以提高并发效率)

​ 尽量保证每个Region中的数据量相当,防止数据倾斜。(合理利用计算资源)

分区的效果 :

​ 每个Region维护一对StartKey和EndKey,限定维护输入rowkey范围。

​ 添加数据时,将rowkey放入匹配的region中。

创建表时分区,手动指定 :

命令: create “namespace:表”,“列族”,SPLITS=>[“100000”,“200000”,“300000”,“400000”]

了解:解决分区问题:

​ 通常有两种方案:
方案 1:shell 方法
create ‘tb_splits’, {NAME => ‘cf’,VERSIONS=> 3},{SPLITS => [‘10’,‘20’,‘30’]}
方案 2: JAVA 程序控制
· 取样,先随机生成一定数量的 rowkey,将取样数据按升序排序放到一个集合里;
· 根据预分区的 region 个数,对整个集合平均分割,即是相关的 splitKeys;
· HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][]splitkeys)可以指定预分区的 splitKey,即是指定 region 间的 rowkey 临界值

6.Flush刷写 把内存中数据持久化到硬盘中文件合并局部/全局

​ 将RegionServer中内存中的数据Memstore,写入到硬盘中。 ,保护数据不丢失。

时机 :

  1. Region Server整个服务总内存占用达到40%该flush会暂停客户端读写操作

  2. MemStore持续写入1h

  3. 单个Region中的数据文件大小超过128M。

手动flush

Store File Compaction:刷写到磁盘里的store的合并

​ 目的 :

​ storefile小文件过多,查询时,需要遍历所有文件,效率低。

​ storefile中遍布过期数据,占用空间,且查询效率低。

简言:为提高检索效率,合并store。

分类和时机

​ minor compact(局部合并 )

​ 特点:少量相邻(加速合并,并有序)文件的合并

​ 时机:发生频率较高,不影响性能。 手动命令: compact “namespace:表名”

​ majorcompact(全局合并)

​ 特点: 1. 全局的所有store file文件的合并。

​ 2.去除删除被覆盖的文件。 (防止诈尸),删除过期数据

​ 3… 特别消耗RegionServer的性能资源。(重点)

​ 时机:默认每7天执行一次:参数: hbase.hregion.majorcompaction

​ 一般手动触发。 手动触发命令: major_compact “namespace:表名”

7.为什么HBase数据读取速度快BlockCache

​ 1 Memstore Region内存中 特点: (内存) (数据最新的) (有序)

​ 2 BlockCache(LRU) HBase缓存中。 缓存策略:LRU(数据淘汰机制),最近最少使用原则,保留最近最新使用多的数据。

​ 3:磁盘storeFile(每个小file中rowkey是有序的) LSM 磁盘的检索速度慢是因为寻道。 磁盘合并大storeFile(减少file数量,可以提高磁盘检索效率)

​ 1.storefile文件数量少,减少遍历。

​ 2.文件内以及文件在磁盘中,rowkey有序,代码检索,磁盘寻道大大节省时间。

8.HBase和MySql区别

RDBMS(关系型数据库- MySQL|Oracle)问题
1.建表限定列数量,无法动态随意扩展。
2.MySQL单表数据库500w左右,数量小。
3.MySQL单表数据量过大,横向切分。(手动开发、复杂、稳定性低、效率低)

4.MySQL中单表的列过多,影响表的CRUD效率。( MySQL单表列不能30个)

5.MySQL单表的列过多,纵向切分。(手动开发、复杂、稳定性)

6.MySQL数据库允许空值,空值的空间是占用了的。(海量数据面前,空间浪费)

7.MySQL数据库中每个单元格,只能保存一个值(一个版本)。
总结:
海量数据存储问题
实时检索问题

HBase 数据库(海量存储实时检索)

8.HBase 和 HDFS 关系

​ HDFS是Hadoop分布式文件系统。
​ HBase的数据通常存储在HDFS上。HDFS为HBase提供了高可靠性的底层存储支持。
​ Hbase是Hadoop database即Hadoop数据库。它是一个适合于非结构化数据存储的数据库,HBase基于列的而不是基于行的模式。
​ HBase是Google Bigtable的开源实现,类似Google Bigtable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统;Google运行MapReduce来处理Bigtable中的海量数据,HBase同样利用Hadoop MapReduce来处理HBase中的海量数据。
​ HDFS为HBase提供了高可靠性的底层存储支持,Hadoop MapReduce为HBase提供了高性能的计算能力,Zookeeper为HBase提供了稳定服务和failover机制。Pig和Hive还为HBase提供了高层语言支持,使得在HBase上进行数据统计处理变的非常简单。 Sqoop则为HBase提供了方便的RDBMS(关系型数据库)数据导入功能,使得传统数据库数据向HBase中迁移变的非常方便。

9.如何提高 HBase 客户端的读写性能?请举例说明

1 开启 bloomfilter (布隆过滤器)过滤器,开启 bloomfilter 比没开启要快 3、4 倍
2 Hbase 对于内存有特别的需求,在硬件允许的情况下配足够多的内存给它
3 通过修改 hbase-env.sh 中的
export HBASE_HEAPSIZE=3000 #这里默认为 1000m
4 增大 RPC 数量
通过修改 hbase-site.xml 中的 hbase.regionserver.handler.count 属性,可以适当的放大RPC 数量,默认值为 10 有点小。

了解:

​ bloom filter的数据存在StoreFile的meta中,一旦写入无法更新,因为StoreFile是不可变的。Bloomfilter是一个列族(cf)级别的配置属性,如果你在表中设置了Bloomfilter,那么HBase会在生成StoreFile时包含一份bloomfilter结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。

10.直接将时间戳作为行健,在写入单个 region 时候会发生热点问题,为什么呢?

​ region 中的 rowkey 是有序存储,若时间比较集中。就会存储到一个 region 中,这样一个 region 的数据变多,其它的 region 数据很少,加载数据就会很慢,直到 region 分裂,此问题才会得到缓解。

总结:短时间操作比较集中的话,用时间rowkey是连续的会造成一个region数据增则块,知道达到分裂条件才会好

11.请描述如何解决 HBase 中 region 太小和 region 太大带来的冲突?

​ Region 过大会发生多次compaction(压实即持久化),将数据读一遍并重写一遍到 hdfs 上,占用io,region过小会造成多次 split,region 会下线,影响访问服务,最佳的解决方法是调整 hbase.hregion.max.filesize 为 256m

12.HBase 优化

(1)高可用
在 HBase 中 Hmaster 负责监控 RegionServer 的生命周期,均衡 RegionServer 的负载,如果 Hmaster 挂掉了,那么整个 HBase 集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。所以 HBase 支持对 Hmaster 的高可用配置。

(2)预分区
每一个 region 维护着 startRow 与 endRowKey,如果加入的数据符合某个 region 维护的rowKey 范围,则该数据交给这个 region 维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高 HBase 性能 .

(3)RowKey 设计
一条数据的唯一标识就是 rowkey,那么这条数据存储于哪个分区,取决于 rowkey 处于哪个一个预分区的区间内,设计 rowkey 的主要目的 ,就是让数据均匀的分布于所有的 region中,在一定程度上防止数据倾斜。接下来我们就谈一谈 rowkey 常用的设计方案

​ Rowkey的设计原则:各个列族数据平衡,长度原则、相邻原则,创建表的时候设置表放入regionserver缓存中,避免自动增长和时间,使用字节数组代替string,最大长度64kb,最好16字节以内,按天分表,两个字节散列,四个字节存储时分毫秒。

(4)7.4 内存优化
HBase 操作过程中需要大量的内存开销,毕竟 Table 是可以缓存在内存中的,一般会分配整个可用内存的 70%给 HBase 的 Java 堆。但是不建议分配非常大的堆内存,因为 GC 过程持续太久会导致 RegionServer 处于长期不可用状态,一般 16~48G 内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。

13.简述 HBase 中 compact (合并)用途是什么,什么时候触发,分为哪两种,有什么区别,有哪些相关配置参数?

​ 在 hbase 中每当有 memstore 数据 flush 到磁盘之后,就形成一个 storefile,当 storeFile的数量达到一定程度后,就需要将 storefile 文件来进行 compaction 操作。
Compact 的作用:
​ ① 合并文件
​ ② 清除过期,多余版本的数据
​ ③ 提高读写数据的效率
HBase 中实现了两种 compaction 的方式:minor and major. 这两种 compaction 方式的
区别是:
1、Minor 操作只用来做部分文件的合并操作以及包括 minVersion=0 并且设置 ttl 的过
期版本清理,不做任何删除数据、多版本数据的清理工作。
2、Major 操作是对 Region 下的 HStore 下的所有 StoreFile 执行合并操作,最终的结果
是整理合并出一个文件。

14.大的HFile为什么要Split成小的HFile?Flush刷新的文件只会合并?

​ compact将多个HFile合并单个HFile文件,随着数据量的不断写入,单个HFile也会越来越大,大量小的HFile会影响数据查询性能,大的HFile也会,HFile越大,相对的在HFile中搜索的指定rowkey的数据花的时间也就越长,HBase同样提供了region的split方案来解决大的HFile造成数据查询时间过长问题。

​ 其实,split只是简单的把region从逻辑上划分成两个,并没有涉及到底层数据的重组,split完成后,Parent region并没有被销毁,只是被做下线处理,不再对外部提供服务。而新产生的region Daughter A和Daughter B,内部的数据只是简单的到Parent region数据的索引,Parent region数据的清理在Daughter A和Daughter B进行major compact以后,发现已经没有到其内部数据的索引后,Parent region才会被真正的清理。

15.如何规避hbase热点写问题,解决短时间的大量访问?

​ 检索habse的记录首先要通过row key来定位数据行,当大量的Cient访问Hbase集群的一个或少数几个节点,会造成少数RegionServer的读/写请求过多、负载过大,而其他RegionServer负载却很小,就造成了“热点”现象.

大量访问会使热点region所在的单个主机负载过大,引起性能下降甚至region不可用。
(1)加盐
这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。给多少个前缀? 这个数量应该和我们想要分散数据到不同的region的数量一致(类似hive里面的分桶)。
( 自己理解: 即region数量是一个范围,我们给rowkey分配一个随机数,前缀(随机数)的范围是region的数量)
加盐之后的rowkey就会根据随机生成的前缀分散到各个region(因为rowkey不是连续的了)上,以避免热点。*
(2)哈希
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据。

(3)反转
第三种防止热点的方法是反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。

​ 反转rowkey的例子:以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,从而避免诸如139、158之类的固定号码开头导 致的热点问题。

(4)时间戳反转
一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。

(5)尽量减少行和列的大小
在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。 如果你的rowkey和列名很大,HBase storefiles中的索引(有助于随机访问)会占据HBase分配的大量内存,因为具体 的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey 和列名的大小。压缩也有助于更大的索引。

13.Hive的问题

1.简单介绍一下Hive.特点/缺点/架构?为什莫使用?

​ hive是一 个基于大数据技术的数据仓库(DataWareHouse)技术,主要是通过将用户书写的SQL语句翻译成MapReduce 代码,然后发布任务给MR框架执行,完成SQL 到 MapReduce的转换。可以将结构化的数据文件映射为一张 数据库表,并提供类SQL查询功能。

总结

Hive是一个数据仓库(数据库)

Hive构建在HDFS上

Hive允许程序员使用SQL命令来完成数据的分布式计算,计算构建在yarn之上。(Hive会将SQL转化为 MR操作)

优点:

简化程序员的开发难度,写SQL即可,避免了去写mapreduce,减少开发人员的学习成本

缺点:

延迟较高,适合做大数据的离线处理(TB PB级别的数据,统计结果延迟1天产出),小数据量延迟高,不适 合。

Hive 的架构 :

HDFS:用来存储hive仓库的数据文件

yarn:用来完成hive的HQL转化的MR程序的执行

MetaStore:保存管理hive维护的元数据 (存储表的描述信息,数据库MySql)

Hive:用来通过HQL的执行,转化为MapReduce程序的执行,从而对HDFS集群中的数据文件进行统计。

程序员-------HQL/SQL----->JDBC/ODBC------->ThriftServer(远程调用)------------->Hive驱动(解释器/解析器/优化器【把HQL转换成MR程序交给Yarn执行】)------->Yarn集群---->HDFS

2.hive数据导入 的方式

​ 1 默认分隔符 \n (用来分割数据的,每行1条数据 )

​ ^A(用来分割字段(列 )

​ ^B (用来分割array元素、struct元素、map中kv和kv之间。 )

​ ^C(用于map的k和v之间的分割。 )

​ 2. 自定义分隔符 :自己设计分隔符

​ 3.JSON分割符

​ 4.CSV格式映射

​ 5 .正则分隔符

3.Hive中表的分类,什么是外部/管理表/分区?作用?

管理表 :由Hive全权管理的表

​ 所谓的管理表指hive是否具备数据的管理权限,如果该表是管理表,当用户删除表的同时,hive也会将表所 对应的数据删除,因此在生产环境下,为了防止误操作,带来数据损失,一般考虑将表修改为非管理表-外部 表

​ 缺点:数据不安全

外部表 :引用管理HDFS数据作为表管理,但无法删除数据

​ 外部表和管理表最大的区别在于删除外部表,只是将MySQL中对应该表的元数据信息删除,并不会删除hdfs 上的数据,因此外部表可以实现和第三方应用共享数据。在创建外表的时候需要添加一个关键字"external"即 可。create external xxx()…

分区表 :将表按照某个列的一定规则进行分区,减少海量数据情况下的数据检索范围,提高查询效率;

​ 应用:依据实际业务功能,拿查询条件的列作为分区列来进行分区,缩小MapReduce的扫描范围,提高 MapReduce的执行效率,

​ 总结:

​ table中的多个分区的数据是分区管理

​ 1:删除数据按照分区删除。如果删除某个分区,则将分区对应的数据也删除(外部表,数据删除,数据文件 依然在)。

​ 2:查询统计,多个分区被一个表管理起来。

​ 好处:如果where条件中有分区字段,则Hive会自动对分区内的数据进行检索(不再扫描其他分区数据),提高hive 的查询效率。(不是实时)。

3.Hive与HBase 的区别

区别 :

是什么 :

​ Hive:构建在Hadoop基础设施之上的数据仓库。通过Hive可以使用HQL语言查询存放在HDFS上的数据。(不 是用来做增删改查的)

​ HBase:是一种Key/Value数据库,它运行在HDFS之上

核心作用 :

​ Hive:SQL执行引擎。核心是用来简化MapReduce代码的。

​ HBase:构建在HDFS之上的分布式NoSQL数据库,专门用来做海量数据的实时检索

应用场景 :

​ Hive:海量离线数据分析,计算趋势或者网站的日志

​ HBase:海量数据实时查询。

4.hive中存放的是什么?

​ 存的是和hdfs的映射关系,hive是逻辑上的数据仓库,实际操作的都是hdfs上的文件,HQL就是用SQL 语法来写的MR程序

5.Hive与关系型数据库的关系?和数据的比较?

​ 没有关系,hive是数据仓库,不能和数据库一样进行实时的CRUD操作。

​ 是一次写入多次读取的操作,可以看成是ETL的工具。

Hive 和数据库除了拥有类似的查询语言,再无类似之处。

1)数据存储位置

​ Hive 存储在 HDFS 。数据库将数据保存在块设备或者本地文件系统中。

2)数据更新

​ Hive中不建议对数据的改写。而数据库中的数据通常是需要经常进行修改的,

3)执行延迟

​ Hive 执行延迟较高。数据库的执行延迟较低。当然,这个是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优势。

4)数据规模

​ Hive支持很大规模的数据计算;数据库可以支持的数据规模较小。

6.请说明hive中Sort By、Order By、Cluster By,Distribute By各代表什么意思?4个By区别?

​ order by:会对输入做全局排序,因此只有一个reducer(多个reducer无法保证全局有序)。只有一个 reducer,会导致当输入规模较大时,需要较长的计算时间。

​ sort by:不是全局排序,其在数据进入reducer前完成排序。

​ distribute by:按照指定的字段对数据进行划分输出到不同的reduce中,类似MR中Partition,进行分区,结合sort by使用

​ cluster by:除了具有 distribute by 的功能外还兼具 sort by 的功能 ,但是排序只能是升序排序,不能指定排序规则为ASC或者DESC。

7.Hive优化【了解】

1)MapJoin

​ 如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join。容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。

2)行列过滤

​ 列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT *。

​ 行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤。

3)列式存储

4)采用分区技术

5)合理设置Map数

​ (1)通常情况下,作业会通过input的目录产生一个或者多个map任务。

​ 主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。

​ (2)是不是map数越多越好?

​ 答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

​ (3)是不是保证每个map处理接近128m的文件块,就高枕无忧了?

​ 答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

​ 针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;

6)小文件进行合并

​ 在Map执行前合并小文件,减少Map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。

7)合理设置Reduce数

​ Reduce个数并不是越多越好

​ (1)过多的启动和初始化Reduce也会消耗时间和资源;

​ (2)另外,有多少个Reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

​ 在设置Reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的Reduce数;使单个Reduce任务处理数据量大小要合适;

8)常用参数

​ // 输出合并小文件

​ SET hive.merge.mapfiles = true; – 默认true,在map-only任务结束时合并小文件

​ SET hive.merge.mapredfiles = true; – 默认false,在map-reduce任务结束时合并小文件

​ SET hive.merge.size.per.task = 268435456; – 默认256M

​ SET hive.merge.smallfiles.avgsize = 16777216; – 当输出文件的平均大小小于16m该值时,启动一个独立的map-reduce任务进行文件merge

9)开启map端combiner(不影响最终业务逻辑)

​ set hive.map.aggr=true;

10)压缩(选择快的)

​ 设置map端输出、中间结果压缩。(不完全是解决数据倾斜的问题,但是减少了IO读写和网络传输,能提高很多效率)

11)开启JVM重用

14.Scala的问题

1.为什么用Scala?

​ 1.Scala是一门多范式的静态类型编程语言,Scala支持面向对象和函数式编程

​ 2.Scala源代码(.scala)会被编译成 java字节码(.class),然后运行在JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接

我们学习scala的原因:由于spark和flink需要用到scala

​ 相对于Java的类型系统,Scala无疑要复杂的多!也正是这复杂多变的类型系统才让面向对象编程和函数式编程完美 的融合在了一起!Scala中,**所有的值都是类对象,而所有的类,包括值类型,**都最终继承自一个统一的根类型 Any。统一类型,是Scala的又一大特点。更特别的是,Scala中还定义了几个底层类(Bottom Class),比如Null 和Nothing。

15.Flink的问题【重要】

1.介绍下Flink,特点?好处?应用场景?对比Spark

介绍:没啥用,呲牛逼的

​ 大数据的飞速发展,也促使了各种计算引擎的诞生。2006年2月诞生的Hadoop中的MapReduce,2014年9月份诞 生的Storm以及2014年2月诞生的Spark都有着各自专注的应用场景。特别是Spark开启了内存计算的先河,其计算 速度比Hadoop的MapReduce快100倍,以此赢得了内存计算的飞速发展。或许因为早期人们对大数据的分析认知 不够深刻,亦或许当时业务场景大都局限在批处理领域,从而使得Spark的火热或多或少的掩盖了其他分布式计算 引擎的身影。但就在这个时候,有些分布式计算引擎也在默默发展着,直到2016年,人们才开始慢慢意识到流计 算的重要性。

整个的分布式计算引擎中,通常被划分为三代

第一代 Hadoop的MapReduce做静态计算、Storm流计算。两套独立的计算引擎,使用难度比较大

第二代 Spark RDD做静态批处理、DStream|StructuredStreaming流计算;统一技术引擎,使用难 度小

第三代 Flink DataSteam做流计算、DataSet批处理;统一技术引擎,使用难度一般

什么是Flink

​ ApacheFlink是一个用于在无界和有界数据流上进行有状态计算的框架和分布式处理引擎。Flink被设计成在所有 常见的集群环境中运行,以内存速度和任何规模执行计算。

特点 :

​ 1.可以处理无界以及有界数据流

​ 2.随处部署应用程序

​ 3.以任何规模运行应用程序

​ 4.充分利用内存性能

Flink应用场景 :

​ 系统监控、舆情监控、交通预测、国家电网、疾病预测、金融行业风控、电商实时搜索优化等

Flink与Spark设计理念恰好相反 :

​ Spark底层计算是批处理模型,在批处理基础上模拟流,从而导致了流计算实时性较低

​ Flink底层计算是连续的流计算模型,在流计算上模拟批处理,既可以保证流的实时性又可以实现批处理

2.Flink程序的部署方式

1.本地 执行(直接在idea《开发工具》中运行代码)

2.远程脚本部署

通过mvn package生成jar包 把生成的jar传输到Linux系统的/tmp/flink目录下

然后用命令执行

3.Web UI部署

​ 通过访问flink的web界面,提交job完成部署

4.跨平台部署

​ 通过mvn package将程序打包,mvn clean清理之前的jar包

​ 运行main方法完成部署

3.Flink运行架构job/task/subtask,task划分?

​ Flink是一个分布式流计算引擎,该引擎将一个计算job拆分成若干个Task(等价于Spark中的Stage)。每个Task都有 自己的并行度,每个并行度都由一个线程表示,因此一个Task底层对应一系列的线程,Flink称为这些线程为该 Task的subtask。

​ job---->Task---->subtask(task的并行度一个一个线程

与Spark不同的地方在于Spark是通过RDD的依赖关系实现Stage的划分而Flink是通过 Operator Chain的概念 实现Task的拆分。该方式将多个算子归并到一个task中,从而优化计算,减少Thread-to-Thread的通信成本

​ 多个算子形成的多个task,然后Operator chain操作链根据算子执行要消耗的资源分成多个 并行度一个并行度就是一个Substask,一个算子的task的substask直接对应下一个算子的task的substask最后汇 总输出

Task/Subtask/Operator Chain的关系

​ Task - 等价spark中的Stage,每个Task都有若个Subtask

​ Subtask - 等价于一个线程,是Task中的一个子任务

​ Operator Chain - 将多个算子归并到一个Task的机制,归并原则类似于SparkRDD的宽窄依赖

4.Flink框架运行的进程?负责资源调度的

​ Job Manager :(也称为master) :

​ 负责协调分布式执行。负责任务调度,协调检查点,协调故障恢 复等。等价于Spark中的Master+Driver的功能。通常一个集群中至少有1个Active的JobManager,如 果在HA模式下其他处于StandBy(待机/备用)状态

​ Task Manager - (也称为Worker) :

​ 真正负责Task执行计算节点,同时需要向JobManager汇报自身状态 信息和工作负荷(定期心跳)。通常一个集群中有若干个TaskManager,但必须至少一个。

​ Client - Flink :

​ Client - Flink中的Client并不是集群计算的一部分,Client仅仅准备和提交dataflow给JobManager。提交 完成之后,可以直接退出,也可以保持连接以接收执行进度,因此Client并不负责任务执行过程中调度。 Client可以作为触发执行的Java/Scala程序的一部分运行,也可以在命令行窗口中运行。

​ 类似Spark中的Driver,但又特别不同,因为Spark Driver负责任务调度和恢复

4、Flink的任务插槽TaskSlots和资源Resources,slot共享?

​ 每一个Worker(TaskManager)是一个JVM进程,可以执行一个或者多个子任务(Thread/SubTask)。为 了 控制Worker节点能够接收多少个Task任务,提出了所谓Task slot用于表达一个计算节点的计算能力(每 个计算节点至少有一个Task slot)。

​ 每个Task slot表示的是TaskManager计算资源的固定子集。例如,一个TaskManager拥有3个Task slot, 则每个Task slot占用TaskManager所管理内存资源的1/3。每个Job启动的时候都拥有固定的Task Slot,这些 被分配的Task Slot资源只能被当前job的所有Task使用,不同Job的Task之间不存在资源共享和抢占问题。

​ 但是每个Job会被拆分成若干个Task,每个Task由若干个SubTask构成(取决于Task并行度)。默认Task slot 所对应的内存资源只能在同一个Job下的不同Task的subtask间进行共享,也就意味着同一个Task的不同 subtask不能运行在同一个Taskslot中。

​ 如果同一个Job下的不同Task的subtask间不能共享slot,就会造成非密集型subtask的阻塞,从而浪费内 存

​ 非密集型任务:source()/map()。操作占用内存量小

​ 密集型任务:keyBy()/window()/apply()。操作过程中涉及shuffle,会占用大量内存

​ 因此,Flink的底层设计为**同一Job下的不同Task的subtask间共享slot。**可以将并行度调整从而充分利用资 源。将上述示例的并行度从2调整为6,Flink底层会确保heavy subtasks 均衡分布于TaskManager之间的 slots

​ Flink默认行为:同一Job下的不同Task的subtask间共享slot,就意味着一个job运行需要的的Task slot个数 应该等于该Job中Task的并行度最大值。当然用户可以设置Task slot的共享策略

​ 在Flink的应用中,用户只需要指定job的并行度即可,无需指定运行需要的资源数

5.Flink的保存状态的StateBackends和储存点Savepoints?

​ Flink是一个基于状态计算的流计算引擎。存储的key/value状态索引的确切数据结构取决于所选的State Backends. 除了定义保存状态的数据结构外,State Backend还实现了获取key/value状态时间点快照并且将该快照储存为 checkpoint一部分的逻辑。

Flink中定义了三种State Backend

​ The MemoryStateBackend :内存

​ The FsStateBackend :文件

​ The RocksDBStateBackend :rocksDB数据库

Savepoints(储存点) :

​ 用Data Stream API编写的程序可以从savepoint恢复执行。Savepoint允许在不丢失任何状态的情况下更新程序和 Flink集群。

​ Savepoint是手动触发的checkpoint,它获取程序的快照并将其写入state backend。Checkpoint依赖于常规的检 查点机制:在执行过程中个,程序会定期在TaskManager上快照并且生成checkpoint。为了恢复,只需要最后生成 的checkpoint。旧的checkpoint可以在新的checkpoint完成后安全地丢弃。

​ Savepoint与上述的定期checkpoint类似,只是他们由用户触发,并且在新的checkpoint完成时不会自动过期。

​ Savepoint可以通过命令行创建,也可以通过REST API在取消Job时创建。

5.Flink的分布式运行总结,Flink主要组件【重要】

​ 分布式运行架构

  • task and operator chain

    一个job会有若干个task组成。每一个task会拆分成若干个subtask(并行度决定的)。

    operator chain就是把subtask连接起来的一种机制

  • jobmanager/taskmanager/client

    • jobmanager:管理者(也被称为master);必须保证至少一个jobmanager。如果有多个,一个是leader,其他的处于standby。负责任务调度
    • taskmanager:负责计算的节点(也被称为worker);必须保证至少一个taskmanager
    • client:client不是flink集群的一部分
  • taskslot and resource

    计算节点如何更好的利用资源

    task slot表示的就是计算节点的计算能力。task slot表示是taskmanager可以接收多少个task

    一个taskmanager至少有一个task slot

    一个taskmanager需要的task slot的数据应该是job中的最大并行度,在开发中,不需要指定task slot,只需要指定并行度

    同一个job的不同task的subtask可以共享slot。用另外的一句话讲同一个job的同一个task的不同subtask要分配到不同的slot中

  • state backend

    • 决定了state存储时的数据结构
    • 保存state到内存、文件系统、rocksdb
  • savepoint

    • checkpoint:flink定期自动把状态保存起来。新的生成之后,老的会自动清除
    • savepoint:手动的checkpoint。savepoint不会被自动清除

6.结果输出到HDFS注意:

​ 如果使用HDFS作为输出目的地,需要大量的数据才能看到效果,因为HDFS文件系统写入时的缓冲区比较 大。当然程序结束掉也会把数据写入HDFS文件系统中

​ 这里退出NetCat程序就会正常退出ctrl+c,hdfs里面就会有数据了,写到文件里面也要这样才能看到数据

​ 如果没到时间或者存储的数据不够,形成的文件是临时文件以点开头的,不能用进行读取,spark就不能读

​ 可以对这个进行设置,设置多长时间/写入多少数据把临时文件变成永久文件,

7.Flink的分区策略,就是数据进入哪个分区

​ Flink提供的有默认分区策略,轮询rebalance

​ 如果上游并行度是1,下游并行度是多个,默认使用轮询策略

​ 如果有需要,Flink还可以通过以下function对转换后的DataStream进行低级别分区控制

​ 1.Rebalancing平衡 (Round-robin partitioning轮询/循环分区rebalance)

​ 分区元素循环,为每个分区创建相等的负载。在数据歪斜的情况下可用于性能优 化

​ Flink默认就是采用轮询分区策略实现rebalancing

​ 2.Random partitioning(随机shuffle)

​ 3.Rescale(重新缩放)

​ 调用Rescale的算子的并行度(分区)随机分发到下一分区的分区编号一遍,以后就按照第一次随机分发的分区进 行分发,一直循环 例如第一次随机分发的分区是:2 1 1 1 2 2这按照这5个的顺序后面的就保持这个队形,就一直回放第一的动作

​ 4.Broadcasting(广播) :每个分区都会去,设定几个并行度打印几次

​ 输入一个aa显示: 1> aa 2> aa

​ 输入一个b显示: 2> b 1> b

​ 输入一个打印两次

​ 5.Custom partitioning(自定义分区策略)

8.Flink的任务链TaskChaining和资源组ResourceGroups (优化)

​ 对两个子转换进行chain,意味着会将这两个算子放置在同一个线程中以此可以提高性能(减少了线程间的开 销)。默认情况下,flink会尽可能地Chain(链条/连接)算子。

​ 1.disableOperatorChaining(禁用整个Job的操作链) 可以禁用整个job的操作链,但不建议

​ 2.Start new chain(开启一新的链)

​ 从这个操作员开始,开始一个新的链。两个映射器将被链接,而筛选器将不链接到 第一个映射器

​ 3.Disable chaining(禁用某个算子的链) :例(对map算子禁用操作链

​ 4.Set slot sharing group(设置算子的资源共享组)

​ 设置算子的slot(插槽)共享组

​ Flink会将具有相同slot共享组的算子放入到同一个slot,没有slot共享组的算子放入在其他slot。这样可以用来隔 离slot。如果上游算子在同一个slot共享组中,那么下游算子会自动继承该共享组。默认的slot的共享组的名字是 default 。也可以通过调用方法 slotSharingGroup 显式设置算子所在的slot共享组

9.Flink的状态State是什么,干什么用的,有什么?

Flink是基于状态的流计算引擎。

State:是可以获取之前输入的数据,对这些数据进行统计,求和,求平均值等操作

​ 在Flink中有两种基本类型的state,分别是 Keyed State 和 Operator State 。Keyed State只能应用在 KeyedStream上的操作。每一个keyed operator都会绑定一个或多个状态值。Operator State又被称为non-keyed state,每一个算子都会有对应的operator state。

​ Keyed State以及Operator State都会以两种方式存储:managed(被管理的)和raw(原始的)。

​ managed state指的是由Flink控制state的数据结构,比如使用内部hash表、RocksDB(数据库)等。正是基于此, Flink可以更好地在managed state基础上进行内存优化和故障恢复。

​ RocksDB:数据库,key-value,存数据的用户可以决定存储的数据的类型,自主权比较大,不用跟其他的数据库 一样要进行序列化才能存储

​ raw state指的是Flink只知道state是一些字节数组,其余一无所知。需要用户自己完成state的序列化以及反序列 化。因此,Flink不能基于raw state进行内存优化以及故障恢复。所以在企业实战中,很少使用raw state

总结:managed state可以自己控制要往数据库里面存储什么类型的数据,对数据的控制权比较大

raw state 要往数据库里面存数据必须要按照该数据库要求的格式去存储

​ Managed State:处理过的状态,自定义数据处理

​ raw state :原始状态,没陪处理的的数据

总结:flink的satate可以对输入的内容进行统、计算,可有通过其他项目远程查看该状态计算结果,可以把这个状态保存起来保存到HDFS里面(检查点checkpoint)

10、Managed Keyed State 有几种有啥区别[重要]:

​ managed keyed state 接口提供了对不同数据类型的state的访问,这些state都是和key绑定的。这也就意味着 managed keyed state只能应用在KeyedStream上。Flink内置的有以下几种managed keyed state

1.value State:该状态用于存储单一状态值,key对应的值只能是一个值例如元祖

2.List State:该状态用于存储集合状态值,key对应的值是一个list集合

3.Map state:该状态用于存储Map集合状态值,key对应的是一个Map集合

4.ReducingState :该状态用于存储单一状态值。该状态会通过调用用户提供的 ReduceFunction,将添加的元素和历史状态自动做运算

5.**AggregatingState <IN,OUT>:**该状态用于存储单一状态值。该状态会通过调用用户提供的 AggregateFunction,将添加的元素和历史状态自动做运算。 该状态和ReducingState不同点在于,输入数据类型和输出数 据类型可以不同

6.FoldingState <T,ACC>:该状态用于存储单一状态值。该状态会通过调用用户提供的 FoldFunction,将添加的元素和历史状态自动做运算。该状 态和ReducingState不同点在于,输入数据类型和中间结果数 据类型可以不同(已过时)

​ 必须记住,这些状态对象仅用于与状态交互。状态不一定存储在内部,但可 能位于磁盘或其他位置。要记住的第二件事是,**从状态获取的值取决于输入元素的键。**因此,如果涉及的键 不同,则在用户函数的一次调用中获得的值可能与在另一次调用中获得的值不同

​ 代码实现整体思路【重要】:

  1. 写一个类,继承RichMapFunction类

  2. 重写RichMapFunction里面的open方法 在open方法中,通过RuntimeContext对象的getXxxState(XxxStateDescriptor)方法获取到XxxState对象 (XxxState=getRuntimeContext.getXxx[value的省略]State(new XxxxStateDescriptor【[泛型]】(“唯一标记字符 串” ,createTypeInformation【泛型】这里几种情况不固定,看代码))) (XXX指的是valueState就是Value,ListState就是list,MapState就是map) (XXXState对象要设置成全局的)

  3. 实现RichMapFunction里面的map方法 在map方法中,通过XxxState对象根据业务需要实现具体功能

  4. 在代码中的KeyedStream上使用自定义的MapFunction

11.状态存活时间TTL是啥?内存存储State太多?统计一天内数据?失效立即清理?

​ 状态存活时间,State Time-To-Live 该设置默认是关闭的

​ 在Flink中,支持对所有的keyed state设置存活时间。该特性默认是关闭的,一旦开启并且状态值已经过期, Flink将会尽最大努力清楚所存储的状态值。

​ TTL支持单一值失效特性,也就意味着ListState中的每一个元素和MapState中的每一个entry都会有单独的失效 时间

​ 要使用stateTTL,首先需要构建一个StateTtlConfig 配置对象。然后通过调用StateDescriptor对象中的 enableTimeToLive方法并且将配置对象传递过去来开启TTL机制

注意:

  1. 一旦开启了TTL机制,系统为每个存储的状态数据额外开辟8个字节的空间,用来存储state的时间戳
  2. TTL目前仅支持processing time(处理时间)
  3. 如果程序一开始没有启用TTL,重启服务开启了TTL,则服务在故障恢复时StateMigrationException(状态 迁移异常

Cleanup of Expired State(清除过期状态)的策略

1.默认情况下,过期数据只有在被读取的时候才会被删除,比如调用ValueState.value()

​ 1.9以及之前版本:

​ 这就意味着,在默认情况下,如果过期数据没有被读取,就不会被删除。很有可能导致过期数据越来越大而 占用太多内存。可以通过调用StateTtlConfig.Builder的.cleanupInBackground方法开启后台清理

​ 1.10版本:

​ 如果配置的state backend,则在后台定期进行垃圾回收。可以通过API禁用后台清理

2.Cleanup in full snapshot(在完整快照中清除)

​ 可以通过配置Cleanup in full snapshot机制,在系统恢复或者启动的时候, 加载状态数据,此时会将过期的数 据删除

​ 也就是只有Flink服务重启的时候才会清理过期数据

3.Incremental cleanup(增量清理)

​ 增量清理策略,在用户每一次读取或者写入状态数据的时候,该清理策略就会运行一次。系统的state backend会 保存所有状态的一个全局迭代器。每一次访问状态或者/和记录处理时,该迭代器就会增量迭代一个批次的数据, 检查是否存在过期的数据,如果存在就删除

​ 该策略需要两个参数

​ cleanuspize(清洗)-任何密钥的状态触摸时从队列中提取以进行清理的最大密钥数一次检查的key的数量

​ runCleanupForEveryRecord-对每个处理的记录运行增量清理 )

​ 是否每一次record processing(记录处理)都会触发incremental cleanup(增量清理)。如果为false, 就表示只有访问状态时才触发incremental cleanup;true则表示访问状态以及记录处理都会触发 incremental cleanup

注意:

​ 如果没有状态访问或者记录处理,过期的数据就不会删除,会被持久化

​ incremental cleanup增量清理需要花费时间,从而增加了record processing的延迟

​ 目前,incremental cleanup仅支持 Heap state backend(堆/放在状态后端上)。如果是 RocksDB,该机制不起作用

4.Cleanup(清理) during(期间) RocksDB compaction(压实)(压实到数据库的期间进行清理)

​ 如果使用的是RocksDB作为state backend(状态后端),Flink将会通过Compaction filter(压实过滤器)实 现后台清理。Compaction(压实机制) filter会检查状态的时间戳以获取剩余存活时间并把过期数据清除掉

​ 参数queryTimeAfterNumEntries表示处理了多少个key之后去获取时间。以对比存储的时间戳,将过期的数据 删除掉

​ 频繁的更新时间戳会提高清理速度。但是由于采用JNI调用本地代码,会降低压实性能。默认情况,每处理1000个 key,RocksDB backend会查询一次当前时间戳从而清理过期数据

扩展 :

​ RocksDB是一个基于内存+磁盘的嵌入式的轻量级的NoSQL产品,底层维护一张HashTable。所有的记录都 是顺序追加到磁盘,最新状态存储在内存中。RocksDB不支持更新磁盘。但是RocksDB底层有一套 Compaction机制(压实机制),用于合并磁盘文件,以防止文件过大

注意:

​ 在Flink1.10之前,RocksDB的CompactionFilter特性是默认关闭的,需要使用,应该在flin-conf.yaml配 置文件中开启

12.Flink的State的保存机制Checkpoint(自动)和Savepoint(手动)持久化流程?[重要]

​ Flink是一个有状态的流计算引擎,因此状态的管理和容错是非常重要的。为了程序的健壮性,Flink提出了 Checkpoint机制,该机制用于持久化计算节点的状态数据,从而实现Flink故障恢复。

​ Checkpoint机制指的是Flink会定期将状态数据持久化到远程文件系统,比如HDFS(这取决于state backend)

​ JobManager负责checkpoint的发起以及协调。

Flink的Checkpoint的流程【重要】:

1、JobManager节点会定期向TaskManager节点发送Barrier(障碍/分割线)(实际上是JobManager创建的 CheckpointCoordinator(检查点协调员))

2、TaskManager接收到Barrier信号,会把Barrier信号作为数据流的一部分传递给所有算子

3、每一个算子接收到Barrier信号后会预先提交自己的状态信息并且给JobManger应答,同时会将Barrier信号传 递给下游算子。

4、JobManager接收到所有算子的应答后才认定此次Checkpoint是成功的,并且会自动删除上一次Checkpoint 数据。否则,如果在规定的时间内没有收到所有算子的应答,则认为本次Checkpoint快照制作失败 。

JobManage--------发送Barrier信号(开始持久化)-------->TaskManager----------发送Barrier信号(开始持久化)------------------传递给所有算子 -------->每一个算子预先提交自己的状态信息并且给JobManger应答,同时会将Barrier信号传 递给下游算子------------------->下游算子--------------------->JM收到所有算子应答成功否则失败

Savepoint

​ Savepoint是手动触发的checkpoint,它获取程序的快照并将其写入state backend。Checkpoint依赖于常规的 检查点机制:在执行过程中个,程序会定期在TaskManager上快照并且生成checkpoint。为了恢复,只需要最后 生成的checkpoint。旧的checkpoint可以在新的checkpoint完成后安全地丢弃。

​ Savepoint与上述的定期checkpoint类似,只是他们由用户触发,并且在新的checkpoint完成时不会自动过期。 Savepoint可以通过命令行创建,也可以通过REST API在取消Job时创建。

​ 默认情况下,Flink的Checkpoint机制是禁用的,如果需要开启,可以通过API完成

13.State backend(状态后端) state持久化到哪?

​ State backend指定了状态数据(检查点数据)存储的位置以及如何存储。Flink提供了多种State backend的实 现。state backend有两种配置方式

​ 每一个job单独配置state backend (直接在代码里面写就行了)

​ 在flink-conf.yaml中配置所有job使用的state backend

​ 配置文件配置完成之后,重新启动Flink,检查全局state backend配置是否成功

注意:

​ 因为state backend需要将数据同步到HDFS,所以Flink需要和Hadoop集成。需要在环境变量中 配置HADOOP_CLASSPATH

​ 配置完环境变量记得重新加载profile文件

1.MemoryStateBackend(jobmanager)

​ 使用内存存储状态数据,将状态数据存储在Java的heap(堆)中。在Checkpoint的时候,memoryStateBackend讲对状态进行快照,并将其作为检查点确认信息的一部分发送给JobManager(Master), 该JobManager将其存储在heap中。

​ 使用限制:

​ 默认情况下,每个单独状态的大小限制为5 MB。此值可以 在MemoryStateBackend的构造函数中增加。

​ 无论配置的最大状态大小如何,状态都不能大于akka(百度,这里就当是两台 计算机通信的数据的大小)帧大小(请参阅配置)

​ 聚合状态必须适合JobManager内 存。

​ 应用场景: 1. 本地部署和调试 2. 存储少量的状态数据

2.FsStateBackend(filesystem)

​ FsStateBackend将状态数据存储在TaskManager的内存中。在执行checkpoint的时候,会将TaskManager内存 的数据写入到远程文件系统。非常少量的元数据信息会存储在JobManager的内存中

应用场景: 1. 大量的状态数据需要存储 2. 生产环境

3.RocksDBStateBackend(rocksdb)

​ RocksDBStateBackend将状态数据存储在TaskManager节点的RocksDB数据文件中,在Checkpoint的时候会 将RocksDB数据文件写入到远程的文件系统中。少量的元数据存储在JobManager的内存中

​ 使用限制 :由于RocksDB的JNI桥API基于byte[],因此每个键和每个值支持的最大大小分别 为231字节。重要提示:在RocksDB中使用合并操作的状态(例如ListState)可以静默地累积值大小>231 字节,然后在下次检索时失败。这是RocksDB JNI目前的一个限制。​

应用场景: 1. 超大量的状态数据需要存储 2. 生产环境

14.Managed Operator State (了解)和keyState有啥区别?

​ operator state一般应用在source和sink上

​ Operator State又称为non-keyed state.Flink提供了对keyed state的操作,同样提供了对operator state的操 作。要使用operator state,需要实现 CheckpointedFunction 接口或者实现 ListCheckpointed 接口

1.CheckpointedFunction(sink)

CheckpointedFunction接口针对non-keyed state 提供了不同的分发策略。实现该接口需要实现以下两个方法

​ 1.snapshotState:在进行checkpoint的时候,会调用该方法。通常,需要持久化的状态数据在该方法中 完成存储

​ 2.initializeState:初始化→在第一次启动的时候,或者故障恢复的时候都会调用该方法

​ 目前,Flink支持列表样式的managed operator state。该state应该是一个list集合,里面的元素是可序列化的、彼 此独立的,因此在系统故障恢复时可以进行重新分配。CheckpointFunction提供了两种重新分配策略

​ Even-split redistribution - 均分策略,多个分区平分数据

​ redistribution:重新分配

​ 每一个算子返回一个状态元素列表,整个的状态从逻辑上讲是所有列表的串联。当故障恢复时、或者重新分 配时,因为有并行算子,会对整个状态列表均匀地划分为尽可能多的子列表,每一个算子得到一个子列表。 比如说,当并行度是1时,一个算子的checkpointed state包含了element1和element2;当并行度增加为2 时,element1有可能分配给operator instance 0,而element2有可能分配给operator instance 1

​ Union redistribution-多个分区每个分区都有所有完整数据

​ 每一个算子返回一个状态元素列表,整个的状态从逻辑上讲是所有列表的串联。当故障恢复时、或者重新分 配时,每一个算子得到的是完整的整个状态元素列表

说明 :

​ managed operator state一般应用在source,或者sink上。

​ 为了保证通过savepoint/checkpoint恢复数据不会问题,强烈建议每一个算子都添加一个唯一标记, 通过.uid(String)方法完成

2.ListCheckpointed(source)

​ ListCheckpointed接口是CheckpointedFunction的变种写法(相比于CheckpointedFunction,做了更多的限 制),该接口只支持均分策略的list-style state。

15.广播状态Broadcast State Pattern 状态共享?

​ 广播状态是Flink提供的第三种状态共享的场景。通常需要将一个吞吐量比较低的流中的状态数据进行广播 给下游的任务,另外一个流可以以只读的形式读取广播状态

Broadcast State Pattern(模式)

1.non-keyed Stream connect BroadcastStream(广播流)

​ 需要继承BroadcastProcessFunction,实现里面的两个方法

​ processElement :可以获取到低吞吐量流广播过来的状态,处理高吞吐量流相关的业务逻辑

​ processBroadcastElement :用来处理广播流,即对低吞吐量流进行处理

​ 案例:把符合过滤规则的内容过滤掉 可以应用在舆情监控上

**2.Keyed Stream connect BroadcastStream(广播流) **

​ 应用:某电商平台,用户在某一类别下消费总金额达到一定数量,会有奖励

​ 分析:

​ 1.不同类别会有对应的奖励机制,需要把这个奖励机制广播给用户消费对应的流

​ 2.用户的消费应该是一个高吞吐量流

​ 3.通过用户消费流连接奖励机制流,然后通过process处理

​ 4.用户消费流应该根据用户标记以以及类别分组===》流是KeyedStream ProcessFunction应该选中KeyedBroadcastProcessFunction

​ 5.在KeyedBroadcastProcessFunction中完成奖励机制以及用户消费统计、分析、处理

16.状态可查询QueryableState怎样在别的项目直接查看评估/计算/统计结果?

​ Queryable State简单讲,就是flink技术之后的结果(state),允许通过第三方应用程序查询到

​ 查询状态整体的流程

​ 整体的流程:QueryableStateClient发送请求(需要某一个key的状态)到QueryableStateClientProxy, QueryableStateClientProxy会到jobmanager中询问指定的key的状态存储在哪个worker, QueryableStateClientProxy会到对应的taskManager上的QueryableStateServer里面获取到对应的状态数 据。QueryableStateClientProxy把数据响应给QueryableStateClient

QueryableStateServer :就是用来存储可以查询的状态数据,运行在flink集群的taskmanager上

QueryableStateClientProxy:就是用来代理客户端访问JobManager得到状态的地址,运行在flink集群的taskmanager上

​ 是flink集群的一部分内容,运行在flink集群的taskmanager上,用来接收 QueryableStateClient请求。从flink集群内部讲QueryableStateClientProxy就是一个客户端(用来获取 数据)

宏观:客户机访问JOBManager(代理了下)得到TaskManager的位置,访问该TashManager的状态

使用该功能要在配置文件里面配置一下

可以通过以下两种方式让state在外部系统中可见【了解】:

​ [1].创建QueryableStateStream,该Stream只是充当一个sink,将数据存储到queryablestate中

​ [2].通过stateDescriptor.setQueryable(String queryableStateName)方法,将state可查

​ [1]和[2]是两种设置状态可查询的方式,结果都一样,两种都没区别

​ 【2】在控制台可以打印打印计算结果,【1】不能打印

17.Window流计算的核心,核心是啥?Lifecycle(生命周期) ?

​ 窗口是处理无限流的核心。Windows将流分成有限 大小的“bucket”,我们可以在上面进行计算。

​ 在Flink中,将窗口划分成了两大类:keyed窗口和non-keyed窗口

​ keyed windows与non-keyed windows的唯一区别就是,keyedStream上通过 keyBy().window()完成而non-keyedStream通过windowAll()

​ 当应该属于某一个窗口的第一个元素到达时,该窗口被创建。当时间超过了窗口的结束时间加上允许的延迟时间 是,窗口被彻底删除。Flink只删除基于时间的窗口,而不删除像全局窗口这样的其他类型的窗口。

​ 每个window都会绑定一个function和一个trigger(触发器),function的作用是对窗口中的内容进行计算,trigger 的作用是决定什么时候开始应用function进行计算—》触发器决定了窗口什么时候处于就绪状态

​ 除了function和trigger之外,还可以指定evictor。evictor(剔除器)的作用是在trigger触发之后到function运行之 前和/或者运行之后这段时间间,把window中的元素删除

Keyed和Non-Keyed Windows的区别:

Keyed Windows: keyed Stream会根据key讲原始流切分成多个逻辑keyed Stream,每个逻辑keyed Stream都可以独立于其他任务 进行处理,所以Keyed Stream允许多个并行任务执行窗体计算。同一个key的所有元素被分发到同一个并行任务 中。简而言之,在某一个时刻,会触发多个window任务,取决于Key的种类

Non-Keyed Windows:没有key的概念,所以不会将原始流拆分成多个逻辑流,所有窗口逻辑将有单个任务执行, 也就是说并行度是1。简而言之,任意时刻只有一个window任务执行

18.窗口的种类?了如何将元素划分给窗口/选择器 ?滚动/滑动/会话/全局?区别?

​ Window Assigner(分配器) 定义了如何将元素划分给窗口。通过调用window()方法或者windowAll()方法传递一个 WindowAssigner对象参数完成。

​ WindoAssigner负责将传递过来的元素分配给1个或多个窗口。Flink中提供了一些WindowAssigner: tumbling windows, sliding windows, session windows and global windows。也可以自定义WindowAssigner,通过继承 WindowAssigner类完成自定义。除了global windows之外的其他窗口,都是基于时间的窗口,这类窗口会有一 个开始时间戳(包含)和一个结束时间戳(不包含)用来描述窗口的大小

(1)Tumbling Windows(滚动窗口)

​ 滚动窗口分配器将每个元素分配给指定大小的窗口。滚动窗口具有固定的大小并且不重叠。例如,如果指定一个 大小为5分钟的滚动窗口,则将计算当前窗口,并每隔5分钟启动一个新窗口

(2)Sliding(滑动) Windows

​ 滑动窗口分配器将元素分配给固定长度的窗口。与滚动窗口分配程序类似,具有固定的大小,窗口大小由窗口大小参数配置。另一 个,窗口滑动参数控制滑动窗口的创建频率。因此,如果滑动参数小于窗口大小,**则滑动窗口可能重叠。**在这种情 况下,元素被分配给多个窗口。

(3)Session(会话) Windows

​ 会话窗口:分配器按活动的会话对元素进行分组。与滚动窗口和滑动窗口相比,会话窗口不重叠也没有固定的开 始和结束时间。相反,当会话窗口在一段时间内没有接收到元素(也就是说当出现不活动的间隙)时,会话窗口将 关闭。会话窗口分配器可以配置为静态会话间隙,也可以自定义会话间隙。当此时间段过期时,当前会话将关闭, 后续元素将分配给新会话窗口。

(4)Global(全局) Windows

​ 全局窗口分配器将具有相同键的所有元素分配给同一个全局窗口只有指定了触发器时,此窗口才可用。否则,将 不会执行任何计算,因为全局窗口没有可以处理聚合元素的自然结束。,【不会关闭】

19.Window Functions 指定要在每个窗口上执行的计算 ?reduce/aggregate/process?

​ 定义窗口分配器之后,我们需要指定要在每个窗口上执行的计算,这是Window Function的职责。一旦系统确定窗 口已准备好处理时,就可以处理每个窗口的元素。窗口函数可以是ReduceFunction, AggregateFunction, FoldFunction、ProcessWindowFunction、WindowFunction(古董)之一。其中

ReduceFunction和 AggregateFunction在运行效率上比ProcessWindowFunction高,因为前俩个执行的是增量计算,只要有数据抵达 窗口,系统就会ReduceFunction,AggregateFunction实现增量计算;

ProcessWindowFunction在窗口触发之 前会一直缓存接收数据,只有当窗口就绪的时候才会对窗口中 的元素做批量计算,但是该方法可以获取窗口的元数 据信息。

因此可以通过将ProcessWindowFunction与 ReduceFunction,AggregateFunction结合使用,即可以获 得窗口元素的增量聚合,又可以接收到窗口元数据。

ProcessWindowFunction的注意

通过ProcessWindowFunction可以获取到每一个窗口的状态数据 windowState,也可以获取到所有窗口汇总数据 globalState

​ 1. process()调用接收到的上下文对象上有两种方法,允许访问两种类型的状态:

​ globalState() :它允许访问不在窗 口范围内的键控状态,也就是全局状态,所有数据

​ windowState() :它允许访问同时 作用于窗口的键控状态,只有本窗口的数据

​ 2.在某些可以使用ProcessWindowFunction的地方,也可以使用WindowFunction。这是较旧版本的 ProcessWindowFunction。WindowFunction提供的不是上下文对象而是window对象,所以在使用 ProcessWindowFunction中通过上下文对象获取的信息,在使用WindowFunction时就没有办法继续使用。比 如说globalState

20.Triggers(高级特性-触发器) 是什么?用处?

​ Trigger决定了窗口何时可以通过windowFunction进行计算。也就是说Trigger确定了窗口何时就绪。每一个 windowAssinger都有一个默认的trigger,当默认的trigger不能满足需要时,可以自定义trigger

1.默认触发器

​ ProcessingTimeTrigger :系统时间超过了窗口的最后时间就会触 发

​ EventTimeTrigger :一旦水印通过窗口末端就会触 发的触发器

​ NeverTrigger :永不触发

2.自定义触发器

​ 触发器接口中,需要重点关注五个方法。这些方法运行触发器对不同事件作出反应 【了解】

​ onElement() -每添加一个元素到指定窗口,就会调用一次这个方法

​ onEventTime()-当注册的event(事件)-time计时器触发时,会调用该方法

​ onProcessingTime()-当注册的processing-time计时器触发时,会调用该方法

​ onMerge()-当多个窗口合并到一个窗口时触发,例如session window

​ clear() -在删除相应窗口时执行所需的任何操作,比如清除定时器、删除存储的状态等

​ 前三个方法通过返回TriggerResult来决定,如何处理它们的调用事件

​ CONTINUE : 继续,当前窗口不做任何处理, FIRE : 触发计算, PURGE : 清理窗口中的元素 FIRE_AND_PURGE : 触发计算,然后清除窗口中的元素

​ 这些方法中的任何一个都可以用于process-time和event-time计时器以完成后续的操作

21.Evictors(高级特性-剔除器) 是什么?

​ Flink的窗口,除了允许指定WindowAssigner(选择器)以及Trigger(触发器),还可以指定Evictor,可以使用evictor(…)方法完成。 evictor可以从窗口中移除元素。evictor会在trigger触发之后,window function执行之前或之后执行

Flink提供的Evictor

​ 1.CountEvictor-在窗口中保留用户指定数量的元素,并从窗口缓冲区的开头丢弃其余的元素

​ 2.DeltaEvictor-需要一个DeltaFunction和阈值,计算窗口缓冲区中最后一个元素与其余每个元素之间的增量, 并删除增量大于或等于阈值的元素

​ 3.TimeEvictor-需要一个毫秒为单位的参数interval,对于给定的窗口,在其元素中查找最大时间戳max_ts,并 删除时间戳小于max_ts-interval的所有元素

自定义Evictor :参考CountEvictor(源码)完成自定义Evictor

22.窗口高级Event-time/Watermark-水位线

Flink在流计算的过程中,支持多种时间概念。Event Time / Processing Time / Ingestion Time

Processing Time:处理时间是指执行相应操作的机器的系统时间。

Event Time:事件时间是每个事件在其生产设备上发生的时间

Ingestion(摄入):摄取时间是事件进入Flink的时间

​ 在Flink的窗口计算中,如果Flink在使用的时候不做显示声明,默认使用的是ProcessingTime。IngestionTime和 ProcessingTime类似都是由系统自动产生不同的是IngestionTime是由DataSource产生,ProcessingTime 由算子产生。因此以上两种时间策略都不能很好的表达在流计算中事件产生时间(因为存在网络延时迟)

Flink支持事件时间策略。事件时间是每个事件在其生产设备上发生的时间。此时间通常在记录进入Flink之前嵌入 到记录中,并且可以从每个记录中提取事件时间戳。在事件时间中,时间的进度取决于数据,而不是任何时钟。事 件时间程序必须指定如何生成事件时间Watermarks,这是事件时间进程的信号机制

Watermark-水位线

​ 流处理从事件产生,到source,再到operator,中间是有一个过程和时间的,虽然大部分情况下,流到operator 的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络等原因,导致乱序的产生,所谓乱序,就是指 Flink接收到的事件的先后顺序不是严格按照事件的Event Time顺序排列的

​ 因为有可能乱序,如果只根据eventTime决定窗口的运行,就不能明确数据是否全部到位,但又不能无限期的等 待,此时必须要有一种机制来保证一个特定的时间后,必须触发window function进行计算,这个机制就是 Watermark。

​ Watermark是Flink中测量事件时间进度的机制。Watermark作为数据流的一部分流动,并带有时间戳t。数据流 中的Watermark用于表示时间戳小于Watermark的数据,都已经到达了。因此流中不应该再有时间戳 t’<=watermark的元素。因此只有水位线越过对应窗口的结束时间,窗口才会关闭和进行计算

​ Flink接收到每一条数据时,都会产生一条Watermark,这条Watermark就等于当前所有到达数据中的 maxEventTime -(减) 允许延迟的时间

watermark=maxEventTime-maxAllowedLateness (阈值)

总结

Watermark watermark是一个衡量事件时间进度的机制

watermark是解决乱序问题的重要依据

在窗口计算中,watermark没过了窗口的结束时间就触发窗口计算(属于该窗口的元素都已经到达)

watermark的计算应该是:最大事件时间-最大允许迟到时间

Watermark计算

Flink中常用watermark计算方式有两种

​ With Periodic(周期性) Watermarks–定期提取水位线

​ With Punctuated Watermarks–每一个event计算一个水位线(不常用)

​ 最新数据的时间减去上一次最新的时间就是新的水位线,当水位线超过设置的最大允许时间,窗口就会触发进行计 算,处理该窗口时间段内的数据,水位线是只能增加不能减小的,一个并行度和多个并行度是不一样的

​ 水位线=最大时间-最大迟到时间2秒 当水位线=窗口的的结束时间时计算该窗口里面的所有数据,之后该窗口在迟 到的数据就不要了

自定义水位线:

​ extends AssignerWithPeriodicWatermarks[(String,Long)]

​ 这种方式计算水位线是常用的方式

使用水位线处理在规定迟到时间内的数据

水位线=最大时间-最大迟到时间2秒 当水位线=窗口的的结束时间时计算该窗口里面的所有数据,之后该窗口在迟 到的数据就不要了

With Punctuated Watermarks(每一个event计算一个水位线不推荐使用)

​ 每一个事件都会生成一个水位线。在生产环境中,过多的生成水位线会影响程序的性能

多并行度的Watermarks

在并行情况下 :算子的eventTime以最小的输入流eventTime为准

23.处理迟到数据方式?

在flink中对于迟到数据进行了三种处理

** 默认处理方式:直接丢弃(spark就是采用这个方式)**

** 在允许迟到的范围内,会重新开启窗口进行重新计算**

** 超出了范围的数据,可以采用边输出的方式呈现处理方便后续的处理**

​ 在Flink中,水位线一旦没过窗口的EndTime,如果还有数据落入到此窗口,这些数据被定义为迟到数据。默认情 况下,迟到数据将被删除。但是,Flink允许为窗口操作符指定允许的最大延迟,在允许的延迟范围内到达的元素 仍然会添加到窗口中。根据使用的触发器,延迟但未丢弃的元素可能会导致窗口再次触发。

如果Watermarker时间t < 窗口EndTime t’’ +允许迟到时间 t’ ,则该数据还可以参与窗口计算。

如果Watermarker时间t >= 窗口EndTime t’’ + 允许迟到时间t’ 则该数据会被丢弃。为了能够更直观的呈现 这些too late数据,可以通过side out输出

五、项目中大数据模块的问题[重要]

1.登录风险评估的流程

参照当天笔记代码看

​ (1)、处理日志中的登录数据,用正则对数据进行拆分,把拆分的数据封装到评估数据的实体类中,可以把拆分的过程封装到一个工具类中

​ (2) 创建一个评估报告类,把评估的结果以key-value的方式存到一个map集合里面,key就是评估因子,v就是评估结果,true和false

(2)、因为要评估的项目很多,就需要很多评估因子,按照面向对象的编程原则,不可能去手动的一个一个去调用评估因子类,我们要让他自动的去递增调用评估因子,直到所有因子都被调用,可以设计一个责任(因子)调用链的类去去 自动调用

(3)这个责任调用对象的执行流程可以这样设计,改类里面有一个List集合用来存储所有的评估因子(这时候就需要把所有的评估因子对象的类型给定义成一样的,我们定义一个抽象类,所有评估因子类都继承该类,这样独所有的评估因子类的类型就一致了,也可以在抽象类里面把都要用的方法定义成公共方法,例如评估方法),有一个用来递增集合下标的属性,里面设计一个调用因子的方法,把评估因子需要的本次数据/历史数据/评估报告,定义为形参,在改方法里面写一个循环,循环的初始值就是之前定义的递增的属性。最终值,就是集合的最大值size,在循环里面,通过刚的递增的的值去集合中取评估因子,得到评估因子后,改递增的属性+1,让其递增,然后用刚获取到的评估 因子,调用评估方法把评估需要的数据本次数据/历史数据/评估报告和当前的责任链对象为实参传进去,

(4)、这时候流程就走到评估因子对象的评估方法,在方法里面我调用私有方法根据传来参数得到评估结果,也就是true和false,然后调用传来的评估报告类调用里面的方法,把评估因子代表的枚举类型为key,刚的评估结果为value存到一个 map集合里面

然后该评估因子的评估方法在根据之前传来的责任链对象在调用该类里面的的掉用因子的方法把数据穿进去

(5)、这时候流程有进入到了责任链中的方法,在进入到掉用因子的方法里面,因为坐标之前+1了获取到了集合中的下一个因子,继续调用该因子的评估方法,

(6)这时候有进入了因子类的的评估方法里面,里面评估完又调用责任链里的方法,就这样一直循环,直到循环结束

2.七个评估因子

七个评估因子

基于业务系统提交过来的评估数据完成、同时需要有历史数据作为评估标准

在业务系统在执行登录功能之前需要对本次登录数据进行评估,评估完成之后决定是否需要惩罚措施

  • 登录地区评估:当前评估数据的登录地区是否在历史数据中存在,如果不存在就认定该项有风险

  • 使用设备评估:当前评估数据的设备信息是否在历史数据中存在,如果不存在就认定该项有风险

  • 密码相似度评估:通过余弦相似度(性),把当前评估数据的乱序密码信息和历史数据中的密码信息进行比对;如果相似度达到规定的阈值,认定该项没有风险

  • 登录位移速度评估:

    • 根据球面距离计算本次评估数据的地理位置信息和上一次登录成功的地理位置信息的距离distance
    • 通过本次评估数据的登录时间和上一次登录成功的登录时间计算时间差time
    • speed=distance/time
    • 当计算出来的位移速度speed大于规定的阈值,认定该项有风险
  • 登录习惯评估:

    本次评估数据中的登录时间(星期以及时间段<小时>),是否符合用户的登录习惯;如果不符合认定该项有风险

    在历史数据中会存储用户的星期以及时间段对应的登录次数:Map<星期几,Map<时间段,次数>>

  • 当天累计登录次数评估:

    历史数据中存储当天累计登录次数,如果该数据大于规定的阈值;认定该项有风险

  • 用户输入特征评估:

    通过欧式距离(欧几里得距离)完成输入特征的评估

    • 在历史数据中会存储历次登录成功的输入特征(不一定存储所有的数据,可以存储最近10条)
    • 对历史数据进行统计分析,计算中心点:把历史数据对应位置求平均
    • 对历史数据进行处理,计算一个作为阈值的欧式距离
      1. 对历史数据两两进行欧式距离计算
      2. 对欧式距离进行排序
      3. 取三分之二位置的值作为阈值
    • 通过当前评估数据以及中心点,计算欧式距离;如果该距离大于阈值,认定该项有风险

3.更新历史数据

​ 对登录成功的数据进行分析处理,保存登录成功之后相关的数据信息:设备信息、登录地、最后一次的登录地地理位置、最后一次的登录时间、乱序密码、输入特征、当天累计登录次数、登录习惯

​ 跟上面的评估一样,每个评估需要的历史数据都需要保存更新,然后有个责任链了,负责调用这些更新数据的类,跟上面的调用方法一样

4.Jdk1.8新特性流Stream的使用-类型转换

1.Double[]和double[]的互转

    Double[] inputFeaturesString={1.2,2.3,3.4,5.6};
    //把Double[]转换成double[]
    double[] inputFeatures = Arrays.stream(inputFeaturesString).mapToDouble(Double::doubleValue).toArray();

    System.out.println(Arrays.toString(inputFeatures));

    //把double[]转换成Double[]
    Double[] d = Arrays.stream(inputFeatures).boxed().toArray(Double[]::new);

2.String[]转double[]

//       result是一个字符串
String[] inputFeaturesString = result.split(",");
//把string数组转换成double数组:通过JDK新特性Stream完成转换
//::lambda中写法,方法引用,可以调用类里面的构造方法和一些特殊的方法
double[] inputFeatures = Arrays.asList(inputFeaturesString).stream().mapToDouble(Double::valueOf).toArray();

5.使用Flink对数据进行实时计算

1.评估的时候每历史输据

​ flink在把输据从kafka种取出来,把非登录成功的输据或者评估输据过滤后,要把输据用map变成key-value的形式,key为APPName和用户名,日志输据为value,这里,登录成功和评估输据的key一定要一样,不然进的就不是一个task,评估的时候就得不到历史输据

如果项目依赖其他项目,在package的时候现在依赖的项目上install

6.使用Spark进行离线计算

流程:读取Flink写入的评估报告,对数据进行计算处理,写入MySq

在把项目放到spark服务其上的时候要把mysql数据库设置为允许远程访问 ,mysql版本不同还不一样,百度

因为写入需要mysql的jar包,要把该jar包考到spark的服务器上,修改代码中的spark远程地址以及mysql数据库连接的远程地址

因为Flink如果对写入hdfs的数据进行处理,写入到hdfs的是临时文件也就是点开头的文件名这个文件Spark是不能被读取的

7.Flink把结果写入HDFS上文件名是点开头Spring不能读取问题

因为Flink如果对写入hdfs的数据没有进行处理,写入到hdfs的是临时文件也就是点开头的文件名这个文件Spark是不能被读取的 我们需要在写入的时候 设置除了写入内容达到多少会把临时文件变正常,还可以设置一个时间达到一定时间就把临时文件变成永久文件

六、项目内容总结

  • 业务系统(包含分布式)-把用户登录信息发送到kafka、业务系统本该有的内容

    包含了以下module

    • 后端:完成数据的处理(待完成任务:到rdb中读取离线分析之后的数据)

    • 前端:完成数据的收集以及展示。flume的集成:把数据形成日志,通过flume发送到kafka

      (待完成任务:通过echarts提供的map《地图》,把后端提供的数据已地图的方式呈现)

    • 注册中心:可以保证服务被发现

    • 配置中心:可以保证微服务使用的配置统一管理

  • 大数据系统-做实时处理以及离线分析

    通过flink完成实时处理(流计算):评估登录是否有风险(七个因子),生成一个评估报告

    发布一个接口,供业务系统获取对应的评估报告

    包含了以下module

    • 评估模型
    • 评估系统:在评估系统中以依赖的方式把评估模型加载进来
    • 评估报告查询接口:供业务系统查询评估报告使用。在这个里面读取flink的可查询状态

    通过spark完成离线分析(批处理):读取hdfs中存储的报告数据,分析,把统计数据写入到MySql

    业务系统把数据读出来用echarts中的地图展示出来


  1. 0-9 ↩︎

  • 2
    点赞
  • 1
    评论
  • 10
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值