--针对需要学习的东西 建议最好找官方文档或官方网页
--一个教程并不一定全部都是正确的他可能是部分正确 当遇到走不通的部分或者经过实证行不通时 但是前面部分或者其他部分又是可以的时候 尝试把不行的一部分作为整体 这部分重新去寻找答案
--springcloud: 请求-->网关(springcloud-gateway)->sentinel-->服务A--openflign---->服务B 服务的注册发现用nacos
--针对第三方插件的学习 如果是需要解决关于配置的问题 建议的方式是跟源码打debug 看配置的属性是否正常获取同时把值是否正常设置进想要的对象的属性中
--配置或者第三方相关的问题: 1 网上搜索相关文档+官方文档 2 debug源码(最靠谱 但比较难 注意使用异常断点 + 条件断点很有用)
--要学会用自定义注解替代配置的方式。 注解定义在注解类上面的相当于是只要是使用了该注解的类/方法/属性都需遵守的规范 定义在注解类里面的属性字段相当于可以在使用该注解的地方使用其实例时的属性
-- 过滤器 拦截器 aop三者的区别 (使用都要指明路径 及 对应的处理逻辑)
--实现原理 过滤器基于servlet 拦截器基于反射机制
--适用范围 过滤器适用web容器,拦截器是spring框架提供拦截controller的
--顺序不同 请求->过滤前(放行前逻辑)->拦截前->action->拦截后->过滤后(放行后逻辑)->响应
--设计系统时的注意事项
1 规范 这个极其重要
2 数据的生命周期 来源 创建 使用 状态变更 (数据的特性 来源方式有几种 字段值有无重复 )
3 最细粒度的数据的操作 以及其他相关的
4 封装工具类的最少知道原则
5 避免重复 与 流程清晰职责分明 之间的取舍问题
-- 分库分表(一张表主要有行数和列数——即字段数)
--垂直拆分 --->垂直分库: 将表分类 彼此联系紧密的分在一个库 增加了库的数量 (将表竖直排列 然后水平切割 每个水平切割后的结果做为一个库) 垂直分表: 垂直切割改变列数 原来的行数不变 增加了表的数量
--水平拆分 --->水平分库: 水平切割改变行数 原来的列数不变 增加了库的数量 水平分表: 改变行数 增加了表的数量
--相关工具 mysql可以使用mycat
或者shardingJDBC ShardingSphere
-- 学习使用新的框架和微服务先学习基本概念 基本原理 了解大概 然后学习如何配置配置文件 搜索配置文件的说明 有些不清楚含义以及使用方式 网上也搜不到的尝试修改然后根据效果来推断
-- 大胆的改 大胆的试 代码以及设想的逻辑的先后顺序很重要
-- feign的使用
--角色: 生产者 生产者api(可能还有生产者client--一般加上生产者client是 client继承api接口 然后消费者注入生产者client,再调用 下面所写方式不带有生产者client) 消费者
-- 生产者模块与消费者模块都引用生产者api模块 ,
-- 生产者api定义接口说明请求哪个应用目标地址以及参数等(生产者api要FeignClient注解说明是哪个应用)
-- 生产者按照目标地址参数等实现这个服务(所以生产者要开启服务注册和发现功能)
-- 消费者注入生产者api类然后调用(消费者要开启服务注册发现以及feign调用功能-->通过参数指定包名调用生产者api哪个包下的api)
--枚举: 即为固定数量的常量集合 每个常量可以有多个属性
--到底选择数据字典还是选择使用代码的枚举类 优先是枚举类 因为枚举类到时增加修改删除维护很方便不会有硬编码 数据库的话需要将key值定义成常量才能无
--硬编码 劣势在于每次增删改要改代码 并且使用方便不需要查数据库少了一次交互 一看就能知道其对应的中文含义 但是如果频繁更改则建议定义在数据库中 定义在数据库优势每次增删改不需要改代码 但是如果增删改后涉及业务逻辑的变动的话依然要改代码
---------------------简而言之:数据字典仅适合 频繁增删改且增删改后不影响系统的逻辑---------------------------------
--父类子类中有同名变量时:
--重写和重载是针对方法的,子类的变量可以覆盖父类的变量,但是不能改变父类的变量(意思即为: 方法是可以被子类重写的,但是成员变量不行。 当父类子类存在同名变量时如果当前方法是被子类重写,定义在子类中的方法,
--同时该方法访问了父类子类同名的成员变量,则访问的同名成员变量是子类的成员变量的值。
--如果不是被重写的,是定义在父类中的且访问了父类子类同名成员变量,虽然该方法的调用者是子类 但是此时访问的成员变量实际访问的是父类的成员变量值)。
--父类和子类的变量是同时存在的,即使是同名。
--子类中看到的是子类的变量,父类中看到的是父类中的变量。
--它们互相隐藏,而同名的方法则是实实在在的覆盖(重写)。
--调用方法时,是根据对象的实际类型调用的;
--而访问成员变量就不同了,它是父类时,访问的是父类的成员变量,转型为子类时,访问的就是子类的成员变量了。
--分布式系统的数据一致性
1 要么同时成功要么同时失败 通过分布式事务解决
--集群的主从数据一致性
1 集群的主节点与从节点数据通过最终一致性来解决 不要求立即一致
--写的代码很重要的一点 在写通用工具类或通用方法时要约束使用者 让其不能不按照要求使用 保证数据的完整性
--写工具类或者任何代码在传参的时候 尽量使用对象 这样有一个好处应对未来参数变多或者变少的情况 如果是字符串或者数字的话 当代码需要改造以适应参数变多以及调用场景变复杂的情况时能很好的应对
-- 多线程高并发的问题: (原子性问题 可见性问题 有序性问题)
--如果在java中存在线程共享变量,尽量避免设计线程共享变量 则考虑用java的方式来解决线程安全问题 如 synchronized,volatile(可见性但该关键字不能保证原子性)关键字等 或者乐观锁的思想(但是一般这个变量没有版本这个属性) 或者ACID(好像是这个)思想
--如果不是在java中存在线程共享变量 而是高并发同时操作数据库的某条数据时导致的线程不安全问题 在数据库里面的数据保证线程安全可以用乐观锁思想
--高并发在要求线程安全的前提下提高性能的方式(首先明确高并发下如果不限制执行顺序一定是可能存在数据问题的): 1 降低锁的粒度 2 乐观锁思想 先认定不会出问题 在涉及增删改操作时去看其他线程是否并发进行了增删改操作判断是否真的无问题
-- 如果其他线程进行并发的增删改操作(版本概念)则认定有问题抛出异常
-- 事务的隔离级别
-- 1 读未提交
--可能出现的问题: 读取到其他会话未提交的数据-->脏读
--解决方式: 设置为读已提交
-- 2 读已提交
--可能出现的问题:在同一事务中根据同一查询条件结果却前后不一致(这个其他会话提交的事务可能是 对当前查询结果的某一条数据的修改操作 也可能是新增的一条会被纳入当前查询结果的数据),问题即为不可重复度
-- 当其他会话提交的事务是对当前查询结果中某条数据的修改操作 为不可重复读
-- 当其他会话提交的事务是会新纳入\或减少当前查询结果的新增\删除操作 为不可重复读中的特殊情况 幻读
-- 解决方式: 设置为可重复度( 设置为可重复读时只会加行锁 不会加表锁 只能解决不可重复读 不能解决幻读)
-- 3 可重复读
-- 可能出现的问题: 在高并发下,一个事务执行过程中另一个事务执行了,即执行顺序中a包含b,导致b事务看起来像没执行,只执行了a事务一样,这样的问题
-- 即为幻读(其实称之为幻操作更恰当 因为自己明明执行了某个操作但是最终结果看起来像是没有执行这个操作
-- 亦或是明明没执行某个操作最终结果看来来却像是执行了这个操作一样 ) (高并发下两个事务存在包含与被包含 最后被包含的像是没有执行一样)
-- 解决方式: 设置为串行化(强制要求两个事务排序 一个事务执行了后另一个事务才能执行)
-- 4 串行化
-- 问题: 性能较低 并发弱 但安全
--git的项目使用从初始化开始
--查看版本
git --version
--初始化Git仓库的目录(空目录上进行此操作)
git init
git clone <remote-repository-url> --注意clone后要进入被clone目录内 然后再执行接下来的命令
--
--
--注意clone后要进入被clone目录内 然后再执行接下来的命令
--
--
--拉取最新代码
git pull -- 如果需要拉取指定分支的代码,可以使用 git pull 命令指定分支名称,例如 git pull origin branch-name
--查看本地状态
git status
--添加修改到 Stage:如果需要提交修改,可以使用 git add 命令将修改的文件添加到 Stage 中 这里的 <file-name> 是要添加到 Stage 中的文件名称,可以使用 . 代表所有修改的文件
git add <file-name>
--提交修改:添加完修改后,可以使用 git commit 命令将 Stage 中的修改提交到本地仓库:
git commit -m "commit message"
--推送修改到远程仓库:提交完本地修改后,可以使用 git push 命令将修改推送到远程仓库: 如果需要推送指定分支的修改,可以使用 git push 命令指定分支名称,例如 git push origin branch-name。
git push --需要注意的是,如果在拉取最新代码之前本地仓库中已经存在修改,那么在拉取最新代码时可能会出现冲突。此时需要先解决冲突,然后再提交修改。可以使用 git merge 命令或 git rebase 命令来解决冲突。
--maven引入jar包注意事项
1 pom文件引入了可能实际没引入是否引入看maven窗口 对应模块是否有对应jar包
2 有时候引入不了jar 发现这个引入的jar没有设置version哪怕其他的没设置version照样引入了,此时加上version一般都能成功引入
3 缓存 引入不了时及时clean再重新编译
4 有时编译不了 或者在maven窗口执行不了clean 或者编译等命令 那及时在该模块的pom文件里面引入maven编译插件 可能就可以正常使用了
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<!--build-info 生成构件信息 repackage表示再次打包需要有main入口 -->
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
5 有时在B模块引用了C-jar包, A模块又引用了B模块按照道理 A也能使用C-jar包。 但是实际有时候不行,这个时候可以先把C-jar包直接放到A模块里面,等在maven窗口有了后再把C-jar包放到B模块中
不对特殊字符进行特殊处理的正则表达式-->对特殊字符进行特殊处理的正则表达式-->字符串
--使用jvisualvm的小技巧
1 可以调大内存
2 各个对象彼此之间的关系就像一棵树的结构一样。 找到一个实例对象后--->字段选项就是这个实例对象的下级结构 引用选项就是这个实例对象的上级结构 这样找到任意一个实例其父子都出来了
--使用泛型时的小技巧
1 明确泛型是在何处定义的(定义在类上 还是定义在方法上)
2 泛型具体被认定了为何类型是在何处(即第一次被确定该泛型表示何类 eg:ArrayList在类上定义了泛型(不指定时为object类),在代码时具体表示为哪个类是在new对象时指定在ArrayList上的类 )
-- 又如Collectors类的toMap()-->泛型定义在方法上 则有效范围只在该方法范围内,在写代码时第一次出现是在方法的传参上,则该类具体指代的类由定义Function类的实例时指定的具体类确定
-- 如果涉及到? super K 或者 ?extends V -->在实际写代码时先弄清楚在当前代码用的实例对象问号表示的类型 再弄明白K 或V 指代的类型
--返回值的类型越细粒度越好 作为参数的类型越宽泛越好这样传啥参数都可以
容器思想很重要map,list,json其实都可以理解为一个容器。
某一段代码反复用到 但是有硬编码 不灵活的情况的时候 如果该方法仅供内部使用不提供给外部 也可以封装成一个方法。
热启动与热部署有区别
hutool--jar包maven地址
--spring的面向切面编程 实现的效果: 在某个目标(方法)之前或之后或环绕执行执行方法,有时需要获取目标当中的参数根据参数不同做不同处理
重要的东西: 目标 添加的方法 目标的参数(通知传递参数)
切面 切点 连接点 通知
@Aspect--->定义该类为一个切面
@Pointcut--->切点 定义是哪些目标要做拦截
@Around--->环绕通知 @Around("action() && @annotation(controllerLog)")--->表达的意思是使用环绕通知 且连接点为action() && @annotation(controllerLog)
--@annotation(controllerLog)意思是将目标对象(此时目标对象是个注解)作为参数传递过来且名称叫controllerLog
如果是args则表示将目标对象(此时目标对象是一个方法)的某个参数传递过来
整个叫通知 上面的注解叫连接点
同一字段 数据库类型与javaBean类型不一致时(基本是出现在日期类型的数据上 eg:javaBean为日期类型 数据库为字符串类型 )考虑在 通过javaBean对象入库时要进行的转换(java应用到数据库)
查询时将数据库数据查询结果封装为java对象时要进行的转换(数据库到java应用)
返回给前端时 是否要将javaBean的数据进行转换(java应用到前端)
设计一个功能代码最好放在这个包含这个功能需要的信息最多的那个对象里
信息放在需要这个信息最近的地方
代码原则 可复用 ,尽量不要硬编码 出现不只一次重复的字符串用变量表示
--影响代码运行效率的几点
1 代码的问题(解析方式是否可以采用更高效合理的方式? 是否使用多线程?)
2 磁盘IO (IO交互是否过多? 是否可以减少?)
3 网络传输的问题
4 参数的设置。(eg: mysql单次允许接收的包的最大大小 默认4M 更改为256M ) 5 单次少量提交变单次大量提交(eg: 一次提交2000条变一次提交10000条)
5 是否利用缓存
6 CPU( 几核的? 内存多大? 时i5? i7?i12? cpu处理数据能力怎么样)
--lambda表达式(函数式编程):
--1 标准语法: <变量名>=(参数1,参数2)--->{方法体}
lambda表达式达到的效果基本跟匿名内部类差不多-->不用新建一个类, 即完成对一个接口的实现和对象的创建
重点需要关注的是返回值? 参数? 实现new一个对象且这个对象的成员方法就是方法体的方法
核心相当于实现接口,所以明确实现的是哪个接口中的哪个方法,该方法有哪些参数返回值是什么
一 反射
--1 创建类类型创建方式三种(先有创建对象再有其他)
-- Class c1=Date.class
-- Date date = new Date ; Class c2= date.getClass();
-- Class c3 =Class.forName("");--->且该方式是动态加载类
--编译时加载类称为静态加载类, 静态加载类 在编译时刻就要加载所有可能用到的类
--运行时加载类称为动态加载类---动态加载类 在运行时刻再决定要加载哪个类
--2 Class类基本操作
-- clazz.getName(); 类全名 含报名
-- clazz.getSimpleName(); 不包含报名的全名
-- clazz.getMethods(); 获取所有public修饰的方法对象,包括从父类继承来的 getMethod(name,Class...)-->根据方法名称与参数的类类型列表获取方法对象 获取的是public修饰的方法;
-- clazz.getDeclaredMethods();-- 获取自己声明的所有方法 无论访问权限,不含父类 getDeclaredMethod(name,Class...)---->根据方法名称与参数的类类型列表获取方法对象 获取的是自己声明的方法;
--Method类 getName(); getReturnType();getParameterTypes();
-- clazz.getFields(); 获取的所有public修饰的成员变量对象,包括父类继承而来的
-- clazz.getDeclaredFields()
--Field类 getName(); getType();
--3 方法的反射
-- 方法的名称以及方法的参数决定唯一的一个方法
method.invoke(对象,参数列表)
--4 反射的操作都是编译之后的操作,
-- 查询jvm配置的内存大小
//返回Java虚拟机中的堆内存总量
long xmsMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
//返回Java虚拟机中使用的最大堆内存
long xmxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
————————————————
版权声明:本文为CSDN博主「TabKey9」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/by_talang/article/details/128199540
--nginx的正向代理与反向代理的区别
--正向代理: 客户端需要告诉正向代理服务器需要访问的目标服务器,客户端知道真正提供服务的是谁
--反向代理:客户端对代理是无感的,由反向代理服务器自行决定要去访问的目标服务器。 客户端不知道真正提供服务的是谁,看起来好像就是反向代理服务器提供的服务一样。
在nginx的一个server中配置多个location后会出现访问看着的是请求同一个ip地址同一个端口号,但是因为匹配了不同的location最后实际请求的却是完全不同的ip地址和端口号下的东西
23种设计模式
--设计模式核心点要知道其中的变与不变。 把要变的设计成接口,未来拓展的时候实现接口,就做到了不改变原代码只需要改变传递的具体实现类,就完成了拓展了功能。 先按照不变的逻辑写出代码,再发现其中可能要变的地方,把可能要变的地方变成未来可拓展的方式来实现代码
--eg: 策略模式, 变的可能是策略,所以把策略设计成接口,具体的策略通过实现接口来完成。
-- 装饰器模式 变的可能是对被装饰对象的具体装饰方式,被装饰对象前或后增加方法,所以将装饰器设计成接口,并持有被装饰对象的引用,未来实现装饰接口就完成扩展
-- 状态模式 根据状态的改变改变行为模式。行为发生改变,(状态与行为绑定)
--
--->是为达到某个功能(目的)而广泛采用且得到证明的最好实践,所以学习某个设计模式时首先明确该设计模式的功能是什么
-- 不只要关注设计模式里类与类的继承或实现关系,同时也要关注成员变量,成员变量的类型等,因为设计模式很多模式都是定义了一个接口,两个实现类(有的是一个具体实现类一个抽象实现类),但是如
装饰者模式就在抽象实现类的构造方法中要求传递被装饰者然后赋值给成员变量 组合模式则在一个类中有一个将装有最顶层类型的list容器作为成员变量,即类关系都差不多但因为成员变量,构造方法的不同
变成了不同的设计模式。
--设计模式的实现方式 1 通过父类与子类(策略模式、) 2 通过两个类之间 3 类的状态 4 通过中间类
-- 构造方法要求传参则一旦创建这个对象的时候就可以拿到一些我们希望要的信息过来
-- 设计模式的开闭原则 对扩展开放,对修改关闭。 因为实际在使用时根据具体扩展的哪个实现类就用哪个实现类 所以在实际使用时还是要修改的,但是修改的只是实际用的具体实现类(当然这个也可以通过配置实现)
或者说我们需要扩展时不修改已有的代码,只是新增实现类,同时在使用时创建新增实现类,同时我们没去修改已有代码。
开闭原则一定要明确哪些是我们既有的代码。
-- 一个类跟另一个类发生交互 可以的方式有 继承 实现 作为另一个类的成员变量的类 作为另一个类的成员方法中的参数或返回值 注解
-- 23种设计模式的共性
-- 1 基本都有接口、接口的实现类, 使用接口(将接口类作为参数,或者接口类做为成员变量等)的类-->接口使用类,使用接口使用类的类--->客户端(根本应该理解为当设计模式设计好 最终测试时的那个main方法所在的类)
-- 所以基本(只是基本 不是所有)都有接口类、接口实现类、接口实用类、客户端 这四个
--简单工厂设计模式: 核心点--->构建一个工厂类专门根据参数来创建对象,不在客户端中直接去创建类 所以减少了在客户端出现的类,即减少了依赖 (专门构建类 体现了单一职责的设计原则,这个类只完成专门的功能)
-- 抽象工厂设计模式: 构建一个抽象类(或接口)接口中定义创建需要返回的对象的方法, 实现抽象工厂的具体实现类,
--建造者设计模式: 接口,具体的实现类,使用接口的类(有参数为接口的一个方法 ,同时也有类为需要创建的类成员变量)-->指挥角色类,客户端----->好处可根据需求传递自己想要的实现类进行扩展而不改变原代码
--适配器模式: (继承+实现) 将一个类转换成另一个类 专门创建里面有一个适配方法的接口,实现这个接口的类则可以被作为参数 ,根据这个类来做具体的操作 可扩展 且不改变源码(我们说的不改变源码是不改变底层的,在具体应用使用时还是需要改变传递的参数)
(-- 都专门增加了类 这个类来完成某个操作 )
-- 订阅发布模式
--装饰器模式
--观察者模式: 抽象被观察者, 具体被观察者( 持有装有观察者容器的成员变量) 观察者
--策略模式 将策略封装为一个类,策略有多种且未来仍可能变化
--享元模式: 将可能会重复创建的对象放入缓存池,每次去取如果已存在则从池子取,不存在则新建然后放入池内。 -----> 享元抽象类 具体的享元对象 享元工厂(返回具体的享元对象 )
--组合模式: 表示树形结构
--桥接模式: 一个接口 ,一个抽象类, 抽象类持有接口类的成员变量,则通过此抽象类将接口的子类与抽象类的子类关联起来了
--委派模式: 一个接口,三个实现类,三个实现类中有一个的实现类的实现方法是根据参数具体情况,调用其他两个实现类的方法。
--责任链模式: 形成一个链条依次执行。
--命令模式: 命令封装成接口,具体实现类持有真正完成命令的成员变量,专门封装一个类去执行命令,-------
-- 状态模式:内部状态发生改变时行为跟着发生改变---行为发生改变 ,状态与行为绑定, 行为要发生改变 所以接口要有方法 handler()
-- 备忘录模式: 不破坏封装的前提下保存一个对象的内部状态,以后需要的时候可将对象回复到保存(备忘)时的状态--->不存在改变与不改变的事只是实现这个功能的模式
被备忘录角色 备忘录发起角色 备忘录管理角色(容器)
-- 中介者模式: 通过一个中介对象,完成其他对象之间的交互(链家) --->duboo注册中心 抽象中介者 抽象同事
-- 解释器模式:给定一个语言,定义其文法,定义解释器来解释句子-- 接口表达式角色 上下文角色
-- 访问者模式: 将数据结构与数据操作相分离的一种设计模式。 访问者 被访问者
--java多线程
--1 需要java多线程来提高运行效率 设计java多线程时,在线程任务中 避免使用线程共享的变量,或者把本来可共享可不共享的变量设置成不共享的变量。
--2 在多线程代码中重点并且常使用的 线程池 ThreadPoolTaskExecutor 是 Spring 框架提供的一个线程池实现
--向线程池提交任务 并获取返回结果 Future.get() ---如果执行任务报错则会抛出异常 该方法会导致调用该方法的线程阻塞 等待线程执行结果
--向线程池提交任务,等线程池任务执行完毕后再继续执行主线程任务
// 关闭线程池
taskExecutor.shutdown();
// 等待线程池中的任务全部执行完毕
while (!taskExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
// 等待任务执行完毕
}
-- 第二种方式 用Future接收返回值 再调用get()方法 该方法会使当前线程阻塞
// 提交任务
Future<Integer> future = taskExecutor.submit(() -> {
// 执行任务,返回结果
return 1 + 2;
});
// 获取任务执行结果 该方法会使当前线程阻塞
Integer result = future.get();
-- 如果时线程类对象 join() 方法也可以
-- 使用
--此次多线程代码遇到的问题 适用mysql5.7以下
1 调试的问题 要打Thread类型断点
2 debug捕获异常的问题,怎么设置在某个线程某个类在执行过程中报错时自动断点(挂起)
3 多线程内存的问题,多线程设置线程池线程数与核数的关系? 多个任务同时执行 怎么设置避免内存溢出?
4 多线程事务的问题,当新建立一个线程时该线程如果需要事务 则需要手动开启事务。 虽然主线程有事务 但是在线程池里的这个线程没有事务
5 手动开启事务后,在这个事务开启与关闭之间有其他方法与数据库进行交互对其他表进行了增删改操作,这个新操作虽然是个新的事务 但是此时相当于加入了前面的事务,这个
事务会处于lock wait状态需要等待前面提交了才能提交,不过此时可以对这个交互操作采用开启一个新事务的方式(前面事务挂起,新开事务,与前面的事务相互独立)的方式 避免等待超时
6 连接等待超时的问题,当开启了一个事务,前面一直在处理业务(这个业务在多线程环境中处理,多线程中的操作与主线程的事务彼此独立) 等业务处理完毕后已经过去很久了,这时再进行数据库交互
(无论是增删改查) 此时会报连接等待超时的问题,因为前面方法在注解上使用了事务,所以后面的操作一直需要用这个连接,但是等待时间过长,此时可以修改数据库 和 java配置连接池的等待时间
或者把这个交互不放到这个事务中
-- 线程创建方式之可以获取返回值的方式 结合FutureTask 与Callable类
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>(){
@override
public Integer call() throws Exception{
System.out.print(234);
return 123;
}
});
Thread t = new Thread(task)
--java启动时开启jconsole的远程连接
java -Djava.rmi.server.hostname=<远程主机IP> -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<端口号> -Dcom.sun.management.jmxremote.ssl=<是否安全连接> -Dcom.sun.management.jmxremote.authenticate=<是否认证> java类名称
--实测使用如下命令后能连接上
nohup java -Djava.rmi.server.hostname=10.157.107.155 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=12345 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar -Xms512m -Xmx4096m /data1/www/ifrs-admin.jar &
--java启动设置GC策略 内存大小 新生代大小
-- 1: -XX:+UseSerialGC:启用 Serial GC 策略。 -XX:+UseParallelGC:启用 Parallel GC 策略。 -XX:+UseConcMarkSweepGC:启用 CMS GC 策略。 -XX:+UseG1GC:启用 G1 GC 策略。
-- 1: -Xms:设置堆内存的初始大小。 -Xmx:设置堆内存的最大大小。 -XX:NewSize:设置新生代的初始大小。 -XX:MaxNewSize:设置新生代的最大大小
-XX:NewSize与-Xmn 都是设置新生代大小的,不同的是-Xmn直接指定大小 newSize与maxSize指定初始大小与最大大小 故是弹性的
java -XX:+UseParallelGC -Xms512m -Xmx4096m -XX:MaxNewSize=1024m -jar myapp.jar
--线程阻塞
调用sleep wait join方法会使当前线程进入阻塞状态, 其他线程对还处于这三个方法调用后状态的线程进行 interrupt(打断)操作 时 对应线程会清空打断标记, 打断正常运行的线程不会清空打断标记
线程对其他线程对象调用打断操作,并不会导致其他线程停止执行,只是会将其他线程的打断标识标记为true,其他线程本身可以根据这个打断标识来结束自己的线程
isinterupted() -->不会清除打断标记
interupted() -->在获取到打断标识后会清除打断标记 重置为false
如果A线程调用了LockSupport.park()方法,则A线程对进入阻塞状态, 此时对A线程调用interrupt() 方法会使A线程回复正常执行,但此时再执行LockSupport.park()不会使A线程进入阻塞,因为打断标记值已经为true了,要想使
该方法再次生效则需要将打断标记置为false
--多线程设计模式
1 两阶段终止模式: 在一个线程中 优雅的 终止另一个线程
守护线程: 当其他非守护线程结束后,即使守护线程代码没执行完也会结束。 (被守护者已经不存在了,所以自身也没存在的必要了) t1.setdaemon(true)-->该线程则 设置为了守护线程
-- 线程的状态
初始状态 就绪态 运行状态 阻塞状态 终止状态--->从操作系统层面看
从javaAPI来看六种状态: new runnable blocked(在有synchronized关键字且不持有锁的线程) waiting(调用join()方法的主线程) timed_waiting(调用sleep()方法的线程) 终止
java执行的最小单位是将.java文件编译成.class文件后的一行代码。
--并发编程之共享模型管程
--共享问题
--临界区与竟态条件
避免临界区的竟态条件发生解决方案有
--阻塞式的 synchronized Lock (synchronized---->谁拥有锁对象,在遇到被这个锁对象锁住的代码区域时谁就能执行这部分被锁住的代码。)
--非阻塞式的 原子变量
--线程变量的安全分析
--对两个共享变量进行操作时加锁的问题
--synchronized
--线程安全分析
--Monitor(锁/监视器)
-- 每一个对象都可以持有一个monitor. 最终效果:一个锁对象关联了一个monitor,这个monitor记录了其owner(拥有者)所有者的线程,同时记录下了entrylist被阻塞线程形成的链表,还有waitset
--waitset是之前获得过锁,但条件不满足进入waiting状态的线程
--重量级锁 轻量级锁 偏向锁之间的区别: 轻量级锁在没发生多线程竞争以及其他线程在第一次来竞争之前不需要频繁上锁解锁
--轻量级上锁流程解锁流程: 上锁 将mark word与轻量级锁地址互换 解锁: 将换的又换回来
--重量级上锁解锁流程 : 上锁: owner值置为指定线程 解锁owner值置为空
--偏向锁的上锁解锁流程: 上锁: 相比轻量级锁由原来的mark word与轻量级锁地址互换变成了将线程id设置到锁对象头的mark word中
--撤销对象的偏向锁:1 调用对象.hashcode() 会禁用偏向锁 2 其他线程使用对象做锁 此时会升级成轻量级锁 3 调用wait/notify
--批量重偏向
--批量撤销
--锁消除
--java对象头
--在java虚拟机中java对象都是由java对象头和其他组成的
--以32为jdk为例:
--对象头
--KLASS WORD
--MARK WORD
--hashcode
--age(分代年龄)
--是否偏向锁 枷锁状态
--wait/notify
--线程状态转换
--活跃性
--Lock
--乐观锁 悲观锁
--并发编程之非共享模型
--学习源码看源码的技巧
--1 抓住主要重点方法中的第一个,其他的调用由该方法衍变来,由该方法逐步去看调用的方法,被调用方法可能出现不知道具体到底是哪个类的情况,这个时候参照2方式去判断
--2 对一个继承及实现体系复杂的类,优先找到最子类的那个或者说在代码里面debug时最终实例化的那个类,然后打开其
--uml图(idea里的显示图) 然后看继承及实现体系,同时在debug时看调用的是多态形式的哪个方法,在uml图里展开属性/方法
--看对应的方法是距离最近的哪个父类的,当然完全也可能该方法里面的其他方法被实例化的对象重写了 这种情况也要注意 不过概率较小
--通过这种方式就定位到了到底调用的哪个类的哪个方法,在走代码的时候就不用太担心该方法是抽象方法了
--3 具备基础的知识
--4 抓脉络,抓主要和重点,不要死扣细节,慢慢抓住主要了后未来再抓细节
--5 记住一些重要方法
--6 想要知道一些重点地方 比如spring的实例化对象时方法的调用流程 则在该处打断点 然后看即可
--7 注意前后代码逻辑的关联性
--javaweb容器学习进阶
--web容器的三大组件
--servlet程序-->处理请求的业务逻辑
--filter过滤器->可以在请求到达servle之前/响应返回给客户端之前拦截请求/响应,然后处理请求/响应
--Listener监听器->监听web程序中的事件,常见的监听器
--ServletContextListener(?监听Web应用程序的启动和关闭事件)?、
--?HttpSessionListener(?监听会话的创建和销毁事件)?、
--?ServletRequestListener(?监听请求的创建和销毁事件)?
--springmvc的执行流程
--因为dispatcherservlet也只是实现了web容器三大组件之一的servlet,所以仍然是请求先到过滤器,再到dispatcherservlet,dispatcherservlet是做
--请求的转发和响应结果的,所以先是转发请求,转发请求通过HandlerMapping实现,根据url以及处理器映射器找到HandlerMapping后返回HandLer处理器
--前端控制器调用处理器适配器去执行Handler,Handler执行完成给适配器返回ModelAndView,ModelAndView是springmvc框架的一个底层对象,包括Model和view
--前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp) 视图解析器向前端控制器返回View
--前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域, 前端控制器向用户响应结果
--先创建tomcat容器,解析web.xml,如果配置了listener会创建一个spring容器,作为后面创建的容器的父容器,因为要配置初始化dispatcherservlet,dispatcherservlet是个servlet遵守
--servlet的相关规范以及其生命周期(创建时执行init方法 请求执行service方法 销毁执行destroy方法) 会先调用init()方法->调用父类FrameworkServlet里的initServletBean()创建容器 并被dispatcherservlet持有
--同时还会指定父容器如果有的话 容器创建好后则调用容器的wac.refresh()方法,利用容器的ioc和aop创建对应的对象(这里面会涉及spring容器对象实例化的相关知识) ,
--接下来执行initStrategies()方法 初始化策略, 在该方法里面会从容器里面解析路径与bean,按照一定的规则认定为 handler,形成handlermapping与handleradapter, 等 --->
--maven版本冲突如何解决:
--1 路径近者优先
--2 哪个先声明哪个优先
--3 可以使用exclusion标签排除一个被依赖的jar包
--4 锁定jar包版本 dependencyManagement标签
--jvm的认识(出生-->消亡)
--1 堆 存放实例化后的对象的,对象实例化时基本都分配在堆内存里,这是被所有线程所共享的,堆里面又分为新生代 老年代
--2 栈 在数据结构与算法中栈是一种数据结构,有入栈出栈操作,满足先进后出的特性,在java中每个线程被创建时也会创建一个java虚拟机栈
--其内部持有一个个的栈帧,对应着一次次的方法调用,当前正在被执行的方法对应着一个活动的栈帧,即当前帧,如果当前方法调用了其他方法
--则新的栈帧就会被创建出来(栈帧里面存着局部变量,操作数栈,动态链接,方法返回地址),成为新的当前帧,一直到它返回结果或者执行结束,
--同时java栈的操作跟数据结构中的栈操作一样 压栈出栈
--3 程序计数器 每个线程都有自己的程序计数器 在任意时间点一个线程都只有一个方法在执行,称为当前方法,
--程序计数器记录了当前方法的jvm指令地址,如果执行的是本地方法则是undefined
--4 方法区/元空间/永久代(1.7之前叫永久代,之后叫元空间) 方法区就是元空间的实现 这也是被所有线程共享的区域,主要用来存放元数据。例如类结构 字段 方法代码,运行时常量池 等(就是类加载的时候 将class文件读取转换成静态数据结构放到方法区的东西)
-- 运行时常量池 这是方法区的一部分 用来存放各种常量信息
--5 本地方法栈 跟java虚拟机栈很相似,支持对本地方法的调用,同样的每个线程都会创建一个
--java垃圾收集
--1 Serial GC 作为client时的默认算法 单线程的
--2 ParNew GC 多线程的
--3 CMS GC 基于标记-清除算法 存在内存碎片化,且会跟用户抢cpu和线程
--4 Parallel GC 在jdk8中是作为server时的默认算法
--5 G1 GC 在jdk9后的默认算法
--java类加载机制
--jvm调优的一般思路
--1 确定目标 内存占用? 延时情况? 吞吐量?
--ioc
--通过createBeanFactory 创建一个ioc容器 默认的为defaultlistablebeanfactory,创建后为容器的某些属性赋值如: beanpostprocessor, 将读取到的bean定义信息 转换为beandefinition对象, 放入beandefinitionmap容器中 beandefinition对象有很多属性,比如
--作用域,是否懒加载 ,父类名称,依赖项,init方法名称, 是否单例,然后执行postprocessbeanfactory对容器进行增强,做比如
--将属性值由占位符变成真实对应的值,然后实例化对象,对自定义属性赋值,检查是否是aware接口,以对容器对象属性赋值,执行beanpostprocessor前置增强()
--检查是否实现了initializingBean接口,是否实现了init-method接口 ,再进行beanpostprocessor后置增强(spring的aop就是在此实现的),将bean放到注册表中,使用 销毁,
--aop
--面向切面编程, 达到解耦的目的,一般可以用于日志记录 权限控制 事务 自己项目通过aop实现了审批 跟业务代码完全隔离 核心概念有
--切面 将关注点模块化,在java代码中表现为有Aspect注解的类 接下来我们要面向切面,那就需要解决什么时间 对谁 进行什么处理
--切点 对谁,满足什么特征条件的添加逻辑 eg:有指定名称注解的
--连接点-joinPoint (是由通知引出来的一个概念 可以理解为时间点)在程序执行过程中某个特定的点, eg:目标方法执行的时间点,处理异常的时间点 () 主要提供上下文信息
--通知 解决在什么时间点添加逻辑, 即在某个特定的连接点执行的动作 常见的有around,before,after等
--织入 把切面跟连接点连接起来的一个动作。 个人理解类似创建动态代理对象
--接口与抽象类的区别
--1 接口是自上向下的 只是定义规范 约束 抽象类是自下向上的,是将子类的共性抽取出来后 形成的类
--beanFactory与factoryBean的区别
--factoryBean是自定义方式创建实例
--beanFactory是流水线
--spring解决循环依赖的方式
getBean()->doGetBean()->createBean()->doCreateBean()->createBeanInstance()->populateBean()
--三级缓存
--基本背景 a依赖b,b依赖a,先创建a
-- 先a实例化,a实例化后放入三级缓存 准备设置属性, 发现要实例化b,b实例化后也放入三级缓存,准备对b设置属性,发现又要实例化a(a->b->a 造成循环),a发现在三级缓存里面有则将a删除三级缓存移入二级缓存,同时为b设置a属性指向a的地址,
--b此时为一个成品对象了,将b放入一级缓存,b变成成品对象了,可以将b赋值给a了,a也变成成品对象 放入一级缓存。
--singletonObjects -->一级缓存(放成品对象的)
--singletonFactories -->三级缓存 (放lambda表达式)-->原因是我们要引用对象时不知道是要引用原始对象 还是通过aop处理后的代理对象,所以用lambda表达式 到时根据实际需要来取
--earlySingletonObjects -->二级缓存(放半成品对象)
--先看spring 再看springmvc 再看springboot springcloud
--spring
--refresh()方法与13个方法
--最顶层的容器对象为BeanFactory 但是在创建的时候 根据不同情况创建的不同的类型eg: xmlpATHApplicationContext,DefaultListableApplicationContext, webApplicationContext
--spring中用到的设计模式
-- 单例模式 bean默认都是单例的
-- 原型模式 指定作用域为prototype
-- 工厂模式 beanfactory
-- 模板方法 postProcessBeanFactory, onRefresh,initPropertyValue (交给子类去实现)
-- 策略模式 xmlBeanDefinitionReader, PropertiesBeanDefinitionReader
-- 观察者模式 listener,event,multicast (观察者模式的好处 可以根据需要添加删除观察者,当被观察者发起通知观察者的动作的时候 观察者发起相应的动作)
--观察者 被观察者动作发起后,通知给观察者,观察者接收消息发起相关动作 ,提供接收到消息后采取的动作 receivemessage
--被观察者 动作的发起者,持有观察者的集合引用 提供注册 移除 通知观察者的功能 sendmessage
-- 适配器模式 Adapter
-- 装饰者模式 BeanWrapper
-- 责任链模式 使用aop的时候会先生成一个拦截器链
-- 代理模式 动态代理
-- 委托者模式 delegate
-- 使用设计模式的时候 其实spring我们学习设计模式时的那个client
--spring的aop底层实现原理
--动态代理 aop是ioc的一个扩展功能, 先有的ioc再有的aop, bean对象在ioc容器的生命周期的BeanPostProcessor方法时实现了aop的扩展
--
--springmvc
--springmvc的mvc的含义
--M model 模型层 mybatis
--V view 视图层 html css
--C controller 控制层 servlet的封装-springmvc
--答题技巧
--1 突出技术名词 核心概念, 接口, 类, 方法
--2 避重就轻
--3 底层实现 工作原理?数据结构?流程?设计模式?设计思想?
--BIO NIO AIO分别是什么
--BIO 同步阻塞IO,使用BIO读取数据时,线程会阻塞,需要线程主动去查询是否还有数据可读,并且需要处理完一个socket之后才能处理下一个socket eg:inputstream outputstream , Read ,Writer
--NIO 同步非阻塞IO,使用NIO的时候,线程不会阻塞,需要线程主动的去查询是否有IO事件 eg: Channel Buffer Selector 原理:服务端通过ServerSocketChannel监听请求,一旦有请求过来则
--注册到Selector ,实现一个客户端 开启一个线程 一个Selector,一个Selector然后连接多个客户端
--AIO 异步非阻塞IO, 线程不会阻塞,有数据可读时会主动通知给线程,不需要线程主动去查询,主线程可以同时做其他事,不需要一个while(true)的循环了 -- 服务端通过AsynchronousServerSocketChannel监听连接请求,一旦有连接到来,会调用accept方法,并通过回调方式处理连接就绪事件。
--springcloud与dubbo 的区别有哪些
--
--springcloud有哪些常用组件
--Eureka 注册中心
--Nacos 注册中心 配置中心
--Consul 注册中心 配置中心
--Spring Cloud Config 配置中心
--Feign/OpenFeign
--Kong 服务网关
--zuul 服务网关
--Spring Cloud GateWay 服务网关
--Ribbon 负载均衡
--Sprign Cloud Sleuth 链路追踪
--Zipkin 链路追踪
--Seata 分布式事务
--Dubbo RPC调用
--Sentinel 服务熔断
--HyStrix 服务熔断
--NetFlix
--SpringCloud与SpringCloudAlibaba
--springcloud是面对分布式框架共有的一些问题 如负载均衡 服务熔断 网关 分布式事务 注册中心 配置中心 等提供的一套可以快速上手
--构建项目的一套解决方案
--springboot
--总
--自动装配? 什么是自动装配? 解决了什么问题? 说场景? 如果没有springboot时我们想用spring容器管理第三方jar包时怎么做?现在有了又可以怎么做?
--springboot大家基本的称呼是脚手架 提供了自动装配(把tomcat或者其他的一些配置写在一个配置文件里)的功能,我们在项目中会引入很多第三方包,如果没有
--springboot之前我们想用spring容器管理第三方的bean,要么写xml要么加个configuration注解作为配置类里面再添加Bean注解来实例化这个Bean交由spring容器管理
--现在有了springboot,我们可以通过spring.factories(该文件的key为自动配置类 value为配置类)读取自动配置类来完成上述过程
--分
-- 启动流程
--创建spring容器对象 做一些准备工作 eg:配置类,容器类型,
--创建容器的过程中,会根据classpath能否加载到指定的类要确定容器类型,同时根据堆栈的main方法所在类认定为启动类,也会读取spring.factories(这个地方可以自定义扩展),将读取到的信息缓存起来以备后面用,也会设置容器初始化的监听器等 (创建容器对象的过程会指定配置类,根据classpath里面能否加载到默认的一些类来确定容器类型,设置在初始化的回调函数,监听器等 )
--下一步执行run()方法启动容器
--获取所有的SpringApplicationRunListener监听器,再通知监听器spring开始启动
--接下来会有两个重要的方法prepareContext()与refreshContext()
--prepareContext()顾名思义是对context的一些属性赋值与初始化操作 准备好未来可能要用的东西, 比如环境对象,这里面还有个load()方法 将启动类作为BeanDefinition注册到registry中 ,以便未来解析其上的springbootapplication,enableautoconfiguration等注解
--refreshContext()会调用spring的refresh()方法
--refresh()方法会调用invokeBeanFactoryPostProcessor()方法,在这个方法里会得到ConfigurationClassPostProcessor类(该类是BeanDefinitionRegistryPostProcessor的实现类),
--会调用该类的 postProcessBeanDefinitionRegistry()方法,找到启动类,用parse()方法解析,解析相应注解比如Bean componentScans import
--最重要的是import的注解,
--解析import注解时会用递归的方法读取所有的import注解,在processImports()方法的时候又会对import注解里的类进行分类
--解析配置类上的注解( 比较重要的是执行processImports方法 启动spring时在该方法上加断点 看方法的调用栈)
--类加载过程
--五个步骤 加载 验证 准备 解析 初始化
--加载的含义: 加载是一个读取class文件,并将其转化为某种静态数据结构存储在方法区内,同时在堆中生成一个便于用户调用的java.lang.Class类型的对象的过程
--验证: 对文件格式的验证(这一步其实发生在加载的过程中) 对元数据以及字节码的验证,保证其是安全可靠不会危害jvm,符合引用的验证(发生在解析阶段)
--准备: 对静态变量分配内存 并赋默认值 赋0值
--解析: 将符号引用替换为直接引用(A类中包含B类,在A加载时是不知道B有没加载的,且此时B是肯定没有被加载的,此时会用一个字符串表示B的地址,这个符合此时就称为符号引用,在解析过程中发现B没加载,就去加载B,将B加载到jvm中后,符合引用就会被替换为直接引用
-- 如果B类是一个具体的类 此时称为静态解析,如果B类是一个接口或者抽象类,其实在解析时是不知道指向的具体的类的,只有在调用的时候才知道具体的
-- 实现类 这时我们称为动态解析)
--初始化: 资源的主动初始化操作, 比如 静态代码块的执行 静态成员变量的赋值
--对象创建执行了哪些操作过程是什么
--检查类是否加载(如果没加载 则执行加载)
--分配内存
--对象初始化零值
--设置对象头
--执行init()方法
--对context的理解: context 上下文 ->可以理解成未来可能会用到的一些相关信息 可以是变量值 属性值 引用值等
--sentinel学习
--雪崩问题的常见解决方案:
--1 超时断连
--2 舱壁模式(也叫线程隔离) 限定每个服务使用的线程数,调用指定服务时使用指定的线程池
--3 熔断降级 由【断路器】统计业务执行的异常比例,超出阈值则【熔断】该业务 拦截访问该业务的一切请求;当服务恢复时,断路器又会放行访问该服务器的请求
--线程隔离和熔断降级都是对客户端(服务的的调用方)进行的保护
--4 流量控制 限制业务访问的QPS
--sentinel与hystrix的服务保护比较
-- sentinel hystrix
--隔离策略 信号量隔离 线程池隔离/信号量隔离
--熔断降级策略 基于慢调用比例或异常比例 基于失败比例
--限流 基于QPS,支持基于调用关系的限流 有限的支持
--流量整形(将瞬间突发的大量请求变为均匀的排队请求
--eg:突发的1000变成5秒每秒200) 支持慢启动,匀速排队模式 不支持
--控制台 开箱即用 可配置规则
--sentinel的原理 (sentinelResuorce注解)
--1 限流规则--->测试可以用jmeter做压测
--簇点链路 即调用链路 sentinel默认会监控springmvc的每一个端点
--设置QPS
--设置流控模式
--直接 统计当前资源的请求 触发阈值时对当前资源直接限流
--关联 统计与当前资源 相关的另一个资源 触发阈值时,对当前资源限流
--链路 统计从指定链路访问到本资源的请求,触发阈值时 对指定链路限流
--流控效果
--快速失败
--预热模式
--排队等待
--热点参数限流
--2 隔离和降级
--feign整合sentinel
--1 feign开启sentinel功能
--2 给feignClient编写调用失败后的降级逻辑( 通过FallBackFactory 对远程调用的异常做处理 )
--3 将刚才的降级逻辑所在类注册为bean
--4 在调用客户端中使用 刚才那个bean(FallBackFactory)
--线程隔离
--熔断降级
--会有三个状态(形成了闭环) closed(断路器关闭状态)---失败异常比例达到阈值------>Open(断路器开启)----熔断时间结束------------->Half-Open(尝试放行 一次请求)
<----尝试放行,如果继续失败---
<------------------------------------------------------尝试放行,如果成功-----
--断路器熔断策略有三种
--慢调用比例 统计多长时间内的 最少多少次请求 多长时间认定为慢调用 比例阈值多少
--异常比例
--异常数
--授权规则 (RequestOriginParser)
--白名单
--黑名单
--3 自定义异常结果(BlockExceptionHandler)
--默认情况下发生限流 降级 授权拦截时都会抛出异常到调用方 如果要自定义异常返回时结果 需要实现BlockExceptionHandler接口
--FlowException DegradeException ParamFlowException AuthorityException
--4 规则持久化(规则默认保存在内存里,要让他保存在数据库里)
--规则管理模式
--1 原始模式--->默认保存在内存里,重启则丢失规则
--2 pull模式--->控制台的配置规则推送给sentinel客户端,然后客户端推送给数据库或本地文件,集群中的其他sentinel客户端去数据库里面同步(存在时效性问题)
--3 push模式--->将控制台的配置规则,推送到远程配置中心。 eg:nacos sentinel客户端监听nacos 获取配置变动信息,完成本地配置更新
--5 相关面试题
--1 sentinel与hystrix的线程隔离有什么区别?
--2 sentinel限流与GateWay的限流有什么区别
--常见限流算法有
--1 计数器算法
--固定窗口计数器算法
--将时间划分为多个窗口,每个窗口为1秒,每个窗口维护一个计数器,每有一次请求则计数器加一,超过限流阈值则做出对应的限流处理
--滑动窗口计数器算法
--滑动窗口计数器算法会将一个1秒的窗口,再划分为n(比如为2)个更小的区间,超过限流阈值仍然会被限流,但窗口会根据当前请求所在
--时间进行移动,窗口范围变为从当前请求时间-窗口时间跨度(1秒)之后的第一个更小的时区到当前请求时间所在的更小时区这个范围
--窗口划分的区间越多,区间足够细越准确。
--2 令牌桶算法(核心思想,定一个速率,通过能否继续从桶中获取到令牌来判断 请求是否过多该限流了)--->问题: 存在没及时被消耗的令牌。 eg:前面生成了5个,且令牌桶没满,后面又生成了5个,突然来了7个请求,本来阈值是6,但7个都被放行了。
--以固定的速率生成令牌,存入令牌桶,如果令牌桶满了以后,多余的令牌丢弃
--请求进入后,必须先尝试从令牌桶中获取令牌,获取到令牌才放行,如果令牌桶中已无法获取到令牌,则请求被丢弃或者等待
--3 漏桶算法
--将每个请求视作水滴 放入漏桶进行存储,漏桶以固定速率向外漏水,漏水表示执行请求 如果漏桶空了则停止漏水,如果漏桶满了则多余的水滴直接丢弃。
--三种算法对比
对比项 滑动窗口计数器算法 令牌桶 漏桶
能否保证流量曲线平滑
能否应对突增流量
流量控制精确度
--参考答案
--限流模式常用的有三种算法, 滑动窗口计数器算法 令牌桶算法 漏桶算法 。 gateway是基于redis实现的令牌桶的算法,sentinel是三种都有
--默认限流模式是基于滑动时间窗口的算法
--排队等待的限流模式是基于漏桶的算法
--热点参数限流是基于令牌桶的算法
--nacos学习
--配置中心
--服务治理的三个角色
--服务提供者
--服务消费者
--注册中心: 记录并监控(心跳机制)微服务各实例状态,推送服务变更信息
--服务注册(针对服务的提供方)
--引入discovery依赖
--配置里面配置nacos地址
--服务发现(针对服务的调用方)
--引入discovery依赖
--配置里面配置nacos地址
--使用DiscoveryClient获取服务实例(获取到是个list)
--openfeign学习
--1 连接池
--openfeign内部发起请求是通过client频繁创建连接销毁连接的方式 这种方式效率比较低,所以改用连接池的方式避免资源的频繁创建与销毁
--改造为连接池的方式
--1 引入依赖
--可以用apache的httpclient,或者 叫okhttp的
--2 feign开启连接池功能
--网关
--就是网络的关口,负责请求的路由 转发 身份校验
--网关路由
--路由属性
--id 路由唯一标识
--uri 路由目标地址
--predicates 路由断言(spring提供了12种基本的RoutePredicateFactory的实现)
--Path
--After
--Before
--Between
--Cookie
--Header
--Host
--Method
--Query
--RemoteAddr
--Weight
--Xforwarded Remote Addr
--filters 路由过滤器 对请求或响应做特殊处理(gateway内部提供了33种路由过滤器)
--AddRequestHeader
--RemoveResponseHeader
--RewritePath 请求路径重写
--StripPrefix 去除请求路径中的N段前缀 eg: StripPrefix=1,则路径/a/b转发时只保留/b
--seata分布式事务
--三个重要角色
--1 TC 事务协调者(最重要的角色) 维护全局和分支事务的状态,协调全局事务的提交或回滚
--2 TM 事务管理者(管理全局事务) 定义全局事务的范围,开始全局事务,提交或回滚全局事务
--3 RM 资源管理器(管理分支事务) 管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态
--部署seata
--1 部署TC服务(seata服务)
--2 微服务集成Seata(其他服务 比如 订单服务 商品服务 集成seata)
--XA模式(两阶段思想)
--XA描述了全局TM与局部的RM之间的接口,几乎所有的主流的关系型数据库都支持XA规范
--优点:强一致性 满足ACID 没有入侵 基本数据库都支持 简单方便 无业务代码入侵
--缺点:阻塞 等待 性能低
--AT模式(seata主推 )
--子事务执行sql且提交,不过提交前记录下更改前的数据快照,未来如果全局事务判断成功则删除快照 如果失败则恢复快照
--AT模式是最终一致 XA是强一致
--AT模式不锁定资源
-- 两阶段提交与三阶段提交
--总: 两阶段提交与三阶段提交都是一种解决分布式事务的思想
--分:
--两阶段提交: 准备阶段 提交阶段
--事务协调者会询问各子事务是否可以提交,如果各子事务回应可以提交,进入下一阶段,提交各子事务
--优点是:简单 方便 但是过于依赖事务协调者,且会阻塞,
--三阶段提交: 准备阶段 预提交阶段 提交阶段
--事务协调者先会询问是否已准备好,如果响应超时会终止掉此事务,如果得到响应说明服务正常,且回复已准备好 则进入下一阶段
--下一阶段会询问所有参与者是否可以进行预提交 ,如果可以则进入下一阶段 提交各子事务
--总: 两个方式比较 三阶段提交多了一个一次询问各子事务,超时处理的步骤,这样避免了因为二阶段的因为服务挂掉一直等待响应 处于阻塞状态的问题
-- 但是多了一次询问性能要差一些。
--JUC包的学习和使用
--什么是协程:
--可以在一个线程内部创建多个协程,这些协程之间共享一个线程资源, 不需要操作系统的介入,协程的创建和销毁的开销很小。能更有效的利用资源
--原子类 AtomicInteger Unsafe LongAddr LockSupport.park() AQS
--countDownLatch(减少计数器 -条件触发 教室里有6个人,值日生在6个人离开教室后关灯)
--semaphore(计数信号量 -案例: 6辆汽车停3个车位)
--CyclicBarrier(循环栅栏-场景 集齐七颗龙珠可以召唤神龙) Exchanger(两个线程之间的交换器)
--谈谈你对AQS的理解
-- AQS是一个线程同步器 JUC包里面的一个基础组件 很多都用到了AQS 比如 计数器 信号量
--线程间通信
--死锁
--eg: 有两个资源A,B 线程1 同步锁里面是先操作A后操作B,线程B里面是先操作B再操作A
--线程1(持有A的锁准备获取B的锁操作B) 线程2(持有B的锁准备获取A的锁操作A)
--A(锁已被持有) B(锁已被持有)
--B A
--此时发生死锁理由如下: 线程1持有A的锁需要获取B的锁操作完B后才能释放A锁,但是B的锁在线程2持有
--线程1需要获取B锁-->需要线程2释放B锁-->需要线程2操作完A才能释放B锁-->线程2操作A需要获得A锁-->A锁被线程1持有
-->需要线程1释放A锁-->需要线程1执行完对B得操作才能释放A锁-->需要线程1获取B锁
--阻塞队列
--特点
--当添加元素时如果队列已满则会阻塞 等队列移除一个元素时才会把未入队的入队
--当获取元素时如果队列已空则会阻塞 等队列有值时再获取
--常见的阻塞的队列
--ArrayBlockingQueue
--LinkedBlockingQueue
--核心方法
--方法类型 抛出异常 特殊值(true或false或其他) 阻塞式的 超时
--插入 add(e) offer(e) put(e) offer(e,time,unit)
--移除 remove() poll() take() poll(time,unit)
--检查 element() peek() 不可用 不可用
--java中的线程池通过ExeCutor框架实现
--七个核心参数
--四种拒绝策略
--1 abortPolicy 抛出异常(默认)
--2 callerRunsPolicy 返回给调用者运行
--3 DiscardOldestPolicy 抛弃队列中等待最久的任务 然后把当前任务加入到队列中
--4 DiscardPolicy 不予任何处理
--合并/拆分框架
--ForkJoinTask
--RecursiveTask
--JVM调优(知识树)
--程序最难调试的bug
--野指针: 同一个对象,两个指针,一个释放了,另外一个不知道还拿来用
-- 同一个指针,不同位置
-- 不再指向任何对象的指针
--并发问题
--内存释放的相关问题
--1 忘记释放 对象已经不使用了,没有指针指向他,但这部分空间没被释放,空间仍然被占用
--2 释放多次 释放后 其他对象使用了这个空间 多次释放导致别人的对象莫名奇妙的被删除
--对象树
--GC操作(GC的演化是随着内存大小的不断增长而变化的)
--1 寻找垃圾认定为垃圾
--引用计数法 --> 有变量引用他则不视为垃圾。 有漏洞因为对象之间彼此循环引用,但是没有任何一个变量指向这个循环体中的任何一个对象
--根可达算法
--怎么算根: 线程栈变量 静态变量 常量池 JNI指针 本地方法栈,
--2 清除垃圾算法
--1 标记清除--->标记为垃圾,标记为垃圾的可以进行清除内存空间回收,存在的问题:内存碎片化
--2 拷贝------->使用每次只使用一半空间,在进行回收时只将非垃圾的复制到未被使用的空间, 再回收以前使用的整个空间
--3 标记整理(压缩)--->在进行回收的时候 就将非垃圾在内存里面整理好,不会有内存碎片化的问题
--新生代 老年代
--新生代(使用copy算法)YGC
--eden区 survivor survivor (8 :1 :1)
--新new出来的对象
--老年代(使用标记清除与标记整理算法的组合) FullGC
--经历过多次垃圾回收,当超过一定阈值的时候将其移动到老年代
--内存回收算法根据历史
--1 serial 单线程STW垃圾回收 年轻代 老年代
--2 parallel
--3 Concurrent GC
--concurrent mark sweep (CMS)
--四个阶段
--1 初始标记 STW(STOP THE WORLD) 的方式来标记根(只找根)
--2 并发标记 此时不会STW的,业务线程是可以继续运行的
--3 重新标记 此时会STW
--4 并发清理
--三色标记算法
--黑色 自己已经标记 fields都标记完成
--灰色 自己标记完成 没来得及标记fields
--白色 没有遍历到的节点
--java架构师
--技术选型:
--是否兼容多种类型的数据库
--ORM框架还是mybatis?
--是否前后端分离?
--根据团队成员的组成情况
--以快捷为主 还是以未来扩展为主?
--工作流模块?
--某项技术的上手难度?
--
--是单机版还是微服务版?
--微服务可插拔 ? jdk版本? 数据库设计?
--并发量怎么样?数据量大小怎么样?
--相关规范
--数据库的基本字段
--创建时间 创建人 修改时间 修改人 是否删除 版本
--负载均衡策略
--哪些地方加缓存
--逻辑闭环 风险评估 项目推进 提出自己的意见虽然可能不被采纳
--日志记录
--权限控制
--redis的几种数据结构(都以value值为字符串为例)
--1 字符串 --------------> value 值就是个普通的字符串,只有一个 不一定非得是字符串 二进制的字节序列
--2 hash --------------> value 值类似一个json对象
--3 list --------------> value 值是按照插入顺序排序的且有多个字符串可重复
--4 set --------------> value 值不可重复有多个
--5 sorted set --------------> value 值有序不可重复
--i17项目总结
--现在电脑的基本特征
--多核 决定了可以同时执行多个线程,同一时间每个内核都可以执行一个线程 所以存在线程并发问题
--大内存
--线程并发保证线程安全的条件
--原子性
--可见性
--有序性 程序的执行按照代码的先后顺序执行( 因为jvm存在指令重排来实现对代码的优化,单线程下指令重排不会有结果跟预期最终不一致的情况,多线程下会有
--可以通过 synchronized volatile 以及锁来保证有序性)
--偏向锁 轻量级锁 重量级锁
--偏向锁(不存在竞争) : 开启偏向锁时,某个线程先获得锁,然后释放锁,当该线程下次再想要获得锁时 不需要重新申请获得锁(即忽略synchronized关键字)就可以执行同步代码块,
--轻量级锁(自旋锁)(轻微竞争): 当加锁时,发现锁被其他线程持有会通过自旋(会浪费cpu资源)方式尝试获取锁。(因为是多核电脑 所以完全可能在自己尝试获取锁的过程中,持有锁的一方会释放锁,
--而不是采用一旦没获取到锁就进入阻塞状态,频繁的切换上下文 造成性能低下, 但是如果一直自旋尝试获取锁也会造成cpu资源的浪费 所以在尝试一定次数后如果还是不能获取到锁最好就阻塞升级到重量级锁的方式 把cpu资源让渡出去 )
--重量级锁() 性能较差 会有内核态与用户态的切换 线程阻塞造成的上下文切换(线程阻塞 上下文切换 操作系统线程调度)
--java中的锁
--乐观锁 CAS
--悲观锁 synchronized
--自旋锁 CAS
--可重入锁 synchronized ReentrantLock Lock
--读写锁 ReentrantReadWriteLock CopyOnWriteArrayList
--公平锁 ReentrantLock(true)
--非公平锁 synchronized ReentrantLock(false)
--共享锁 ReentrantReadWriteLock中的读锁
--独占锁 synchronized ReentrantReadWriteLock中的写锁
--重量级锁 synchronized
--轻量级锁 锁优化技术
--偏向锁 锁优化技术
--分段锁 concurrentHashMap
--互斥锁 synchronized
--同步锁 synchronized
--死锁
--锁粗化 锁优化技术
--锁消除 锁优化技术
--重要的类: LongAddr AtomicInteger
--KafKa学习
--kafka定义:
--传统定义:
--kafka是一个分布式的基于发布/订阅模式的消息队列
--最新定义:
--开源的分布式事件流平台, 用于高性能数据管道 流分析 数据集成 和关键任务应用
--两种模式:
--点对点
--一个生产者 一个消费者 一个topic 会删除数据 很少公司这样用
--发布订阅
--多个生产者 多个消费者 相互独立 多个topic 不会删除数据
--kafka特性:
-- 一个topic可以有多个分区partition
-- 配合分区的设计 提出消费者组的概念 组内每个消费者并行消费
-- 每个分区有若干副本
-- zookeeper中记录谁是leader 2.8版本后也可以不采用zk;
--kafka的安装(集群部署 特征1 配置每个节点的唯一标识 2 把节点关联起来)
--去kafka.apache.org下载kafka
--准备三台linux机器(此例以三台机器作为实例的集群)
--解压到 /opt/mysoft/kafka
--进入config目录配置server.properties
--重要参数配置
--batch.size 只有数据积累到batch.size时sender才会发送消息 默认16k
--linger.ms 如果数据迟迟未到batch.size,sender等待linger.ms 的时间也会发送消息 单位ms 默认值为0ms
--应答acks: 0 1 -1(all)
--retries : 重试 默认integer的最大值
--异步发送
--不带回调的异步发送
--带回调函数的异步发送
--生产者分区策略
--生产者默认的分区策略
--自定义分区器 方式: 实现partitioner接口 重写对应方法 在properties将自定义分区器与生产者关联起来
--生产者如何提高吞吐量
--修改batch.size 一般改32k
--修改linger.ms 一般改为5-100ms
--compression.type 压缩snappy
--recordAccumulator : 缓冲区大小 修改为64m 当分区过多时 改大了会导致数据延迟高、
--生产发送数据的可靠性
--ack设置为-1 存在的问题 某一个follower挂掉 导致一直没有收到leader的ack响应
--解决方式: leader维护了一个动态的in-sync replica set(ISR) 意为和 Leader保持同步的Follower+leader集合(leader: 0, ISR: 0,1,2)
-- 如果follower长时间未向leader 发送通信请求或同步数据则该follwer被踢出ISR 时间阈值参数名称为: replica.lag.time.max.ms 默认为30s
-- 如果分区副本设置为1,或者ISR应答的最小副本数量(min.insync.replicas 默认为1) 设置为1 和ack=1的效果一样
-- 数据完全可靠条件 =ACK级别设置为-1 + 分区副本大于等于2 + ISR里应答的最小副本数量大于等于2
--生产者数据重复的问题
--ack=0即最多一次
--至少一次 可以保证数据不丢失 但是不能保证数据不重复
--至多一次 可以保证数据不重复 但是不能保证数据不丢失
--精确一次 对于一些重要数据 比如和钱相关的 要求数据既不能重复也不能丢失
--精确一次
--幂等性: 生产者不论向broker发送多少次重复数据 broker端都只会持久化一条 保证了不重复( 只能保证在单分区 单会话内不重复)
--重复的判断标准: <PID(kafka每次重启都会分配的一个新的值 生产者id号 ),Partition(分区号),seqNumber(单调自增的序列号)> 这三个维度来判定唯一
--开启幂等性配置 enable.idempotence=true(默认为true false为关闭)
--kafka生产者事务
--生产者数据有序
--单分区内 有序 (条件: 未开启幂等性的情况下: max.in.flight.requests.per.connection =1 开启幂等性的情况下: max.in.flight.requests.per.connection =5 (<=5皆可))
--多分区 分区与分区间无序
--生产者的数据乱序
--kafka与zookeeper
--节点的服役与退役
--
--SSH保证安全的方式
--客户端发送请求,服务端获取到请求将公钥返回
--客户端拿到公钥,对原文进行加密,发送给服务器,服务器用私钥解密信息
--服务端根据客户端请求,将想发送给客户端的信息用私钥加密,发送给客户端
--客户端收到信息后用公钥解密。 保证安全通信
--Scala学习
--Scala是基于java的支持函数式编程的面向对象的语言,一大重要特性就是函数自身可以作为一个变量,作为另一个函数的传参,同时也可以作为对象
--同时scala也是面向对象的,一切皆对象。 所以要了解在某一个具体的函数式编程中函数与对象的关系。
--函数与对象的关系: 函数是对象的成员属性, 当某一个类所有的函数都是非抽象方法时,此时就可以实例化一个对象,然后调用这个对象的方法
--scala的函数式编程是 首先我定义某个函数A需要的传参类型为Function类 ,对该Funcation类的相关标准已做好定义
--eg: 成员变量 成员属性但根据现有信息Funcation类一般 1 只有一个抽象方法导致其不可实例化(道理很简单 如果此时的Funcation类可以实例化,则直接实例化这个Funcation类然后对想改的方法进行重写即可,没必要将函数作为参数,同时应当
-- 只有一个抽象方法就可以实例化这个类 否则我们在传递函数补充信息的时候不知道 是补充的这个Funcation类中的哪个函数的信息),
--此时如果我将某个函数B作为参数传递进函数A时,有了这个函数B的信息,那么此时这个Function类
-- Funcation类一定是可以实例化的了,此时传递的这个函数B相当于补充信息且将Funcation类实例化作为对象变量给了函数A,这样整个函数A就像使用
-- 普通函数一样
--整个最重要的过程是 一定要知道函数A需要的传参函数B的规范是什么 ,只有满足规范的函数B才能作为参数传递进来
--docker学习
--docker环境迁移到离线环境
--Hadoop学习
--Hadoop的MapReduce
--假设文本为: --hello zhangsan hello lisi hello wangwu
--hello zhangsan hello lisi
--hello zhangsan
--重点:map阶段有输入输出 然后经过 shuffle到达shuffle阶段 reduce阶段也有输入输出 (要注意map阶段的输入输出的key-value类型,也要注意reduce阶段的输入输出的key-value类型)
--map阶段的输入输出
--输入: 行号为key, 行值为value 。 【0,hello zhangsan hello lisi hello wangwu】
--输出: 自己定义新的key,新的value(只要前后逻辑保持一致) 在wordCount中为【hello,1】【zhangsan,1】【hello,1】....
--shuffle: 会将同key的value值合并在一起, 变为【key,value-list】
-- 即为: 【hello,{1,1,1,1,1,1}】 【zhangsan,{1,1,1}】 【lisi,{1,1}】.....
--reduce阶段的输入输出
--map阶段执行完shuffle即为reduce阶段的输入,此时一个key对应的所有取值都已经在valu-list中的,不可能有第二个相同的key了
--输出: 根据自己的业务需要处理为自己想要的key,想要的value 作为输出。
--hive学习
--掌握数据仓库概念,起源
--数仓
--概念:用于存储,分析,报告的数据系统(面向分析)
--特征
-- 不生产数据,不消费数据,供外部系统使用
--主要特性: 面向主题(需要从多个系统中 抽取出共性 以主题方式来构建) 集成性(数据需要从多个系统中来) 非易变性(不产生数据,只是分析数据) 时变性(随着时间变化更新数据)
--为什么不用OLTP数据库
--1 读取压力倍增
--2 数据分散在不同系统中,字段属性不一致
--3 只有数天或数周的数据
--4 使用原系统分析时可能会影响原系统的正常运行
--OLAP 面向分析 支持分析
--数仓主要特征,分层架构
--为什么分层
--清晰数据结构
--方便数据血缘追踪
--减少重复开发(中间层 创建通用的数据结构 各个模块都可使用)
--复杂问题 简单化 (每层只做一部分操作 )
--屏蔽原始业务的异常数据
--Hive架构,组件,数据模型
--Hive的原理
--1 将数据文件映射为一张表
--2 需要元数据,记录hadoop里的文件与转换后的表的映射关系(文件地址与表名 表的字段与文件的字段 文件字段的分隔符 )
--3 Hive承担的职责,将sql语句转为MapReduce
--Hive架构
--Hive数据按照粒度分有:
--表 分区 分桶(对指定字段的值 经过hash计算,将数据文件划分成若干个小文件)
--分桶的好处: 提高join查询效率 方便抽样查询
--hive.metastore.warehouse.dir
--Hive中表的数据存储在HDFS上面,但是元数据存储在RDBMS中即关系型数据库中
--hive的元数据 元数据服务 metastore
--元数据服务配置的三种方式: 1 内嵌模式 2 本地模式 3 远程模式
--Hive元数据
--Hive安装部署
--内嵌模式
--本地模式
--远程模式
--客户端
--使用第一代客户端 hive -->需要启动metastore服务
--使用第二代客户端 beeline -->需要先启动metastore服务 再启动HiveServer2服务
--将结构化文件与HIVE中的表映射起来
--创建文件(文件内容为结构化的数据 id name age)
--vim user.txt
--创建表 并指定以逗号为字段分隔符
--create table t_user1(id int ,name varchar(255), age int) row format delimited fields terminated by ',';
--将文件上传到对应库对应表的目录下( 元数据的默认存储目录为 /usr/hive/warehouse)
--hadoop fs -put user.txt /
--hadoop fs -mv /user.txt /usr/hive/warehouse/itheima.db/t_user1
--再itheima库查看t_user1表
--select * from t_user1;
--HIVE的数据定义DDL语言(HIVE SQL 即 HQL)
--desc tablename 很重要
--HIVE的数据类型
--原生数据类型
--数字 字符串 时间日期 杂项(eg: 布尔二进制)
--复杂数据类型(复杂数据类型一般要配合分隔符指定语法使用)
--数组 map类型 struct结构类型(类似map) union联合体
--HIVE的序列号与反序列化
--SerDe
desc formatted t_user1; --可以查看表的相关SerDe信息
--Hive默认分隔符( '\001' ---是ascii编码的值 )
--DDL建表基础语法
--DDL建表高阶语法
create table t_hot_hero(
id int,
name STRING,
win_rate int,
skin_price map<String,int> --注意这个map为复杂数据类型
) row format delimited --以lazysimple序列化的方式
fields terminated by ',' --指定字段分隔符
collection items terminated by '-' --指定集合元素之间的分隔符
map keys terminated by ':' ; --指定map元素key-value之间的分隔符
--DDL其他语法
--show 语法
--Hive的内外部表 分区表 分桶表
--内部表 hive无论对数据还是元数据完全管理其生命周期
--外部表 hive只管理元数据的生命周期
--分区表 (分区字段是虚拟字段,其数据并不存储再底层文件中)
--分区表数据加载
--静态分区方式 (手动指定非根据实际值动态变化)-->将指定文件作为指定表的指定分区
load data[local] inpath 'filepath' into table tablename partition(分区字段='分区值'...);
--动态分区
--多分区表(分区字段之间是一种递进关系,而非并列关系,所以要注意顺序的合理性。 递进关系的表现形式即为第一个分区字段作为父文件夹,第二个分区字段作为
--子文件夹。 而非 第一个分区字段与第二个分区字段为同级文件夹)
--动态分区是指分区的字段值基于查询结果(参数位置) 自动推断出来的 核心语法是 insert + select
--启动hive动态分区需要在hive中设置两个参数
--set hive.exec.dynamic.partition=true;
--指定动态分区模式。 strict(严格模式-要求至少有一个分区为静态分区) nonstrict(非严格模式)
--set hive.exec.dynamic.partition.mode=nonstrict;
--动态分区示例:
insert into table t_all_hero_part_dynamic partition(role) select tmp.*,tmp.role_main from t_all_hero tmp; --注意不一定要求查询对应分区字段的名称与分区表的分区字段的名称相同 依据位置推断
--分桶表(问题1 按照哪个字段分桶 问题2 分成几个桶 问题3 桶内按照什么字段排序)
--作用
--将数据文件在底层按照分桶数量 分成若干独立的更小的文件
--开启分桶(Hive2.0 开始默认开启分桶)
set hive.enforce.bucketing=true
--数据加载 通过 insert select
--好处
--1 查询时避免全表扫描
--2 join时可以提高MR效率,减少笛卡尔积(join的字段是分桶字段)
--3 大数据量情况下 分桶表数据抽样更高效 合理
--Hive事务表 视图 物化视图
--Hive中的事务(Hive0.14开始支持事务)
--应用场景: 数据流式传输 尺寸变化缓慢 数据修改
--特点:
--尚不支持begin commit 和rollback 所有语言操作都是自动提交
--仅支持ORC文件格式(stored as orc)
--需要修改配置参数才能开启,且必须是分桶表才可以使用
--相关配置
-- 开启事务配置(可以使用set设置当前session生效 也可以配置hive-site.xml)
set hive.support.concurrency=trye; --支持并发
set hive.enforce.bucketing=true; --开启分桶 (hive2.0后默认开启)
set hive.exec.dynamic.partition.mode=nonstrict;
set hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
set hive.compactor.initiator.on=true; --是否在metastore实例上运行启动压缩合并
set hive.compactor.worker.threads=1; --在此metastore实例上运行多少个合并程序工作线程
--表参数 transactional 必须为true
--外部表不能成为ACID表 不允许从非ACID会话读取/写入ACID表
--事务表不支持load data 语句
--Hive中的视图
--查看视图定义 也可查看表定义
show create table viewName;
--物化视图(Hive3.0开始)
--特征:
--物化视图支持将数据存储在外部系统( 如 druid)
--关注关键字 disable rewrite 禁止重写(当使用查询时 如果当前查询能够命中物化视图则会使用已有物化视图 但是可能会有弊端 因为物化视图可能不是最新的)
--物化视图的查询自动重写机制
--Hive3.0丢弃了index索引的语法支持,推荐使用物化视图和列式存储文件格式来加快查询速度
--物化视图的相关操作
--主动触发物化视图重构
alter materialized view materialized_view_name rebuild;
--禁用指定物化视图自动重写查询
alter materialized view materialized_view_name enable|disable rewrite;
--database/schema的DDL操作
--更改数据库属性
alter (database|schema) database_name set dbproperties(property_name=property_value);
--更改数据库所有者
alter (database|schema) database_name set owner user username;
--更改数据库位置
alter (database|schema) database_name set location hdfs_path;
--强行删除数据库
drop database itheima cascade;
--Hive 的分区DDL操作
--增加分区( add partition 会更改元数据 但不会加载数据,如果分区位置不存在数据,查询不会返回结果。因此需要保证增加分区位置路径下,数据已存在,或者增加完分区后导入分区数据)
--一次添加一个分区
alter table table_name add partition (dt='20170101') location '/usr/hadoopp/warehouse/table_name/dt=20170101';
--一次添加多个分区
alter table table_name add partition (dt='20170101',country='us') location '/path/to/us/part170101'
partition (dt='20170102',country='us') location '/path/to/us/part170102';
--删除分区(会删除分区数据和元数据 末尾添加 purge 会直接删除数据 不进入垃圾桶 慎用)
alter table table_name drop if exists partition (dt='20170101',country='us') ;
--重命名分区
alter table table_name partition (dt='20170101') rename to partition (dt='2017-01-01')
--修复分区(MSCK metastore check)-->元数据检查操作 可用于元数据的修复
MSCK [REPAIR] TABLE table_name [ADD|DROP|SYNC partitions];
--修改分区
--修改分区文件存储格式
alter table table_name partition (dt='20170101') set FILEFORMAT file_format;
alter table table_name partition (dt='20170101') set location "new location";
--Hive的show用法
--显示所有数据库 show databases;
--
--Hive的数据操作语言 增删改查等
--总体
--HIVE加载数据 插入数据 修改数据 删除数据 查询语句 插入以动态分区方式 hive的join
--Hive加载数据(1 加载哪个文件 2 加载到哪个表 3 使用哪个serde 4 是否以覆盖方式) 加载分区表时要特别注意
load data [local] inpath 'filepath' [OVERWRITE] into table tablename [partition(partcol1=val1)]
--hive3.0后开始支持
load data [local] inpath 'filepath' [OVERWRITE] into table tablename [partition(partcol1=val1)] [INPUTFORMAT 'inputformat' SERDE 'serde']
--HIVE的insert语句
insert overwrite table tablename [partition(partcol1=val1)] [IF NOT EXISTS] select * from from_table;
--HIVE的多重插入: 一次扫描,多次插入。在一次查询中完成多次insert 操作
--多重插入
from tablename
insert overwrite table student_insert1
select fielda
insert overwrite table student_insert2
select fieldb;
--HIVE导出数据(重点 重点: 导出操作是一个OVERWRITE覆盖操作 会覆盖参数指定的目录 要慎重)
--HIVE事务表
--delta文件夹。 正在执行的事务是一个以staging 开头的文件夹,执行结束就是一个delta文件夹(会标识事务id 根据事务id可以看到操作的历史信息)
--合并器: 随着表的操作,产生了越来越多的delta增量文件,此时合并器就会将这些delta增量文件合并,保持性能
--小合并(minor compactor 将一组delta增量文件重写为单个增量文件 默认触发条件为10个delta文件)
--大合并(major compactor 将一个或多个delta增量文件和基础文件重写为新的基础文件 默认触发条件为delta文件占比达到基础文件10%时)
--Hive的select查询
--查询匹配正则表达式的所有字段
set hive.support.quoted.identifiers =none; --设置反引号不再解释为其他含义,被解释为正则表达式
select `^c.*` from table_name;
--Hive的order by , cluster by ,distribute by ,sort by
--order by
order by 全局排序,只有一个reducer,结果输出在一个文件中。
--cluster by
set mapreduce.job.reduces =3;
cluster by 既有分组,又有排序,只能正序且分组字段与排序字段必须为同一字段。分组规则为hash散列(跟分桶表规则一样, hash_func(col_name)%reduceTask个数 分为几组取决于reduceTask个数)
--distribute by + sort by
distribute by colName 根据指定字段作为分组字段 算法为hash散列
sort by colName 分组后,组内再进行排序
--子查询
--不相关子查询
--相关子查询: 子查询应用父查询中的列
--Hive的join连接
--内连接 inner join (取交集) 不写时默认为内连接
--左连接 left join (也为 left outer join )
--右连接
--全外连接 full outer join 等价 full join (取并集 等价于对两个数据集分别进行左连接 与右连接 然后合在一起再消除重复行)
--左半开连接 left semi join (会返回左边表的记录 前提:满足与右边表的on的判定条件 效果类似:进行inner join后只返回左表有的字段)
eg: 左: 1,2,3,4,6 右 1,2,4,6 ,7 ( 返回 1,2,4,6 的记录 但字段只返回左表的字段)
--cross join 交叉连接
--Hive中的函数
--Hive执行时的某些情况(执行某个sql语句 或者执行sql文件 )
${HIVE_HOME}/bin/hive -f sqlFileName
${HIVE_HOME}/bin/hive -e sqlStatement
--将执行的sql语句结果转储到文件中
${HIVE_HOME}/bin/hive -S -e 'select * from tblName' > a.txt
--Hive中的属性配置
--明确 1 是临时生效还是永久? 2 这个属性配置的功能是?
set配置方式 只是在当前会话生效
hive-site.xml + 在启动客户端时的 --hive.conf
只针对特定服务的配置: hivemetastore-site.xml hiveserver2-site.xml
hive的配置会覆盖hadoop的配置
--相关看系统函数的方式
--显示所有的函数和运算符 show functions;
--查看运算符或函数的使用说明 decribe function count; decribe function +;
--使用extended 查看更详细的使用说明 decribe function extended count;
--老版本hive创建伪表dual
create table dual(id string); load data local inpath 'filepath' into table dual --文件里面只有一个空格
select 1+1 from dual; --使用伪表
--关系运算符
hive中的like里面 _表示任意单个字符 %表示任意数量字符
rlike/regexp 是正则匹配 与 regexp_like()是同一含义
--算术运算符
div--->取整
&--->位与 进行计算的两个数字转为2进制后 都为1的部分才为1
|--->位或 进行计算的两个数字转为2进制后 有一个为1的部分就为1
^--->位异或 进行计算的两个数字转为2进制后 对应位的2进制值的值不同就为1
~--->位取反
--Hive中创建map struct named_struct array的方式与取值
with a as (
select map('name','zhangsan','age',15) as myMap
,struct('张三',15,175) as mystru
,named_struct('id',1,'name','zhangsan','age',16) as mynamed
,array('zhang',15,175) as myarray
)
select myMap['name'],mystru.col1,mynamed.name,myarray[0] from a;
--创建union type类型 select create_union(); ---->目前在本地的hive版本上执行这个报错 建议 先查询一下 union type类型是什么?
--相关函数
--内置函数(数值类函数 日期时间函数 字符串函数 集合函数 条件函数 类型转换函数 数学函数)
--字符串函数
concat_ws('.','www',array('itcast','cn')) -->带分隔符的字符串连接函数
select regexp_replace('100-200','(\\d+)','num')
--正则表达式解析函数 提取正则匹配到的指定组的内容
select regexp_extract('100-200','(\\d+)-(\\d+)',2)
select parse_url('http://www.baidu.com/p1.php?query=1','HOST');
select split('zhangsan,李四',',')
--json解析函数 参数: json格式的字符串, 需要解析的指定内容
select get_json_object(json_txt,path);
describe function extended get_json_object;
select get_json_object('{"id":"张三","age":16}','$.id');
select get_json_object('[{"id":"张三","age":16},{"id":"lisi","age":18}]','$[1].age');
--时间函数
select unix_timestamp();
select from_unixtime(12345);
select datediff();select date_add(); select date_sub(); select year('2023-09-01 17:16:15');select day('2023-09-01 17:16:15');
select weekofyear();
--数学函数
--取整且指定精度(该函数也可不指定精度)
select round(3.1415926,4);
--向下取整 向上取整
select floor(3.1415); select ceil(3.1415);
--得到随机数 select rand();
--得到稳定的随机数序列 下次调用该函数时若参数仍为3则得到的值不变
select rand(3);
select size(array(11,22,33));
select size(map("id",100,'name','zhangsan'));
--取map的所有的key值
select map_keys(map("id",100,'name','zhangsan'));
--取map的所有的value值
select map_values(map("id",100,'name','zhangsan'));
--判断数组是否包含指定元素
select array_contains(array(11,22,33),11)
--数组排序
select sort_array(array(12,2,34))
--条件函数
--如果条件为真返回第一个 为假返回第二个 特别注意如果判断里面有null值则会返回第二个
select if(1=2,100,200);select if(null=null,100,200);
-- isnull(),isnotnull(),nvl()
--非空查找函数,返回参数中第一个非空值如果都为null 返回null
select coalesce(null,null,3,5,null);
-- case when then else end;
--如果两个值相同则返回null,不同则返回第一个
select nullif(11,11);
--断言函数 如果条件为真返回null,条件不为真报错
select assert_true(11>10);
--类型转换
--任意数据类型转换用cast,如果转换不成功返回null
select cast('13' as int);
select cast('zhangsan' as int);
--数据脱敏函数
--将查询的记过 大写字母转大X 小写转x 数字转n(默认 同时可自定义)
select mask('abc123DEF');
select mask('abc123DEF','-','.','^');
--对前几位或后几位脱敏
select mask_first_n('dsdfsd',4);
select mask_last_n('dsdfsd',4);
--除了前几位或后几位其余进行脱敏处理
select mask_show_first_n('dsdfsd',4);
select mask_show_last_n('dsdfsd',4);
--
select mask_hash("dsdfds"); select crc32("crc32位加密");select md5(""); select sha1("sha1加密");select sha2("sha2加密",512);
--杂项函数
--超级无敌大招
--hive调用java的方法,如果你想调用的方法hive字段的jar包没有,则可以使用add jar的方式添加进来
select java_method('java.lang.Math','max',11,12);-->java类 类中的方法 方法的参数
--反射函数
select reflect('java.lang.Math','max',11,12);-->java类 类中的方法 方法的参数
select hash("dsd");
----------------
select collect_set(sex) from student;-->收集学生性别不可重 转为集合
select collect_list(sex) from student;
--udf(一进一出) udaf(多进一出) udtf(一进多出) (用户自定义的函数)
--udtf:表生成函数 返回的结果类似于表(只有一列的表)
select explode( array(1,2,3))--->只接收map array类型的参数;
--udtf与Lateral VIEW(侧视图)
--lateral view 侧视图是一种特殊语法,主要搭配UDTF函数一起使用,一般只要用udtf就要使用侧视图。效果有点类似join
select a.team_name,b.year from tableA a lateral view explode(champion_year) b as year order by b.year desc;
--UDF实现方式
1 写一个java类,继承UDF,重载evaluate方法,实现函数业务逻辑。
2 重载则在一个java类中可以实现多个函数
3 打成jar包 上传hserver2本地或HDFS
4 客户端添加命令行将jar包上传到hive的classpath: hive>add jar filepath;
5 注册成临时函数(给UDF命名) : create temporary function 函数名 as 'UDF类全路径';
6 HQL中使用函数
即: 继承 重载 实现 上传jar 注册为临时函数 使用
eg: 示例创建一个UDF函数
即.......
--struct函数的一个应用:
找出student表中男女学生中年龄最大的及其名字(使用struct构造数据 然后找出最多的后取值)
--->特别注意 必须把年龄放在struct的第一个 否则有问题,最大值是找第一个字段的最大值
select sex, max(struct(age,name)).col1 as age, max(struct(age,name)).col2 as name from student group by sex;
--增强聚合
grouping_sets;
cube;--->表示group by的维度的所有可能组合 如果有3个维度则所有可能的组合为 3选0+3选1+3选2+3选3 = 2的3次方 =8 即2的N次方
rollup;--->rollup是cube的子集,以最左侧的维度为主 eg a,b,c三个维度 则rollup的所有维度为 a,b,c|a,b|a|空;
--开窗函数
--如果函数有over子句,则它是开窗函数
--语法规则:
function(arg1,...argn) over([ partition by ...] [order by ...]) [window_expression]
--控制窗口范围大小的方式示例:按照指定列分组然后排序 级联求和当前行前1行到当前行后两行的值
sum(colName1) over(partition by groupColName order by orderColName rows between 1 preceding and 2 following) as otherName
function可以是如下函数
--聚合函数 sum max avg
--排序函数 rank row_number ntile
--分析函数 lead lag(colName,n,defaultValue)-->取当前窗口当前行colName列往上的第n行值,如果取不到则为默认值 first_value
window_expression--->用于指定每个窗口中操作的数据范围 默认为窗口中的所有行, 一般一个分组为一个窗口
特别要强调的 使用sum即聚合函数时的窗口函数时 (avg max 类似 在有order by 时有连续累计求和 连续累计求最大值 连续累计求平均值)
sum(年龄) over(partition by 班级,order by 生日) -->该操作的含义是对班级分组 以生日排序 然后对年龄进行连续累积求和
sum(年龄) over(order by 生日)--->该操作的含义是对所有以生日排序 所有的对年龄连续累计求和
连续累计求和的含义:
1 ---> 1
8 ---> 9
20 ---> 29
50 ---> 79
--ntile的窗口函数: 可以将每个窗口的数据再分成若干部分,为每个部分编号, 且每个部分里面的数据条数最多相差1
--
--抽样函数
--真正的随机抽样
select * from student order by rand() limit 2 ;-->随机抽样2个
--基于数据块block的抽样 仅在这个数据块上随机-->可以指定随机抽取多少行 百分比多少 多大大小的数据
select * from student tablesample(10 rows);
select * from student tablesample(50 percent);
select * from student tablesample(20k);
--针对分桶表的抽样 优点:既随机速度又快
tablesample( bucket X out of Y [on colName])-->x,y为数字
y-->必须是分桶数的倍数或者因子
x-->表示从哪个桶开始抽取,x的值必须小于等于y的值
on colName-->基于什么抽取 on rand()表示随机抽取; on 分桶字段 表示基于分桶字段抽取 效率更高
--函数使用实例
--1 多字节分隔符的解决方案
--方案1 提前将多字节分隔符变为单字节(使用MR程序)
--方案2 使用RegexSerDe 正则加载
--多种SerDe用于解析和加载不同类型的数据文件,常用的有ORCSerDe,RegexSerDe,JsonSerDe等
--eg:当使用正则序列化时:
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
"input.regex"="([^]*) ([^]*) ([^]*) (-|\\[^\\]*\\]))"
)
STORED AS TEXTFILE;
--方案3 自定义inputFormat
--2 对url的解析 两个函数
describe function extended parse_url;
describe function extended parse_url_tuple;--->一进多出函数
如果udtf函数即一行入多行出的函数不产生数据时,这时侧视图与原表关联的结果将为空
如果加上outer关键字 则会保留原表数据 eg: from tableA lateral view outer explode(array()) et as colNAME;
--行列互转
--多行转多列 case when then else end -->将表中的单元格转为列的值,要求在每个分组内,非分组字段都有类似a,b,c的值
--多行转单列 eg: 分组后将非分组指定字段的值合成一个 需要用到 concat_ws 以及collect(收集 收集后变成数组了) --->concat(a,b)任意一个参数为空结果就为空 concat_ws可以指定分隔符 同时任意一个不为空,结果就不为空
collect_list()-->一列的多行合成一行 有序且不去重 collect_set-->一列的多行合成一行 无序且去重
--多列转多行 union -->结果去重且排序 union all-->结果不去重且不排序
--单列转多行 explode-->爆炸函数结合侧视图 lateral view
--JSON数据处理
--1 JSON函数处理
--get_json_object函数
get_json_object(json_txt,path)-->可以从json字符串中返回指定的某个属性的值,获取某个属性的方式 $.colName, $表示整个json对象,
缺点: 一次只能返回一个属性的值
--json_tuple函数
json_tuple(json_txt,attr1,attr2,attr3...)-->返回的每一个属性都是字符串类型,一次可以返回多个属性的值,
缺点: 属于UDTF类型的函数 一般要搭配lateral view使用
eg: 单独使用 select json_tuple(jsonStr,"col1","col2") as (col1,col2) from mytableName;
搭配侧视图: select jsonStrColName,myDefineNewTablName.col1,myDefineNewTablName.col2 from myTablName
lateral view json_tuple(jsonStrColName,"col1","col2") myDefineNewTablName as col1,col2;
--2 JSON Serde 加载数据(建表时指定Serde 加载JSON文件到表中 会自动解析为对应的表格式)
重点部分: row format Serde 'org.apache.hive.hcatalog.data.JsonSerDe' stored as textfile; -->详见hive配置文件学习截图中的 解析json格式数据文件映射为表
--案例学习
--1 统计连续N次登录的用户 (N>=2)
--1 通过自连接实现
--2 通过窗口函数实现 lead(colName,N,defaultValue)-->colName 取哪一列 N 向后便宜N行 defaultValue 如果取不到返回的默认值
--2 级联累加求和
--1 通过自连接实现
--2 sum()函数与窗口函数结合
--3 分组TopN的问题
--拉链表
--问题产生背景: 已经同步过的数据发生变化时应该如何同步? (注意拉链表 同步频率的单位应该与同步时间最细粒度的单位相同 否则有一系列的问题)
--核心:
--拉链表有生效时间,失效时间 标识这条数据当前的状态/生命周期
--实现过程
--1 从源系统中根据一定规则(一般为更新时间) 标识出增量数据
--2 原拉链表数据与标识出的增量数据进行left join(原拉链表数据为主表)得到临时表A 如果关联不上或者主表的失效时间非9999-12-31 说明数据没发生变化或者说这条数据本来就是失效数据则保留原来的值 其他说明发生了变化则修改对应数据的失效时间为增量表的生效时间
--3 临时表A与标识出的增量数据表 进行UNION ALL 得到最终结果表
--Hive性能优化与3.0的新特性
--hive表设计优化
--大原则:
--分区表 分桶表 hive索引优化(hive3.0已经去除了) 了解explain extended
--分区表结构设计 :分区表会按照分区字段分成多个文件夹(将不同分区的数据单独使用一个HDFS目录来进行存储) 分区表提高效率的前提条件-->分区字段作为查询条件
--分桶表结构设计: 分桶表会分成多个文件 分桶表提高效率的前提条件-->关联时关联字段是分桶字段, 且分桶规则尽量满足(两边的桶相等 或者成倍数关系)
需要开启相应配置才会桶与桶进行join
set hive.optimize.bucketmapjoin=true; --开启分桶SMB(将数据分桶且桶内排序)join
set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin.sortedmerge=true;
--索引设计 (查看截图Hive索引相关)
--hive表数据优化
--大原则:
--hive数据文件格式 与数据压缩的优化 存储优化 (行式存储 列式存储 小文件的存储)
--文件存储格式( 关键语句 stored as file_format 默认为textFile)
--textFile
--默认存储格式 按行存储 导入时把数据拷贝一份放到hdfs不会做任何处理 适合小量数据的存储查询
--优点:数据格式简单 k可以直接查看 便于和其他工具共享数据 可以搭配压缩一起使用
--缺点: 耗费存储空间 IO性能低 结合压缩时Hive不进行数据切分合并 不能并行操作 查询效率低 按行存储 读取列的性能低
--sequenceFile
--Hadoop里用来存储序列化的键值对即二进制的一种文件格式,sequenceFile可以作为mapreduce作业的输入和输出 适合小量数据但是查询列比较多的场景
--优点:以二进制的KV形式存储数据,与底层交互更加友好,性能更快 可压缩可分割可并行操作数据,查询效率高 也可以用于存储多个小文件
--缺点:存储空间消耗最大,与非hadoop生态之外的工具不兼容 构建sequenceFile需要通过textFile文件转换加载
构建sequenceFile的方式: 1 先指明 stored as sequencefile;
2 然后通过普通txt文件将数据加载到Hive
3 使用 insert into table tableName1 select * from tableName2;
--ORC(没有其他特殊情况下 优先推荐使用)
--hadoop生态圈的列式存储格式 适用hive中大型的存储、查询
--优点:列式存储 存储效率非常高 可压缩 高效的列存储 查询效率较高 支持索引 支持矢量化查询
--缺点:加载时性能消耗较大 需要通过text文件转化生成读取全量数据时性能较差
--Parquet (适用字段数非常多 无更新 只取部分列的查询)
--支持嵌套结构的列式存储文件格式
--优点:更高效的压缩和编码 可压缩 可分割 优化磁盘利用率和IO 支持映射下推以及谓词下推功能 可用于多种数据处理框架
--缺点:不支持update insert delete ACID
构建parquet的方式与结构sequenFile的方式基本相同
--数据压缩
--数据压缩对于资源 最小化磁盘I/O 和网络传输非常有帮助
--数据压缩的优点
--减小文件存储空间 加快文件传输效率 降低IO次数
--数据压缩的缺点
--处理数据时需要先解压 加重CPU负荷
--Hive中的压缩就是使用了Hadoop中的压缩实现的,所以hadoop中支持的压缩在hive中都可以直接使用
--hadoop中支持的压缩算法
--deflate
--gzip
--bzip2
--lzo
--lz4
--snappy
--要想在hive中使用压缩 需要对mapreduce和hive进行相应的配置
查看在hive的map和reduce过程中开启压缩的配置的截图
创建表指定为orc格式并使用snappy压缩的示例
create table tb_sogou_orc_snappy stored as orc tblproperties("orc.compress"="snappy")
as select * from tb_sogou_source;
--存储优化
--1 如何避免生成小文件
--为何要避免小文件产生: 每个小文件都会产生一条元数据信息, mapReduce中的每个小文件都会启动一个MapTask计算任务
--如下配置可以自动判断是否为小文件 ,如果是小文件可以自动将小文件合并
set hive.merge.mapfiles=true; --如果hive的程序只有maptask,将maptask产生的所有小文件进行合并
set hive.merge.mapredfiles=true; --如果hive的程序 有map和reduceTask,将reduceTask产生的所有小文件进行合并
set hive.merge.size.per.task=256000000; --每一个合并的文件的大小(244M)
set hive.merge.smallfiles.avgsize=16000000; --平均每个文件的大小,如果小于这个值就会进行合并(15M)
--2 如果生成了小文件如何对小文件更高效处理
--如果输入的是小文件 Hive中提供了输入类 CombineHiveInputFormat 可以将小文件合并以后再进行处理
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;--设置hive中底层mapreduce读取数据的输入类 将所有文件合并为一个大文件作为输入
--3 ORC的索引优化
--行组索引(row group index)
--建立orc格式表时指定表参数 "orc.create.index"="true" 为了使行组索引利用效率更高,向表中加载数据时 必须对需要使用索引的字段进行排序
set hive.optimize.index.filter=true; --开启索引配置
create table tableName stored as orc tblproperties("orc.create.index"="true")
as select * from tableName_source distribute by fieldName sort by fieldName;
--当进行范围查询或者等值查询时就可以基于构建的索引进行查询
select * from tableName where fieldName>1000;
--布隆过滤器索引(Bloom Filter Index)
--建表时指定对应参数,来为对应字段建立布隆过滤索引。 在生成数据的时候,会在每个stripe中为该字段建立布隆过滤器的数据结构
create table tableName stored as orc tblproperties("orc.create.index"="true","orc.bloom.filter.columns"="fieldName")
as select * from tblName_source distribute by fieldName sort by fieldName;
--ORC的矢量化查询
--hive默认查询执行引擎一次处理一行,而矢量化查询是hive针对orc文件出的 一次每批1024行读取数据,使用矢量查询 必须以ORC格式存储数据
========开启矢量化查询=========
set hive.vectorized.execution.enabled=true;
set hive.vectorized.execution.reduce.enabled=true;
--job作业执行优化
--大原则:
--mapreduce的优化 join的优化(小表 大表)
--通过explain查看作业执行计划
explain [formatted|extended|dependency|authorization] query
其中extended比较常用: 提供一些额外信息,比如文件的路径信息
explain的具体如何解析 请查看截图 explain含义截图
--在文件数据量较小时开启本地模式 而不是走yarn (需要在hive安装机器上beeline客户端 idea客户端执行无效)
set hive.exec.mode.local.auto=true; --开启本地模式 会根据实际情况自动切换是否提交到yarn进行资源调度
--JVM重用(多个task共用一个jvm 3.0已经不支持了)
--并行执行(当多个stage之间没有依赖关系时 允许多个stage并行执行)
set hive.exec.parallel=true; -- 开启stage并行化
set hive.exec.parallel.thread.number=16; --指定并行化线程数 默认为8
--join优化
--map join 适合小表join小表 或者小表join大表 (原理是将小表缓存 然后大表分发到分布式系统 然后进行join 核心在于join的两张表只要有一张表在进行join时是全量的 就....)
set hive.auto.convert.join=true; --hive默认开启了map join
hive中小表的大小限制及认定 2.0版本 和 2.0版本以上参数不一样 set hive.auto.convert.join.noconditionaltask.size=512000000;-- hive2.0以后的参数控制 2.0以前自己去查阅
--reduce join 适合大表 join 大表
--bucket join 适合大表 join 大表
--将join关联字段作为分桶字段 同时作为排序字段 可以大大提高效率
set hive.optimize.bucketmapjoin=true; --开启分桶SMB(将数据分桶且桶内排序)join
set hive.auto.convert.sortmerge.join=true; --
set hive.optimize.bucketmapjoin.sortedmerge=true; --
set hive.auto.convert.sortmerge.join.noconditionaltask=true; --
要求: 分桶字段=join字段=排序字段 桶的个数相等或者成倍数
--关联优化(这里面的关联指 操作彼此之间有关联性)
--对有关联关系的操作进行解析时,尽量放在同一个MapReduce中实现
set hive.optimize.correlation=true; --开启关联优化配置
--优化器引擎选择(索引默认的优化器引擎是RBO)
--RBO(基于规则的优化器引擎)
--特点:
--1 索引的默认优化器引擎
--2 根据预先设定的规则对程序进行优化
--CBO(基于成本/代价的优化器引擎)
--特点
--1 根据不同场景需要付出的代价来选择优化的方案 (对数据的分布信息 数值出现的次数 最大值 最小值 来综合判断哪种处理的方案是最优方案)
--2 更智能
--3 需要获取数据的分布信息 所以需要结合analyze分析器
--配置底层的优化器引擎为CBO
set hive.cbo.enable=true;
set hive.compute.query.using.stats=true;
set hive.stats.fetch.column.stats=true;
--analyze分析器
--作用: 提前运行一个Mapreduce程序将表或者分区的信息构建一些元数据(表的信息 分区信息 字段信息)搭配cbo引擎使用
--构建指定分区信息元数据
analyze table tablaName [partition(parColName[=val1]...)]
compute statistics [noscan];
--构建指定列信息元数据
analyze table tableName [partition(parColName[=val1]...)]
compute statistics for columns(columns colName1...) [noscan];
--查看元数据
desc formatted [tableName] [colName];
--谓词下推 PPD(尽量先过滤减少数据量 再参与其他计算)
--谓词
--用来描述或判定客体性质、特征或者客体之间关系的词项。 比如 3大于2 中大于是一个谓词
--谓词下推
--将过滤表达式尽可能移动至靠近数据源的位置。 即在不影响最终结果的情况下,尽量将过滤条件提前执行。 使用left join on 时如果在on后面写上关联条件则从表会进行谓词下推 先过滤从表满足on关联条件的语句再去关联
--Hive谓词下推后 过滤条件会下推到map端提前执行过滤 减少map到reduce的数据传输
set hive.optimize.ppd=true; --开启hive的谓词下推 默认开启
--数据倾斜
--数据分布很不均匀的时候会出现,大多数的task都完成了,只有那一两个task迟迟不能完成。导致整体进度卡在99%
--常见数据倾斜
--1 group by、count(distinct) 导致的数据倾斜
--方案1:开启Map端的聚合
set hive.map.aggr=true; --开启Map端的聚合,减少shuffle的数据量和reduceer阶段的执行时间
--方案2:实现随机分区(不将同一个key值的放入同一个分区)
select * from tableName distribute by rand();--distribute by 用于指定底层按照哪个字段作为key实现分区,分组等 通过rand()函数 随机值 实现随机分区 避免数据倾斜
--方案3: 数据倾斜时自动负载均衡
set hive.groupby.skewindata=true; --开启该参数以后 程序会自动分成两个mapreduce来执行 第一个 自动进行随机分布到reduce中 每个reduce做部分聚合操作 输出结果
--第二个 将上一个mapreduce的结果 再按照业务(group by key)进行处理,保证相同的分布到一起,最终聚合得到结果
--2 join过程导致的数据倾斜
--当两张表很大时无法走map join 只能走reduce join 当关联字段的某一个值非常多时此时会导致数据倾斜
--解决方案:
--核心思想: 当面对join产生的数据倾斜,核心思想是尽量避免reduce join 优先使用map join
--常用方案:
--1 提前过滤 将大表转为小表 实现map join
--2 构建桶表 使用bucket join
--3 使用skew join
--原理: 如果某个值出现了数据倾斜,将数据分成产生数据倾斜了的数据与没有产生数据倾斜的数据,产生数据倾斜的数据走map join ,没有产生
数据倾斜的数据走reduce join 最终将map join与reduce join 结果进行union合并
--配置开启skew join
set hive.optimize.skewjoin=true; --开启运行过程中skew join
set hive.skewjoin.key=100000; --如果这个key的出现次数超过这个值 则将这个认定为产生数据倾斜的key
set hive.optimize.skewjoin.compiletime=true;--在编译时判断是否会产生数据倾斜
set hive.optimize.union.remove=true; --不合并 提升性能
set mapreduce.input.fileinputformat.input.dir.recursive=true; --如果Hive的底层走的是mapreduce 必须开启这个属性 才能实现不合并
--hive3的新特性
--大原则:
--数据倾斜的问题
--谓词下推PPD的基本规则
--掌握CBO优化器与analyze的使用
--执行引擎TeZ
--Hive底层计算是由分布式计算框架实现,目前支持三种计算引擎。 MapReduce(默认) Tez Spark
set hive.execution.engine=tez ;--hive控制使用哪个执行引擎的参数 spark性能最好 所以基本不用
set hive.tez.container.size=1024; --设置tez的容器大小为1024m
--Hive集成Tez计算引擎 需要集成 hadoop3.x + Hive3 +Tez整合
--LLAP特性(实时长期处理) 目前只支持Tez引擎
--
--metastore独立模式
--从hive3.0开始metastore可以在不安装hive其他部分的情况下单独运行, 用于实现允许其他非hive的系统,eg: spark Impala等与metastore集成。
--Spark学习
--环境准备
--jdk
--在windows上开发用 windows版本的scala的sdk, linux则linux版本的
--spark软件
--环境搭建(具体请看hadoop生态环境搭建目录下的spark部署)
--统一的前期准备(spark-3.0.1 上传到服务器 解压 修改chown 为root chgrp为root)
--上传解压spark-3.0.1 并修改安装目录chown 为root chgrp为root
--local模式
--standalone模式 (独立集群 一主多从 容易出现单点故障)
--几个重要地址
--查看集群部署UI的地址 (8080)
--查看集群master提交任务情况的UI地址(7077)
--查看spark任务运行情况的web-ui (4040)
--standalone-HA模式(高可用集群 多主多从 借助zookeeper 注册中心)
--on-yarn模式
--spark-shell和spark-submit的区别(借助帮助 看使用,spark-shell可以直接写代码 spark-submit是打成jar包交由spark集群去执行)
${spark}/bin/spark-shell --help
${spark}/bin/spark-submit --help
--Spark Core
--RDD
--RDD的五个特征
--核心问题是数据的组成形式 以及如何计算 RDD之间的关系
--分区列表 如何进行分区(kv默认为 hash方式) 计算在数据所在节点上计算 函数作用于每个分片(范围) RDD有依赖关系
--RDD于dataframe,dataSet之间的区别:
--RDD: 可以理解为一行行的数据,每一行的数据只有value没有key,当前行的数据结构长这样,下一行的可能就不长这样了 (形如:没有固定结构规律的一行行数据)
--dataframe: 可以理解为每一行的数据结构都相同, 在这行数据上只有value值没有key值,但是我们可以知道当前数据对应的key是什么 (形如:数据库中的表)
--DataSet: 每一行的数据值 既有key值又有value值 不需要我们专门费心思去获取当前数据对应的key是什么 其已经给好了 (形如: 一个JSON对象格式的数据)
--如何将数据封装到RDD中(sc表示spark context)
--并行化本地集合
--引用加载外部存储系统(hdfs,hive,hbase,kafka,es)
--相关API
--(获取RDD对象)
--sc.parallelize(本地集合,分区数)
--sc.makeRDD(本地集合,分区数)
--sc.textFile("filePath",分区数)-->不要用它来读取大量小文件 文件路径可以是文件与文件夹
--sc.wholeTextFiles("filePath",分区数)-->专门用来读取小文件 文件路径为文件夹
--获取RDD分区数
--rdd.getNumPartitionst
--rdd.partitions.length
--RDD的操作
--1 transformation(返回值是一个新的RDD)
--常用的tf算子 数学 set Relational data structure/I/O
--map sample union(并集) keyBy
--filter randomSplit intersection(交集) zipWithIndex
--flatMap subtract zipWithUniqueID
--mapPartitions distinct zipPartitions
--mapPartitionsWithIndex cartesian coalesce
--groupBy zip repartition
--sortBy pipe
--2 action(返回值非RDD)
--reduce --count takeOrdered saveAsTextFile
--collect --takeSample saveAsSequenceFile
--aggregate --max/min/sum saveAsObjectFile
--fold --histogram keys saveAsHadoopDataSet
--first --mean values saveAsHadoopFile
--take --variance countByKey saveAsNewAPIHadoopDataSet
--foreach --stdev countByValueApprox saveAsNewAPIHadoopFile
--top --sampleVariance countApproxDistinctByKey
--treeAggregate --countApprox countByKeyApprox
--treeReduce --countApproxDistinct sampleByKeyExast
--RDD基本操作
--map
--flatMap
--filter
--foreach
--saveAsTextFile
--RDD的分区操作
--每个RDD由多个分区组成,实际使用中 mapPartitions代替map , foreachPartition代替foreach
--RDD的重分区改变分区数操作
--repartition coalesce
--RDD的聚合操作
--无key的聚合
--sum,reduce,fold(初始值,全局聚合)
--aggregate(初始值)(局部聚合,全局聚合)
--有key的聚合
--groupByKey + sum/reduce
--reduceByKey
--foldByKey
--aggregateByKey
--RDD的关联(RDD中的关联join函数都在PairRDDFunctions中)
--join
--leftOuterJoin
--rightOuterJoin
--RDD的排序
--sortBy
--sortByKey
--top
--RDD的持久化/缓存
--rdd.cache
--rdd.persist //cache底层调用的该方法
--rdd.persist(StorageLevel.MENORY_AND_DIST) //存储级别 开发中一般使用该方式,默认的为MEMORY仅在内存中
--rdd.unpersist //清空缓存
--RDD的checkpoint(检查点)-->目的为: 进行更加可靠的数据持久化 一般放到hdfs中 hdfs具有高容错性 高可靠性
--sc.setCheckPointDir(一般为hdfs路径)
--rdd.checkpoint
--RDD的持久化与checkpoint之间的区别
--1 位置上: 持久化默认存在内存中 一般设置为内存加磁盘 而checkpoint一般存储在hdfs
--2 功能上: 持久化保证后续使用的效率高 checkpoint保证安全可靠的存储也能一定程度上提高效率
--3 依赖关系: 持久化保留了rdd之间的依赖关系 checkpoint没有
--共享变量
--广播变量(把变量在所有节点的内存之间进行共享)
--累加器(累加器 支持在所有不同节点之间进行累加计算,比如计数或者求和)
--外部数据源
--文本文件
--Sequence文件
--文件系统
--对象文件
--数据库(支持jdbc的就支持)
--SprakCore原理
--DAG:spark的DAG就是spark任务执行的流程图
--DAG的开始:从创建RDD开始
--DAG的结束:从action结束,一个spark程序中有几个action操作就有几个DAG
--Stage: 是DAG中根据shuffle划分出来的阶段,前面的阶段执行完才可以执行后面的阶段。reduceByKey有shuffle过程
--依赖(是宽依赖还是窄依赖主要从父RDD的角度 看其是到多个子RDD去还是只到一个RDD中去)
--宽依赖 会有shuffle过程 一个父RDD会传递到多个子RDD里面去 (类似于多个分区里面有同一个key,但是经过shuffle后下一层key只会在一个分区里面不会在多个分区里了)
--spark可以根据宽依赖进行stage阶段划分,同一个stage阶段的都是都是窄依赖 ,窄依赖就可以进行优化
--宽依赖的子RDD数据来源于多个父RDD, 所以需要多个父RDD都执行完毕了该子RDD数据才算获得全了
--窄依赖 一个父RDD只会传递到一个子RDD里面去
--spark可以对窄依赖优化形成管道
--窄依赖的子RDD数据来源只来源一个父RDD,所以只要该父RDD执行完成了,该子RDD数据就算获取全了
--总结:
--窄依赖:并行化+容错
--宽依赖:进行阶段划分的依据
--基本名词概念:
--driver: 驱动程序,用来执行main方法的jvm进程,里面会执性driver端的代码 比如创建sparkContext 设置日志级别等。
--clusterManager:集群管理器,对于yarn模式来说就是 ResourceManager/ApplicationsManster 对于standalone模式就是Master
--worker: 工作节点,运行task的机器
--Executor:运行在worker中的JVM进程
--DAG/Job: DAG即有向无环图,RDD的执行流程是静态的, job就是按照DAG实际执行的流程是动态的
--Stage:
--Task: 一个stage的同一个分区的一系列操作即为一个Task
--TaskSet: 同一个stage的所有分区的task组成的集合
--搜狗搜索日志分析
--依赖工具:1 中文分词第三方jar包 HanLP中文分词
--Sprak Streaming
--Sprak Streaming概述
--流式计算: 对无边界的数据进行连续不断的处理,聚合和分析
--场景: 天猫双11实时大屏 实时监控
--流式计算处理模式
--原生流模式
--对所有输入记录会一条接一条的处理 storm和flink都是采用这种方式( flink做了优化 通过设置缓冲块大小阈值/时间阈值 毫秒级触发数据块传输 做到低延迟 高吞吐)
--微批处理
--将输入数据以固定的时间间隔T ,划分为多个微批量数据。然后对每个批量数据进行处理 spark Streaming与structuredStreaming采用这种方式
--spark Streaming的优点
--1 易用 2 容错性高 3 与spark无缝集成
--spark streaming处理流程
--一般sparkstreaming处理流程为 [flume]-->kafka-->spark Streaming-->HDFS|databases
--spark Streaming基本数据抽象 DStream 很重要
--DStream: 它表示一个连续的数据流, 每个微批就是一个RDD,这些时间上连续的RDD就组成了DStream,所以DStream本质上就是一系列时间上连续的RDD即DStream=>Seq[RDD]
--特性:
--因为DStream底层是RDD,所以对Dstream操作返回Dstream
--同样具有依赖性 容错性
--DStream的API:分transfrom与output(DStream叫output)
--transform相关的API
--大多数与RDD类似,但有特殊的针对特定类型使用的函数 。 eg: updateStateByKey window窗口函数等
--map --count --union
--flatmap --reduce --join
--filter --reduceByKey --cogroup
-- --reduceByValue --
--output函数
--将DStream中每批次RDD处理结果resultRDD输出
--saveAsObjectFiles --print --foreachRDD
--saveAsHadoopFiles
--saveAsTextFiles
--案例1: linux服务器作为生产者 通过TCP socke 向消费者spark Streaming发送消息 sparkStream实时做词频统计
--案例2: 状态管理 实现在上一批次的结果上进行累加 eg: 第一批次的结果为 (spark,2) (hadoop,3) 这次再输入spark 则结果(spark,3) -->状态管理两个常用的api mapWithState updateStateByKey
--案例3: 状态恢复
--案例4: 窗口函数 场景: 每隔10分钟(周期/滑动间隔)计算最近30分钟内(长度/数据范围)的网站到访数量。
--1 滑动间隔多久? 2 窗口长度多长? 滑动窗口 滚动窗口
--案例5: 使用窗口计算模拟热搜排行榜,每隔10s计算最近20s的热搜排行榜。 案例4的基础上加上排序
--DStream没有直接的排序方法,所以调用transform方法对DStream底层的RDD进行操作排序, transform(函数)->该函数会作用到DStream底层的RDD上
--案例6: 自定义输出到控制台/HDFS/MySql 使用DStream的foreachRDD()方法
--spark Streaming整合kafka
--连接有两种模式
--1 Reciver接收器模式(已废弃实际中不用): kafka主动推送数据给Streaming的Receiver, Streaming接收数据可能未被处理 造成数据丢失,数据存可靠文件系统又可能造成重复消费
--2 Direct模式: Streaming主动去kafka拉取数据,拉到什么数据消费什么数据,不存数据,offset通过调用kafka底层API,自己存储和维护,根据kafka各个分区的偏移量获取数据,默认由spark维护在checkpoint中
--调用KafKaUtils.createDirectStream()直连方式
--Sprak SQL
--数据分析方式
--1 命令式 --->即写代码 但灵活
--2 sql语句--->
--saprksql特性:
--1 统一的数据访问
--2 与Hive集成
--3 支持标准的数据库连接协议
--4 支持离线也支持实时
--spark sql基本数据抽象: DataFrame和DataSet 底层是RDD
--结构化数据 由固定结构和schema(字段/字段类型)的数据
--DataFrame和DataSet: DataFrame没有泛型 DataSet有泛型 在python语言中是没泛型的,所以以操作dataframe的方式写的代码可以直接迁移到python。
--案例:
--案例1: 以sparksql形式加载数据
--案例2: RDD与DataFrame互转
--将RDD转为DataFrame:
--1 使用样例类
--2 指定类型+列名
--1 自定义schema
--rdd与dataframe与dataset互转
--spark SQL花式查询
--1 将dataframe/dataset注册为临时表或视图 编写sql语句 类似hivesql
--2 dsl编程,调用相关函数。(联想java的querydsl)
--spark SQL加载外部数据源: txt/json/csv/orc/parquet/文件系统/数据库
--分析电影数据
--spark SQL自定义udf函数,自定义函数
--spark sql整合hive(spark-sql操作hive数据库)
--HiveOnSpark(淘汰了) 与SparkOnHive
--sparksql命令行整合Hive(spark3.0需要Hive版本在2.3.7以上)
--1 启动hive的元数据服务 nohup ${hive}/bin/hive --service metastore &
--2 告诉sparkSQL: Hive的元数据库在哪 即将${hive}/conf/hive-site.xml 放到${spark}/conf/目录下
--(注意可能要对hive-site.xml的有些地址进行微改 反正逻辑是spark-sql去访问metastore metastore去访问mysql元数据库 可能需要把连mysql的驱动加到jars里面)
--3 启动${spark}/bin目录下的spark-sql: ${spark}/bin/spark-sql 然后执行sql语句访问即可
--sparksql代码整合Hive
--1 导入依赖
<!-- sparkSQL+hive依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.12</artifactId>
<version>${spark.version}</version>
</dependency>
--2 启动metastore
--3 编写代码 代码里面要有相关的配置项
--sparksql的分布式引擎(实现类似hive的效果,hive可以在A机器启动metastore服务 在B机器访问metastore服务来访问hive数据库)
--实现在A机器开启hive的metastore服务,同时在装有spark的A机器启动spark的ThriftServer,然后在装有spark的B机器启动spark的beenline 来访问A机器的ThriftServer进而访问hive
--1 在A机器启动metastore服务 ---> nohup ${hive}/bin/hive --service metastore &
--2 在A机器(hadoop101)启动sparksql的thriftServer(类似HiveServer2) ---->${spark}/sbin/start-thriftserver.sh --hiveconf hive.server2.thrift.port=10000 --hiveconf hive.server2.thrift.bind.host=hadoop101 --master local[2]
--可以去访问 hadoop101:4040/jobs/ 看效果
--3 在B机器使用beeline连接sparksql的thriftserver ---->${spark}/bin/beeline !connect jdbc:hive2://hadoop101:10000 用户名 密码
--使用jdbc代码方式访问sparksql的thriftServer
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive-thriftserver_2.12</artifactId>
<version>${spark.version}</version>
</dependency>
--导入jar包 写代码
--structured streaming
--structed Streaming概述
--spark streaming的不足
--1 基于微批处理,延迟高,不能做到真正的实时
--2 流批处理不统一(流用的DStream底层是RDD,批用的DF/DS/RDD)
--3 不支持event time
--eg: 一条错误日志产生于23:59:00秒(事件时间) 因为网络延迟等原因于次日00:00:10到达日志处理系统(摄入时间) ,于00:00:20被流系统处理(处理时间)
--4 DStream基于RDD 不直接支持sql
--5 数据的Exactly-once(恰好一次语义)需要手动实现
--数据的一致性语义:
--最多一次
--恰好一次 ,这是我们的目标spark streaming 如果要实现恰好一次,需要手动维护偏移量
--最少一次
--structed Streaming 编程模型(编程模型为无解的动态表格,数据抽象为DataFrame/DataSet)
--一个流的数据源当做一个不断增长的数据表格
--输入的流来源: (要明白如何加载外部流与如何写输出流)
--1 file inputstream -->主要测试中用 实际开发偶尔用
--2 kafka inputstream --> 实际开发中用
--1 socket inputstream -->测试中用
--1 rate source -->测试中用
--operation操作
--使用sql语句 -->以前怎么弄dataframe在structuredstreaming就怎么弄
--使用dsl -->以前怎么弄ataframe在structuredstreaming就怎么弄
--sink
--output输出模式
--1 append 追加模式(默认模式) 表示只输出新增的数据,只支持简单查询 不支持聚合
--2 complete 完整模式,表示输出所有数据,必须要有聚合操作
--3 update 更新模式 表示只输出有变化的/有更新的数据 不支持排序 支持聚合
--输出的终端位置
--console memory
--Foreach Sink 与 ForeachBatchSink (针对每一条进行处理 与 针对每个分区(批)进行处理)
--通过jdbc将结果写入数据库
--触发间隔(Triggers)
--once 只一次即离线
--fixed interval 固定时间间隔
--continuous 连续处理并指定checkpoint时间间隔 (在spark streaming时微批处理可以每个批次做checkpoint容错/保持状态 在做连续处理的时候也需要checkpoint 那么什么时候做呢? --需要指定时间)
--structed Streaming整合kafka---->记得去安装kafka集群
--1 导入依赖
<!-- structuredStreaming+kafka依赖 要注意对应的spark kafka scala jdk 三者版本的对应关系-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql-kafka-0-10_2.12</artifactId>
<version>${spark.version}</version>
</dependency>
--写代码读与写
--案例: 基于事件时间的窗口计算(注意窗口的划定标准 是以事件时间作为标准的,即被处理的流数据里面的某个表示事件时间的字段
-- 相当于给数据打上时间标识,然后根据时间标识划分数据所属窗口 然后对这个窗口按照要求做相应处理)
--3个时间概念: 事件时间 摄入时间 处理时间
--基于事件时间进行窗口计算 + WaterMaker水位线/水印可以解决数据延迟到达的问题
--流去重
--spark中的批数据去重很简单 直接: df.dropDuplicates("列名1","列名2")
--流式数据去重需要保存历史数据的状态才可以做去重,structured Streaming 的状态管理是自动的所以
--structuredStreaming的去重跟批处理一样: df.dropDuplicates("列名1","列名2")
--spark综合训练:
--推荐算法入门:
--推荐系统: 海量数据+挖掘/训练---->模型/用户的兴趣爱好等特征--->给用户做推荐
--推荐系统算法分类:
--基于人口统计/用户的推荐算法: A用户喜欢XX商品,B用户与A用户的年龄 性别等相似 所以将XX商品也推荐给B用户
--基于内容的推荐算法: A用户喜欢XX商品, YY商品与XX商品相似,将YY商品也推荐给A用户
--基于用户的协同推荐算法:借助用户与物品的交互信息(日志 点击 收藏 转发 点赞 评论等) 用户A喜欢商品XX,YY 用户B也喜欢商品XX,YY 现在用户B增加喜欢ZZ了,把ZZ推荐给A用户
--基于物品的协同推荐算法 物品XX,YY同时被用户A,B喜欢,-->简易推出喜欢XX商品的同时YY商品,现在用户C喜欢XX,把YY就推荐给C
--基于隐语义模型的协同推荐
--ALS推荐算法
--显示评分-->用户直接打分
--隐式评分-->用户进行了某个操作,不同操作对应不同分。 eg: 点赞 收藏等
--核心思想:
--数据库里面的表 相当于是一个矩阵, 这个矩阵的值不全,是一个稀疏矩阵,该稀疏矩阵的可以拆分为两个矩阵相乘
--这两个拆分的矩阵 满足已有数据的约束, 根据两个相乘后能把稀疏矩阵填满 原来为空的值就变得不空了
--不空的值即为预测值 拆分的两个矩阵 我们可以分别称为 用户隐藏的特征矩阵 物品隐藏的特征矩阵
--Scala学习 (表达含义的唯一确定性)
--类与对象
--Scala中用class来创建类 用new 来创建对象
--定义var类型的成员变量时 可以使用_来初始化成员变量 不同类型对应的初始值跟java类似-->注意val类型的不能使用此方式初始化 必须手动指定
--scala中类的修饰符
--private
--private[this]--->主要用于伴生对象-->如果某个成员的权限设置为此 表示只能在当前类中访问 伴生对象也不可以访问
--protected
--默认
--类的构造器
--主构造器
--辅构造器
def this(参数名:类型){
//第一行需要调用主构造器或者其他构造器
构造器代码
}
--定义单例对象
--定义伴生对象
--一个class和一个object具有相同的名称 则这个object称为伴生对象 这个class称为伴生类 (其实就是java中将static修饰的与非static修饰的分开)
--1 伴生对象和伴生类必须要名称相同
--2 两个必须在同一个scala源文件中
--3 伴生对象和伴生类可以互相访问private属性
--apply方法:
--在scala中可以免new 创建对象。 前提条件:在伴生对象object中定义apply方法 apply方法中要new
最终形式 val p =Person("张三",23)
--scala中的特质 trait关键字--->场景: 有动物类 和猴子类 大象类 都有跑 跳 吃的功能 但是部分猴子经过训练有了骑独轮车的功能, 这是部分猴子的特质
--对象混入trait
val/var 对象名 = new 类 with 特质
--包对象
--包的可见性
--private private[com] var name=""
--protected
--默认
--包的引入
--scala默认引入了java.lang包 scala包(并不是完全引用使用有的仍需要先导入) 以及Predef包 (并不是完全引用使用有的仍需要先导入)
--样例类
--定义样例类后编译器会自动帮助我们生成一些方法 常用的如下
--apply() 免new创建对象
--toString() 打印属性值
--equals() 比较属性值是否相同
--hashCode() 算hashcode值 同一对象的hashcode肯定相同 不同对象的一般不同
--copy() 复制属性值相同的对象
--unapply() 一般用作提取器
--样例对象
--一般可以当作枚举值用 格式
case object 样例对象名
--遍历数组
--可变列表
--可变列表的常用操作
--不可变列表
--集合
--不可变集合
set --->常见操作:size() 添加删除元素用 + 和 - 生成一个新的一个不可变集合对象
--可变集合(需要先导包 创建方式跟不可变集合完全一致) 使用+= 和 -= 改变了原集合对象
--map
--不可变map
--可变map(需要先导包 创建方式跟不可变map一致)
mutable.Map()
--函数式编程
--常用方法 (主要都是涉及类似sql操作的效果的方法)
--foreach
--map
--flatMap
--filter
--sorted
--sortWith
--sortBy
--gorupby -->对集合元素进行指定分组
=========================重点学习 有其他知识点=======================
--reduce -->对集合元素进行聚合计算
--fold-->对集合元素进行折叠计算 fold方法与reduce方法很像,只不过fold方法多了一个初始值参数
=========================重点学习=======================
--简化函数定义:
--通过下划线来简化函数定义
--当函数参数,只在函数体中出现一次,而且函数体没有嵌套调用时,可以使用下划线来简化函数定义
--Option类型 偏函数 模式匹配 提取器
--模式匹配
--简单模式匹配
--匹配数据类型
--守卫
--匹配样例类
--匹配集合
--匹配for表达式
--偏函数
--结合map函数使用
--正则表达式
val 正则对象名 = """具体的正则表达式""".r