数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
6.1 Reading and Writing documents
一个索引会被切分为多个分片,每个分片会有多个副本。一个分片的所有副本被称为replication group,彼此之间保持同步。
replication group中存在一个主分区(primary shard)。主分区承担所有索引操作,同时负责把索引变更同步到其他副本。
Basic wirte model
所有索引操作首先都会根据document ID被路由到一个replication group,然后从中选出主分区。主分区需要验证这个操作,并同步到其他副本。在es的master节点上为每个replication group维护一个in-sync副本列表,主分区需要把索引操作同步到所有in-sync副本上。只有当所有需要同步的副本都反馈完成时,才反馈给client操作完成。
若主分区自身操作失败,则该node会向master请求重新选择primary shard。master自己也会主动监测所有分片副本,会重新选择失效的主分区。当主分区接受请求之后,它就会确保副本获得同步。
Basic read model
接受读请求的node会执行如下行为:
1. 找出所有需要查询的分区(可能属于多个不同索引)
2. 为每个分区选出一个合适的副本
3. 将查询发送到选出的副本上
4. 收集结果并返回
6.2 Index API
PUT twitter/_doc/1 {.....} 添加索引
POST twitter/_doc/ {....} 添加索引,自动生成ID
PUT twitter/_doc/1?op_type=create or PUT twitter/_doc/1/_create doc存在则直接失败,不更新
POST twitter/_doc?routing=kimchy 使用kimchy作为route函数的入参
添加document API会自动创建index以及dynamic type mapping
index.write.wait_for_active_shards(默认为1)参数会在写入document前一直等待直到有wait_for_active_shards个或许副本为止(算上primary shard)。这个参数只作用在写入操作开始前,操作开始之后若replica失效也会导致实际写入副本数少于该配置。
6.3 Get API
通过ID获得doc:GET twitter/_doc/0 返回结果中_source包含了doc原文
get API默认是实时的,和refresh间隔无关
通过_source参数指定是否返回_source结构或者部分fields:
GET twitter/_doc/0?_source=false
GET twitter/_doc/0?_source=*.id,retweeted
GET twitter/_doc/1/_source 只返回_source
6.4 Delete API
根据ID删除一个doc: DELETE /twitter/_doc/1
可以添加以下参数
wait_for_active_shards 等待直到线上有wait_for_active_shards个活跃副本
DELETE /twitter/_doc/1?timeout=5m 指定超时时间
6.5 Update API
PUT test/_doc/1 { "counter" : 1,"tags" : ["red"]} 构建/替代一个document
POST test/_doc/1/_update { "doc" : { "name" : "new_name" }} 部分更新一个doc
scripted updates用scripts更新
6.6 Multi Get API
通过一次调用获得多个docs:
GET /_mget { "docs" : [ { "_index" : "test", "_type" : "_doc", "_id" : "1" }, { "_index" : "test", "_type" : "_doc", "_id" : "2" } ] }
也可以在URI中指定index\type:
GET /test/_mget {.....}
GET /test/type/_mget {....}
6.7 Bulk API
/_bulk, /{index}/_bulk, /{index}{type}/_bulk 下可以执行批量操作
6.8 ?refresh
通过在index、update、delete、bulk请求中添加?refresh参数可以控制变更可见的时间:
?refresh or ?refresh=true
立即更新索引,有较大性能影响
?refresh=wait_for
请求一直等待直到索引更新完再返回
?refresh=false(默认值)
无影响,依靠索引默认的刷新机制
大数据
2019-07-27 16:40:09
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
今天,中国领先的开发者服务提供商极光(Aurora Mobile, NASDAQ:JG)登陆纳斯达克正式期满一周年。经过在开发者服务领域长达7年的奋力前行,极光在2018年7月26日正式在纳斯达克挂牌上市,开启全新发展篇章。
从2011年成立至今,极光服务开发者的初心从未改变。我们也在持续倾听开发者的声音,根据他们的需求不断完善产品矩阵,提升技术和服务水平。
在探索如何更好服务开发者的过程中,极光逐步形成了基于智能开发者生态、从开发者产品到数据产品的产品矩阵。我们的开发者服务也从最初的消息推送,逐步扩展至即时通讯、统计分析、社会化组件、短信、一键认证、深度链接、可信云和物联网等模块,以满足移动开发者的不同需求,提升开发效率和用户体验。
过去一年,极光一直在开发者服务领域奋力前行,我们的努力也收获了行业权威机构的认可: 针对更快捷、安全的注册登录验证需求,我们上线了一键登录认证产品“极光认证”,提升用户认证过程的体验和安全性;
基于深度链接技术在产品交互、用户激活、用户体验上的显著效果,我们收购了深度链接产品“mLink” 并更名为“极光魔链”,助力开发者驱动用户增长;
为了进一步优化产品功能、提升开发者的使用体验,我们对极光开发者服务平台进行了升级,使开发者服务产品之间更加独立化,功能模块之间的区分也更加清晰;
针对物联网使用场景,我们推出了“极光IoT”产品,提供安全连接、实时统计、设备管理、影子设备等一系列解决方案;
为了帮助开发者深度挖掘数据价值,我们上线了“极光可信数据云”,将积累多年的海量数据和分析能力向开发者开放;
与此同时,极光开发者服务的技术实力和服务水平也得到了泰尔实验室等行业及国家权威机构的认可。
为了进一步践行赋能开发者的承诺,极光接下来将会把过去8年所积累的万亿级别数据,以及在该数据上所积累的人工智能和数据处理、分析能力逐步打造成开放平台的产品形态,以人工智能驱动的数据开放生态反哺开发者。极光可信数据云正是我们打造共享开放的开发者生态迈出的坚实一步。
极光的成长离不开移动开发者的支持,我们将持续探索如何帮助各位在移动互联网的种种变化趋势中走向成功,为打造更加开放、多元的开发者生态贡献一分力量。这个初心将会伴随极光,走向今后的每个征途。
感谢各位对极光的支持!
关于极光
极光(纳斯达克股票代码:JG)成立于2011年,是中国领先的开发者服务提供商。极光专注于为移动应用开发者提供稳定高效的消息推送、即时通讯、统计分析、社会化组件、短信、一键认证、深度链接、物联网等开发者服务。截止到2019年3月份,极光已经为超过40万移动开发者和116.5万款移动应用提供服务,其开发工具包(SDK)安装量累计227亿,月度独立活跃设备10.7亿部。同时,极光持续赋能开发者和传统行业客户,推出精准营销(极光效果通)、金融风控、市场洞察、商业地理服务产品,致力于为社会和各行各业提高运营效率,优化决策制定。
大数据
2019-07-26 11:32:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
如果你是第一次开始使用 Akka,我们推荐你先运行简单的 Hello World 项目。情况参考 Quickstart Guide 页面中的内容来下载和运行 Hello World 示例程序。上面链接中的 快速使用指南 来帮助你了解如何定义 actor 系统,actors 和消息,以及如何使用测试模块和日志系统。
在 30 分钟内,应该可以运行 Hello World 示例程序和项目的结构。
这个 快速使用指南 能够提供下一个级别的信息。这些文章提供了为什么 actor 模型符合现代分布式系统的需求同时包括了帮助你未来使用 Akka 需要的相关信息。主题包括有: Why modern systems need a new programming model How the actor model meets the needs of concurrent, distributed systems Overview of Akka libraries and modules 一些使用 Akka 设计模式用来构建 Hello World 的 更多的完整示例程序
https://www.cwiki.us/display/AkkaZH/Introduction+to+Akka
大数据
2019-07-23 10:24:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 咪咪
wiremock-py
wiremock-py 是基于WireMock实现的, 使用Python批量生成不同 测试场景 下不同HTTP API的 mock 数据, 然后作为mock server快速全面地对 API 进行测试。
背景
在数澜地产应用的前端测试中, 前端一般依赖于后端的数据, 前端通过后端在网关上发布的 HTTP API 获取数据. 要对前端进行充分的测试, 理想的做法是, 等待后端部署完成, 并且在数据层直接输入不同类型的数据源, 然后前端直接调用后端发布在网关上的 API 进行测试。
然而现实的情况是, 前端和后端的开发进度不完全一致, 如果前端先开发完成了, 必须要等后端对应的 API 开发完成后才能开始测试, 而且数据层的数据也不容易构造。
为了解决这个问题, 网关平台做了简单的 mock 功能, 每个 API 可以填写一个 mock数据, 然后前端调用 API 时直接使用这个 mock数据:
这种方式下, 网关充当了mock server:
但由于大家都使用同一个网关, 一个 API 只能保存一份 mock 数据, 所以有以下一些缺点: 不同的测试场景需要不同的 mock 数据来测试, 此时需要删掉上个测试场景的 mock 数据, 再创建新场景的 mock 数据才能进行测试 不能根据测试场景来按照一定的规则动态生成 API 对应的 mock 数据 不能多人同时使用测试同一个 API时, 只能都使用同一份 mock 数据, 不能各用各的
wiremock-py 可以解决上述这些问题: wiremock-py 通过传入不同的测试场景参数来生成不同的 mock 数据, 同时不同测试场景下使用的 mock 数据可以保存起来; 生成 mock 数据时, wiremock-py 支持使用Python和js代码来动态生成 mock 数据(也支持直接使用 json 数据, 如果 mock 数据中的数据量很大, 人工手写 mock 时的数据量会很大, 使用代码生成则比较容易); 不同的测试人员使用各自自己的 mock server, 不会影响到其他测试人员的测试。
测试人员需要做的是: 确定哪些 API 需要进行 mock 以及不同测试场景下对应的 mock 规则是什么。
依赖环境
Java 1.8.0_144
Node v8.6.0
Python 3.4.3
演示
快速开始
以贸数v1.1.0版本 测试环境为例演示使用 wiremock-py 对楼层客流分布和店铺客流分布两张图分布在3种场景下的测试方法
先确定本地浏览器能过正常访问 http://mall-data.com:9012
准备
克隆代码
git clone http://git.dtwave-inc.com:30000/baomi.wbm/wiremock-py.git
安装依赖
cd wiremock-py
pip install -r requirements.txt
npm install mockjs
生成目录
python mock.py -g "demo"
➜ wiremock-py git:(master)✗ python mock.py -g "demo"
DEBUG:root:mockdir=, scene=, target=, proxy_port=5506, generate=demo, wiremock=False, rewrite=False
DEBUG:root:正在生成目录 /Users/wangbaomi/autotest/wiremock-py/demo
DEBUG:root:创建目录成功: demo
DEBUG:root:创建目录成功: demo/js
DEBUG:root:创建目录成功: demo/json
DEBUG:root:创建目录成功: demo/python
DEBUG:root:创建目录成功: demo/wiremock
DEBUG:root:创建文件成功: demo/mappings.json
DEBUG:root:生成目录完成: /Users/wangbaomi/autotest/wiremock-py/demo
填写 mappings.json、json、python、js 数据
mappings.json 中填写内容:
[ { "response": { "default": { "proxyBaseUrl": "target"
} }, "mapping_name": "request url not start with /api", "request": { "method": "ANY", "urlPattern": "/(?!api).*" } }, { "mapping_name": "楼层客流分布", "request": { "urlPattern": "/api/v1/mall_data/customer_flow/every_floor\\?(.*)", "method": "POST" }, "response": { "default": { "proxyBaseUrl": "target" }, "测试场景1": { "bodyFileName": { "json": "楼层客流分布.json" } }, "测试场景2": { "bodyFileName": { "python": "楼层客流分布.py", "python_args": "测试场景2" } }, "测试场景3": { "bodyFileName": { "js": "楼层客流分布.js" } } } }, { "mapping_name": "店铺客流分布", "request": { "urlPattern": "/api/v1/mall_data/customer_flow/every_shop\\?(.*)", "method": "POST" }, "response": { "default": { "proxyBaseUrl": "target" }, "测试场景1": { "bodyFileName": { "js": "店铺客流分布.js" } }, "测试场景2": { "bodyFileName": { "json": "店铺客流分布.json" } }, "测试场景3": { "bodyFileName": { "python": "店铺客流分布.py", "python_args": "测试场景3" } } } }
]
js 文件夹中新建店铺客流分布.js文件, 内容为: var r = { "success": true, "code": null, "message": null, "content": { "meta": {}, "multi": { "group": [ { "id": "rank", "name": "排名", "value": [ 1, 2, 3, 4 ] } ], "result": [ { "id": "the_shop", "name": "店铺", "value": [ "店铺1", "店铺2", "店铺3", "第4个店铺" ] }, { "id": "customer_count", "name": "人数", "value": [ 10, 100, 1000, 3242 ] } ] }, "single": [] } }; console.log(JSON.stringify(r));
js 文件夹中新建楼层客流分布.js文件, 内容为: var r = { "success": true, "code": null, "message": null, "content": {"meta": {}, "multi": { "group": [ { "id": "the_floor", "name": "楼层", "value": [ "-1楼", "1楼", "2楼", "3楼", ] } ], "result": [ { "id": "customer_count", "name": "人数", "value": [ 100, 1000, 5000, 567 ] } ] }, "single": [] } }; console.log(JSON.stringify(r)); json 文件夹中新建店铺客流分布.json, 内容为: { "success": true, "code": null, "message": null, "content": { "meta": {}, "multi": {
大数据
2019-07-23 10:08:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
下划线这个符号几乎贯穿了任何一本Scala编程书籍,并且在不同的场景下具有不同的含义,绕晕了不少初学者。正因如此,下划线这个特殊符号无形中增加Scala的入门难度。
1. 用于替换Java的等价语法
由于大部分的Java关键字在Scala中拥有了新的含义,所以一些基本的语法在Scala中稍有变化。
1.1 导入通配符
*在Scala中是合法的方法名,所以导入包时要使用_代替。 //Java import java.util.*; //Scala import java.util._
1.2 类成员默认值
Java中类成员可以不赋初始值,编译器会自动帮你设置一个合适的初始值: class Foo{ //String类型的默认值为null String s; }
而在Scala中必须要显式指定,如果你比较懒,可以用_让编译器自动帮你设置初始值: class Foo{ //String类型的默认值为null var s: String = _ }
该语法只适用于类成员,而不适用于局部变量。
1.3 可变参数
Java声明可变参数如下: public static void printArgs(String ... args){ for(Object elem: args){ System.out.println(elem + " "); } }
调用方法如下: //传入两个参数 printArgs("a", "b"); //也可以传入一个数组 printArgs(new String[]{"a", "b"});
scala的可变参数方法声明: def echo(args: String*) = for (arg <- args) println(arg)
在Java中可以直接将数组传给printArgs方法,但是在Scala中,你必须要明确的告诉编译器,你是想将集合作为一个独立的参数传进去,还是想将集合的元素传进去。如果是后者则要借助下划线: echo(List("a", "b"): _*)
1.4 类型通配符
Java的泛型系统有一个通配符类型,例如List,任意的List类型都是List的子类型,如果我们想编写一个可以打印所有List类型元素的方法,可以如下声明: public static void printList(List list){ for(Object elem: list){ System.out.println(elem + " "); } }
对应的Scala版本为: def printList(list: List[_]): Unit ={ list.foreach(elem => println(elem + " ")) }
2 模式匹配
2.1 默认匹配 str match{ case "1" => println("match 1") case _ => println("match default") }
2.2 匹配集合元素 //匹配以0开头,长度为三的列表 expr match { case List(0, _, _) => println("found it") case _ => } //匹配以0开头,长度任意的列表 expr match { case List(0, _*) => println("found it") case _ => } //匹配元组元素 expr match { case (0, _) => println("found it") case _ => } //将首元素赋值给head变量 val List(head, _*) = List("a")
3. Scala特有语法
3.1 访问Tuple元素 val t = (1, 2, 3) println(t._1, t._2, t._3)
3.2 简写函数字面量(function literal)
如果函数的参数在函数体内只出现一次,则可以使用下划线代替: val f1 = (_: Int) + (_: Int) //等价于 val f2 = (x: Int, y: Int) => x + y list.foreach(println(_)) //等价于 list.foreach(e => println(e)) list.filter(_ > 0) //等价于 list.filter(x => x > 0)
3.3 定义一元操作符
在Scala中,操作符其实就是方法,例如1 + 1等价于1.+(1),利用下划线我们可以定义自己的左置操作符,例如Scala中的负数就是用左置操作符实现的: -2 //等价于 2.unary_-
3.4 定义赋值操作符
我们通过下划线实现赋值操作符,从而可以精确地控制赋值过程: class Foo { def name = { "foo" } def name_=(str: String) { println("set name " + str) } val m = new Foo() m.name = "Foo" //等价于: m.name_=("Foo")
3.5 定义部分应用函数(partially applied function)
我们可以为某个函数只提供部分参数进行调用,返回的结果是一个新的函数,即部分应用函数。因为只提供了部分参数,所以部分应用函数也因此而得名。 def sum(a: Int, b: Int, c: Int) = a + b + c val b = sum(1, _: Int, 3) b: Int => Int = b(2) //6
3.6 将方法转换成函数
Scala中方法和函数是两个不同的概念,方法无法作为参数进行传递,也无法赋值给变量,但是函数是可以的。在Scala中,利用下划线可以将方法转换成函数: //将println方法转换成函数,并赋值给p val p = println _ //p: (Any) => Unit
4. 小结
下划线在大部分的应用场景中是以语法糖的形式出现的,可以减少击键次数,并且代码显得更加简洁。但是对于不熟悉下划线的同学阅读起来稍显困难,希望通过本文能够帮你解决这个的困惑。

大数据
2019-07-18 22:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一、zookeeper集群的搭建
zookeeper集群的搭建相对比较简单就是在单机的基础上增加一些配置,然后将该配置分发到其他机器上具体操作如下: 将zookeeper-3.4.5.tar.gz通过sftp上传至hadoop-ip-101:/home/hadoop/soft目录下;并将压缩文件解压到/home/hadoop/module目录下: tar -zxvf /home/hadoop/soft/zookeeper-3.4.5.tar.gz -C /home/hadoop/module/ 切换到zookeeper-3.4.5目录下创建 /home/hadoop/module/zookeeper-3.4.5/zkdata 配置zoo.cfg中的:进入/home/hadoop/module/zookeeper-3.4.5/conf: cp zoo_sample.cfg zoo.cfg 配置参数 tickTime=2000 initLimit=10 syncLimit=5 dataDir=/home/hadoop/module/zookeeper-3.4.5/zkdata clientPort=2181 #############配置集群############## ##:其中server.num中num是各自服务器上/home/hadoop/module/zookeeper-3.4.5/zkdata/myid中的内容 server.1=hadoop-ip-101:2888:3888 server.2=hadoop-ip-102:2888:3888 server.3=hadoop-ip-103:2888:3888 将hadoop-ip-101:/home/hadoop/module/zookeeper-3.4.5/拷贝至hadoop-ip-102和hadoop-ip-103 scp -r /home/hadoop/module/zookeeper-3.4.5/ hadoop@hadoop-ip-102:/home/hadoop/module/ scp -r /home/hadoop/module/zookeeper-3.4.5/ hadoop@hadoop-ip-103:/home/hadoop/module/ 配置myid 在hadoop-ip-101 执行操作 echo "1" >> /home/hadoop/module/zookeeper-3.4.5/zkdata/myid 在hadoop-ip-102 执行操作 echo "2" >> /home/hadoop/module/zookeeper-3.4.5/zkdata/myid 在hadoop-ip-103 执行操作 echo "3" >> /home/hadoop/module/zookeeper-3.4.5/zkdata/myid hadoop-ip-101,hadoop-ip-102,hadoop-ip-103切换至/home/hadoop/module/zookeeper-3.4.5/bin,在该目录下执行 ./zkServer.sh start:待显示成功启动以后执行下面命令查看 ./zkServer.sh status
出现以上情况则证明集群正确启动了
二、特点 Zookeeper:一个领导者(leader),多个跟随者(follower)组成的集群。 Leader负责进行投票的发起和决议,更新系统状态 Follower用于接收客户请求并向客户端返回结果,在选举Leader过程中参与投票 集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。 全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的。 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行。 数据更新原子性,一次数据更新要么成功,要么失败。 实时性,在一定时间范围内,client能读到最新数据。
三、选举机制 半数机制:集群中半数以上机器存活,集群可用。所以zookeeper适合装在奇数台机器上。 Zookeeper虽然在配置文件中并没有指定master和slave。但是,zookeeper工作时,是有一个节点为leader,其他则为follower,Leader是通过内部的选举机制临时产生的 以一个简单的例子来说明整个选举的过程。 假设有五台服务器组成的zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么。 (1)服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING状态。 (2)服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1、2还是继续保持LOOKING状态。 (3)服务器3启动,根据前面的理论分析,服务器3成为服务器1、2、3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader。 (4)服务器4启动,根据前面的分析,理论上服务器4应该是服务器1、2、3、4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了。 (5)服务器5启动,同4一样当小弟。
大数据
2019-07-10 22:19:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一、环境准备 已安装Apache Spark 2.2.0(此版本仅支持Apache Spark 2.2.0, 其他Spark 版本后续会兼容) 已安装MySQL并启动,且开启远程访问 各安装节点已经配置ssh免密登录
二、下载
moonbox-0.3.0-beta下载: https://github.com/edp963/moonbox/releases/tag/0.3.0-beta
三、解压 tar -zxvf moonbox-assembly_2.11-0.3.0-beta-dist.tar.gz
四、修改配置文件
配置文件位于conf目录下
step 1: 修改slaves mv slaves.example slaves vim slaves
将会看到如下内容: localhost
请根据实际情况修改为需要部署worker节点的地址, 每行一个地址
step 2: 修改moonbox-env.sh mv moonbox-env.sh.example moonbox-env.sh chmod u+x moonbox-env.sh vim moonbox-env.sh
将会看到如下内容: export JAVA_HOME=path/to/installed/dir export SPARK_HOME=path/to/installed/dir export YARN_CONF_DIR=path/to/yarn/conf/dir export MOONBOX_SSH_OPTS="-p 22" export MOONBOX_HOME=path/to/installed/dir # export MOONBOX_LOCAL_HOSTNAME=localhost export MOONBOX_MASTER_HOST=localhost export MOONBOX_MASTER_PORT=2551
请根据实际情况修改
step 3: 修改moonbox-defaults.conf mv moonbox-defaults.conf.example moonbox-defaults.conf vim moonbox-defaults.conf
将会看到以下内容,其中: catalog
配置元数据存储位置, 必须修改, 请根据实际情况修改 rest
配置rest服务, 按需修改 tcp
配置tcp(jdbc)服务, 按需修改 local
配置Spark Local模式作业, 值为数组, 有多少个元素表示每个Worker节点启动多少个Spark Local模式作业。如不需要可删除。 cluster
配置Spark yarn模式作业, 值为数组, 有多少个元素表示每个Worker节点启动多少个Spark Yarn模式作业。如不需要可删除。 moonbox { deploy { catalog { implementation = "mysql" url = "jdbc:mysql://host:3306/moonbox?createDatabaseIfNotExist=true" user = "root" password = "123456" driver = "com.mysql.jdbc.Driver" } rest { enable = true port = 9099 request.timeout = "600s" idle.timeout= "600s" } tcp { enable = true port = 10010 } } mixcal { pushdown.enable = true column.permission.enable = true spark.sql.cbo.enabled = true spark.sql.constraintPropagation.enabled = false local = [{}] cluster = [{ spark.hadoop.yarn.resourcemanager.hostname = "master" spark.hadoop.yarn.resourcemanager.address = "master:8032" spark.yarn.stagingDir = "hdfs://master:8020/tmp" spark.yarn.access.namenodes = "hdfs://master:8020" spark.loglevel = "ERROR" spark.cores.max = 2 spark.yarn.am.memory = "512m" spark.yarn.am.cores = 1 spark.executor.instances = 2 spark.executor.cores = 1 spark.executor.memory = "2g" }] } } optional: 如果HDFS 配置了高可用(HA)、或者HDFS 配置了kerberos、或者YARN 配置了高可用(HA)、或者YARN 配置了kerberos
将cluster元素中相关部分改为以下配置, 请根据实际情况修改。具体值可查阅hdfs配置文件和yarn配置文件。 #### HDFS HA #### spark.hadoop.fs.defaultFS="hdfs://service_name" spark.hadoop.dfs.nameservices="service_name" spark.hadoop.dfs.ha.namenodes.service_name="xxx1,xxx2" spark.hadoop.dfs.namenode.rpc-address.abdt.xxx1="xxx1_host:8020" spark.hadoop.dfs.namenode.rpc-address.abdt.xxx2="xxx2_host:8020" spark.hadoop.dfs.client.failover.proxy.provider.abdt="org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider" spark.yarn.stagingDir = "hdfs://service_name/tmp" #### HDFS kerberos #### dfs.namenode.kerberos.principal = "" dfs.namenode.kerberos.keytab = "" #### YARN HA #### spark.hadoop.yarn.resourcemanager.ha.enabled=true spark.hadoop.yarn.resourcemanager.ha.rm-ids="yyy1,yyy2" spark.hadoop.yarn.resourcemanager.hostname.rm1="yyy1_host" spark.hadoop.yarn.resourcemanager.hostname.rm2="yyy2_ho st" #### YARN kerberos #### spark.yarn.principal = "" spark.yarn.keytab = ""
五、分发安装包
将MySQL Jdbc驱动包放置到libs和runtime目录下, 然后将整个moonbox安装目录拷贝到所有安装节点, 确保位置与主节点位置一致。
六、启动集群
在master节点执行 sbin/start-all.sh
七、停止集群
在master节点执行 sbin/stop-all.sh
八、检查集群是否成功启动
在master节点执行如下命令, 将会看到 MoonboxMaster 进程 jps | grep Moonbox
在worker节点执行如下命令, 将会看到 MoonboxWorker 进程 jps | grep Moonbox
在worker节点执行如下命令, 将会看到与配置文件对应个数的 SparkSubmit 进程 jps -m | grep Spark
使用moonbox-cluster命令查看集群信息 bin/moonbox-cluster workers bin/moonbox-cluster apps
如果检查通过, 则集群启动成功, 即可参阅examples部分开始体验啦。 如果检查失败, 可通过查看master节点或者worker节点上logs目录下的日志进行问题排查。
开源地址: https://github.com/edp963/moonbox
拓展阅读: 数据虚拟化即服务(DVtaaS)平台解决方案 - Moonbox 宜信开源|Moonbox_v0.3_beta重大发布 | Grid全新重构,更快更解耦
来源:宜信技术学院
大数据
2019-07-10 10:51:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
导入数据-iObjects Python with JupyterHub for K8s。
In [1]: from iobjectspy import (import_shape, import_img) import os import sys In [2]: # 设置示例数据路径 example_data_dir = '' # 设置示例数据路径 example_data_dir = '/home/jovyan/data/smdata/' # 设置结果输出路径 out_dir = os.path.join(example_data_dir, 'out') if not os.path.exists(out_dir): os.makedirs(out_dir) print("Makedir: ", out_dir) else: print("Existed dir: ", out_dir) Existed dir: /home/jovyan/data/smdata/out In [3]: os.path.exists("/home/jovyan/data/smdata/out") Out[3]: True In [4]: def progress_func(step_event): sys.stdout.write('%s,%s %%\n' % (step_event.title, step_event.message)) def print_result(result): if result is not None: for item in result: if isinstance(item, str): sys.stdout.write('导入数据成功,导入数据到数据集 %s \n' % item) else: sys.stdout.write('导入数据成功,导入数据到数据集 %s \n' % item.name) else: sys.stdout.write('导入数据失败\n') def import_shp_test(): """导入 county_p.shp 文件到 county_p.udb 数据源中。导入成功后返回结果数据集的名称。""" input_path = os.path.join(example_data_dir, 'County_p.shp') result = import_shape(input_path, os.path.join(out_dir, 'out_import_data_county_p.udb'), progress=progress_func) print_result(result) def import_image_test(): """ 导入 multibands.img 文件到 image.udb 数据源中,以多波段数据集方式导入,即导入成功后会得到一个多波段的影像数据集。 导入成功后返回结果数据集的名称 """ input_path = os.path.join(example_data_dir, 'multibands.img') result = import_img(input_path, os.path.join(out_dir, 'out_import_data_multibands.udb'), multi_band_mode='MULTIBAND', progress=progress_func) print_result(result) In [5]: if __name__ == '__main__': # 导入 shape 文件 import_shp_test() # 导入多波段 img 文件 import_image_test() java -cp /opt/conda/lib/python3.6/site-packages/iobjectspy/_jsuperpy/jars/com.supermap.jsuperpy-9.1.1.jar com.supermap.jsuperpy.ApplicationExample 127.0.0.1 58639 [iObjectsPy]: Connection gateway-service successful, Python callback port bind 34241 数据导入,正在导入 'County_p.shp' 文件,已经完成 0% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 0% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 0% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 1% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 1% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 1% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 2% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 2% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 2% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 2% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 3% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 3% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 3% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 4% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 4% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 4% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 5% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 5% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 5% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 5% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 6% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 6% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 6% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 7% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 7% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 7% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 7% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 8% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 8% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 8% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 9% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 9% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 9% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 10% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 10% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 10% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 10% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 11% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 11% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 11% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 12% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 12% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 12% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 12% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 13% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 13% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 13% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 14% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 14% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 14% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 15% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 15% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 15% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 15% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 16% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 16% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 16% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 17% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 17% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 17% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 17% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 18% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 18% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 18% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 19% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 19% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 19% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 20% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 20% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 20% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 20% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 21% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 21% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 21% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 22% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 22% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 22% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 22% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 23% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 23% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 23% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 24% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 24% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 24% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 25% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 25% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 25% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 25% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 26% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 26% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 26% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 27% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 27% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 27% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 28% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 28% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 28% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 28% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 29% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 29% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 29% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 30% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 30% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 30% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 30% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 31% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 31% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 31% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 32% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 32% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 32% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 33% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 33% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 33% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 33% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 34% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 34% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 34% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 35% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 35% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 35% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 35% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 36% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 36% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 36% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 37% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 37% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 37% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 38% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 38% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 38% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 38% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 39% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 39% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 39% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 40% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 40% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 40% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 40% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 41% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 41% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 41% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 42% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 42% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 42% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 43% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 43% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 43% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 43% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 44% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 44% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 44% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 45% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 45% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 45% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 45% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 46% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 46% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 46% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 47% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 47% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 47% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 48% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 48% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 48% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 48% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 49% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 49% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 49% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 50% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 50% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 50% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 51% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 51% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 51% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 51% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 52% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 52% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 52% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 53% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 53% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 53% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 53% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 54% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 54% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 54% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 55% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 55% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 55% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 56% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 56% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 56% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 56% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 57% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 57% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 57% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 58% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 58% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 58% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 58% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 59% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 59% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 59% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 60% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 60% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 60% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 61% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 61% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 61% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 61% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 62% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 62% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 62% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 63% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 63% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 63% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 63% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 64% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 64% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 64% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 65% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 65% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 65% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 66% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 66% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 66% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 66% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 67% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 67% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 67% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 68% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 68% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 68% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 68% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 69% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 69% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 69% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 70% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 70% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 70% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 71% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 71% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 71% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 71% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 72% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 72% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 72% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 73% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 73% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 73% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 73% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 74% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 74% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 74% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 75% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 75% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 75% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 76% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 76% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 76% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 76% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 77% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 77% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 77% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 78% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 78% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 78% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 79% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 79% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 79% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 79% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 80% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 80% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 80% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 81% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 81% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 81% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 81% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 82% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 82% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 82% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 83% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 83% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 83% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 84% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 84% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 84% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 84% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 85% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 85% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 85% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 86% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 86% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 86% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 86% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 87% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 87% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 87% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 88% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 88% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 88% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 89% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 89% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 89% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 89% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 90% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 90% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 90% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 91% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 91% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 91% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 91% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 92% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 92% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 92% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 93% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 93% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 93% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 94% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 94% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 94% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 94% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 95% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 95% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 95% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 96% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 96% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 96% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 96% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 97% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 97% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 97% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 98% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 98% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 98% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 99% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 99% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 99% % 数据导入,正在导入 'County_p.shp' 文件,已经完成 100% % 导入数据成功,导入数据到数据集 County_p 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 4% % 数据导入,正在导入 'multibands.img' 文件,已经完成 4% % 数据导入,正在导入 'multibands.img' 文件,已经完成 8% % 数据导入,正在导入 'multibands.img' 文件,已经完成 8% % 数据导入,正在导入 'multibands.img' 文件,已经完成 12% % 数据导入,正在导入 'multibands.img' 文件,已经完成 12% % 数据导入,正在导入 'multibands.img' 文件,已经完成 12% % 数据导入,正在导入 'multibands.img' 文件,已经完成 16% % 数据导入,正在导入 'multibands.img' 文件,已经完成 16% % 数据导入,正在导入 'multibands.img' 文件,已经完成 16% % 数据导入,正在导入 'multibands.img' 文件,已经完成 20% % 数据导入,正在导入 'multibands.img' 文件,已经完成 20% % 数据导入,正在导入 'multibands.img' 文件,已经完成 24% % 数据导入,正在导入 'multibands.img' 文件,已经完成 24% % 数据导入,正在导入 'multibands.img' 文件,已经完成 28% % 数据导入,正在导入 'multibands.img' 文件,已经完成 28% % 数据导入,正在导入 'multibands.img' 文件,已经完成 28% % 数据导入,正在导入 'multibands.img' 文件,已经完成 32% % 数据导入,正在导入 'multibands.img' 文件,已经完成 32% % 数据导入,正在导入 'multibands.img' 文件,已经完成 32% % 数据导入,正在导入 'multibands.img' 文件,已经完成 36% % 数据导入,正在导入 'multibands.img' 文件,已经完成 36% % 数据导入,正在导入 'multibands.img' 文件,已经完成 40% % 数据导入,正在导入 'multibands.img' 文件,已经完成 40% % 数据导入,正在导入 'multibands.img' 文件,已经完成 44% % 数据导入,正在导入 'multibands.img' 文件,已经完成 44% % 数据导入,正在导入 'multibands.img' 文件,已经完成 44% % 数据导入,正在导入 'multibands.img' 文件,已经完成 48% % 数据导入,正在导入 'multibands.img' 文件,已经完成 48% % 数据导入,正在导入 'multibands.img' 文件,已经完成 48% % 数据导入,正在导入 'multibands.img' 文件,已经完成 52% % 数据导入,正在导入 'multibands.img' 文件,已经完成 52% % 数据导入,正在导入 'multibands.img' 文件,已经完成 56% % 数据导入,正在导入 'multibands.img' 文件,已经完成 56% % 数据导入,正在导入 'multibands.img' 文件,已经完成 60% % 数据导入,正在导入 'multibands.img' 文件,已经完成 60% % 数据导入,正在导入 'multibands.img' 文件,已经完成 60% % 数据导入,正在导入 'multibands.img' 文件,已经完成 64% % 数据导入,正在导入 'multibands.img' 文件,已经完成 64% % 数据导入,正在导入 'multibands.img' 文件,已经完成 64% % 数据导入,正在导入 'multibands.img' 文件,已经完成 68% % 数据导入,正在导入 'multibands.img' 文件,已经完成 68% % 数据导入,正在导入 'multibands.img' 文件,已经完成 72% % 数据导入,正在导入 'multibands.img' 文件,已经完成 72% % 数据导入,正在导入 'multibands.img' 文件,已经完成 76% % 数据导入,正在导入 'multibands.img' 文件,已经完成 76% % 数据导入,正在导入 'multibands.img' 文件,已经完成 76% % 数据导入,正在导入 'multibands.img' 文件,已经完成 80% % 数据导入,正在导入 'multibands.img' 文件,已经完成 80% % 数据导入,正在导入 'multibands.img' 文件,已经完成 80% % 数据导入,正在导入 'multibands.img' 文件,已经完成 85% % 数据导入,正在导入 'multibands.img' 文件,已经完成 85% % 数据导入,正在导入 'multibands.img' 文件,已经完成 90% % 数据导入,正在导入 'multibands.img' 文件,已经完成 90% % 数据导入,正在导入 'multibands.img' 文件,已经完成 96% % 数据导入,正在导入 'multibands.img' 文件,已经完成 96% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 50% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 50% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 50% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 50% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 50% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 50% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 0% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 数据导入,正在导入 'multibands.img' 文件,已经完成 100% % 导入数据成功,导入数据到数据集 multibands
大数据
2019-07-09 14:23:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一、资源相关参数
1.以下参数是在用户自己的mr应用程序中配置就可以生效
配置参数 参数说明 mapreduce.map.memory.mb 一个Map Task可使用的资源上限(单位:MB),默认为1024。如果Map Task实际使用的资源量超过该值,则会被强制杀死。
mapreduce.reduce.memory.mb 一个Reduce Task可使用的资源上限(单位:MB),默认为1024。如果Reduce Task实际使用的资源量超过该值,则会被强制杀死。
mapreduce.map.java.opts Map Task的JVM参数,你可以在此配置默认的java heap size等参数, e.g.
“-Xmx1024m -verbose:gc -Xloggc:/tmp/@taskid@.gc” (@taskid@会被Hadoop框架自动换为相应的taskid), 默认值: “”
mapreduce.reduce.java.opts Reduce Task的JVM参数,你可以在此配置默认的java heap size等参数, e.g.
“-Xmx1024m -verbose:gc -Xloggc:/tmp/@taskid@.gc”, 默认值: “”
mapreduce.map.cpu.vcores
mapreduce.reduce.cpu.vcores
每个Map task可使用的最多cpu core数目, 默认值: 1
每个Reduce task可使用的最多cpu core数目, 默认值: 1
2.应该在yarn启动之前就配置在服务器的配置文件中才能生效
配置参数 参数说明 yarn.scheduler.minimum-allocation-mb 1024 给应用程序container分配的最小内存
yarn.scheduler.maximum-allocation-mb 8192 给应用程序container分配的最大内存
yarn.scheduler.minimum-allocation-vcores 1
yarn.scheduler.maximum-allocation-vcores 32
yarn.nodemanager.resource.memory-mb 8192
3.shuffle性能优化的关键参数,应在yarn启动之前就配置好
配置参数 参数说明
mapreduce.task.io.sort.mb 100 mapreduce.map.sort.spill.percent 0.8
shuffle的环形缓冲区大小,默认100m 环形缓冲区溢出的阈值,默认80%
二、容错相关参数
配置参数 参数说明 mapreduce.map.maxattempts 每个Map Task最大重试次数,一旦重试参数超过该值,则认为Map Task运行失败,默认值:4。
mapreduce.reduce.maxattempts 每个Reduce Task最大重试次数,一旦重试参数超过该值,则认为Map Task运行失败,默认值:4。
mapreduce.map.failures.maxpercent 当失败的Map Task失败比例超过该值为,整个作业则失败,默认值为0. 如果你的应用程序允许丢弃部分输入数据,则该该值设为一个大于0的值,比如5,表示如果有低于5%的Map Task失败(如果一个Map Task重试次数超过mapreduce.map.maxattempts,则认为这个Map Task失败,其对应的输入数据将不会产生任何结果),整个作业扔认为成功。
mapreduce.reduce.failures.maxpercent
mapreduce.task.timeout
当失败的Reduce Task失败比例超过该值为,整个作业则失败,默认值为0。
Task超时时间,经常需要设置的一个参数,该参数表达的意思为:如果一个task在一定时间内没有任何进入,即不会读取新的数据,也没有输出数据,则认为该task处于block状态,可能是卡住了,也许永远会卡主,为了防止因为用户程序永远block住不退出,则强制设置了一个该超时时间(单位毫秒),默认是300000。如果你的程序对每条输入数据的处理时间过长(比如会访问数据库,通过网络拉取数据等),建议将该参数调大,该参数过小常出现的错误提示是“AttemptID:attempt_14267829456721_123456_m_000224_0 Timed out after 300 secsContainer killed by the ApplicationMaster.”。
大数据
2019-07-08 23:12:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
代码下载地址: https://github.com/tazhigang/big-data-github.git
一、前期准备 由于本案例是在案例六的基础上做的优化,所以需求及数据输入输出请参考案例六;初次之外需要拷贝pd.txt文件在本地电脑J盘的根目录下以做参考 本案例只需要上传order.txt到HDFS上即可-"/user/hadoop/order_productv2/input"
二、代码 DistributedCacheDriver.java package com.ittzg.hadoop.orderproductv2; import com.ittzg.hadoop.orderproduct.OrderAndProductBean; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.util.HashMap; import java.util.Map; /** * @email: tazhigang095@163.com * @author: ittzg * @date: 2019/7/6 20:46 */ public class DistributedCacheDriver { public static class DistributedCacheMapper extends Mapper{ Map map = new HashMap(); @Override protected void setup(Context context) throws IOException, InterruptedException { //获取缓存在中的文件 BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("j:/pd.txt"))); String line; while(StringUtils.isNotEmpty(line = reader.readLine())){ // 2 切割 String[] fields = line.split("\t"); // 3 缓存数据到集合 System.out.println(fields[0]+":"+fields[0].trim().length()); map.put(fields[0], fields[1]); } // 4 关流 reader.close(); } OrderAndProductBean orderAndProductBean= new OrderAndProductBean(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { System.out.println(map.toString()); String line = value.toString(); String[] split = line.split("\t"); orderAndProductBean.setOrderId(split[0]); orderAndProductBean.setPdId(split[1]); orderAndProductBean.setAccount(split[2]); orderAndProductBean.setPdName(map.get(split[1])); orderAndProductBean.setFlag("0"); context.write(orderAndProductBean,NullWritable.get()); } } public static class OrderProDuctReduce extends Reducer{ @Override protected void reduce(OrderAndProductBean key, Iterable values, Context context) throws IOException, InterruptedException { context.write(key,NullWritable.get()); } } public static void main(String[] args) throws Exception{ // 设置输入输出路径 String input = "hdfs://hadoop-ip-101:9000/user/hadoop/order_productv2/input"; String output = "hdfs://hadoop-ip-101:9000/user/hadoop/order_productv2/output"; Configuration conf = new Configuration(); conf.set("mapreduce.app-submission.cross-platform","true"); Job job = Job.getInstance(conf); // job.setJar("F:\\big-data-github\\hadoop-parent\\hadoop-order-product\\target\\hadoop-order-product-1.0-SNAPSHOT.jar"); job.setMapperClass(DistributedCacheMapper.class); job.setReducerClass(OrderProDuctReduce.class); job.setMapOutputKeyClass(OrderAndProductBean.class); job.setMapOutputValueClass(NullWritable.class); job.setOutputKeyClass(OrderAndProductBean.class); job.setOutputValueClass(NullWritable.class); // 6 加载缓存数据 job.addCacheFile(new URI("file:/j:/pd.txt")); FileSystem fs = FileSystem.get(new URI("hdfs://hadoop-ip-101:9000"),conf,"hadoop"); Path outPath = new Path(output); if(fs.exists(outPath)){ fs.delete(outPath,true); } FileInputFormat.addInputPath(job,new Path(input)); FileOutputFormat.setOutputPath(job,outPath); boolean bool = job.waitForCompletion(true); System.exit(bool?0:1); } }
三、运行结果 网页浏览
文件内容下载浏览
大数据
2019-07-07 12:24:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> # 计算矩阵 # 导入numpy模块,并给它指定别名np import numpy as np # 使用numpy模块的array()函数,输入一个二维矩阵 a = np.array([[1,2],[3,4]]) # 使用np.linalg.det()函数,计算二维矩阵的行列式 b = np.linalg.det(a) # 计算二维矩阵的行列式 print(b) # 输入一个三维矩阵 c = np.array([[2,22,3],[40,5,66],[77,8,19]]) # 使用np.linalg.det()函数,计算此三维矩阵的行列式 d = np.linalg.det(c) # 计算此三维矩阵的行列式 print(d) # 生成一个 3×3 单位矩阵 m1 = np.eye(3) print(m1) # 再生成另一个 3×3 矩阵用来做乘法运算 m2 = np.arange(1,10).reshape(3,3) print(m2) # 将两个矩阵相乘 m_multiply = np.dot(m1,m2) print(m_multiply)
大数据
2019-07-06 02:55:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
准备
要执行Map reduce程序,首先得安装hadoop,hadoop安装可以参考 hadoop安装
启动hdfs和yarn start-dfs.cmd start-yarn.cmd
创建待处理数据目录: hadoop fs -mkdir /wc hadoop fs -mkdir /wc/in # 查看目录 hadoop fs -ls -R /
上传待处理数据文件: hadoop fs -put file:///G:\note\bigdata\hadoop\wordcount\word1.txt /wc/in hadoop fs -put file:///G:\note\bigdata\hadoop\wordcount\word2.txt /wc/in
其中数据文件内容如下: word1.txt hello world hello hadoop
word2.txt hadoop world hadoop learn
WordCount与Map Reduce流程 import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.IOException; import java.util.StringTokenizer; public class WordCount { public static class TokenizerMapper extends Mapper{ private final static IntWritable one = new IntWritable(1); private Text word = new Text(); @Override public void map(Object key, Text value, Context context) throws IOException, InterruptedException { System.out.println("[map-key]:" + key + " [map-value]:" + value); StringTokenizer stringTokenizer = new StringTokenizer(value.toString()); while (stringTokenizer.hasMoreTokens()){ word.set(stringTokenizer.nextToken()); context.write(word,one); } } } public static class IntSumReducer extends Reducer { private IntWritable result = new IntWritable(); @Override public void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException { int sum = 0; StringBuffer sb = new StringBuffer(); for(IntWritable num : values){ sum += num.get(); sb.append(num); sb.append("、"); } result.set(sum); context.write(key,result); System.out.println("[reduce-key]:" + key + " [reduce-values]:" + sb.substring(0,sb.length()-1)); } } //job:http://localhost:8088/ //hdfs:http://localhost:9870/ public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration configuration = new Configuration(); configuration.set("fs.default.name", "hdfs://localhost:9000"); Job job = Job.getInstance(configuration, "WC"); job.setJarByClass(WordCount.class); job.setMapperClass(TokenizerMapper.class); // job.setCombinerClass(IntSumReducer.class); job.setReducerClass(IntSumReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); Path inPath = new Path("/wc/in/"); Path outPath = new Path("/wc/out/"); FileInputFormat.addInputPath(job, inPath); FileOutputFormat.setOutputPath(job, outPath); System.exit(job.waitForCompletion(true) ? 0:1); } }
如果输出目录已经存在,可以使用下面的命令删除: hadoop fs -rm -r /wc/out
我们先来看一下程序的输出: [map-key]:0 [map-value]:hadoop world [map-key]:14 [map-value]:hadoop learn [map-key]:0 [map-value]:hello world [map-key]:13 [map-value]:hello hadoop [reduce-key]:hadoop [reduce-values]:1、1、1 [reduce-key]:hello [reduce-values]:1、1 [reduce-key]:learn [reduce-values]:1 [reduce-key]:world [reduce-values]:1、1
从输出我们可以推测hadoop的map过程是:hadoop把待处理的文件 按行 拆分,每一行调用map函数,map函数的key就是每一行的起始位置,值就是这一行的值。
map处理之后,再按key-value的形式写中间值。
reduce函数就是处理这些中间过程,参数的key就是map写入的key,value就是,map之后按key分组的value。
Combin过程
再Map和Reduce中间还可以加入Combin过程,用于处理中间结果,减少网络间数据传输的数据量。
Map->Reduce->Combin
我们把上面程序中job.setCombinerClass(IntSumReducer.class);注释去掉就可以获取到有Combiner的输出: [map-key]:0 [map-value]:hadoop world [map-key]:14 [map-value]:hadoop learn [reduce-key]:hadoop [reduce-values]:1、1 [reduce-key]:learn [reduce-values]:1 [reduce-key]:world [reduce-values]:1 [map-key]:0 [map-value]:hello world [map-key]:13 [map-value]:hello hadoop [reduce-key]:hadoop [reduce-values]:1 [reduce-key]:hello [reduce-values]:1、1 [reduce-key]:world [reduce-values]:1 [reduce-key]:hadoop [reduce-values]:2、1 [reduce-key]:hello [reduce-values]:2 [reduce-key]:learn [reduce-values]:1 [reduce-key]:world [reduce-values]:1、1
从上面的输出我们可以看到,map之后有一个reduce输出,其实是combin操作,combin和reduce的区别是combin是在单节点内部执行的,为了减小中间数据。
注意:combin操作必须满足结合律,例如: 加法,求总和:a+b+c+d = (a+b) + (c+d) 最大值:max(a+b+c+d) = max(max(a,b),max(c,d))
均值就不能使用combin操作: (a+b+c+d)/4 明显不等价于 ((a+b)/2 + (c+d)/2)/2
pom文件 4.0.0 org.curitis hadoop-learn 1.0.0 5.1.3.RELEASE 4.11 3.0.2 1.10.1 org.apache.hadoop hadoop-common ${hadoop.version} com.fasterxml.jackson.core * org.apache.hadoop hadoop-hdfs ${hadoop.version} com.fasterxml.jackson.core * org.apache.hadoop hadoop-client ${hadoop.version} org.apache.hadoop hadoop-mapreduce-client-core ${hadoop.version} com.fasterxml.jackson.core * org.apache.parquet parquet-hadoop ${parquet.version} org.apache.parquet parquet-column ${parquet.version} org.apache.parquet parquet-common ${parquet.version} org.apache.parquet parquet-encoding ${parquet.version} com.alibaba fastjson 1.2.56 org.springframework spring-test ${spring.version} test junit junit ${junit.version} test io.netty netty-all 4.1.25.Final org.apache.maven.plugins maven-compiler-plugin 8 8
大数据
2019-07-04 08:21:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
《Spark 官方文档》Spark SQL, DataFrames 以及 Datasets 编程指南
spark-1.6.0 [原文地址]
Spark SQL, DataFrames 以及 Datasets 编程指南
概要
Spark SQL是Spark中处理结构化数据的模块。与基础的Spark RDD API不同,Spark SQL的接口提供了更多关于数据的结构信息和计算任务的运行时信息。在Spark内部,Spark SQL会能够用于做优化的信息比RDD API更多一些。Spark SQL如今有了三种不同的API:SQL语句、DataFrame API和最新的Dataset API。不过真正运行计算的时候,无论你使用哪种API或语言,Spark SQL使用的执行引擎都是同一个。这种底层的统一,使开发者可以在不同的API之间来回切换,你可以选择一种最自然的方式,来表达你的需求。

本文中所有的示例都使用Spark发布版本中自带的示例数据,并且可以在spark-shell、pyspark shell以及sparkR shell中运行。

SQL
Spark SQL的一种用法是直接执行SQL查询语句,你可使用最基本的SQL语法,也可以选择HiveQL语法。Spark SQL可以从已有的Hive中读取数据。更详细的请参考 Hive Tables 这一节。如果用其他编程语言运行SQL,Spark SQL将以 DataFrame 返回结果。你还可以通过命令行 command-line 或者 JDBC/ODBC 使用Spark SQL。
DataFrames
DataFrame是一种分布式数据集合,每一条数据都由几个命名字段组成。概念上来说,她和关系型数据库的表 或者 R和Python中的data frame等价,只不过在底层,DataFrame采用了更多优化。DataFrame可以从很多数据源( sources )加载数据并构造得到,如:结构化数据文件,Hive中的表,外部数据库,或者已有的RDD。
DataFrame API支持 Scala , Java , Python , and R 。
Datasets
Dataset是Spark-1.6新增的一种API,目前还是实验性的。Dataset想要把RDD的优势(强类型,可以使用lambda表达式函数)和Spark SQL的优化执行引擎的优势结合到一起。Dataset可以由JVM对象构建( constructed )得到,而后Dataset上可以使用各种transformation算子(map,flatMap,filter 等)。
Dataset API 对 Scala 和 Java 的支持接口是一致的,但目前还不支持Python,不过Python自身就有语言动态特性优势(例如,你可以使用字段名来访问数据,row.columnName)。对Python的完整支持在未来的版本会增加进来。
入门
入口:SQLContext Scala Java Python R
Spark SQL所有的功能入口都是 SQLContext 类,及其子类。不过要创建一个SQLContext对象,首先需要有一个SparkContext对象。 val sc: SparkContext // 假设已经有一个 SparkContext 对象 val sqlContext = new org.apache.spark.sql.SQLContext(sc) // 用于包含RDD到DataFrame隐式转换操作 import sqlContext.implicits._
除了SQLContext之外,你也可以创建HiveContext,HiveContext是SQLContext 的超集。
除了SQLContext的功能之外,HiveContext还提供了完整的HiveQL语法,UDF使用,以及对Hive表中数据的访问。要使用HiveContext,你并不需要安装Hive,而且SQLContext能用的数据源,HiveContext也一样能用。HiveContext是单独打包的,从而避免了在默认的Spark发布版本中包含所有的Hive依赖。如果这些依赖对你来说不是问题(不会造成依赖冲突等),建议你在Spark-1.3之前使用HiveContext。而后续的Spark版本,将会逐渐把SQLContext升级到和HiveContext功能差不多的状态。
spark.sql.dialect选项可以指定不同的SQL变种(或者叫SQL方言)。这个参数可以在SparkContext.setConf里指定,也可以通过 SQL语句的SET key=value命令指定。对于SQLContext,该配置目前唯一的可选值就是”sql”,这个变种使用一个Spark SQL自带的简易SQL解析器。而对于HiveContext,spark.sql.dialect 默认值为”hiveql”,当然你也可以将其值设回”sql”。仅就目前而言,HiveSQL解析器支持更加完整的SQL语法,所以大部分情况下,推荐使用HiveContext。
创建DataFrame
Spark应用可以用SparkContext创建DataFrame,所需的数据来源可以是已有的RDD( existing RDD ),或者Hive表,或者其他数据源( data sources .)
以下是一个从JSON文件创建DataFrame的小栗子: Scala Java Python R val sc: SparkContext // 已有的 SparkContext. val sqlContext = new org.apache.spark.sql.SQLContext(sc) val df = sqlContext.read.json("examples/src/main/resources/people.json") // 将DataFrame内容打印到stdout df.show()
DataFrame操作
DataFrame提供了结构化数据的领域专用语言支持,包括 Scala , Java , Python and R .
这里我们给出一个结构化数据处理的基本示例: Scala Java Python R val sc: SparkContext // 已有的 SparkContext. val sqlContext = new org.apache.spark.sql.SQLContext(sc) // 创建一个 DataFrame val df = sqlContext.read.json("examples/src/main/resources/people.json") // 展示 DataFrame 的内容 df.show() // age name // null Michael // 30 Andy // 19 Justin // 打印数据树形结构 df.printSchema() // root // |-- age: long (nullable = true) // |-- name: string (nullable = true) // select "name" 字段 df.select("name").show() // name // Michael // Andy // Justin // 展示所有人,但所有人的 age 都加1 df.select(df("name"), df("age") + 1).show() // name (age + 1) // Michael null // Andy 31 // Justin 20 // 筛选出年龄大于21的人 df.filter(df("age") > 21).show() // age name // 30 Andy // 计算各个年龄的人数 df.groupBy("age").count().show() // age count // null 1 // 19 1 // 30 1
DataFrame的完整API列表请参考这里: API Documentation
除了简单的字段引用和表达式支持之外,DataFrame还提供了丰富的工具函数库,包括字符串组装,日期处理,常见的数学函数等。完整列表见这里: DataFrame Function Reference .
编程方式执行SQL查询
SQLContext.sql可以执行一个SQL查询,并返回DataFrame结果。 Scala Java Python R val sqlContext = ... // 已有一个 SQLContext 对象 val df = sqlContext.sql("SELECT * FROM table")
创建Dataset
Dataset API和RDD类似,不过Dataset不使用Java序列化或者Kryo,而是使用专用的编码器( Encoder )来序列化对象和跨网络传输通信。如果这个编码器和标准序列化都能把对象转字节,那么编码器就可以根据代码动态生成,并使用一种特殊数据格式,这种格式下的对象不需要反序列化回来,就能允许Spark进行操作,如过滤、排序、哈希等。 Scala Java // 对普通类型数据的Encoder是由 importing sqlContext.implicits._ 自动提供的 val ds = Seq(1, 2, 3).toDS() ds.map(_ + 1).collect() // 返回: Array(2, 3, 4) // 以下这行不仅定义了case class,同时也自动为其创建了Encoder case class Person(name: String, age: Long) val ds = Seq(Person("Andy", 32)).toDS() // DataFrame 只需提供一个和数据schema对应的class即可转换为 Dataset。Spark会根据字段名进行映射。 val path = "examples/src/main/resources/people.json" val people = sqlContext.read.json(path).as[Person]
和RDD互操作
Spark SQL有两种方法将RDD转为DataFrame。
1. 使用反射机制,推导包含指定类型对象RDD的schema。这种基于反射机制的方法使代码更简洁,而且如果你事先知道数据schema,推荐使用这种方式;
2. 编程方式构建一个schema,然后应用到指定RDD上。这种方式更啰嗦,但如果你事先不知道数据有哪些字段,或者数据schema是运行时读取进来的,那么你很可能需要用这种方式。
利用反射推导schema Scala Java Python
Spark SQL的Scala接口支持自动将包含case class对象的RDD转为DataFrame。对应的case class定义了表的schema。case class的参数名通过反射,映射为表的字段名。case class还可以嵌套一些复杂类型,如Seq和Array。RDD隐式转换成DataFrame后,可以进一步注册成表。随后,你就可以对表中数据使用SQL语句查询了。 // sc 是已有的 SparkContext 对象 val sqlContext = new org.apache.spark.sql.SQLContext(sc) // 为了支持RDD到DataFrame的隐式转换 import sqlContext.implicits._ // 定义一个case class. // 注意:Scala 2.10的case class最多支持22个字段,要绕过这一限制, // 你可以使用自定义class,并实现Product接口。当然,你也可以改用编程方式定义schema case class Person(name: String, age: Int) // 创建一个包含Person对象的RDD,并将其注册成table val people = sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)).toDF() people.registerTempTable("people") // sqlContext.sql方法可以直接执行SQL语句 val teenagers = sqlContext.sql("SELECT name, age FROM people WHERE age >= 13 AND age <= 19") // SQL查询的返回结果是一个DataFrame,且能够支持所有常见的RDD算子 // 查询结果中每行的字段可以按字段索引访问: teenagers.map(t => "Name: " + t(0)).collect().foreach(println) // 或者按字段名访问: teenagers.map(t => "Name: " + t.getAs[String]("name")).collect().foreach(println) // row.getValuesMap[T] 会一次性返回多列,并以Map[String, T]为返回结果类型 teenagers.map(_.getValuesMap[Any](List("name", "age"))).collect().foreach(println) // 返回结果: Map("name" -> "Justin", "age" -> 19)
编程方式定义Schema Scala Java Python
如果不能事先通过case class定义schema(例如,记录的字段结构是保存在一个字符串,或者其他文本数据集中,需要先解析,又或者字段对不同用户有所不同),那么你可能需要按以下三个步骤,以编程方式的创建一个DataFrame: 从已有的RDD创建一个包含Row对象的RDD 用StructType创建一个schema,和步骤1中创建的RDD的结构相匹配 把得到的schema应用于包含Row对象的RDD,调用这个方法来实现这一步:SQLContext.createDataFrame
For example:
例如: // sc 是已有的SparkContext对象 val sqlContext = new org.apache.spark.sql.SQLContext(sc) // 创建一个RDD val people = sc.textFile("examples/src/main/resources/people.txt") // 数据的schema被编码与一个字符串中 val schemaString = "name age" // Import Row. import org.apache.spark.sql.Row; // Import Spark SQL 各个数据类型 import org.apache.spark.sql.types.{StructType,StructField,StringType}; // 基于前面的字符串生成schema val schema = StructType( schemaString.split(" ").map(fieldName => StructField(fieldName, StringType, true))) // 将RDD[people]的各个记录转换为Rows,即:得到一个包含Row对象的RDD val rowRDD = people.map(_.split(",")).map(p => Row(p(0), p(1).trim)) // 将schema应用到包含Row对象的RDD上,得到一个DataFrame val peopleDataFrame = sqlContext.createDataFrame(rowRDD, schema) // 将DataFrame注册为table peopleDataFrame.registerTempTable("people") // 执行SQL语句 val results = sqlContext.sql("SELECT name FROM people") // SQL查询的结果是DataFrame,且能够支持所有常见的RDD算子 // 并且其字段可以以索引访问,也可以用字段名访问 results.map(t => "Name: " + t(0)).collect().foreach(println)
数据源
Spark SQL支持基于DataFrame操作一系列不同的数据源。DataFrame既可以当成一个普通RDD来操作,也可以将其注册成一个临时表来查询。把DataFrame注册为table之后,你就可以基于这个table执行SQL语句了。本节将描述加载和保存数据的一些通用方法,包含了不同的Spark数据源,然后深入介绍一下内建数据源可用选项。
通用加载/保存函数
在最简单的情况下,所有操作都会以默认类型数据源来加载数据(默认是Parquet,除非修改了spark.sql.sources.default 配置)。 Scala Java Python R val df = sqlContext.read.load("examples/src/main/resources/users.parquet") df.select("name", "favorite_color").write.save("namesAndFavColors.parquet")
手动指定选项
你也可以手动指定数据源,并设置一些额外的选项参数。数据源可由其全名指定(如,org.apache.spark.sql.parquet),而对于内建支持的数据源,可以使用简写名(json, parquet, jdbc)。任意类型数据源创建的DataFrame都可以用下面这种语法转成其他类型数据格式。 Scala Java Python R val df = sqlContext.read.format("json").load("examples/src/main/resources/people.json") df.select("name", "age").write.format("parquet").save("namesAndAges.parquet")
直接对文件使用SQL
Spark SQL还支持直接对文件使用SQL查询,不需要用read方法把文件加载进来。 Scala Java Python R val df = sqlContext.sql("SELECT * FROM parquet.`examples/src/main/resources/users.parquet`")
保存模式
Save操作有一个可选参数SaveMode,用这个参数可以指定如何处理数据已经存在的情况。很重要的一点是,这些保存模式都没有加锁,所以其操作也不是原子性的。另外,如果使用Overwrite模式,实际操作是,先删除数据,再写新数据。
仅Scala/Java 所有支持的语言 含义
SaveMode.ErrorIfExists (default) "error" (default) (默认模式)从DataFrame向数据源保存数据时,如果数据已经存在,则抛异常。
SaveMode.Append "append" 如果数据或表已经存在,则将DataFrame的数据追加到已有数据的尾部。
SaveMode.Overwrite
SaveMode.Ignore
"overwrite"
"ignore"
如果数据或表已经存在,则用DataFrame数据覆盖之。
如果数据已经存在,那就放弃保存DataFrame数据。这和SQL里CREATE TABLE IF NOT EXISTS有点类似。
保存到持久化表
在使用HiveContext的时候,DataFrame可以用saveAsTable方法,将数据保存成持久化的表。与registerTempTable不同,saveAsTable会将DataFrame的实际数据内容保存下来,并且在HiveMetastore中创建一个游标指针。持久化的表会一直保留,即使Spark程序重启也没有影响,只要你连接到同一个metastore就可以读取其数据。读取持久化表时,只需要用用表名作为参数,调用SQLContext.table方法即可得到对应DataFrame。
默认情况下,saveAsTable会创建一个”managed table“,也就是说这个表数据的位置是由metastore控制的。同样,如果删除表,其数据也会同步删除。
Parquet文件
Parquet 是一种流行的列式存储格式。Spark SQL提供对Parquet文件的读写支持,而且Parquet文件能够自动保存原始数据的schema。写Parquet文件的时候,所有的字段都会自动转成nullable,以便向后兼容。
编程方式加载数据
仍然使用上面例子中的数据: Scala Java Python R Sql // 我们继续沿用之前例子中的sqlContext对象 // 为了支持RDD隐式转成DataFrame import sqlContext.implicits._ val people: RDD[Person] = ... // 和上面例子中相同,一个包含case class对象的RDD // 该RDD将隐式转成DataFrame,然后保存为parquet文件 people.write.parquet("people.parquet") // 读取上面保存的Parquet文件(多个文件 - Parquet保存完其实是很多个文件)。Parquet文件是自描述的,文件中保存了schema信息 // 加载Parquet文件,并返回DataFrame结果 val parquetFile = sqlContext.read.parquet("people.parquet") // Parquet文件(多个)可以注册为临时表,然后在SQL语句中直接查询 parquetFile.registerTempTable("parquetFile") val teenagers = sqlContext.sql("SELECT name FROM parquetFile WHERE age >= 13 AND age <= 19") teenagers.map(t => "Name: " + t(0)).collect().foreach(println)
分区发现
像Hive这样的系统,一个很常用的优化手段就是表分区。在一个支持分区的表中,数据是保存在不同的目录中的,并且将分区键以编码方式保存在各个分区目录路径中。Parquet数据源现在也支持自动发现和推导分区信息。例如,我们可以把之前用的人口数据存到一个分区表中,其目录结构如下所示,其中有2个额外的字段,gender和country,作为分区键: path └── to └── table ├── gender=male │ ├── ... │ │ │ ├── country=US │ │ └── data.parquet │ ├── country=CN │ │ └── data.parquet │ └── ... └── gender=female ├── ... │ ├── country=US │ └── data.parquet ├── country=CN │ └── data.parquet └── ...
在这个例子中,如果需要读取Parquet文件数据,我们只需要把 path/to/table 作为参数传给 SQLContext.read.parquet 或者 SQLContext.read.load。Spark SQL能够自动的从路径中提取出分区信息,随后返回的DataFrame的schema如下: root |-- name: string (nullable = true) |-- age: long (nullable = true) |-- gender: string (nullable = true) |-- country: string (nullable = true)
注意,分区键的数据类型将是自动推导出来的。目前,只支持数值类型和字符串类型数据作为分区键。
有的用户可能不想要自动推导出来的分区键数据类型。这种情况下,你可以通过 spark.sql.sources.partitionColumnTypeInference.enabled (默认是true)来禁用分区键类型推导。禁用之后,分区键总是被当成字符串类型。
从Spark-1.6.0开始,分区发现默认只在指定目录的子目录中进行。以上面的例子来说,如果用户把 path/to/table/gender=male 作为参数传给 SQLContext.read.parquet 或者 SQLContext.read.load,那么gender就不会被作为分区键。如果用户想要指定分区发现的基础目录,可以通过basePath选项指定。例如,如果把 path/to/table/gender=male作为数据目录,并且将basePath设为 path/to/table,那么gender仍然会最为分区键。
Schema合并
像ProtoBuffer、Avro和Thrift一样,Parquet也支持schema演变。用户从一个简单的schema开始,逐渐增加所需的新字段。这样的话,用户最终会得到多个schema不同但互相兼容的Parquet文件。目前,Parquet数据源已经支持自动检测这种情况,并合并所有文件的schema。
因为schema合并相对代价比较大,并且在多数情况下不是必要的,所以从Spark-1.5.0之后,默认是被禁用的。你可以这样启用这一功能: 读取Parquet文件时,将选项mergeSchema设为true(见下面的示例代码) 或者,将全局选项spark.sql.parquet.mergeSchema设为true Scala Python R // 继续沿用之前的sqlContext对象 // 为了支持RDD隐式转换为DataFrame import sqlContext.implicits._ // 创建一个简单的DataFrame,存到一个分区目录中 val df1 = sc.makeRDD(1 to 5).map(i => (i, i * 2)).toDF("single", "double") df1.write.parquet("data/test_table/key=1") // 创建另一个DataFrame放到新的分区目录中, // 并增加一个新字段,丢弃一个老字段 val df2 = sc.makeRDD(6 to 10).map(i => (i, i * 3)).toDF("single", "triple") df2.write.parquet("data/test_table/key=2") // 读取分区表 val df3 = sqlContext.read.option("mergeSchema", "true").parquet("data/test_table") df3.printSchema() // 最终的schema将由3个字段组成(single,double,triple) // 并且分区键出现在目录路径中 // root // |-- single: int (nullable = true) // |-- double: int (nullable = true) // |-- triple: int (nullable = true) // |-- key : int (nullable = true)
Hive metastore Parquet table转换
在读写Hive metastore Parquet 表时,Spark SQL用的是内部的Parquet支持库,而不是Hive SerDe,因为这样性能更好。这一行为是由spark.sql.hive.convertMetastoreParquet 配置项来控制的,而且默认是启用的。
Hive/Parquet schema调和
Hive和Parquet在表结构处理上主要有2个不同点: Hive大小写敏感,而Parquet不是 Hive所有字段都是nullable的,而Parquet需要显示设置
由于以上原因,我们必须在Hive metastore Parquet table转Spark SQL Parquet table的时候,对Hive metastore schema做调整,调整规则如下: 两种schema中字段名和字段类型必须一致(不考虑nullable)。调和后的字段类型必须在Parquet格式中有相对应的数据类型,所以nullable是也是需要考虑的。 调和后Spark SQL Parquet table schema将包含以下字段: 只出现在Parquet schema中的字段将被丢弃 只出现在Hive metastore schema中的字段将被添加进来,并显式地设为nullable。
刷新元数据
Spark SQL会缓存Parquet元数据以提高性能。如果Hive metastore Parquet table转换被启用的话,那么转换过来的schema也会被缓存。这时候,如果这些表由Hive或其他外部工具更新了,你必须手动刷新元数据。 Scala Java Python Sql // 注意,这里sqlContext是一个HiveContext sqlContext.refreshTable("my_table")
配置
Parquet配置可以通过 SQLContext.setConf 或者 SQL语句中 SET key=value来指定。
属性名 默认值 含义
spark.sql.parquet.binaryAsString false 有些老系统,如:特定版本的Impala,Hive,或者老版本的Spark SQL,不区分二进制数据和字符串类型数据。这个标志的意思是,让Spark SQL把二进制数据当字符串处理,以兼容老系统。
spark.sql.parquet.int96AsTimestamp true 有些老系统,如:特定版本的Impala,Hive,把时间戳存成INT96。这个配置的作用是,让Spark SQL把这些INT96解释为timestamp,以兼容老系统。
spark.sql.parquet.cacheMetadata true 缓存Parquet schema元数据。可以提升查询静态数据的速度。
spark.sql.parquet.compression.codec gzip 设置Parquet文件的压缩编码格式。可接受的值有:uncompressed, snappy, gzip(默认), lzo
spark.sql.parquet.filterPushdown true 启用过滤器下推优化,可以讲过滤条件尽量推导最下层,已取得性能提升
spark.sql.hive.convertMetastoreParquet true 如果禁用,Spark SQL将使用Hive SerDe,而不是内建的对Parquet tables的支持
spark.sql.parquet.output.committer.class
spark.sql.parquet.mergeSchema
org.apache.parquet.hadoop.
ParquetOutputCommitter
false
Parquet使用的数据输出类。这个类必须是 org.apache.hadoop.mapreduce.OutputCommitter的子类。一般来说,它也应该是 org.apache.parquet.hadoop.ParquetOutputCommitter的子类。注意:1. 如果启用spark.speculation, 这个选项将被自动忽略

2. 这个选项必须用hadoop configuration设置,而不是Spark SQLConf
3. 这个选项会覆盖 spark.sql.sources.outputCommitterClass
Spark SQL有一个内建的org.apache.spark.sql.parquet.DirectParquetOutputCommitter, 这个类的在输出到S3的时候比默认的ParquetOutputCommitter类效率高。
如果设为true,那么Parquet数据源将会merge 所有数据文件的schema,否则,schema是从summary file获取的(如果summary file没有设置,则随机选一个)
JSON数据集 Scala Java Python R Sql
Spark SQL在加载JSON数据的时候,可以自动推导其schema并返回DataFrame。用SQLContext.read.json读取一个包含String的RDD或者JSON文件,即可实现这一转换。
注意,通常所说的json文件只是包含一些json数据的文件,而不是我们所需要的JSON格式文件。JSON格式文件必须每一行是一个独立、完整的的JSON对象。因此,一个常规的多行json文件经常会加载失败。 // sc是已有的SparkContext对象 val sqlContext = new org.apache.spark.sql.SQLContext(sc) // 数据集是由路径指定的 // 路径既可以是单个文件,也可以还是存储文本文件的目录 val path = "examples/src/main/resources/people.json" val people = sqlContext.read.json(path) // 推导出来的schema,可由printSchema打印出来 people.printSchema() // root // |-- age: integer (nullable = true) // |-- name: string (nullable = true) // 将DataFrame注册为table people.registerTempTable("people") // 跑SQL语句吧! val teenagers = sqlContext.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19") // 另一种方法是,用一个包含JSON字符串的RDD来创建DataFrame val anotherPeopleRDD = sc.parallelize( """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil) val anotherPeople = sqlContext.read.json(anotherPeopleRDD)
Hive表
Spark SQL支持从 Apache Hive 读写数据。然而,Hive依赖项太多,所以没有把Hive包含在默认的Spark发布包里。要支持Hive,需要在编译spark的时候增加-Phive和-Phive-thriftserver标志。这样编译打包的时候将会把Hive也包含进来。注意,hive的jar包也必须出现在所有的worker节点上,访问Hive数据时候会用到(如:使用hive的序列化和反序列化SerDes时)。
Hive配置在conf/目录下hive-site.xml,core-site.xml(安全配置),hdfs-site.xml(HDFS配置)文件中。请注意,如果在YARN cluster(yarn-cluster mode)模式下执行一个查询的话,lib_mananged/jar/下面的datanucleus 的jar包,和conf/下的hive-site.xml必须在驱动器(driver)和所有执行器(executor)都可用。一种简便的方法是,通过spark-submit命令的–jars和–file选项来提交这些文件。 Scala Java Python R
如果使用Hive,则必须构建一个HiveContext,HiveContext是派生于SQLContext的,添加了在Hive Metastore里查询表的支持,以及对HiveQL的支持。用户没有现有的Hive部署,也可以创建一个HiveContext。如果没有在hive-site.xml里配置,那么HiveContext将会自动在当前目录下创建一个metastore_db目录,再根据HiveConf设置创建一个warehouse目录(默认/user/hive/warehourse)。所以请注意,你必须把/user/hive/warehouse的写权限赋予启动spark应用程序的用户。 // sc是一个已有的SparkContext对象 val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc) sqlContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)") sqlContext.sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src") // 这里用的是HiveQL sqlContext.sql("FROM src SELECT key, value").collect().foreach(println)
和不同版本的Hive Metastore交互
Spark SQL对Hive最重要的支持之一就是和Hive metastore进行交互,这使得Spark SQL可以访问Hive表的元数据。从Spark-1.4.0开始,Spark SQL有专门单独的二进制build版本,可以用来访问不同版本的Hive metastore,其配置表如下。注意,不管所访问的hive是什么版本,Spark SQL内部都是以Hive 1.2.1编译的,而且内部使用的Hive类也是基于这个版本(serdes,UDFs,UDAFs等)
以下选项可用来配置Hive版本以便访问其元数据:
属性名 默认值 含义
spark.sql.hive.metastore.version 1.2.1 Hive metastore版本,可选的值为0.12.0 到 1.2.1
spark.sql.hive.metastore.jars builtin 初始化HiveMetastoreClient的jar包。这个属性可以是以下三者之一:
builtin
目前内建为使用Hive-1.2.1,编译的时候启用-Phive,则会和spark一起打包。如果没有-Phive,那么spark.sql.hive.metastore.version要么是1.2.1,要就是未定义 maven
使用maven仓库下载的jar包版本。这个选项建议不要再生产环境中使用 JVM格式的classpath。这个classpath必须包含所有Hive及其依赖的jar包,且包含正确版本的hadoop。这些jar包必须部署在driver节点上,如果你使用yarn-cluster模式,那么必须确保这些jar包也随你的应用程序一起打包
spark.sql.hive.metastore.sharedPrefixes
spark.sql.hive.metastore.barrierPrefixes
com.mysql.jdbc,
org.postgresql,
com.microsoft.sqlserver,
oracle.jdbc
(empty)
一个逗号分隔的类名前缀列表,这些类使用classloader加载,且可以在Spark SQL和特定版本的Hive间共享。例如,用来访问hive metastore 的JDBC的driver就需要这种共享。其他需要共享的类,是与某些已经共享的类有交互的类。例如,自定义的log4j appender
一个逗号分隔的类名前缀列表,这些类在每个Spark SQL所访问的Hive版本中都会被显式的reload。例如,某些在共享前缀列表(spark.sql.hive.metastore.sharedPrefixes)中声明为共享的Hive UD函数
用JDBC连接其他数据库
Spark SQL也可以用JDBC访问其他数据库。这一功能应该优先于使用JdbcRDD。因为它返回一个DataFrame,而DataFrame在Spark SQL中操作更简单,且更容易和来自其他数据源的数据进行交互关联。JDBC数据源在java和python中用起来也很简单,不需要用户提供额外的ClassTag。(注意,这与Spark SQL JDBC server不同,Spark SQL JDBC server允许其他应用执行Spark SQL查询)
首先,你需要在spark classpath中包含对应数据库的JDBC driver,下面这行包括了用于访问postgres的数据库driver SPARK_CLASSPATH=postgresql-9.3-1102-jdbc41.jar bin/spark-shell
远程数据库的表可以通过Data Sources API,用DataFrame或者SparkSQL 临时表来装载。以下是选项列表:
属性名 含义
url 需要连接的JDBC URL
dbtable 需要读取的JDBC表。注意,任何可以填在SQL的where子句中的东西,都可以填在这里。(既可以填完整的表名,也可填括号括起来的子查询语句)
driver JDBC driver的类名。这个类必须在master和worker节点上都可用,这样各个节点才能将driver注册到JDBC的子系统中。
partitionColumn, lowerBound, upperBound, numPartitions
fetchSize
这几个选项,如果指定其中一个,则必须全部指定。他们描述了多个worker如何并行的读入数据,并将表分区。partitionColumn必须是所查询的表中的一个数值字段。注意,lowerBound和upperBound只是用于决定分区跨度的,而不是过滤表中的行。因此,表中所有的行都会被分区然后返回。
JDBC fetch size,决定每次获取多少行数据。在JDBC驱动上设成较小的值有利于性能优化(如,Oracle上设为10)
Scala Java Python R Sql val jdbcDF = sqlContext.read.format("jdbc").options( Map("url" -> "jdbc:postgresql:dbserver", "dbtable" -> "schema.tablename")).load()
疑难解答 JDBC driver class必须在所有client session或者executor上,对java的原生classloader可见。这是因为Java的DriverManager在打开一个连接之前,会做安全检查,并忽略所有对原声classloader不可见的driver。最简单的一种方法,就是在所有worker节点上修改compute_classpath.sh,并包含你所需的driver jar包。 一些数据库,如H2,会把所有的名字转大写。对于这些数据库,在Spark SQL中必须也使用大写。
性能调整
对于有一定计算量的Spark作业来说,可能的性能改进的方式,不是把数据缓存在内存里,就是调整一些开销较大的选项参数。
内存缓存
Spark SQL可以通过调用SQLContext.cacheTable(“tableName”)或者DataFrame.cache()把tables以列存储格式缓存到内存中。随后,Spark SQL将会扫描必要的列,并自动调整压缩比例,以减少内存占用和GC压力。你也可以用SQLContext.uncacheTable(“tableName”)来删除内存中的table。
你还可以使用SQLContext.setConf 或在SQL语句中运行SET key=value命令,来配置内存中的缓存。
属性名 默认值 含义
spark.sql.inMemoryColumnarStorage.compressed
spark.sql.inMemoryColumnarStorage.batchSize
true
10000
如果设置为true,Spark SQL将会根据数据统计信息,自动为每一列选择单独的压缩编码方式。
控制列式缓存批量的大小。增大批量大小可以提高内存利用率和压缩率,但同时也会带来OOM(Out Of Memory)的风险。
其他配置选项
以下选项同样也可以用来给查询任务调性能。不过这些选项在未来可能被放弃,因为spark将支持越来越多的自动优化。
属性名 默认值 含义
spark.sql.autoBroadcastJoinThreshold 10485760 (10 MB) 配置join操作时,能够作为广播变量的最大table的大小。设置为-1,表示禁用广播。注意,目前的元数据统计仅支持Hive metastore中的表,并且需要运行这个命令:ANALYSE TABLE COMPUTE STATISTICS noscan
spark.sql.tungsten.enabled
spark.sql.shuffle.partitions
true
200
设为true,则启用优化的Tungsten物理执行后端。Tungsten会显式的管理内存,并动态生成表达式求值的字节码
配置数据混洗(shuffle)时(join或者聚合操作),使用的分区数。
分布式SQL引擎
Spark SQL可以作为JDBC/ODBC或者命令行工具的分布式查询引擎。在这种模式下,终端用户或应用程序,无需写任何代码,就可以直接在Spark SQL中运行SQL查询。
运行Thrift JDBC/ODBC server
这里实现的Thrift JDBC/ODBC server和Hive-1.2.1中的 HiveServer2 是相同的。你可以使用beeline脚本来测试Spark或者Hive-1.2.1的JDBC server。
在Spark目录下运行下面这个命令,启动一个JDBC/ODBC server ./sbin/start-thriftserver.sh
这个脚本能接受所有 bin/spark-submit 命令支持的选项参数,外加一个 –hiveconf 选项,来指定Hive属性。运行./sbin/start-thriftserver.sh –help可以查看完整的选项列表。默认情况下,启动的server将会在localhost:10000端口上监听。要改变监听主机名或端口,可以用以下环境变量: export HIVE_SERVER2_THRIFT_PORT= export HIVE_SERVER2_THRIFT_BIND_HOST= ./sbin/start-thriftserver.sh \ --master \ ...
或者Hive系统属性 来指定 ./sbin/start-thriftserver.sh \ --hiveconf hive.server2.thrift.port= \ --hiveconf hive.server2.thrift.bind.host= \ --master ...
接下来,你就可以开始在beeline中测试这个Thrift JDBC/ODBC server: ./bin/beeline
下面的指令,可以连接到一个JDBC/ODBC server beeline> !connect jdbc:hive2://localhost:10000
可能需要输入用户名和密码。在非安全模式下,只要输入你本机的用户名和一个空密码即可。对于安全模式,请参考 beeline documentation .
Hive的配置是在conf/目录下的hive-site.xml,core-site.xml,hdfs-site.xml中指定的。
你也可以在beeline的脚本中指定。
Thrift JDBC server也支持通过HTTP传输Thrift RPC消息。以下配置(在conf/hive-site.xml中)将启用HTTP模式: hive.server2.transport.mode - Set this to value: http hive.server2.thrift.http.port - HTTP port number fo listen on; default is 10001 hive.server2.http.endpoint - HTTP endpoint; default is cliservice
同样,在beeline中也可以用HTTP模式连接JDBC/ODBC server: beeline> !connect jdbc:hive2://:/?hive.server2.transport.mode=http;hive.server2.thrift.http.path=
使用Spark SQL命令行工具
Spark SQL CLI是一个很方便的工具,它可以用local mode运行hive metastore service,并且在命令行中执行输入的查询。注意Spark SQL CLI目前还不支持和Thrift JDBC server通信。
用如下命令,在spark目录下启动一个Spark SQL CLI ./bin/spark-sql
Hive配置在conf目录下hive-site.xml,core-site.xml,hdfs-site.xml中设置。你可以用这个命令查看完整的选项列表:./bin/spark-sql –help
升级指南
1.5升级到1.6 从Spark-1.6.0起,默认Thrift server 将运行于多会话并存模式下(multi-session)。这意味着,每个JDBC/ODBC连接有其独立的SQL配置和临时函数注册表。table的缓存仍然是公用的。如果你更喜欢老的单会话模式,只需设置spark.sql.hive.thriftServer.singleSession为true即可。当然,你也可在spark-defaults.conf中设置,或者将其值传给start-thriftserver.sh –conf(如下): ./sbin/start-thriftserver.sh \ --conf spark.sql.hive.thriftServer.singleSession=true \ ...
1.4升级到1.5 Tungsten引擎现在默认是启用的,Tungsten是通过手动管理内存优化执行计划,同时也优化了表达式求值的代码生成。这两个特性都可以通过把spark.sql.tungsten.enabled设为false来禁用。 Parquet schema merging默认不启用。需要启用的话,设置spark.sql.parquet.mergeSchema为true即可 Python接口支持用点(.)来访问字段内嵌值,例如df[‘table.column.nestedField’]。但这也意味着,如果你的字段名包含点号(.)的话,你就必须用重音符来转义,如:table.`column.with.dots`.nested。 列式存储内存分区剪枝默认是启用的。要禁用,设置spark.sql.inMemoryColumarStorage.partitionPruning为false即可 不再支持无精度限制的decimal。Spark SQL现在强制最大精度为38位。对于BigDecimal对象,类型推导将会使用(38,18)精度的decimal类型。如果DDL中没有指明精度,默认使用的精度是(10,0) 时间戳精确到1us(微秒),而不是1ns(纳秒) 在“sql”这个SQL变种设置中,浮点数将被解析为decimal。HiveQL解析保持不变。 标准SQL/DataFrame函数均为小写,例如:sum vs SUM。 当推测任务被启用是,使用DirectOutputCommitter是不安全的,因此,DirectOutputCommitter在推测任务启用时,将被自动禁用,且忽略相关配置。 JSON数据源不再自动加载其他程序产生的新文件(例如,不是Spark SQL插入到dataset中的文件)。对于一个JSON的持久化表(如:Hive metastore中保存的表),用户可以使用REFRESH TABLE这个SQL命令或者HiveContext.refreshTable来把新文件包括进来。
1.3升级到1.4
DataFrame数据读写接口
根据用户的反馈,我们提供了一个新的,更加流畅的API,用于数据读(SQLContext.read)写(DataFrame.write),同时老的API(如:SQLCOntext.parquetFile, SQLContext.jsonFile)将被废弃。
有关SQLContext.read和DataFrame.write的更详细信息,请参考API文档。
DataFrame.groupBy保留分组字段
根据用户的反馈,我们改变了DataFrame.groupBy().agg()的默认行为,在返回的DataFrame结果中保留了分组字段。如果你想保持1.3中的行为,设置spark.sql.retainGroupColumns为false即可。 Scala Java Python // 在1.3.x中,如果要保留分组字段"department", 你必须显式的在agg聚合时包含这个字段 df.groupBy("department").agg($"department", max("age"), sum("expense")) // 而在1.4+,分组字段"department"默认就会包含在返回的DataFrame中 df.groupBy("department").agg(max("age"), sum("expense")) // 要回滚到1.3的行为(不包含分组字段),按如下设置即可: sqlContext.setConf("spark.sql.retainGroupColumns", "false")
1.2升级到1.3
在Spark 1.3中,我们去掉了Spark SQL的”Alpha“标签,并清理了可用的API。从Spark 1.3起,Spark SQL将对1.x系列二进制兼容。这个兼容性保证不包括显式的标注为”unstable(如:DeveloperAPI或Experimental)“的API。
SchemaRDD重命名为DataFrame
对于用户来说,Spark SQL 1.3最大的改动就是SchemaRDD改名为DataFrame。主要原因是,DataFrame不再直接由RDD派生,而是通过自己的实现提供RDD的功能。DataFrame只需要调用其rdd方法就能转成RDD。
在Scala中仍然有SchemaRDD,只不过这是DataFrame的一个别名,以便兼容一些现有代码。但仍然建议用户改用DataFrame。Java和Python用户就没这个福利了,他们必须改代码。
统一Java和Scala API
在Spark 1.3之前,有单独的java兼容类(JavaSQLContext和JavaSchemaRDD)及其在Scala API中的镜像。Spark 1.3中将Java API和Scala API统一。两种语言的用户都应该使用SQLContext和DataFrame。一般这些类中都会使用两种语言中都有的类型(如:Array取代各语言独有的集合)。有些情况下,没有通用的类型(例如:闭包或者maps),将会使用函数重载来解决这个问题。
另外,java特有的类型API被删除了。Scala和java用户都应该用org.apache.spark.sql.types来编程描述一个schema。
隐式转换隔离,DSL包移除 – 仅针对scala
Spark 1.3之前的很多示例代码,都在开头用 import sqlContext._,这行将会导致所有的sqlContext的函数都被引入进来。因此,在Spark 1.3我们把RDDs到DataFrames的隐式转换隔离出来,单独放到SQLContext.implicits对象中。用户现在应该这样写:import sqlContext.implicits._
另外,隐式转换也支持由Product(如:case classes或tuples)组成的RDD,但需要调用一个toDF方法,而不是自动转换。
如果需要使用DSL(被DataFrame取代的API)中的方法,用户之前需要导入DSL(import org.apache.spark.sql.catalyst.dsl), 而现在应该要导入 DataFrame API(import org.apache.spark.sql.functions._)
移除org.apache.spark.sql中DataType别名 – 仅针对scala
Spark 1.3删除了sql包中的DataType类型别名。现在,用户应该使用 org.apache.spark.sql.types中的类。
UDF注册挪到sqlContext.udf中 – 针对java和scala
注册UDF的函数,不管是DataFrame,DSL或者SQL中用到的,都被挪到SQLContext.udf中。 Scala Java sqlContext.udf.register("strLen", (s: String) => s.length())
Python UDF注册保持不变。
Python DataTypes不再是单例
在python中使用DataTypes,你需要先构造一个对象(如:StringType()),而不是引用一个单例。
Shark用户迁移指南
调度
用户可以通过如下命令,为JDBC客户端session设定一个 Fair Scheduler pool。 SET spark.sql.thriftserver.scheduler.pool=accounting;
Reducer个数
在Shark中,默认的reducer个数是1,并且由mapred.reduce.tasks设定。Spark SQL废弃了这个属性,改为 spark.sql.shuffle.partitions, 并且默认200,用户可通过如下SET命令来自定义: SET spark.sql.shuffle.partitions=10; SELECT page, count(*) c FROM logs_last_month_cached GROUP BY page ORDER BY c DESC LIMIT 10;
你也可以把这个属性放到hive-site.xml中来覆盖默认值。
目前,mapred.reduce.tasks属性仍然能被识别,并且自动转成spark.sql.shuffle.partitions
缓存
shark.cache表属性已经不存在了,并且以”_cached”结尾命名的表也不再会自动缓存。取而代之的是,CACHE TABLE和UNCACHE TABLE语句,用以显式的控制表的缓存: CACHE TABLE logs_last_month; UNCACHE TABLE logs_last_month;
注意:CACHE TABLE tbl 现在默认是饥饿模式,而非懒惰模式。再也不需要手动调用其他action来触发cache了!
从Spark-1.2.0开始,Spark SQL新提供了一个语句,让用户自己控制表缓存是否是懒惰模式 CACHE [LAZY] TABLE [AS SELECT] ...
以下几个缓存相关的特性不再支持: 用户定义分区级别的缓存逐出策略 RDD 重加载 内存缓存直接写入策略
兼容Apache Hive
Spark SQL设计时考虑了和Hive metastore,SerDes以及UDF的兼容性。目前这些兼容性斗是基于Hive-1.2.1版本,并且Spark SQL可以连到不同版本的Hive metastore(从0.12.0到1.2.1,参考: http://spark.apache.org/docs/latest/sql-programming-guide.html#interacting-with-different-versions-of-hive-metastore )
部署在已有的Hive仓库之上
Spark SQL Thrift JDBC server采用了”out of the box”(开箱即用)的设计,使用很方便,并兼容已有的Hive安装版本。你不需要修改已有的Hive metastore或者改变数据的位置,或者表分区。
支持的Hive功能
Spark SQL 支持绝大部分Hive功能,如: Hive查询语句: SELECT GROUP BY ORDER BY CLUSTER BY SORT BY 所有的Hive操作符: Relational operators ( = , ⇔ , == , <> , < , > , >= , <= , etc) Arithmetic operators ( + , - , * , / , % , etc) Logical operators ( AND , && , OR , || , etc) Complex type constructors Mathematical functions ( sign , ln , cos , etc) String functions ( instr , length , printf , etc) 用户定义函数(UDF) 用户定义聚合函数(UDAF) 用户定义序列化、反序列化(SerDes) 窗口函数(Window functions) Joins JOIN {LEFT|RIGHT|FULL} OUTER JOIN LEFT SEMI JOIN CROSS JOIN Unions 查询子句 SELECT col FROM ( SELECT a + b AS col from t1) t2 采样 执行计划详细(Explain) 分区表,包括动态分区插入 视图 所有Hive DDL(data definition language): CREATE TABLE CREATE TABLE AS SELECT ALTER TABLE 绝大部分Hive数据类型: TINYINT SMALLINT INT BIGINT BOOLEAN FLOAT DOUBLE STRING BINARY TIMESTAMP DATE ARRAY<> MAP<> STRUCT<>
不支持的Hive功能
以下是目前不支持的Hive特性的列表。多数是不常用的。
不支持的 Hive 常见功能 bucket表:butcket是Hive表的一个哈希分区
不支持的 Hive 高级 功能 UNION类操作 去重join 字段统计信息收集:Spark SQL不支持同步的字段统计收集
Hive输入、输出格式 CLI文件格式:对于需要回显到CLI中的结果,Spark SQL仅支持TextOutputFormat。 Hadoop archive — Hadoop归档
Hive优化
一些比较棘手的Hive优化目前还没有在Spark中提供。有一些(如索引)对应Spark SQL这种内存计算模型来说并不重要。另外一些,在Spark SQL未来的版本中会支持。 块级别位图索引和虚拟字段(用来建索引) 自动计算reducer个数(join和groupBy算子):目前在Spark SQL中你需要这样控制混洗后(post-shuffle)并发程度:”SET spark.sql.shuffle.partitions=[num_tasks];” 元数据查询:只查询元数据的请求,Spark SQL仍需要启动任务来计算结果 数据倾斜标志:Spark SQL不会理会Hive中的数据倾斜标志 STREAMTABLE join提示:Spark SQL里没有这玩艺儿 返回结果时合并小文件:如果返回的结果有很多小文件,Hive有个选项设置,来合并小文件,以避免超过HDFS的文件数额度限制。Spark SQL不支持这个。
参考
数据类型
Spark SQL和DataFrames支持如下数据类型: Numeric types(数值类型) ByteType : 1字节长的有符号整型,范围: -128 到 127 . ShortType : 2字节长有符号整型,范围: -32768 到 32767 . IntegerType : 4字节有符号整型,范围: -2147483648 到 2147483647 . LongType : 8字节有符号整型,范围: -9223372036854775808 to 9223372036854775807 . FloatType : 4字节单精度浮点数。 DoubleType : 8字节双精度浮点数 DecimalType : 任意精度有符号带小数的数值。内部使用java.math.BigDecimal, BigDecimal包含任意精度的不缩放整型,和一个32位的缩放整型 String type(字符串类型) StringType : 字符串 Binary type(二进制类型) BinaryType : 字节序列 Boolean type(布尔类型) BooleanType : 布尔类型 Datetime type(日期类型) TimestampType : 表示包含年月日、时分秒等字段的日期 DateType : 表示包含年月日字段的日期 Complex types(复杂类型) ArrayType(elementType, containsNull) :数组类型,表达一系列的elementType类型的元素组成的序列,containsNull表示数组能否包含null值 MapType(keyType, valueType, valueContainsNull) :映射集合类型,表示一个键值对的集合。键的类型是keyType,值的类型则由valueType指定。对应MapType来说,键是不能为null的,而值能否为null则取决于valueContainsNull。 StructType(fields): 表示包含StructField序列的结构体。 StructField(name, datatype, nullable): 表示StructType中的一个字段,name是字段名,datatype是数据类型,nullable表示该字段是否可以为空 Scala Java Python R
所有Spark SQL支持的数据类型都在这个包里:org.apache.spark.sql.types,你可以这样导入之: import org.apache.spark.sql.types._
Data type Value type in Scala API to access or create a data type
ByteType Byte ByteType
ShortType Short ShortType
IntegerType Int IntegerType
LongType Long LongType
FloatType Float FloatType
DoubleType Double DoubleType
DecimalType java.math.BigDecimal DecimalType
StringType String StringType
BinaryType Array[Byte] BinaryType
BooleanType Boolean BooleanType
TimestampType java.sql.Timestamp TimestampType
DateType java.sql.Date DateType
ArrayType scala.collection.Seq ArrayType( elementType , [ containsNull ])注意:默认containsNull为true
MapType scala.collection.Map MapType( keyType , valueType , [ valueContainsNull ])注意:默认valueContainsNull为true
StructType
StructField
org.apache.spark.sql.Row
定义字段的数据对应的Scala类型(例如,如果StructField的dataType为IntegerType,则其数据对应的scala类型为Int)
StructType( fields )注意:fields是一个StructFields的序列,并且同名的字段是不允许的。
StructField( name , dataType , nullable )
NaN语义
这是Not-a-Number的缩写,某些float或double类型不符合标准浮点数语义,需要对其特殊处理: NaN == NaN,即:NaN和NaN总是相等 在聚合函数中,所有NaN分到同一组 NaN在join操作中可以当做一个普通的join key NaN在升序排序中排到最后,比任何其他数值都大
原创文章,转载请注明: 转载自 并发编程网 – ifeve.com 本文链接地址: 《Spark 官方文档》Spark SQL, DataFrames 以及 Datasets 编程指南
添加本文到我的收藏
Related Posts: 《Spark 官方文档》Spark快速入门 《Spark 官方文档》 《Spark 官方文档》Spark安全性 《Spark 官方文档》硬件配置 《Spark 官方文档》监控和工具 《Spark 官方文档》Spark作业调度 《Spark 官方文档》在Mesos上运行Spark 《Spark 官方文档》Spark编程指南 《Spark 官方文档》Spark独立模式 《Spark 官方文档》在YARN上运行Spark 《Spark 官方文档》Spark配置 《Spark 官方文档》在Amazon EC2上运行Spark 《Spark 官方文档》Spark调优 《Spark官方文档》Spark Streaming编程指南 Apache Storm 官方文档 —— 使用非 JVM 语言开发
大数据
2019-07-02 18:00:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
语法总结 :
顺序在最后

" * " 就相当于 一整排的意思
insert into 是写入这行 如果遇到非空 必须赋值 哪怕是 null
alter 是改变的意思 用于结构的增加列 一般和 alter table.... add ...
update 是更新数据的意思 用于给 字段 更改值 update ....set....
想显示两个 就在 select后面 两个字段并且用个 , 隔开 如 : pid,pname
想看一组的数据 就先引用 alter add 加上cid列 再 用update set给列分开 最后 用group by 查这组的值


4.1 数据准备 # 创建商品表 create table product( pid int primary key auto_increment, pname varchar(20), price double, pdate timestamp ) # 自动增长列:auto_increment,要求:1,必须整型(int) 2.必须是主键 insert into product values (null,' 谭妮平',0.01,null); insert into product values (null,' 李士雪',38,null); insert into product values (null,' 左慈',-998,null); insert into product values (null,' 黄迎',99999,null); insert into product values (null,' 南国强',99998,null); insert into product values (null,' 士兵',1,null); insert into product values (null,' 李士兵',698,null); 4.2 简单查询(重点前3个) 查询的语法: s elect 要查询的字段 from 表名 where 条件; 1. 查询 所有 商品 s elect * from 表名 ; select * from product; 2. 查询商品名和商品价格 s elect 要查询的字段(多个字段用逗号隔开) from 表名; select pname,price from product; 3. 查询所有商品信息使用表别名
【单表没啥卵用,主要用于多表操作!】 s elect * from 表名 别名; select * from product p; 4. 查询商品名,使用列别名(相对表别名使用要少一些。) select 列名 列别名 from 表名; 4.3 条件查询 语法:select * from 表名 where 条件 1. 查询商品名称为"左慈"的商品信息 - --- 查一行 s elect * from product where pname=’ 左慈 ’ ; 2. 查询价格>60元的所有商品信息 3. 查询商品名称含有"士"字的商品信息【 模糊查询 】 select * from 表名 where 字段名 like ‘% 士% ’ ; 【%表示任意个字符】
查询商品名称以"士"字开头的商品信息 select * from 表名 where 字段名 like ‘ 士% ’; 【%表示任意个字符】
查询商品名称第二字为"士"字的商品信息 select * from 表名 where 字段名 like ‘_ 士% ’; 【_表示1个字符】 4. 查询商品id在(3,6,9)范围内的所有商品信息【关键字 in 】 s elect * from product where pid in(3,6,9); and or not 5. 查询商品名称为士兵 并且 价格>0的商品信息 说明:并且( and ) 6. 查询商品名称含有士字 或者 价格>100的商品信息 7.查询 id 不是2 的商品信息 ----- not (pid = 2); != 也行 8. 查询价格在100到10000之间的商品信息 between A and B (前后闭区间 都包) 4.4 排序 排序语法:select * from 表名 order by 指定的 字段名称 [ asc 升序]/desc降序 查询所有的商品,按价格进行排序(升序、降序) 升序: select * from product order by price asc;
或者 select * from product order by price;
降序: select * from product order by price asc; 1.2. 查询名称有"士"的商品信息并且按照价格降序排序 select * from product where pname like '% 士%' order by price desc; 注意:order by排序操作必须放在where 条件的后面!!! 4.5 聚合 之前的查询操作都是横向操作(以行为单位进行查询),使用聚合函数,可以进行纵向操作(以列为单位进行查询,查询的结果是单独的一列) 就和Excel一样 语法:select 函数名称(给定列名) from 表名 2.1. 获得所有商品的价格的总和 sum s elect sum(price) from product; 2.2. 获得所有商品的平均价格 avg se lect avg(price) from product; 2.3. 获得所有商品的个数 count s elect count( * ) from product; 4.6 分组 语法:select * from 表名 group by 分组的字段名称 这 个只在把一组当成一个整体的时候才用,分组是用的加列的思想; 1. 添加分类id (alter table product add cid varchar(32);) - -加上cid这个列而已 2. 初始化数据 --- 给这个列赋值 update product set cid='1'; update product set cid='2' where pid in (5,6,7);
查询: 3.1. 根据cid字段分组,分组后统计商品的个数。 --- 统计每组的 select cid,count(cid) from product group by cid; 3.2. 根据cid分组,分组统计每组商品的平均价格,并且平均价格大于20000元。 select cid,avg(price) from product group by cid having avg(price)>20000;
如果进行了分组操作,还带有条件,不能使用where关键字,必须使用having,而且只能放在最后面!!! 4.7 分页 查询 LIMIT
select * from product limit 2; 查前两个
select pname from product limit 4,2; 公式 : (要看的页码数 - 1)*每页显示大小
4.9 删除 delete from 表名 全删 delete from 表名 where 条件 delete from product where pid=8; 4.8 单表查询总结 顺序 开发中关于查询语句应该怎么去写 ? 【关键字出现的顺序如下:】 select 被查询的字段(所有的字段写 * )【一般都是 的 字后面的内容】 from 表名 where 条件 【一般都是 的 字前面的内容】 grou p by 分组的字段名称 【一般会明确指定根据什么来分组】 order by 升序还是降序( asc/desc ) 【一般会明确指定根据什么来分组】 having 条件(分组后的条件) 【一般会明确指定根据什么来分组】


大数据
2019-06-20 14:41:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一、Apache Hive简介
官方网址: https://hive.apache.org/
The Apache Hive ™ data warehouse software facilitates reading, writing, and managing large datasets residing in distributed storage using SQL. Structure can be projected onto data already in storage. A command line tool and JDBC driver are provided to connect users to Hive.
(Apache Hive™数据仓库软件使用SQL语句便于读取,写入和管理驻留在分布式存储中的大型数据集。操作结构化数据。提供给用户命令行工具和JDBC驱动程序以连接到Hive)
1、概念
(1)Hive 是建立在 Hadoop上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数据提取转化加载(ETL),这是一种可以存储、查询和分析存储在 Hadoop 中的大规模数据的机制。Hive 定义了简单的类 SQL 查询语言,称为 HQL ,它允许熟悉 SQL 的用户查询数据。同时,这个语言也允许熟悉 MapReduce 开发者的开发自定义的 mapper 和 reducer 来处理内建的 mapper 和 reducer 无法完成的复杂的分析工作。
(2)Hive是SQL解析引擎,它将SQL语句转译成MR Job然后在Hadoop执行。
(3)Hive的表其实就是HDFS的目录,按表名把文件夹分开。如果是分区表,则分区值是子文件夹,可以直接在MR Job里使用这些数据。
(4)Hive相当于hadoop的客户端工具,部署时不一定放在集群管理节点中,可以放在某个节点上。
(5)Hive中存储结构和HDFS里面的存储结构的对应关系
Hive的表———-HDFS的目录
Hive的数据——–HDFS的目录下面的(数据)文件
Hive中行列——–HDFS的目录下面的数据文件的行列
(6)Hive相当于hadoop的客户端工具,部署时不一定放在集群管理节点中,可以放在某个节点上。
2、数据存储
(1)Hive的数据存储基于Hadoop HDFS
(2)Hive没有专门的数据存储格式
(3)存储结构主要包括:数据库、文件、表、视图、索引
(4)Hive默认可以直接加载文本文件(TextFile),还支持SequenceFile、RCFile
(5)创建表时,指定Hive数据的列分隔符与行分隔符,Hive即可解析数据
3、Hive的体系结构
(1)用户接口主要有三个:CLI,JDBC/ODBC和 WebUI
(2)CLI,即Shell命令行
(3)JDBC/ODBC 是 Hive 的Java,与使用传统数据库JDBC的方式类似
(4)WebGUI是通过浏览器访问 Hive
(5)Hive 将元数据存储在数据库中(metastore),目前只支持 mysql、derby。Hive 中的元数据包括表的名字,表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等
(6)解释器、编译器、优化器完成 HQL 查询语句从词法分析、语法分析、编译、优化以及查询计划(plan)的生成。生成的查询计划存储在 HDFS 中,并在随后由 MapReduce 调用执行
(7)Hive 的数据存储在 HDFS 中,大部分的查询由 MapReduce 完成(包含 * 的查询,比如 select * from table 不会生成 MapRedcue 任务)
4、Hive的元数据
(1)metastore是hive元数据的集中存放地。
(2)metastore默认使用内嵌的derby数据库作为存储引擎
(3)Derby引擎的缺点:一次只能打开一个会话
(4)使用MySQL作为外置存储引擎,多用户同时访问
二、Apache Hive安装
hive的下载地址http://hive.apache.org/downloads.html
JAVA_HOME=/usr/local/jdk1.7.0_55
HADOOP_HOME=/usr/local/hadoop-2.6.0
HIVE_HOME=/usr/local/hive-0.14.0
Ubuntu操作系统安装步骤http://www.tuicool.com/articles/bmUjAjj
以下是Centos操作系统
1、Mysql的安装(推荐在线安装)
http://www.cnblogs.com/liuchangchun/p/4099003.html
1°、查看mysql依赖
rpm -qa | grep mysql
2°、删除mysql依赖
[root @hive local]# rpm -e –nodeps rpm -qa | grep mysql
3°、安装mysql
[root @hive local]# yum -y install mysql-server
4°、启动mysql的服务
[root @hive local]# service mysqld start
5°、将mysql的服务加入到开机启动项里
[root @hive local]# chkconfig mysqld on
6°、mysql的配置
[root @hive local]# /usr/bin/mysql_secure_installation
7°、授予远程指定用户的登陆权限
mysql -h hive.teach.crxy.cn -uroot -proot
问题:ERROR 1130 (HY000): Host ‘hive.teach.crxy.cn’ is not allowed to connect to this MySQL server
解决办法:
mysql> grant all privileges on . to ‘root’@’%’ identified by ‘root’;
mysql> flush privileges;
2、安装Hive
前提是:hadoop必须已经启动了*****
1°、解压hive安装包到/usr/local目录下
[root@hive soft]# tar -zvxf apache-hive-0.14.0-bin.tar.gz -C /usr/local/
[root@hive local]# mv apache-hive-0.14.0-bin/ hive-0.14.0
2°、备份配置文件(在$HIVE_HOME/conf目录下)
[root@hive conf]$ cp hive-env.sh.template hive-env.sh
[root@hive conf]$ cp hive-default.xml.template hive-site.xml
3°、配置hive的配置文件
1)、修改hive-env.sh
加入三行内容(大家根据自己的情况来添加)
JAVA_HOME=/usr/local/jdk1.7.0_55
HADOOP_HOME=/usr/local/hadoop-2.6.0
HIVE_HOME=/usr/local/hive-0.14.0
2)、修改hive-site.xml javax.jdo.option.ConnectionURL jdbc:mysql://scbnode1:3306/hivedb?createDatabaseIfNotExist=true javax.jdo.option.ConnectionDriverName com.mysql.jdbc.Driver javax.jdo.option.ConnectionUserName yourpassword javax.jdo.option.ConnectionPassword yourpassword hive.querylog.location /usr/local/hive-0.14.0/tmp hive.exec.local.scratchdir /usr/local/hive-0.14.0/tmp hive.downloaded.resources.dir /usr/local/hive-0.14.0/tmp
4°、拷贝mysql驱动到$HIVE_HOME/lib目录下
[crxy@master bin]$ cp $JAVA_HOME/lib/mysql-connector-java-5.1.17.jar $HIVE_HOME/lib/
5°、启动Hive进入hive cli终端
[root@hive bin]./hive或者
[root@hive bin]./hive --service cli
三、Apache Hive常用命令
(1)登录数据库,-u后面跟用户名,回车之后输入密码即可
mysql -u root -p
(2)查看数据库字符集
show variables like ‘character%’;
(3)创建数据库
create database <数据库名>
(4)显示所有数据库
show databases;
(5)删除数据库
drop database <数据库名>
(6)链接数据库
use <数据库名>
(7)查看当前使用的数据库
select database()
(8)查看当前数据库表信息
show tables
(9)退出
exit
四、Apache Hive操作技巧
1、Hive CLI终端如何和Linux/hadoop fs进行交互
Linux:
hive>! linux命令;
eg.
hive>!pwd;
hadoop:
hive> dfs option args;
eg.
hive> dfs -ls /;
2、Hive的使用方式
命令行方式cli:控制台模式
上课一直在用
脚本文件方式:实际生产中用的最多的方式
3、两种操作的方式
1°、linux终端
sh $HIVE_HOME/bin/hive -f hive.hql(的路径)
2°、hive终端
hive> source hive.hql(的路径)
4、JDBC方式:hiveserver
5、web GUI接口 hwi方式
安装Web GUI访问方式的步骤
1、解压hive源代码到某个目录,并进入到解压后的一个子文件夹hwi(Hive web interface)
[root@hive soft]$ tar -zxvf apache-hive-0.14.0-src.tar.gz
[root@hive soft]$ cd apache-hive-0.14.0-src/hwi
2°、将hwi下面的web/打成一个war包
[root@hive hwi]$ jar cvfM0 hive-hwi-0.14.0.war -C web/ .
3°、将2°中的war拷贝到$HIVE_HOME/lib目录下
[root@hive hwi]$ cp hive-hwi-0.14.0.war /usr/local/hive-0.14.0/lib/
4°、拷贝$JAVA_HOME/lib/tools.jar到$HIVE_HOME/lib目录下
[root@hive hwi]$ cp /usr/local/jdk1.7.0_55/lib/tools.jar /usr/local/hive-0.14.0/lib/
5°、修改hive的配置文件hive-site.xml hive.hwi.listen.host hive.teach.crxy.cn This is the host address the Hive Web Interface will listen on hive.hwi.listen.port 9999 This is the port the Hive Web Interface will listen on hive.hwi.war.file lib/hive-hwi-0.14.0.war This sets the path to the HWI war file, relative to ${HIVE_HOME}.
6°、启动hwi
[crxy@master bin]$ ./hive --service hwi &
查询9999端口进程
netstat -tunlp | grep 9999
7°、浏览器访问hwi
http://:9999/hwi/
http://hive.teach.crxy.cn:9999/hwi/
6、单词统计:
use default;
create table t(line string);
load data local inpath ‘hello’ into table t;(如果数据在hdfs上,去掉local)
set hive.exec.mode.local.auto;
set hive.exec.mode.local.auto-true;
五、Hive的日志信息相关
1、如何去掉hive启动时候的日志信息
启动时发现
SLF4J: Found binding in [jar:file:/usr/local/hive-0.14.0/lib/hive-jdbc-0.14.0-standalone.jar
解决方案:
hive> !mv /usr/local/hive-0.14.0/lib/hive-jdbc-0.14.0-standalone.jar /usr/local/hive-0.14.0/lib/hive-jdbc-0.14.0-standalone.jar.bak;
2、hive的日志
1°、备份日志文件
[root@hive conf]$ cp hive-exec-log4j.properties.template hive-exec-log4j.properties
[root@hive conf]$ cp hive-log4j.properties.template hive-log4j.properties
2°、查看日志配置文件
hive.log.threshold=ALL
hive.root.logger=INFO,DRFA
hive.log.dir=${java.io.tmpdir}/${user.name}
hive.log.file=hive.log
通过SystemInfo.java可以知道
${java.io.tmpdir}=/tmp
${user.name}=root
六、Hive的数据类型
int、boolean、date、array、map、struct等等。
Hive的数据库、表,及其数据库、表与hdfs、metastore中的对应信息
1、Hive数据库,DDL
1°、查看数据库列表
hive> show databases;
查看数据库详细信息
desc database mydb;
查看当前数据库
set hive.cli.print.current.db=true;
set
2°、使用db
hive> use dbName;
eg.
hive> use default;
3°、创建db
hive> create database dbName;
eg.
hive> create database mydb1;
4°、删除
hive> drop database dbName;
eg.
hive> drop database mydb1;
5°、数据库在hdfs上面的位置
默认数据库在hdfs上面的位置
hive.metastore.warehouse.dir /user/hive/warehouse location of default database for the warehouse
也可以通过hive> set hive.metastore.warehouse.dir;来获取
在metastore中查看
有在表DBS中可以看到default的hdfs_uri:hdfs://hive.teach.crxy.cn:9000/user/hive/warehouse
普通的数据库放在了/user/hive/warehouse下面,在metastore中查看==》
我们创建的mydb1的hdfs_uri:hdfs://hive.teach.crxy.cn:9000/user/hive/warehouse/mydb1.db
创建制定存储位置的数据库
hive> create database mydb2 location hdfs_uri;
===》
我们删除,修改hive数据库的定义的时候,对应的hdfs相应的目录和metastore中的相应的列就发生了变化,是同步的。
2、hive中的表
设置查看表的头信息
hive> set hive.cli.print.header;
hive.cli.print.header=false
hive> set hive.cli.print.header=true;
1°、表的DDL
查看表
hive> show tables;
创建表
hive> create table t1(id int);
查看表结构
hive> desc [extended] t1;
extended是可选的,是扩展的表的信息
查看表的创建语句
hive> show create table t1;
LOCATION
'hdfs://hive.teach.crxy.cn:9000/user/hive/warehouse/t1'
说了创建的表在hdfs中的位置
在metastore中查看
在表TBLS中有创建的表的信息
删除表
hive> drop table t1;
重命名表的名称
hive> alter table t1 rename to t1_ddl;
修改表中的某一列
hive> alter table t1_ddl change id t_id int;
增加列
mysql:alter table add column colname coltype;
hive> alter table add columns (colname coltype...);可以增加多列
hive> alter table t1_ddl add columns(name string comment 'name', age int);
替换整个表列
hive> alter table t1_ddl replace columns(name string comment 'name', age int);
移动某一列的位置
将某一列放在最前面
hive> alter table t1_ddl add columns(id int);(增加原有的数据)
hive> alter table t1_ddl change id id int first;
将某一列移动到另外一列的后面或前面
hive> alter table t1_ddl change age age int after id;(将age放在id的后面或name的前面)
2°、hive加载数据
一)、使用hive命令load data
hive> alter table t1_ddl replace columns(id int);
hive> alter table t1_ddl rename to t1;
hive> load data local inpath 'data/t1' into table t1;
查看表中的数据
hive> select * from t1;
二)、使用hadoop fs命令
把数据直接放到hdfs中hive对应的目录下面,hive表会不会感知到呢?
hive> dfs -put data/t1 /user/hive/warehouse/t1/t1_1;
这样hive也是可以感知到加载上来的数据的。
3°、数据加载的模式及其hive表列的分隔符
create table t2(
id int comment "ID",
name string comment "name",
birthday date comment 'birthday',
online boolean comment "is online"
);
load data local inpath 'data/t2' into table t2;
Hive有默认的行列分隔符
行分隔符和linux下面的行分隔符一直都是'\n'
列分隔符是八进制的\001,是不可见的ASCII,怎么输入呢ctrl+v ctrl+a
创建表的时候如何制定行列的分隔符呢?
create table t2_1(
id int comment "ID",
name string comment "name",
birthday date comment 'birthday',
online boolean comment "is online"
) comment "test table's seperator"
row format delimited
fields terminated by '\t'
lines terminated by '\n';
load data local inpath 'data/t2_1' into table t2;
====》有问题,读取错误数据为NULL
====》两种数据的加载模式
读模式
数据库加载数据的时候不进行数据的合法性校验,在查询数据的时候将不合法的数据显示为NULL
好处:加载速度快,适合大数据的加载。
写模式
数据库加载数据的时候要进行数据的合法性校验,在数据库里面的数据都是合法的
好处:适合进行查询,不会担心有不合法的数据存在。
我们的Hive采用的是读模式,加载数据的时候不进行数据的合法性校验,在查询数据的时候将不合法的数据显示为NULL。
3、Hive表中加载数据的两种方式
load data local inpath linux_fs_path into table tblName;
hadoop fs -put linux_path hdfs_uri;
4、Hive中复合数据类型和各种默认的分隔符
复合数据类型:array、map、struct
array元素的引用:arrayName[index]
map元素的引用:mapName[‘key’]
struct元素的引用:structName.item
默认分隔符:
行的默认分隔符:’\n’
列的默认分隔符:’\001’ –> ctrl+v ctrl+a
集合元素的默认分隔符:’\002’ –> ctrl+v ctrl+b
map k-v的默认分隔符:’\003’ –> ctrl+v ctrl+c
在创建表写分隔符的时候,是有顺序要求的,如下:
create table tblName(
columns columns_type comment ‘comment’
) row format delimited—>指明要使用自定义添加分隔符
fields terminated by ‘列分隔符’ –>读取数据的时候是一行一行的,读取好了一行之后,对数据解析,所以要用列分隔符拆分每一列
collection items terminated by ‘集合元素分割符’ –>如果列是集合的话,就要跟着拆分集合元素
map keys terminated by ‘map-key分割符’ –>map 也是集合中的元素,碰到是map的类型了再解析map
lines terminated by ‘行分隔符’; –>解析完一行需要知道下一行,所以这个时候要知道行分隔符了
5、Hive的函数
顾名思义,Hive中的函数,就是为了完成某一操作的某一个功能的抽象,和MySQL中的Hive是一样一样的。
1、浏览所有的函数
show functions;
2、查看具体的注释函数
desc function functionName;
查看具体的注释信息
desc function extended functionName;
3、常用的函数
case -----条件显示,有点类似java中的if else/switch,需要和when结合起来使用
split -----类似java中String类中split()方法
collect_set -----类似java中的list,
collect_list-----类似java中的set,
concat_ws----字符串的连接,和mysql中concat类似
explode ----把一个list转化多行显示
4、常用函数举例
1°、条件显示---case when
select id, case id
when 1 then 'teacher'
when 2 then 'seller'
when 3 then 'student'
else 'others'
end
from tblName;
2°、单词统计---split explode
第一步:文本切割split
select split(word, ' ') from tblName;
第二步:集合元素转多行explode
select explode(split(word, ' ')) from tblName;
第三步:使用聚合函数统计
select word, count(1) as count from (select explode(split(word, ' ')) word from tblName) w group by count;
3°、行转列
第一步:做表关联,分析结果
select u.id, u.name, a.address from t11_user u join t11_address a on u.id = a.uid;
第二步:对出现的多个结果按照id来拼接字符串
select u.id, max(u.name), concat_ws("," collect_set(a.address)) as addr
from t11_user u join t11_address a
on u.id = a.id group by u.id;
4°、列转行
准备数据
create table t_user_addr as
select u.id, max(u.name), concat_ws("," collect_set(a.address)) as addr
from t11_user u join t11_address a
on u.id = a.id group by u.id;
就使用explode行数就可以了
select explode(split(addr, ",") from t_user_addr;
查看多个字段
select id, name, address from t12_user_addr lateral view explode(split(addr, ",")) a as address;
5、自定义函数
hive内嵌的函数,虽然说功能非常的强大,但是我们的业务可能是千变万化的,所以需要针对业务自定义函数!
步骤:
1°、自定义UDF extends org.apache.hadoop.hive.ql.exec.UDF
2°、需要实现evaluate函数,evaluate函数支持重载
3°、把程序打包放到目标机器上去
4°、进入hive客户端,添加jar包:hive>add jar jar路径
5°、创建临时函数:hive> create temporary function 自定义名称 AS '自定义UDF的全类名'
6°、执行HQL语句;
7°、销毁临时函数:hive> drop temporary function 自定义名称
package cn.crxy.teach.hive.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
/*下面的注解是对函数的注释 可以通过desc function fName获取*/
@Description(name = "MyUpper",
value = " _FUNC_(str) - Returns str with all characters changed to uppercase",
extended = "Example:\n"
+ " > SELECT _FUNC_(name) FROM src;")
public class MyUpper extends UDF {
public Text evaluate(Text text) {
if(text != null) {
return new Text(text.toString().toUpperCase());
} else {
return null;
}
}
}
注意,如果用到的是日期Date类型的话,必须是java.sql.Date
6、Hive的JDBC连接方式
首先需要启动咱们的jdbc服务Hive Thrift Server,
sh $HIVE_HOME/bin/hive --service hiveserver2
可以放到后台执行
nohup sh $HIVE_HOME/bin/hive --service hiveserver2 --hiveconf hive.server2.thrift.port=10002 >/dev/null 2>&1 &
其次在eclipse中编写代码,java中的jdbc
package cn.crxy.teach.hive.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class HiveJdbc { public static void main(String[] args) throws Exception { //Class.forName("org.apache.hadoop.hive.jdbc.HiveDriver");//老版本hiveserver的驱动名称 //Connection con = DriverManager.getConnection("jdbc:hive://hive.teach.crxy.cn:10002");//老版本 Class.forName("org.apache.hive.jdbc.HiveDriver"); Connection con = DriverManager.getConnection("jdbc:hive2://hive.teach.crxy.cn:10002/default", "root", ""); PreparedStatement ps = con.prepareStatement("select wc.word, count(1) as count " + "from (select explode(split(word, ' ')) as word from t10_wc) wc " + "group by wc.word order by count desc"); ResultSet rs = ps.executeQuery(); while(rs.next()) { String word = rs.getString(1); int count = rs.getInt(2); System.out.println(word + ", " + count); } rs.close(); ps.close(); con.close(); } }
在windows下可能会报错:
java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
但是可以忽略这个错误,咱们在windows上并没有安装hadoop,所以有问题。
如何解决:
在windows下面配置HADOOP_HOME环境变量,同时将我给大家发下去的hadoop2.6_util(x64).zip解压,
覆盖掉原来的HADOOP_HOME/bin目录,这样在eclipse中执行的时候就没有问题了。
在linux下面执行是不会有问题的。
将程序打成一个jar包,扔到hive服务器上面执行
java -cp hive-0.0.1-SNAPSHOT-jar-with-dependencies.jar cn.crxy.teach.hive.jdbc.HiveJdbc
大数据
2019-06-17 22:55:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
前言
在 架构篇 中我们介绍了现代IM消息系统的架构,介绍了Timeline的抽象模型以及基于Timeline模型构建的一个支持『消息漫游』、『多端同步』和『消息检索』多种高级功能的消息系统的典型架构。架构篇中为了简化读者对Tablestore Timeline模型的理解,概要性的对Timeline的基本逻辑模型做了介绍,以及对消息系统中消息的多种同步模式、存储和索引的基本概念做了一个科普。
本篇文章是对架构篇的一个补充,会对Tablestore的Timeline模型做一个非常详尽的解读,让读者能够深入到实现层面了解Timeline的基本功能以及核心组件。最后我们还是会基于IM消息系统这个场景,来看如何基于Tablestore Timeline实现IM场景下消息同步、存储和索引等基本功能。
Timeline模型
Timeline模型以『简单』为设计目标,核心模块构成比较清晰明了,主要包括: Store:Timeline存储库,类似数据库的表的概念。 Identifier:用于区分Timeline的唯一标识。 Meta:用于描述Timeline的元数据,元数据描述采用free-schema结构,可自由包含任意列。 Queue:一个Timeline内所有Message存储在Queue内。 Message:Timeline内传递的消息体,也是一个free-schema的结构,可自由包含任意列。 Index:包含Meta Index和Message Index,可对Meta或Message内的任意列自定义索引,提供灵活的多条件组合查询和搜索。
Timeline Store
Timeline Store是Timeline的存储库,对应于数据库内表的概念。上图是Timeline Store的结构图,Store内会存储所有的Timeline数据。Timeline是一个面向海量消息的数据模型,同时用于消息存储库和同步库,需要满足多种要求: 支撑海量数据存储 :对于消息存储库来说,如果需要消息永久存储,则随着时间的积累,数据规模会越来越大,需要存储库能应对长时间积累的海量消息数据存储,需要能达到PB级容量。 低存储成本 :消息数据的冷热区分是很明显的,大部分查询都会集中在热数据,所以对于冷数据需要有一个比较低成本的存储方式,否则随着时间的积累数据量不断膨胀,存储成本会非常大。 数据生命周期管理 :不管是对于消息数据的存储还是同步,数据都需要定义生命周期。存储库是用于在线存储消息数据本身,通常需要设定一个较长周期的保存时间。而同步库是用于写扩散模式的在线或离线推送,通常设定一个较短的保存时间。 极高的写入吞吐 :各类场景下的消息系统,除了类似微博、头条这种类型的Feeds流系统,像绝大部分即时通讯或朋友圈这类消息场景,通常是采用写扩散的消息同步模式,写扩散要求底层存储具备极高的写入吞吐能力,以应对消息洪峰。 低延迟的读 :消息系统通常是应用在在线场景,所以对于查询要求低延迟。
Tablestore Timeline的底层是基于LSM存储引擎的分布式数据库,LSM的最大优势就是对写入非常友好,天然适合消息写扩散的模式。同时对查询也做了极大优化,例如热数据进缓存、bloom filter等等。数据表采用Range Partition的分区模式,能提供水平扩展的服务能力,以及能自动探测并处理热点分区的负载均衡策略。为了满足同步库和存储库对存储的不同要求,也提供了一些灵活的自定义配置,主要包括: Time to live(数据生命周期):可自定义数据生命周期,例如永久保存,或者保存N天。 Storage type(存储类型):自定义存储类型,对存储库来说,HDD是最好的选择,对同步库来说,SSD是最好的选择。
Timeline Module
Timeline Store内能存储海量的Timeline,单个Timeline的详细结构图如上,可以看到Timeline主要包含了三大部分: Timeline Meta:元数据部分,用于描述Timeline,包括: Identifier :用于唯一标识Timeline,可包含多个字段。 Meta :用于描述Timeline的元数据,可包含任意个数任意类型的字段。 Meta Index :元数据索引,可对元数据内任意属性列建索引,支持多字段条件组合查询和检索。 Timeline Queue:用于存储和同步消息的队列,队列中元素由两部分组成: Sequence Id :顺序ID,队列中用于定位Message的位点信息,在队列中顺序ID保持递增。 Message :队列中承载消息的实体,包含了消息的完整内容。 Timeline Data:Timeline的数据部分就是Message,Message主要包含: Message :消息实体,其内部也可以包含任意数量任意类型字段。 Message Index :消息数据索引,可对消息实体内任意列做索引,支持多字段条件组合查询和检索。
IM消息系统建模
以一个简易版IM系统为例,来看如何基于Tablestore Timeline模型建模。按照上图中的例子,存在A、B、C三个用户,A与B发生单聊,A与C发生单聊,以及A、B、C组成一个群聊,来看下在这个场景下消息同步、存储以及读写流程分别如何基于Tablestore Timeline建模。
消息同步模型
消息同步选择写扩散模型,能完全利用Tablestore Timeline的优势,以及针对IM消息场景读多写少的特性,通过写扩散来平衡读写,均衡整个系统的资源。写扩散模型下,每个接收消息的个体均拥有一个收件箱,所有需要同步至该个体的消息需要投递到其收件箱内。图上例子中,A、B、C三个用户分别拥有收件箱,每个用户不同的设备端,均从同一个收件箱内拉取新消息。
消息同步库
收件箱存储在同步库内,同步库中每个收件箱对应一个Timeline。根据图上的例子,总共存在3个Timeline作为收件箱。每个消息接收端保存有本地最新拉取的消息的SequenceID,每次拉取新消息均是从该SequenceID开始拉取消息。对同步库的查询会比较频繁,通常是对最新消息的查询,所以要求热数据尽量缓存在内存中,能提供高并发低延迟的查询。所以对同步库的配置,一般是需要SSD存储。消息如果已经同步到了所有的终端,则代表收件箱内的该消息已经被消费完毕,理论上可以清理。但设计上来说不做主动清理,而是给数据定义一个较短的生命周期来自动过期,一般定义为一周或者两周。数据过期之后,如果仍要同步拉取新消息,则需要退化到读扩散的模式,从存储库中拉取消息。
消息存储库
消息存储库中保存有每个会话的消息,每个会话的发件箱对应一个Timeline。发件箱内的消息支持按会话维度拉取消息,例如浏览某个会话内的历史消息则通过读取发件箱完成。一般来说,新消息通过在线推送或者查询同步库可投递到各个接收端,所以对存储库的查询会相对来说较少。而存储库用于长期存储消息,例如永久存储,相对同步库来说数据量会较大。所以存储库的选择一般是HDD,数据生命周期根据消息需要保存的时间来定,通常是一个较长的时间。
消息索引库
消息索引库依附于存储库,使用了Timeline的Message Index,可以对存储库内的消息进行索引,例如对文本内容的全文索引、收件人、发件人以及发送时间的索引等,能支持全文检索等高级查询和搜索。
总结
本篇文章主要对Tablestore Timeline模型进行了详解,介绍了Timeline各模块包括Store、Meta、Queue、Data和Index等,最后以一个简单的IM场景举例如何基于Timeline来建模。在下一篇实现篇中,会直接基于Tablestore Timeline来实现一个简易版的支持单聊、群聊、元数据管理以及消息检索的IM系统,敬请期待。
作者:木洛
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
大数据
2019-05-08 12:01:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> 简评: 现在,从社交媒体上分享的照片到运输网络的运行系统,人们每天产生的数据字节数多达 2.5*100万的三次方(25 后面加 17 个 0)。掌握最新科技的艺术家正在利用这些「大数据」创造一种新的信息视觉语言。
亚伦·科布林
Aaron Koblin 的很多作品都是基于大数据的视觉创作。他的《飞机航班线路图》是对航空交通管理系统数据的视觉呈现,色彩生动地显示了 24 小时内美国境内商业航班的线路。
科布林嵌入了一个颜色编码系统,用来绘制不同飞行高度的航班,以及在机场起降的航班。这个《航班线路图》也会显示东部和西部时区天亮时分航空交通的变化,线路密集处显示了拥有多个机场的主要城市周围航空班次明显大增。
他的另一个作品《阿姆斯特丹短信服务》,用一家移动电话公司提供的数据呈现 24 小时内荷兰阿姆斯特丹市民众发短信的时间和地点情况。
用户可以在三维空间中查看这些数据。这个可视化数据图会显示短信发送数量突然暴升的时段,其中跨年夜的增幅最大。
下面这个称之为《一万美分》的创作是这样开始的 —— 科布尔和川岛高(Takashi Kawashima)通过 Mechanical Turk 网站雇用了一万名艺术家,请他们各自独立制作一张百元美钞的一极小部分。
然后再用计算机拼接成百元美钞版画,每张售价 100 美元,以此资助一个为儿童提供廉价笔记本电脑的慈善机构。这一万名艺术家的报酬是每人一美分。
科布尔说 ——《一万美分》「探索了数字劳动力市场、众包、虚拟经济和数字复制」等网络行为。
科布尔还与 MV 导演克里斯·米尔克(Chris Milk)合作过一个项目,让人们可以用一款画图程序为音乐家约翰尼·卡什的 MV《Ain't No Grave》创作一帧视频。
参与者创作了成千上万帧视频,风格各异,包括抽象派、写生派、现实派和点彩派等风格都有。
尽管最后成片采用的视频由导演克里斯·米尔克选择,但访问网页的用户可以查看所有备选视频,还可以改变自己看到的视频的艺术风格。
最后完成的视频风格大受欢迎,《约翰尼·卡什项目》获得 2011 年格莱美最佳音乐视频提名。
科布尔还与约翰·弗罗斯特(John Frost)合作,为电台司令乐队(Radiohead)推出的歌曲《House of Cards》制作视频。他们用激光和光扫描仪对乐队进行数字化处理后,制作出了一个采用粒子效果的高水平视频。
起初,《House of Cards》被放在了电台司令乐队的付费下载专辑《彩虹里》(In Rainbows)中。考虑到该专辑的开源性质,所获得的数据均免费对外开放,乐迷可创作自己的视频版本。
劳里·弗里克
数据艺术家 Laurie Frick 认为,获取个人信息不一定是件坏事,因为这能够突显出用户的独特个性。
弗里克特意选择制作手工作品,以便将数据从其数字形式中剥离出来。他还指出,未来利用艺术对海量数据进行可视化处理的做法可能会变得很普遍。
《感受到的个性》使用的数据来自在线交友网站 OKCupid。
该网站的用户通过回答评估其诚实度、同理心和忠诚度的问题来获得包容性评分,弗里克则用一系列颜色由深到浅的嵌板来对收集到的数据进行可视化再现。
《睡眠模式》使用的数据来自一个脑电图(EEG)头带。劳拉‧弗里克用这个头带测量了自己在三年时间里的睡眠模式。
弗里克用这些数据创作的作品透过其睡眠模式显示了她的大量大脑活动,表明在有数据记录的 1000 个夜晚里,她的的深度和快速眼动睡眠情况比她以为的要少。
池田亮司
Ryoji Ikeda 的《Datamatics》是一个系列作品,均由硬盘错误产生的二维序列和模式构成。作者用四维数学处理制作出了广阔的视觉景观。
为了创作出一件震撼人心的视频装置作品,池田亮司将数据的可视化同一首使人昏昏欲睡的电子配乐结合了起来。
《Datamatics》[prototype-ver.2.0] 提升了前身的视觉语言,用实时程序计算和数据扫描改变了原始的《Datamatics》序列,并进一步增加了作品的抽象程度。
池田亮司想让作品的动态元素(即极快的帧率和可变的位深)去探索将挑战观众之感知的临界值。
约恩·霍德和乔纳森·皮尔内伊
Jörn Röder 和 Jonathan Pirnay 探讨了为何人们对网络隐私和安全的担忧从来不会延伸到对自己社交媒体账号的态度上。
《fbFaces》用 JavaScript 和 PHP 代码查找并保存 Facebook 用户公开的账号头像、身份和姓名,然后再查找其好友列表中的用户公开头像,并保存同样的信息。
他们最终获取了 10 万张图片。这些图片被印在墙纸上,并在多件装置作品中展出。两位艺术家称,这件作品的目的是把房间变成「如洪水般涌来的信息」。
原文链接: Numbers game: The artists making waves with big data 推荐阅读: Python 的数学仙境之旅
欢迎关注微信号「极光开发者」
大数据
2019-05-06 15:44:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一. 网站结构:
网站截图说明

2. 采集结果截图
检索列表链接
检索结果数据

二、配置模板:
第一步:新建任务
点击加号,在弹窗里填写采集地址,任务名称,如图
新建任务
点击下一步,选择进行数据抽取还是链接抽取,本次采集要闻列表页新闻的正文数据,正文数据是通过点击列表链接进入的,所以本次需要抽取列表链接,所以点击抽取链接,如图:
新建采集任务

第二步:通过地址过滤,得到所需的分区链接。
点击采集预览,在采集预览中有于目标链接相似的其他链接,可通过地址过滤得到分区链接。找到所需要的分区链接,区别于其他链接“ http://news.cnstock.com/news/sns_yw ”,右击复制链接:
勾选地址过滤,过滤规则选择包含,将复制的目标地址粘入,得到要闻分区链接,点击保存,如图:
点击下一步,接着点击采集预览确认链接是否过滤完全,如图

第三步:填写模板二示例地址并新建数据抽取
将模板一过滤得到分区链接,作为模板二的示例地址,如图。
创建列表链接抽取、翻页链接抽取。直接点击模板二,点击上面“新建链接抽取”按钮,得到链接抽取,并重命名,如图
进行列表链接抽取,按住Ctrl+鼠标左键,进行区域选择,按住Shift+鼠标左键,扩大选择区域,点击“确认选区”按钮,如图

点击采集预览确认链接是否过滤完全,如图

第四步:通过标题过滤,过滤翻页链接
点击采集预览,在采集预览中有于目标链接相似的其他链接,可通过地址过滤得到列表链接。通过观察发现目标链接都包含“ http://news.cnstock.com/news,yw- ”+数字,使用过滤串\d得到所需要的链接。右击复制链接,如图过滤串规则说明:\d 表示一串(个)数字。

勾选地址过滤,过滤规则选择包含,地址填入
点击模板预览,选择翻页链接抽取,确认链接是否过滤完全,如图

第五步:创建新的模板,并新建数据抽取
在模板配置,点击“新建模板”按钮,得到新建模板,重命名为数据抽取模板
将模板二新建链接抽取过滤得到的任意一条链接,作为模板三的示例地址。

得到数据抽取
关联模板
在软件中模板的关联关系,与网页中链接跳转的关系相同。
根据网页跳转规律,将“列表链接抽取”关联模板“新建模板:03”,如图

第六步:创建/选择表单
在ForeSpider爬虫中,表单是可以复用的,所以可以在数据表单出直接选择之前建过的表单,也可以通过表单ID来进行查找并关联数据表单。此处使用的方法三。
方法一:通过下拉菜单或表单ID选择已有表单
方法二:点击创建表单进入快速建表页面,新建表单。
方法三:点击“采集配置”-“数据建表”,点击采“采集表单”后面的配置表单。

根据所需内容,配置表单字段(即表头),此处配置了包括网页主键、标题、发布时间、来源、作者、正文内容、采集地址共7个字段,右上角保存,表单如图:
在数据抽取链接处关联表单,如图
第七步:字段取值
取值方法:按住Ctrl+鼠标左键,进行区域选择,按住Shift+鼠标左键,扩大选择区域。btmc字段,如图

第八步:模板预览
①鼠标右键点击“数据抽取”,然后点击“模板预览”,如图
预览结果如图

第九步:采集预览
点击右上角采集预览,如图

双任意一条链接,看看是否可以得到和网页对应的规整的数据,如下图


三. 数据采集
第一步:运行设置
运行设置处可以设置采集速度、采集策略、任务装载等

第二步:选择采集任务
在【任务列表】中勾选需要采集的任务,可勾选多个任务,同时采集。
第三步:开始采集
点击【开始采集】,系统开始进行采集。剩余任务数为0时,系统自动停止采集。用户也可以自己暂停任务或停止任务(停止任务会释放任务,再次启动时重新装载任务)。


第四步:数据浏览
采集一段时间以后,点击【数据浏览】,在数据列表中选中对应的数据表,即可浏览采集到的数据,点击【刷新】按钮可以同步显示数据。

第五步:导出数据
点击【导出】按钮,选择导出文件格式后保存。
保存数据
大数据
2020-01-15 14:08:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在消息系统中,消息的生成和消费通常支持三种模式: at-most-once,at-least-once 和 exactly-once。 这几种模式的区别主要在于当系统发生错误的时候,系统表现何种行为。前两种模式,非常容易理解,出错后不处理或者不停重试直到成功为止,对于exactly-once的模式,系统在出错的时候,则需要进行特殊处理来保证一条消息只被处理一次。
发生错误的场景
上图是常见的消息系统(如Kafka/Plusar/sls loghub等)的架构,消息(message)由生产者(Producer)发送至消息系统的接收者(Broker),Broker将Message持久化到特定分区(Partition)后,供消费者(Consumer)进行消费。在这个过程中,任意阶段都可能出错。 Broker 错误:Broker作为系统的主要组成部分,负责数据的接收和读取,当单个broker fail的时候,不同的系统表现行为也不同,可能会重新选举Leader或进行broker的failover,在这个过程中,消息的读写可能发生失败 Producer到Broker RPC错误 : Producer将数据发送至Broker后,需要收到Broker的ack才能确定消息写入成功,在Broker出错,或Producer到Broker网络异常等情况下,Producer可能无法收到ack信息,这时,Producer无法确认消息是否已经正确持久化,如果Producer忽略错误,这表现为at-most-once模式,进行重试直到成功,则可能是at-least-once Producer :消息的生产者本身也可能会因为程序异常、机器宕机等行为导致异常,在Producer内存中尚未发送的消息,如果不做特殊处理,则会丢失 Consumer :作为消息的消费者,在发生错误的时候如何恢复消费的状态以及从哪个位置再次开始消费数据,则决定消费情况系统表现那种模式
写入模式下Exactly-Once
从上面的错误场景可以看到,错误在任意阶段都可能发生,要做到任意情况下完全的exactly-once写入代价极其昂贵,在线上大规模生产系统中,很难承受: 每条消息有一个唯一的ID Broker在接收到一条消息后,进行全局校验ID是否重复 Broker对于消息ID的校验和写入原子操作
在分布式场景下,做全局全量数据的原子排他性操作,成本无法接受的。那退而求其次,在一定限定条件下,则可以更高效达到exactly-once的效果。可以从以下两方面进行限定: Producer 发送的一条消息,在各种错误情况下,限定只到某个Broker下的确定Partition。这样消息无需做全局的校验,只需要在Partition级别即可; 单个Producer 到单个Partition的消息ID(Sequence ID)单调递增,这样虽然降低了单个Producer到单个Partition的吞吐(串行),但是每个Partition对于Producer消息的ID校验大大简化,只需要效验一个ID即可,无需维护历史所有ID
通过以上两个简化,在Partition级的exactly-once的写入操作,只需要额外一次HashMap的查询即可,而持久化的数据,也只会增加极少量的字段(ProducerID, Sequence ID)。
Broker Failover 处理
在Broker Failover的时候,必须将Broker内存中各个Producer当前写入的SequenceID完全恢复出来,才能保证数据exactly-once写入。在持久化的数据中,有每条消息的ProducerID和SequenceID信息,可以通过扫描持久化信息进行恢复,同时为了加快恢复速度,可以定期将Broker内存中的HashMap作为snapshot保存下来,在恢复的时候,首先恢复snapshot,然后只需要读取少量的信息完成ProducerID和SequenceID映射重建。
Producer Failover 处理
当Producer Failover的时候,对于部分数据源和SequenceID可映射的场景,可以根据ProducerID从Broker中获取最新的SequenceID,根据该ID对数据源进行重置(如Producer的数据源是文件,SequenceID 和文件行号能进行映射),Producer可以从上次最后写入成功的位置继续写入。
消费模式下 Exactly-Once
在完成exactly-once写入后,为了支持端到端的exactly-once处理, 同样需要消费端的配合,这里主要指在消费端failover时,如何确保每条消息只被处理一次。 虽然不少消息系统提供ack机制,消费者只需关系数据的处理即可,如Plusar提供的consumer会自动拉取消息供应用消费,应用只需要关心消息的处理,在处理完毕后,对消息进行ack即可。 Consumer consumer = client.subscribe(...); while (true) { Message msg = consumer.receive(); // Process the message... consumer.acknowledge(msg); }
但是,这种简单的消费模式,无法支持exactly-once,核心在于消息的处理和ack是非原子操作。当消息处理完毕尚未进行ack时,consumer可能crash,重启后,这条消息将被重复消费(broker尚未收到这条消息的ack信息)。



原文链接
本文为阿里云内容,未经允许不得转载。
因此,为了支持exactly-once消费,需要将消息处理的结果(通常使用状态表示)和消息的ID持久化到其他外部系统中,以确保failover能恢复到某个完全精确的状态继续消费,如flink使用的checkpoint机制,将flink内部某时刻状态以及消费的位置信息进行持久化。
即使如此,如果在消费过程中,会额外产生结果并写入其他下游系统时,如果这些系统不支持幂等操作,那么在failover时,consumer重复消费数据时,下游系统还可能看到at-least-once的结果(如消费信息进行短信报警的场景)。
以上是对于消息系统的端对端支持exactly-once的简单探讨,通过一定的条件限定,写入端支持相对容易,而消费端除了较复杂的checkpoint机制外,还依赖消费产出下游系统的支持。
大数据
2020-01-15 11:50:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1.前言
elsaticsearch版本是6.8.3,使用的java-api是基于Java High Level REST Client.
2.数据
3. InitClient
用来初始化客户端 import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; public class InitClient { public static RestHighLevelClient getClient(){ RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( // new HttpHost("192.168.1.101", 9200, "http"), // new HttpHost("192.168.1.102", 9200, "http"), new HttpHost("192.168.1.103", 9200, "http") ) ); return client; }; }
用来初始化带有密码的客户端 import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback; import org.elasticsearch.client.RestHighLevelClient; public class InitClient { public static RestHighLevelClient getClient(){ /** 用户认证对象 */ final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); /** 设置账号密码 */ credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "123456")); /** 创建rest client对象 */ RestClientBuilder builder = RestClient.builder(new HttpHost("127.0.0.1", 9200)) .setHttpClientConfigCallback(new HttpClientConfigCallback() { @Override public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); } }); RestHighLevelClient client = new RestHighLevelClient(builder); return client; }; }
4.查询
4.1查询所有
无条件情况下,查询所有 private static void queryAll(){ try(RestHighLevelClient client = InitClient.getClient()){ //创建SearchRequest SearchRequest searchRequest = new SearchRequest(); //指定索引为poems searchRequest.indices("poems"); //创建SearchSourceBuilder SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //创建BoolQueryBuilder 用于添加条件 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); //排序 按照索引中的id升序排序 searchSourceBuilder.sort(new FieldSortBuilder("_uid").order(SortOrder.ASC)); //分页 searchSourceBuilder.from(0); searchSourceBuilder.size(20); //将查询条件放入searchSourceBuilder中 searchSourceBuilder.query(boolQueryBuilder); //searchRequest解析searchSourceBuilder searchRequest.source(searchSourceBuilder); //获取SearchResponse SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); //获取分片结果 SearchHits hits = searchResponse.getHits(); SearchHit[] searchHits = hits.getHits(); //获得数据 for (SearchHit hit : searchHits) { String sourceAsString = hit.getSourceAsString(); System.out.println(sourceAsString); } //关闭连接 client.close(); } catch (IOException e) { e.printStackTrace(); } }
结果:
4.2match
match查询主要是针对分词情况下的匹配查询.默认情况下,是按照空格分词的.
由于我这里没有设置中文分词,实际上效果并不是很好
例1: MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("content", "三千"); boolQueryBuilder.must(matchQueryBuilder);
这里想查找content字段下,有"三千"的内容,想要的结果应该是只会返回"日照香炉生紫烟,遥看瀑布挂前川。飞流直下三千尺,疑是银河落九天。"
但结果是这样的:
可以看到返回了三条结果,只有望庐山瀑布满足了有"三"和"千"这两个内容,月下独酌只满足了"三",元日满足了"千"
4.3term
term查询是完全匹配查询,只有完全匹配字段的内容,才会查到,
使用term查询,一定要使用keyword属性,否则会被分词,就查不到了.
例1:查找作者是李白的结果 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("author.keyword", "李白"); boolQueryBuilder.must(termQueryBuilder);
结果:
4.4wildcard
wildcard查询是通配符查询,相当于mysql中的like,这个也要使用keyword属性
例1:查找诗歌中包含"三千"的内容, WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("content.keyword", "*三千*"); boolQueryBuilder.must(wildcardQueryBuilder);
结果:
可以看到只返回了一个结果,这也是wildcard和match不同的地方
4.5prefix
prefix查询是前缀查询,也是使用keyword属性
例1:查找所有李姓作者 PrefixQueryBuilder prefixQueryBuilder = QueryBuilders.prefixQuery("author.keyword", "李"); boolQueryBuilder.must(prefixQueryBuilder);
结果:
4.6嵌套查询
对于多条件查询,有时候需要创建多个 QueryBuilders.boolQuery() 来进行嵌套
例1:查找content字段下内容中有"月"的或者有"酒"和"雨" select * from poems where content like '月' or(content like '酒' and content like'雨') BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); BoolQueryBuilder boolQueryBuilderContent = QueryBuilders.boolQuery(); WildcardQueryBuilder wildcardQueryBuilderMoon = QueryBuilders.wildcardQuery("content.keyword", "*月*"); WildcardQueryBuilder wildcardQueryBuilderAlcohol = QueryBuilders.wildcardQuery("content.keyword", "*酒*"); WildcardQueryBuilder wildcardQueryBuilderRainy = QueryBuilders.wildcardQuery("content.keyword", "*雨*"); boolQueryBuilderContent.must(wildcardQueryBuilderAlcohol).must(wildcardQueryBuilderRainy); boolQueryBuilder.should(wildcardQueryBuilderMoon).should(boolQueryBuilderContent);
结果:
5.聚合统计
例1:计算每个诗人的诗歌数 select author, count(*) as author_count from poems group by author private static void aggregation(){ try(RestHighLevelClient client = InitClient.getClient()){ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices("poems"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //指定计数author 这里的author_count可以随意取名 TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("author_count").field("author.keyword"); //将aggregationBuilder 放入searchSourceBuilder searchSourceBuilder.aggregation(aggregationBuilder); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); //获取count 这里的author_count 要和上面取的名字对应上 Terms terms = searchResponse.getAggregations().get("author_count"); //获取结果 for (Terms.Bucket bucket : terms.getBuckets()) { System.out.println("author=" + bucket.getKey()+" count="+bucket.getDocCount()); } client.close(); } catch (IOException e) { e.printStackTrace(); } }
结果:
例2:计算每个朝代每个诗人的诗歌数 select dynasty,author,count(*) as author_count from poems group by dynasty,author private static void aggregation(){ try(RestHighLevelClient client = InitClient.getClient()){ SearchRequest searchRequest = new SearchRequest(); searchRequest.indices("poems"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //设置聚合的字段dynasty 和author TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("dynasty_count").field("dynasty.keyword"); TermsAggregationBuilder aggregationBuilder2 = AggregationBuilders.terms("author_count").field("author.keyword"); //aggregationBuilder2是aggregationBuilder的子聚合 searchSourceBuilder.aggregation(aggregationBuilder.subAggregation(aggregationBuilder2)); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); //获取dynasty_count Terms terms = searchResponse.getAggregations().get("dynasty_count"); //获取结果 for (Terms.Bucket bucket : terms.getBuckets()) { System.out.println("dynasty=" + bucket.getKey()+" count="+bucket.getDocCount()); //获取author_count Terms terms2 = bucket.getAggregations().get("author_count"); for (Terms.Bucket bucket2 : terms2.getBuckets()) { System.out.println("author=" + bucket2.getKey()+ "; 数量=" + bucket2.getDocCount()); } } client.close(); } catch (IOException e) { e.printStackTrace(); } }
结果:
大数据
2020-01-13 16:24:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
维表JOIN-绕不过去的业务场景
在Flink 流处理过程中,经常需要和外部系统进行交互,用维度表补全事实表中的字段。
例如:在电商场景中,需要一个商品的skuid去关联商品的一些属性,例如商品所属行业、商品的生产厂家、生产厂家的一些情况; 在物流场景中,知道包裹id,需要去关联包裹的行业属性、发货信息、收货信息等等。
默认情况下,在Flink的MapFunction中,单个并行只能用同步方式去交互: 将请求发送到外部存储,IO阻塞,等待请求返回,然后继续发送下一个请求。这种同步交互的方式往往在网络等待上就耗费了大量时间。为了提高处理效率,可以增加MapFunction的并行度,但增加并行度就意味着更多的资源,并不是一种非常好的解决方式。
Async I/O异步非阻塞请求
Flink 在1.2中引入了Async I/O,在异步模式下,将IO操作异步化,单个并行可以连续发送多个请求,哪个请求先返回就先处理,从而在连续的请求间不需要阻塞式等待,大大提高了流处理效率。
Async I/O 是阿里巴巴贡献给社区的一个呼声非常高的特性,解决与外部系统交互时网络延迟成为了系统瓶颈的问题。
图中棕色的长条表示等待时间,可以发现网络等待时间极大地阻碍了吞吐和延迟。为了解决同步访问的问题,异步模式可以并发地处理多个请求和回复。也就是说,你可以连续地向数据库发送用户a、b、c等的请求,与此同时,哪个请求的回复先返回了就处理哪个回复,从而连续的请求之间不需要阻塞等待,如上图右边所示。这也正是 Async I/O 的实现原理。
详细的原理可以参考文末给出的第一个链接,来自阿里巴巴云邪的分享。
一个简单的例子如下: public class AsyncIOFunctionTest { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); env.setParallelism(1); Properties p = new Properties(); p.setProperty("bootstrap.servers", "localhost:9092"); DataStreamSource ds = env.addSource(new FlinkKafkaConsumer010("order", new SimpleStringSchema(), p)); ds.print(); SingleOutputStreamOperator order = ds .map(new MapFunction() { @Override public Order map(String value) throws Exception { return new Gson().fromJson(value, Order.class); } }) .assignTimestampsAndWatermarks(new AscendingTimestampExtractor() { @Override public long extractAscendingTimestamp(Order element) { try { return element.getOrderTime(); } catch (Exception e) { e.printStackTrace(); } return 0; } }) .keyBy(new KeySelector() { @Override public String getKey(Order value) throws Exception { return value.getUserId(); } }) .window(TumblingEventTimeWindows.of(Time.minutes(10))) .maxBy("orderTime"); SingleOutputStreamOperator> operator = AsyncDataStream .unorderedWait(order, new RichAsyncFunction>() { private Connection connection; @Override public void open(Configuration parameters) throws Exception { super.open(parameters); Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("url", "user", "pwd"); connection.setAutoCommit(false); } @Override public void asyncInvoke(Order input, ResultFuture> resultFuture) throws Exception { List> list = new ArrayList<>(); // 在 asyncInvoke 方法中异步查询数据库 String userId = input.getUserId(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("select name,age,sex from user where userid=" + userId); if (resultSet != null && resultSet.next()) { String name = resultSet.getString("name"); int age = resultSet.getInt("age"); String sex = resultSet.getString("sex"); Tuple7 res = Tuple7.of(userId, name, age, sex, input.getOrderId(), input.getPrice(), input.getOrderTime()); list.add(res); } // 将数据搜集 resultFuture.complete(list); } @Override public void close() throws Exception { super.close(); if (connection != null) { connection.close(); } } }, 5000, TimeUnit.MILLISECONDS,100); operator.print(); env.execute("AsyncIOFunctionTest"); } }
上述代码中,原始订单流来自Kafka,去关联维度表将订单的用户信息取出来。从上面示例中可看到,我们在open()中创建连接对象,在close()方法中关闭连接,在RichAsyncFunction的asyncInvoke()方法中,直接查询数据库操作,并将数据返回出去。这样一个简单异步请求就完成了。
Async I/O的原理和基本用法
简单的来说,使用 Async I/O 对应到 Flink 的 API 就是 RichAsyncFunction 这个抽象类,继层这个抽象类实现里面的open(初始化),asyncInvoke(数据异步调用),close(停止的一些操作)方法,最主要的是实现asyncInvoke 里面的方法。
我们先来看一个使用Async I/O的模板方法: // This example implements the asynchronous request and callback with Futures that have the // interface of Java 8's futures (which is the same one followed by Flink's Future) /** * An implementation of the 'AsyncFunction' that sends requests and sets the callback. */ class AsyncDatabaseRequest extends RichAsyncFunction> { /** The database specific client that can issue concurrent requests with callbacks */ private transient DatabaseClient client; @Override public void open(Configuration parameters) throws Exception { client = new DatabaseClient(host, post, credentials); } @Override public void close() throws Exception { client.close(); } @Override public void asyncInvoke(String key, final ResultFuture> resultFuture) throws Exception { // issue the asynchronous request, receive a future for result final Future result = client.query(key); // set the callback to be executed once the request by the client is complete // the callback simply forwards the result to the result future CompletableFuture.supplyAsync(new Supplier() { @Override public String get() { try { return result.get(); } catch (InterruptedException | ExecutionException e) { // Normally handled explicitly. return null; } } }).thenAccept( (String dbResult) -> { resultFuture.complete(Collections.singleton(new Tuple2<>(key, dbResult))); }); } } // create the original stream DataStream stream = ...; // apply the async I/O transformation DataStream> resultStream = AsyncDataStream.unorderedWait(stream, new AsyncDatabaseRequest(), 1000, TimeUnit.MILLISECONDS, 100);
假设我们一个场景是需要进行异步请求其他数据库,那么要实现一个通过异步I/O来操作数据库还需要三个步骤:   1、实现用来分发请求的AsyncFunction   2、获取操作结果的callback,并将它提交到AsyncCollector中   3、将异步I/O操作转换成DataStream 其中的两个重要的参数:
Timeouttimeout 定义了异步操作过了多长时间后会被丢弃,这个参数是防止了死的或者失败的请求 Capacity 这个参数定义了可以同时处理多少个异步请求。虽然异步I/O方法会带来更好的吞吐量,但是算子仍然会成为流应用的瓶颈。超过限制的并发请求数量会产生背压。
几个需要注意的点: 使用Async I/O,需要外部存储有支持异步请求的客户端。 使用Async I/O,继承RichAsyncFunction(接口AsyncFunction的抽象类),重写或实现open(建立连接)、close(关闭连接)、asyncInvoke(异步调用)3个方法即可。 使用Async I/O, 最好结合缓存一起使用,可减少请求外部存储的次数,提高效率。 Async I/O 提供了Timeout参数来控制请求最长等待时间。默认,异步I/O请求超时时,会引发异常并重启或停止作业。 如果要处理超时,可以重写AsyncFunction#timeout方法。 Async I/O 提供了Capacity参数控制请求并发数,一旦Capacity被耗尽,会触发反压机制来抑制上游数据的摄入。 Async I/O 输出提供乱序和顺序两种模式。 乱序, 用AsyncDataStream.unorderedWait(...) API,每个并行的输出顺序和输入顺序可能不一致。 顺序, 用AsyncDataStream.orderedWait(...) API,每个并行的输出顺序和输入顺序一致。为保证顺序,需要在输出的Buffer中排序,该方式效率会低一些。
Flink 1.9 的优化
由于新合入的 Blink 相关功能,使得 Flink 1.9 实现维表功能很简单。 如果你要使用该功能,那就需要自己引入 Blink 的 Planner。 org.apache.flink flink-table-planner-blink_${scala.binary.version} ${flink.version}
然后我们只要自定义实现 LookupableTableSource 接口,同时实现里面的方法就可以进行,下面来分析一下 LookupableTableSource的代码: public interface LookupableTableSource extends TableSource { TableFunction getLookupFunction(String[] lookupKeys); AsyncTableFunction getAsyncLookupFunction(String[] lookupKeys); boolean isAsyncEnabled(); }
这三个方法分别是: isAsyncEnabled 方法主要表示该表是否支持异步访问外部数据源获取数据,当返回 true 时,那么在注册到 TableEnvironment 后,使用时会返回异步函数进行调用,当返回 false 时,则使同步访问函数。 getLookupFunction 方法返回一个同步访问外部数据系统的函数,什么意思呢,就是你通过 Key 去查询外部数据库,需要等到返回数据后才继续处理数据,这会对系统处理的吞吐率有影响。 getAsyncLookupFunction 方法则是返回一个异步的函数,异步访问外部数据系统,获取数据,这能极大的提升系统吞吐率。
我们抛开同步访问函数不管,对于getAsyncLookupFunction会返回异步访问外部数据源的函数,如果你想使用异步函数,前提是 LookupableTableSource 的 isAsyncEnabled 方法返回 true 才能使用。使用异步函数访问外部数据系统,一般是外部系统有异步访问客户端,如果没有的话,可以自己使用线程池异步访问外部系统。例如: public class MyAsyncLookupFunction extends AsyncTableFunction { private transient RedisAsyncCommands async; @Override public void open(FunctionContext context) throws Exception { RedisClient redisClient = RedisClient.create("redis://127.0.0.1:6379"); StatefulRedisConnection connection = redisClient.connect(); async = connection.async(); } public void eval(CompletableFuture> future, Object... params) { redisFuture.thenAccept(new Consumer() { @Override public void accept(String value) { future.complete(Collections.singletonList(Row.of(key, value))); } }); } }
一个完整的例子如下:
Main方法: import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.typeutils.RowTypeInfo; import org.apache.flink.api.java.utils.ParameterTool; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer011; import org.apache.flink.table.api.EnvironmentSettings; import org.apache.flink.table.api.Table; import org.apache.flink.table.api.java.StreamTableEnvironment; import org.apache.flink.types.Row; import org.junit.Test; import java.util.Properties; public class LookUpAsyncTest { @Test public void test() throws Exception { LookUpAsyncTest.main(new String[]{}); } public static void main(String[] args) throws Exception { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); //env.setParallelism(1); EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build(); StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env, settings); final ParameterTool params = ParameterTool.fromArgs(args); String fileName = params.get("f"); DataStream source = env.readTextFile("hdfs://172.16.44.28:8020" + fileName, "UTF-8"); TypeInformation[] types = new TypeInformation[]{Types.STRING, Types.STRING, Types.LONG}; String[] fields = new String[]{"id", "user_click", "time"}; RowTypeInfo typeInformation = new RowTypeInfo(types, fields); DataStream stream = source.map(new MapFunction() { private static final long serialVersionUID = 2349572543469673349L; @Override public Row map(String s) { String[] split = s.split(","); Row row = new Row(split.length); for (int i = 0; i < split.length; i++) { Object value = split[i]; if (types[i].equals(Types.STRING)) { value = split[i]; } if (types[i].equals(Types.LONG)) { value = Long.valueOf(split[i]); } row.setField(i, value); } return row; } }).returns(typeInformation); tableEnv.registerDataStream("user_click_name", stream, String.join(",", typeInformation.getFieldNames()) + ",proctime.proctime"); RedisAsyncLookupTableSource tableSource = RedisAsyncLookupTableSource.Builder.newBuilder() .withFieldNames(new String[]{"id", "name"}) .withFieldTypes(new TypeInformation[]{Types.STRING, Types.STRING}) .build(); tableEnv.registerTableSource("info", tableSource); String sql = "select t1.id,t1.user_click,t2.name" + " from user_click_name as t1" + " join info FOR SYSTEM_TIME AS OF t1.proctime as t2" + " on t1.id = t2.id"; Table table = tableEnv.sqlQuery(sql); DataStream result = tableEnv.toAppendStream(table, Row.class); DataStream printStream = result.map(new MapFunction() { @Override public String map(Row value) throws Exception { return value.toString(); } }); Properties properties = new Properties(); properties.setProperty("bootstrap.servers", "127.0.0.1:9094"); FlinkKafkaProducer011 kafkaProducer = new FlinkKafkaProducer011<>( "user_click_name", new SimpleStringSchema(), properties); printStream.addSink(kafkaProducer); tableEnv.execute(Thread.currentThread().getStackTrace()[1].getClassName()); } }
RedisAsyncLookupTableSource方法: import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.java.typeutils.RowTypeInfo; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.table.api.TableSchema; import org.apache.flink.table.functions.AsyncTableFunction; import org.apache.flink.table.functions.TableFunction; import org.apache.flink.table.sources.LookupableTableSource; import org.apache.flink.table.sources.StreamTableSource; import org.apache.flink.table.types.DataType; import org.apache.flink.table.types.utils.TypeConversions; import org.apache.flink.types.Row; public class RedisAsyncLookupTableSource implements StreamTableSource, LookupableTableSource { private final String[] fieldNames; private final TypeInformation[] fieldTypes; public RedisAsyncLookupTableSource(String[] fieldNames, TypeInformation[] fieldTypes) { this.fieldNames = fieldNames; this.fieldTypes = fieldTypes; } //同步方法 @Override public TableFunction getLookupFunction(String[] strings) { return null; } //异步方法 @Override public AsyncTableFunction getAsyncLookupFunction(String[] strings) { return MyAsyncLookupFunction.Builder.getBuilder() .withFieldNames(fieldNames) .withFieldTypes(fieldTypes) .build(); } //开启异步 @Override public boolean isAsyncEnabled() { return true; } @Override public DataType getProducedDataType() { return TypeConversions.fromLegacyInfoToDataType(new RowTypeInfo(fieldTypes, fieldNames)); } @Override public TableSchema getTableSchema() { return TableSchema.builder() .fields(fieldNames, TypeConversions.fromLegacyInfoToDataType(fieldTypes)) .build(); } @Override public DataStream getDataStream(StreamExecutionEnvironment environment) { throw new UnsupportedOperationException("do not support getDataStream"); } public static final class Builder { private String[] fieldNames; private TypeInformation[] fieldTypes; private Builder() { } public static Builder newBuilder() { return new Builder(); } public Builder withFieldNames(String[] fieldNames) { this.fieldNames = fieldNames; return this; } public Builder withFieldTypes(TypeInformation[] fieldTypes) { this.fieldTypes = fieldTypes; return this; } public RedisAsyncLookupTableSource build() { return new RedisAsyncLookupTableSource(fieldNames, fieldTypes); } } }
MyAsyncLookupFunction import io.lettuce.core.RedisClient; import io.lettuce.core.RedisFuture; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.async.RedisAsyncCommands; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.java.typeutils.RowTypeInfo; import org.apache.flink.table.functions.AsyncTableFunction; import org.apache.flink.table.functions.FunctionContext; import org.apache.flink.types.Row; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; public class MyAsyncLookupFunction extends AsyncTableFunction { private final String[] fieldNames; private final TypeInformation[] fieldTypes; private transient RedisAsyncCommands async; public MyAsyncLookupFunction(String[] fieldNames, TypeInformation[] fieldTypes) { this.fieldNames = fieldNames; this.fieldTypes = fieldTypes; } @Override public void open(FunctionContext context) { //配置redis异步连接 RedisClient redisClient = RedisClient.create("redis://127.0.0.1:6379"); StatefulRedisConnection connection = redisClient.connect(); async = connection.async(); } //每一条流数据都会调用此方法进行join public void eval(CompletableFuture> future, Object... paramas) { //表名、主键名、主键值、列名 String[] info = {"userInfo", "userId", paramas[0].toString(), "userName"}; String key = String.join(":", info); RedisFuture redisFuture = async.get(key); redisFuture.thenAccept(new Consumer() { @Override public void accept(String value) { future.complete(Collections.singletonList(Row.of(key, value))); //todo // BinaryRow row = new BinaryRow(2); } }); } @Override public TypeInformation getResultType() { return new RowTypeInfo(fieldTypes, fieldNames); } public static final class Builder { private String[] fieldNames; private TypeInformation[] fieldTypes; private Builder() { } public static Builder getBuilder() { return new Builder(); } public Builder withFieldNames(String[] fieldNames) { this.fieldNames = fieldNames; return this; } public Builder withFieldTypes(TypeInformation[] fieldTypes) { this.fieldTypes = fieldTypes; return this; } public MyAsyncLookupFunction build() { return new MyAsyncLookupFunction(fieldNames, fieldTypes); } } }
十分需要注意的几个点:
1、 外部数据源必须是异步客户端:如果是线程安全的(多个客户端一起使用),你可以不加 transient 关键字,初始化一次。否则,你需要加上 transient,不对其进行初始化,而在 open 方法中,为每个 Task 实例初始化一个。
2、eval 方法中多了一个 CompletableFuture,当异步访问完成时,需要调用其方法进行处理。比如上面例子中的: redisFuture.thenAccept(new Consumer() { @Override public void accept(String value) { future.complete(Collections.singletonList(Row.of(key, value))); } });
3、社区虽然提供异步关联维度表的功能,但事实上大数据量下关联外部系统维表仍然会成为系统的瓶颈,所以一般我们会在同步函数和异步函数中加入缓存。综合并发、易用、实时更新和多版本等因素考虑,Hbase是最理想的外部维表。
参考文章: http://wuchong.me/blog/2017/05/17/flink-internals-async-io/# https://www.jianshu.com/p/d8f99d94b761 https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=65870673 https://www.jianshu.com/p/7ce84f978ae0
声明:本号所有文章除特殊注明,都为原创,公众号读者拥有优先阅读权,未经作者本人允许不得转载,否则追究侵权责任。
关注我的公众号,后台回复【JAVAPDF】获取200页面试题! 5万人关注的大数据成神之路,不来了解一下吗? 5万人关注的大数据成神之路,真的不来了解一下吗? 5万人关注的大数据成神之路,确定真的不来了解一下吗?
欢迎您关注 《大数据成神之路》
大数据
2020-01-10 22:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
摘要:日前,由阿里数据打造的智能用户增长QuickAudience,重磅上线阿里云-公共云,开启公测!该产品旨在围绕着品牌消费资产,通过丰富的用户洞察模型和便捷的策略配置,完成消费者多维洞察分析和多渠道触达,助力企业实现用户增长。
智能用户增长神器Quick Audience开启公共云公测
阿里云数据中台产品矩阵日前又添新成员,智能用户增长Quick Audience(以下简称Quick Audience)已经登陆公共云公测。
在此之前,阿里云数据中台通过旗下产品矩阵的Dataphin和QuickBI,已助力多家大型客户高效自动化构建企业数据中台,提供用户高效智能的数据分析服务,帮助用户更好地通过数据服务体系让数据智能驱动业务。
而此次全新公测的Quick Audience产品,则将致力于挖掘企业更多消费增长共建,围绕着品牌消费资产,通过丰富的用户洞察模型和便捷的策略配置,完成消费者多维洞察分析和多渠道触达,助力企业实现用户增长。
据了解,目前公测版中的Quick Audience共包含以下几大功能模块:数据源及数据集配置、洞察分析(透视分析、AIPL及其流转分析、RFM分析、受众分析)、受众圈选、受众管理。可帮助企业实现快速人群圈选以及多渠道触达,由其可将企业一方消费者人群推送至品牌数据银行侧,连同用户线上线下数据,全面提能提效消费者运营。
Quick Audience应用场景
现阶段,Quick Audience适用于新零售客户群体。其提供以下3大场景服务:
(1) 大促营销场景
在大促前期,通过Quick Audience对已购用户进行分层分析和洞察,从而了解已购客户的TA特征,根据TA圈选相关人群包,搭配不同的素材,推送到渠道侧放大投放;同时针对潜在客户,进行圈选后可推送到不同渠道进行retargeting,从而完成大促前人群蓄水和购买转化
(2) 日常营销场景
在日常的营销场景下,通过Quick Audience用户洞察内置的RFM,AIPL等通用模型的洞察分析,帮助企业更好的做用户分层和管理,同时筛选出的受众与大盘人群相比,在属性、行为特征、偏好上分别有哪些特征帮助企业更好做到不同用户分层的多样化的触达策略。
(3) 全域营销场景
通过Quick Audience和数据银行能力深度打通,分别在阿里云及生态内为客户提供服务,实现品牌线下和线上消费者统一的洞察分析,交叉圈选,并在生态内投放触达,全面提能提效品牌全域消费者运营。
Quick Audience核心功能
(1)数据源接入:提供多数据源、多数据集的接入能力,完成数据源导入及管理,支持接入ADS、HybridDB数据库。
(2)数据集创建:提供各类数据集如消费数据集、AIPL数据集、RFM数据集的模型配置能力,针对AIPL和RFM数据集可自主配置得分规则和阈值。
(3)洞察分析:提供多维洞察分析,包括透视分析、RFM分析、AIPL分析及流转分析等能力。
(4)自定义管理:支持快速自定义圈选目标,提供受众分析、编辑、下载、更新、推送等多种管理功能。
(5)全域营销:帮助品牌建立全域消费数据资产,全面提能、提效品牌全域运营。
Quick Audience核心优势
高效模型创建:通过快速的模型配置,完成用于洞察分析的用户模型;
多维用户洞察: 通过用户模型和360度的标签完成多维度的用户洞察分析;
便捷策略制定:多维度的圈人策略和便捷的计划制定可快速完成人群策略;
多端渠道触达:多投放渠道集成,可一键完成全域营销闭环;



原文链接
本文为阿里云内容,未经允许不得转载。
大数据
2020-01-10 14:36:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
除了基础的数据查询开发功能,DMS还内置SQL审核、性能诊断优化、测试数据自动生成、多环境数据对比同步、数据库日志追踪回滚、不锁表变更、访问控制、敏感数据脱敏、安全审计等高端功能。
1、丰富的数据源
🔸丰富的数据库类型支持(当前已超过25种,还在不断扩展中) MySQL、SQL Server、PostgreSQL、POLARDB等关系型数据库 DRDS等OLTP数据库 AnalyticDB for MySQL、DLA等OLAP数据库 MongoDB、Redis等NoSQL的数据库管理 同时还支持Linux服务器管理
🔸丰富的环境来源支持 阿里云数据库 阿里云ECS自建数据库 本地IDC自建数据库 第三方云服务数据库 第三方云服务器自建数据库
在web端的基础上,本次发布客户端软件版,支持下载到本地即开即用;相对于web端,本次也提供了量大更新更贴近本地客户端的使用方式,降低大家的使用门槛。
1)左侧展现所有已录入DMS使用的数据库资源列表(可与web端控制台数据库列表同步)
2)支持公网方式访问云数据库、云服务器自建数据库
3)支持同时登录多个不同数据库切换使用
2、DMS客户端的大杀器
🔻低成本的数据库日志追踪与恢复
🔻便捷的测试数据构建,快速准备大量测试数据减少数据准备时间
🔻可视化的数据库性能诊断与优化,掌握运行状态、有效优化保障服务稳定运行
SQL的诊断:
一键诊断,实时了解数据库运行情况、快速发现异常快速修复
性能趋势,分析了解运行趋势、定位异常规划容量
更多数据库性能诊断优化服务
3、手把手教你使用DMS客户端
使用步骤如下:
选择对应的OS版本进行下载
示例以MacOS为例,下载到本地
双击打开软件,进入到阿里云账号登录页面,可选多种登录方式
登录云账号进入以下页面:
新增待管理的数据库
或直接检索已有登录过的数据库进行快速登录与使用,可同时登录多个,进行便捷的数据、结构可视化管理

快速的多环境表结构一致性对比与同步
更多丰富特性可前往产品使用体验: 精准的数据库性能分析与诊断、优化 细粒度的权限管控与智能的数据脱敏 智能的SQL风险审核 可配置的语法规范与研发流程、审批流程 稳定的数据变更与结构变更(不锁表) 高效的数据开发任务周期调度管理 易用的数据分析、跨库查询服务


原文链接
本文为阿里云内容,未经允许不得转载。
大数据
2020-01-10 14:23:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Jupyter/JupyterHub/JupyterLab能够以多种方式运行,包括命令行、系统服务、Docker实例、Kubernetes应用等。这里介绍将其作为系统服务运行的方式,通过使用《 IPython 6/Jupyter的magic操作符 》、《 IPython的Magics魔法操作符 》可提供更好的控制台操作(谁用谁知道!如果在Notebook中需要输入密码,可以使用sshpass参考《 Ubuntu上使用sshpass远程脚本免密安全交互 》 ),也支持标准的远程shell控制台。
首先,在宿主机安装Anaconda、Jupyter和JupyterLab。方法如下: JupyterLab的本地安装和使用(Ubuntu 18.04)
1、Jupyter for Ubuntu/Debian Anaconda3 with systemd
创建服务描述文件: #/etc/systemd/system/jupyter.service
编辑文件: sudo nano /etc/systemd/system/jupyter.service
把下面的内容复制进去: [Unit] Description=Jupyter After=syslog.target network.target [Service] User=supermap Environment="PATH=/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/anaconda3/bin" WorkingDirectory=/home/supermap/ ExecStart=/home/supermap/anaconda3/bin/jupyter lab --ip=10.1.1.201 Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target
重新载入服务: sudo systemctl daemon-reload
启动服务: sudo systemctl start jupyter
查看状态: sudo systemctl status jupyter --no-pager
可以看到相应的登录地址和token等信息,将其输入浏览器即可访问。
设置为系统启动时自动启动: sudo systemctl enable jupyter
2、JupyterHub for Ubuntu/Debian Anaconda3 with systemd
如果使用JupyterHub,采用 /opt/anaconda3/jupyterhub 作为配置目录。保存下面内容为文件 /etc/systemd/system/jupyterhub.service。 [Unit] Description=Jupyterhub After=syslog.target network.target [Service] User=root Environment="PATH=/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/anaconda3/bin" ExecStart=/opt/anaconda3/bin/jupyterhub -f /etc/jupyterhub/jupyterhub_config.py [Install] WantedBy=multi-user.target
如果保存配置 c.JupyterHub.cleanup_servers = False 在 JupyterHub config,需要加入 KillMode=process 在 [Service] 段中。缺省情况下,systemd将在退出时kill掉所有的子进程。
使用 sudo systemctl daemon-reload 和 sudo systemctl jupyterhub来启动服务。 sudo systemctl enable jupyterhub 将使 jupyterhub 开机自动启动。
3、JupyterHub for Ubuntu/Debian 的非systemd配置
保存 https://gist.github.com/lambdalisue/f01c5a65e81100356379 为 /etc/init.d/jupyterhub。如下: #! /bin/sh ### BEGIN INIT INFO # Provides: jupyterhub # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start jupyterhub # Description: This file should be used to construct scripts to be # placed in /etc/init.d. ### END INIT INFO # Author: Alisue # # Please remove the "Author" lines above and replace them # with your own name if you copy and modify this script. # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin DESC="Multi-user server for Jupyter notebooks" NAME=jupyterhub DAEMON=/usr/local/bin/jupyterhub DAEMON_ARGS="--config=/etc/jupyterhub/jupyterhub_config.py" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --background --make-pidfile --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac :
然后,执行配置过程:
$ sudo chmod +x /etc/init.d/jupyterhub # Create a default config to /etc/jupyterhub/jupyterhub_config.py $ sudo jupyterhub --generate-config -f /etc/jupyterhub/jupyterhub_config.py # Reload systemctl daemon to notice the init.d script $ sudo systemctl daemon-reload # Start jupyterhub $ sudo service jupyterhub start # Stop jupyterhub $ sudo service jupyterhub stop # Start jupyterhub on boot $ sudo update-rc.d jupyterhub defaults # Or use rcconf to manage services http://manpages.ubuntu.com/manpages/natty/man8/rcconf.8.html $ sudo rcconf
4、JupyterHub for CentOS/Fedora with Generic systemd
假设使用 /etc/jupyterhub 作为 configs,保存下面内容到文件 /lib/systemd/system/jupyterhub.service : [Unit] Description=Jupyterhub [Service] User=jupyterhub ExecStart=/usr/bin/jupyterhub --JupyterHub.spawner_class=sudospawner.SudoSpawner WorkingDirectory=/etc/jupyterhub [Install] WantedBy=multi-user.target 注意 : 需要添加 After=network-online.target 到 [Unit] section,否则设置 service 开机启动将失败,因为网络还不可用。 设置正确的 User 和争取的权限来运行 /usr/bin/jupyterhub。
运行: sudo systemctl daemon-reload . 然后 sudo systemctl jupyterhub来启动服务。 注意 : 确保安装 sudospawner.SudoSpawner ,运行 pip3 install git+https://github.com/jupyter/sudospawner。
5、JupyterHub for OSX
添加 jupyterhub_config.py 到 /etc/jupyterhub (如果没有则创建之)。创建 /Library/LaunchDaemons/com.jupyterhub.plist 粘贴下面的内容并编辑路径,指向jupyterhub。 Label com.jupyterhub.app ProgramArguments /opt/anaconda3/bin/jupyterhub -f /etc/jupyterhub/jupyterhub_config.py KeepAlive StandardErrorPath /var/log/jupyterhuberr.log StandardOutPath /var/log/jupyterhubout.log EnvironmentVariables PATH
载入和启动service,如下: sudo launchctl load -w /Library/LaunchDaemons/com.jupyterhub.plist sudo launchctl start -w /Library/LaunchDaemons/com.jupyterhub.plist
你的jupyterhub 可以通过localhost:8000 (缺省的config)来访问(如果需要外部机器访问,在启动命令行加上--ip=x.x.x.x.参数)。如果访问不了,通过 /var/log/system.log | grep jupyter 来查看状态或者查看日志文件 /var/log/jupyterhubout.log 来查看原因。
6、其他
查看 https://github.com/jupyter/jupyterhub/issues/317 并分享你的经验。
更多参考: 本机运行Jupyter JupyterLab的本地安装和使用(Ubuntu 18.04) BinderHub 安装指南 容器运行Jupyter 容器运行 Jupyter notebook server 基于Docker+Jupyter+Python的科学计算环境 整合GIS和Jupyter Notebook平台 Kubernetes运行Jupyter 快速设置JupyterHub for K8s 使用kubeadm部署高可用Kubernetes 1.17.0 Kubernetes 1.17.0管理界面Dashboard 2 为JupyterHub自定义Notebook Images
大数据
2020-01-08 09:08:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Apache Flink是什么?
在当代数据量激增的时代,各种业务场景都有大量的业务数据产生,对于这些不断产生的数据应该如何进行有效的处理,成为当下大多数公司所面临的问题。随着雅虎对hadoop的开源,越来越多的大数据处理技术开始涌入人们的视线,例如目前比较流行的大数据处理引擎Apache Spark,基本上已经取代了MapReduce成为当前大数据处理的标准。但是随着数据的不断增长,新技术的不断发展,人们逐渐意识到对实时数据处理的重要性。相对于传统的数据处理模式,流式数据处理有着更高的处理效率和成本控制能力。Flink 就是近年来在开源社区不断发展的技术中的能够同时支持高吞吐、低延迟、高性能的分布式处理框架。
https://img2018.cnblogs.com/blog/1089984/201911/1089984-20191118101924177-357668057.jpg
数据架构的演变
https://img2018.cnblogs.com/blog/1089984/201911/1089984-20191118101924451-1484200707.jpg
如图所示,传统的单体数据架构最大的特点便是 集中式数据存储,大多数将架构分为计算层和存储层。
单体架构的初期效率很高,但是随着时间的推移,业务越来越多,系统逐渐变得很大,越来越难以维护和升级,数据库是唯一的准确数据源,每个应用都需要访问数据库来获取对应的数据,如果数据库发生改变或者出现问题,则将对整个业务系统产生影响。
后来随着微服务架构的出现,企业开始采用微服务作为企业业务系统的架构体系。微服务架构的核心思想是:一个应用是由多个小的、相互独立的微服务组成,这些服务运行在自己的进程中,开发和发布都没有依赖。不同的服务能依据不同的业务需求,构建的不同的技术架构之上,能够聚焦在有限的业务功能。 如图
https://img2018.cnblogs.com/blog/1089984/201911/1089984-20191118101926573-867172114.jpg
微服务架构
起初数据仓库主要还是构建在关系型数据库之上。例如Oracle、Mysql等数据库,但是随着企业数据量的增长,关系型数据库已经无法支撑大规模数据集的存储和分析,因为越来越多的企业开始选择基于Hadoop构建企业级大数据平台。同时众多的Sql_on_hadhoop上构建不同类型的数据应用变得简单而高效。
在构建企业数据仓库的过程中,数据往往都是周期性的从业务系统中同步到大数据平台,完成一系列的ETL转换动作之后,最终形成了数据集市等应用。但是对于一些时间要求比较高的应用,例如实时报表统计,则必须有非常低的延时展示统计结果,为此业界提出了一套Lambda架构方案来处理不同类型的数据。
https://img2018.cnblogs.com/blog/1089984/201911/1089984-20191118101928298-517680567.jpg
大数据lambada架构
大数据平台中包含批量计算的Batch Layer和实时计算的Speed Layer,通过在一套平台中将批计算和流计算整合在一起,例如使用Hadoop MapReduce进行批量数据的处理,使用Apache Storm进行实时数据的处理。这种架构在一定程度上解决了不同计算类型的问题,但是带来的问题是框架太多会导致平台复杂度过高、运维成本高等。在一套资源管理平台中管理不同类型的计算框架使用也是非常困难的事情。
后来随着Apache Spark的分布式内存处理框架的出现,提出了将数据切分成微批的处理模式进行流式数据处理,从而能够在一套计算框架内完成批量计算和流式计算。但因为Spark本身是基于批处理模式的原因,并不能完美且高效的处理原生的数据流,因此对流式计算支持的相对较弱,可以说Spark的出现本质上是在一定程度上对Hadoop架构进行了一定的升级和优化。
有状态流计算架构
数据产生的本质,其实是一条条真实存在的事件,前面提到的不同的架构其实都是在一定程度违背了这种本质,需要通过在一定时延的情况下对业务数据进行处理,然后得到基于业务数据统计的准确结果。实际上,基于流式计算技术局限性,我们很难再数据产生的过程中进行计算并直接产生统计结果,因为这不仅对系统有非常高的要求,还必须要满足高性能、高吞吐、低延时等众多目标。
https://img2018.cnblogs.com/blog/1089984/201911/1089984-20191118101929560-1983652334.jpg
基于有状态计算的方式最大的优势是不需要将原始数据重新从外部存储中拿出来,从而进行全量计算,因为这种计算方式的代价可能是非常高的。
Flink通过实现Google Dataflow流式计算模型实现了高吞吐、低延迟、高性能兼具实时流式计算框架。同时Flink支持高度容错的状态管理,防止状态在计算过程中因为系统异常而出现丢失,Flink周期性地通过分布式快照技术Checkpoints实现状态的持久化维护,使得即使在系统停机或者异常的情况下都能计算出正确的结果。
Flink的具体优势有以下几点:
同时支持高吞吐、低延迟、高性能 Flink是目前开源社区中唯一一套集高吞吐、低延迟、高性能三者于一身的分布式流式数据处理框架。像Apache Spark也只能兼顾高吞吐和高性能特性,主要因为在Spark Streaming流式计算中无法做到低延迟保障;而流式计算框架Apache Storm只能支持低延迟和高性能特性,但是无法满足高吞吐的要求。而满足高吞吐、低延迟、高性能这三个目标对分布式流式计算框架来说是非常重要的。
支持事件时间(Event Time)概念 在流式计算领域中,窗口计算的地位举足轻重,但目前大多数框架窗口计算采用的都是系统时间(Process Time),也是事件传输到计算框架处理时,系统主机的当前时间。Flink能够支持基于事件时间(Event Time)语义进行窗口计算,也就是使用事件产生的时间,这种基于事件驱动的机制使得事件即使乱序到达,流系统也能够计算出精确的结果,保持了事件原本产生时的时序性,尽可能避免网络传输或硬件系统的影响。
支持有状态计算 Flink在1.4版本中实现了状态管理,所谓状态就是在流式计算过程中将算子的中间结果数据保存在内存或者文件系统中,等下一个事件进入算子后可以从之前的状态中获取中间结果中计算当前的结果,从而无须每次都基于全部的原始数据来统计结果,这种方式极大地提升了系统的性能,并降低了数据计算过程的资源消耗。对于数据量大且运算逻辑非常复杂的流式计算场景,有状态计算发挥了非常重要的作用。
支持高度灵活的窗口(windows)操作
在流处理应用中,数据是连续不断的,需要通过窗口的方式对流数据进行一定范围的聚合计算,例如统计在过去的1分钟内有多少用户点击某一网页,在这种情况下,我们必须定义一个窗口,用来收集最近一分钟内的数据,并对这个窗口内的数据进行再计算。Flink将窗口划分为基于Time、Count、Session,以及Data-driven等类型的窗口操作,窗口可以用灵活的触发条件定制化来达到对复杂的流传输模式的支持,用户可以定义不同的窗口触发机制来满足不同的需求。
基于轻量级分布式快照(Snapshot)实现的容错 Flink能够分布式运行在上千个节点上,将一个大型计算任务的流程拆解成小的计算过程,然后将tesk分布到并行节点上进行处理。在任务执行过程中,能够自动发现事件处理过程中的错误而导致数据不一致的问题,比如:节点宕机、网路传输问题,或是由于用户因为升级或修复问题而导致计算服务重启等。在这些情况下,通过基于分布式快照技术的Checkpoints,将执行过程中的状态信息进行持久化存储,一旦任务出现异常停止,Flink就能够从Checkpoints中进行任务的自动恢复,以确保数据在处理过程中的一致性。
基于JVM实现独立的内存管理 内存管理是所有计算框架需要重点考虑的部分,尤其对于计算量比较大的计算场景,数据在内存中该如何进行管理显得至关重要。针对内存管理,Flink实现了自身管理内存的机制,尽可能减少JVM GC对系统的影响。另外,Flink通过序列化/反序列化方法将所有的数据对象转换成二进制在内存中存储,降低数据存储的大小的同时,能够更加有效地对内存空间进行利用,降低GC带来的性能下降或任务异常的风险,因此Flink较其他分布式处理的框架会显得更加稳定,不会因为JVM GC等问题而影响整个应用的运行。
Save Points(保存点) 对于7x24 小时运行的流式应用,数据源源不断地接入,在一段时间内应用的终止有可能导致数据的丢失或者计算结果的不准确,例如进行集群版本的升级、停机运维操作等操作。值得一提的是,Flink通过Save Points技术将任务执行的快照保存在存储介质上,当任务重启的时候可以直接从事先保存的Save Points恢复原有的计算状态,使得任务继续按照停机之前的状态运行,Save Points技术可以让用户更好地管理和运维实时流式应用。 声明:本号所有文章除特殊注明,都为原创,公众号读者拥有优先阅读权,未经作者本人允许不得转载,否则追究侵权责任。
关注我的公众号,后台回复【JAVAPDF】获取200页面试题! 5万人关注的大数据成神之路,不来了解一下吗? 5万人关注的大数据成神之路,真的不来了解一下吗? 5万人关注的大数据成神之路,确定真的不来了解一下吗?
欢迎您关注 《大数据成神之路》
大数据
2020-01-06 22:10:04
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
2019年12月18日,阿里云宣布AI智能账单分析功能“成本管家”正式上线,这是国内云厂商首次把数据智能技术与账单系统结合,推出的正式产品。让用户从低效的账单获取、整理和分析的工作中解放出来。让AI为用户整理账单、预测未来的消费趋势以及挖掘异常账单。帮助用户能够更好地管理费用支付,优化成本。
据介绍,成本管家是日志服务(SLS)产品内置的新功能,主打面向账单分析的场景。成本管家能实时从账单中心拉取账单数据,提供账单数据存储、分析的一体化服务,让用户从低效的账单获取、整理和分析的工作中解放出来。用户无需理解各种复杂的帐单格式,便能轻松统计不同类型、不同规格的云产品的使用成本。其系统自带的AI系统,不仅能为用户精准预测未来一段时间内的消费趋势还能智能监控异常账单并及时发出警告,帮助用户自查自纠,不花一笔冤枉钱。
阿里云日志产品负责人周郎表示,相比于传统IT模式,云上资源具有随时开通、规格丰富、弹性规模这三大特点。但用户在享受资源的自由开通和灵活使用的同时,也会带来许多成本管理上的痛点。计费项多、账单明细复杂、数据监控不及时、消费波动异常等,都是很多企业上云后,所要面对的问题与困扰。而此次推出的成本管家产品,能很好地帮助用户规避掉上面提到的所有问题,用户能够以最直观、最形象、最准确的方式来追踪并分析自己的每一笔账单,让用清楚地知道自己的钱花到哪里去了,之后还会再花多少钱以及如何优化自己的成本。
据某物联网公司平台数据分析师所述,“使用成本管家后,能够减少自己许多机械化的劳动,让数据分析的前置步骤不再那么繁琐。而且成本管家的预警功能与预测功能也非常强大,帮助自己的公司节省了许多不必要的成本。”通过这次的发布,阿里云希望能借助AI算法的强大能力,为用户提供最方便、最准确的账单服务,改变用户传统的账单数据统计与分析,让用户不花一笔冤枉钱。


原文链接
本文为阿里云内容,未经允许不得转载。
大数据
2019-12-25 17:12:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
过去无论您是在生产中使用,还是调研Apache Flink,估计您总是会问这样一个问题:我该如何访问和更新Flink保存点(savepoint)中保存的state?不用再询问了,Apache Flink 1.9.0引入了状态处理器( State Processor )API,它是基于DataSet API的强大扩展,允许读取,写入和修改Flink的保存点和检查点(checkpoint)中的状态。
在这篇文章中,我们将解释为什么此功能对Flink来说很重要,以及该功能的用途和用法。最后,我们将讨论状态处理器API的未来规划,以保持与Flink批流统一的未来整体规划一致。
截止到Apache Flink 1.9的状态流处理现状
几乎所有复杂的流处理应用程序都是有状态的,其中大多数都是设计为运行数月甚至数年。随着时间的推移,这些作业积累了很多有价值的状态,如果由于故障而丢失,这些状态的重建将变得代价很高甚至是不可能的。为了保证应用程序状态的一致性和持久性,Flink从一开始就设计了一套复杂巧妙的检查点和恢复机制。在每一个版本中,Flink社区都添加了越来越多与状态相关的特性,以提高检查点执行和恢复的速度、改进应用程序的维护和管理。
然而,Flink用户经常会提出能够“从外部”访问应用程序的状态的需求。这个需求的动机可能是验证或调试应用程序的状态,或者将应用程序的状态迁移到另一个应用程序,或者从外部系统(例如关系数据库)导入应用程序的初始状态。
尽管这些需求的出发点都是合理的,但到目前为止从外部访问应用程序的状态这一功能仍然相当有限。Flink的可查询状态( queryable state )功能只支持基于键的查找(点查询),且不保证返回值的一致性(在应用程序发生故障恢复前后,返回值可能不同),并且可查询状态只支持读取并不支持修改和写入。此外,状态的一致性快照:保存点,也是无法访问的,因为这是使用自定义二进制格式进行编码的。
使用状态处理器(State Processor)API对应用程序状态进行读写
Flink1.9引入的状态处理器API,真正改变了这一现状,实现了对应用程序状态的操作。该功能借助DataSet API,扩展了输入和输出格式以读写保存点或检查点数据。由于DataSet和Table API的互通性,用户甚至可以使用关系表API或SQL查询来分析和处理状态数据。
例如,用户可以创建正在运行的流处理应用程序的保存点,并使用批处理程序对其进行分析,以验证该应用程序的行为是否正确。 或者,用户也可以任意读取、处理、并写入数据到保存点中,将其用于流计算应用程序的初始状态。 同时,现在也支持修复保存点中状态不一致的条目。最后,状态处理器API开辟了许多方法来开发有状态的应用程序,以绕过以前为了保证可以正常恢复而做的诸多限制:用户现在可以任意修改状态的数据类型,调整运算符的最大并行度,拆分或合并运算符状态,重新分配运算符UID等等。
将应用程序与数据集进行映射
状态处理器API将流应用程序的状态映射到一个或多个可以分别处理的数据集。为了能够使用API​​,您需要了解此映射的工作方式。
首先,让我们看看有状态的Flink作业是什么样的。Flink作业由算子( operator )组成,通常是一个或多个source算子,一些进行数据处理的算子以及一个或多个sink算子。每个算子在一个或多个任务中并行运行,并且可以使用不同类型的状态:可以具有零个,一个或多个列表形式的 operator states ,他们的作用域范围是当前算子实例;如果这些算子应用于键控流( keyed stream ),它还可以具有零个,一个或多个 keyed states ,它们的作用域范围是从每个处理记录中提取的键。您可以将keyed states视为分布式键-值映射。
下图显示的应用程序“MyApp”,由称为“Src”,“Proc”和“Snk”的三个算子组成。Src具有一个 operator state (os1),Proc具有一个 operator state (os2)和两个 keyed state (ks1,ks2),而Snk则是无状态的。
MyApp的保存点或检查点均由所有状态的数据组成,这些数据的组织方式可以恢复每个任务的状态。在使用批处理作业处理保存点(或检查点)的数据时,我们脑海中需要将每个任务状态的数据映射到数据集或表中。因为实际上,我们可以将保存点视为数据库。每个算子(由其UID标识)代表一个名称空间。算子的每个 operator state 都射到名称空间中的一个单列专用表,该列保存所有任务的状态数据。operator的所有 keyed state 都映射到一个键值多列表,该表由一列key和与每个 key state 映射的一列值组成。下图显示了MyApp的保存点如何映射到数据库
该图显示了"Src"的 operator state 的值如何映射到具有一列和五行的表,一行数据代表对于Src的所有并行任务中的一个并行实例。类似地,"Proc"的 operator state os2,也映射到单个表。对于 keyed state ,ks1和ks2则是被组合到具有三列的单个表中,一列代表主键,一列代表ks1,一列代表ks2。该表为两个keyed state的每个不同key都保有一行。由于“Snk”没有任何状态,因此其映射表为空。
状态处理器API提供了创建,加载和编写保存点的方法。用户可以从已加载的保存点读取数据集,也可以将数据集转换为状态并将其添加到保存点中。总之,可以使用DataSet API的全部功能集来处理这些数据集。使用这些方法,可以解决所有前面提到的用例(以及更多用例)。如果您想详细了解如何使用状态处理器API,请 查看文档 。
为什么使用DataSet API?
如果您熟悉Flink的未来规划,可能会对状态处理器API基于DataSet API而感到惊讶,因为目前Flink社区计划使用 BoundedStreams 的概念扩展DataStream API,并弃用DataSet API。但是在设计此状态处理器功能时,我们还评估了DataStream API以及Table API,他们都不能提供相应的功能支持。由于不想此功能的开发因此受到阻碍,我们决定先在DataSet API上构建该功能,并将其对DataSet API的依赖性降到最低。基于此,将其迁移到另一个API应该是相当容易的。
总结
Flink用户很长时间以来有从外部访问和修改流应用程序的状态的需求,借助于状态处理器API,Flink为用户如何维护和管理流应用程序打开了许多新可能性,包括流应用程序的任意演变以及应用程序状态的导出和引导。简而言之,状态处理器API得保存点不再是一个黑匣子。



原文链接
本文为阿里云内容,未经允许不得转载。
大数据
2019-12-24 15:42:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
引言 在本系列的前面两篇文章(《数据智能时代来临:本质及技术体系要求》和《多维度分析系统的选型方法》)之中,我们概括性地阐述了对于数据智能的理解,并根据工作中团队涉及到的多维度分析系统的选型方法进行了穿插介绍。按照原先的规划,我们接下去的内容会涉及数据智能平台中的治理、安全计算以及质量保证方面。
不过,计划不如变化快,最近这段时间“数据中台”这个词非常热,有人问了我两个问题:“数据中台”与这个系列的核心“数据智能的技术体系”有什么区别?你们是怎么理解“数据中台”这个概念的呢?
顺着这两个问题,这篇文章就和大家聊聊我们对于“数据中台”的理解,以及和“数据智能的技术体系”间的区别。
正文内容 再从数据的价值谈起
数据的产生来源于我们的产品和服务所提供的直接价值。以打车软件为例,因为APP需要提供给乘客所在地点周围的司机信息,因此系统需要及时收集司机的位置以及车载乘客状态以确定是否可被调度,然后把乘客的轿车需求发送给设定参数范围内的可用车辆。司机在进行抢单或者配单后,就可以接上乘客并按照导航送至目的地。
在这个过程中,乘客的上车位置、下车位置、司机车辆的位置、状态以及车辆行驶过程中的位置信息等数据都是为“打车”这个动作的直接价值服务。
正如大家所知,我们可以利用这些几千几万辆车的位置信息,聚合出每个道路的交通状况,再把这些知识提供给交通优化等。这就是数据的扩展价值,数据的多种价值汇总起来就是数据的选择价值。
再打个比方,数据的首要价值被挖掘后仍能够不断给予,它的真实价值就像漂浮在海洋中的冰山,绝大部分被隐藏在表面下。数据的选择价值也就是“取之不尽,用之不竭”的数据创新成果。这些数据创新并不是事先就规划好或者事先都能想到的。
那么为了保证这种创新的可能性,我们需要让这些数据都能被保存下来,而不是在实现了直接价值后,就弃之如敝屣。这个也是接下来要提到的“数据湖”的由来。
数据湖与数据仓库
数据湖【1】的概念是2011年提出的。由于无法对已流失的数据进行回溯,一些大数据厂商在Hadoop为基础的技术栈上,把一个组织中产生的原始数据存储在一个单一的系统中。一般大家会用开源的Hadoop来构建数据湖,不过数据湖的概念比Hadoop更为广泛。
看到数据湖,大家肯定会想到数据仓库或者数据集市,那么两者的区别在哪里呢?我们先来看看下面的这个图。
图 1 数据湖示意
数据湖存储数据源提供的原始数据,没有对数据的形式进行任何假设。每个数据源可以使用其选择的任何形式,最终数据的消费者会根据他们自己的目的来使用数据,这是数据湖区别于数据仓库的一个非常重要的原因。同时,这也是数据仓库没有走得更远的原因,因为数据仓库首先需要考虑数据方案(schema)。
图 2 数据仓库示意
数据仓库倾向于为所有分析需求设计一个总体的方案表示,但是实际上即使是一个非常小的组织,想要通过一个统一的数据模型来涵盖一切,也是不太实用的。另外,数据仓库在使用中会出现数据质量问题:不同的分析需求对数据的构成有不同的质量要求和容忍度。数据仓库的这个特征导致了漫长的开发周期、高昂的开发成本和维护成本、细节数据丢失等问题的出现。
数据湖在直观上更像一个数据质量差异很大的数据倾倒场,如果只是聚合后的数据,意味着会丢掉很多数据。数据湖应该包含所有数据,因为你不知道人们可以在什么时候找到有价值的东西,可能是在今天,也可能是在未来几年的时间里。
数据湖的这种原始数据的复杂性意味着我们可以通过一些方式来将数据转变成一个易于管理的结构,这样还可以减少数据的体量,更易于处理。数据湖还是不应该经常性地被直接访问,因为数据是很原始的,需要很多技巧才能使之变得有意义。一般可以按照下图来处理,我们可以把它称为数据湖岸集市。
图 3数据湖岸集市
把所有数据放入湖中的一个很关键的点是需要有一个清晰的治理。每个数据项应该有一个清晰的跟踪,以便于知道数据从哪个系统中来以及什么时候产生等,也就是元数据管理、数据血缘以及必要的数据安全。
数据中台
数据中台这个概念是阿里巴巴提出来的。随着业务的快速发展,企业的多条业务线都产生了大量的数据,而且数据都按照不同的形式进行采集、存储、处理等。为了快速满足每个前端业务的需求,公司通常会让前台直接去联系后台。譬如,大部分公司的大后台就是财务,初始可能比较有效,但是随着需求越来越多、越来越频繁,沟通成本大大提高,效率大大降低。
同时,对于一个公司的多个业务来说,哪怕看起来很个性的需求,经过抽象以及合并同类项后,我们发现也可以形成共有的能力。其实,对于后台的很多功能,同样可以抽象出来,成为各业务共有的能力。这样可以让数据更灵活更敏捷地服务于前台的各项业务,这个就是数据中台的初衷。
对于阿里来说,如何更好地把包括自己不同业务的数据、被收购公司的数据在内的多个数据变成One Data , 然后为整个公司的业务服务,也是数据中台的一个核心目标。
事实上,数据中台的建设与数字化转型一样,其实也是一个螺旋上升的过程,往往需要不断根据业务变化需求进行完善。哪怕再宏大的数据中台战略,也必须要用真实的业务场景去实践,通过以小到大的场景不断去锻炼中台。
总结而言,数据中台是练出来的,即数据的复用率决定了数据中台的成功与否。一个数据中台的成功意味着不少数据都在进行着重复使用。此外,我们需要注意数据安全策略的执行,包括底层数据安全的实现以及业务层数据的合规使用。
如果一个公司的数据中台没有和业务中台紧密配合,那么这种纯粹的数据中台只是蹭热点,不会有很大的效果。所以我们认为,更有价值的中台是业务偏向的数据中台,而不是通用型的数据中台。这个观点,和前阿里数据委员会主席车品觉是一致的。
根据上面的分析,我们建议公司在业务或者产品比较单一抑或数据战略并不太清晰的情况下,可以建设数据湖,而不是为了建设中台而去建设。从本系列第一篇文章《数据智能时代来临:本质及技术体系要求》的整体介绍来看,我们数据智能的体系和数据中台的目标是一致的。
结语 从我们自身的理解来看,数据智能体系和数据中台一样,本质上是把数据作为资产,整理出企业的元数据和数据血缘关系,再以这些数据为中心,抽象出公共服务的能力。最后,让前端流程的构造和企业的稳定数据公共服务解耦。这样就沉淀出了公共服务能力,即把这些能力SaaS化。
数据智能体系或者说中台,最根本的目的是敏捷地支撑业务部门的业务创新需求,打造快速服务商业需求的服务能力,并且尽量实时处理,体现数据的资产化及价值最大化。
我们认为中台最主要的用户是数据开发者群体,包括数据研发人员、数据分析及建模人员。建设中台的目的在于提高他们的效率、降低学习曲线、提高数据质量。
下一个系列,我们将回到主线,继续讲讲数据治理、安全计算、数据质量保证等方面的内容,敬请期待。
大数据
2019-12-22 03:22:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1. Hadoop搭建的两种方式
1.1:最早安装hadoop都是采用原生的apache发布的版本,需要自己在apache的官网下载hdfs,hive,mapreduce以及zookeeper和mysql。每个组件都需要单独安装,而且组件的版本的还需要保持一致,以及需要修改很多的置,安装步骤比较繁琐。痛点
1.1.1:集群规模很庞大时搭建Hadoop集群复杂度越来越高,工作量很大
1.1.2:规模很大的集群下升级Hadoop版本很费时费力
1.1.3:需要自己保证版本兼容,比如升级hdfs版本后需要自己保证与Hive、Hbase等的兼容
1.1.4:兼容性差,安全性低
1.2:CDH是由是Hadoop众多分支中的一种,由Cloudera维护,基于稳定版本的Apache Hadoop构建;优点
1.2.1:版本划分清晰
1.2.2:版本更新速度快
1.2.3:支持Kerberos安全认证
1.2.4:支持多种安装方式(Cloudera Manager方式)
1.2.5:对集群进行管理,例如添加、删除节点等操作;监控,诊断都更加方便
1.2.5:文档清晰
2: docker安装cdh
2.1:上述简单提过了两种安装Hadoop的方式,下面开始讲讲如何用docker快速的安装cdh
2.2:docker的安装
2.2.1:docker的安装比较简单这里就不做过多的赘述,需了解的可以百度一下安装docker
2.3:docker下载cdh镜像
两种方式
1)直接拉取官网最新镜像文件( 推荐使用 );
当前官网最新版本是6.3.1
docker pull cloudera/quickstart:latest
设置镜像版本信息
docker tag cloudera/quickstart:latest cdh:6.3.1
2)官网下载镜像文件(这里用的是5.13的版本)
https://www.cloudera.com/downloads/quickstart_vms/5-13.html
解压文件
tar vxf clouderaquickstartvm5.13.00betadocker.tar.gz
上传导入镜像 docker import clouderaquickstartvm5.13.00betadocker.tar cdh:5.13.0
2.4:运行CDH容器
1) 基于下载好的cdh:6.3.1镜像启动容器cdh docker run name cdh hostname=quickstart.cloudera privileged=true t i p 7180:7180 p 7187:7187 p 1080:1080 p 4200:4200 p 7777:7777 p 7788:7788 p 8000:8000 p 8080:8080 p 8744:8744 p 8886:8886 p 9088:9088 p 9089:9089 p 61080:61080 p 61888:61888 p 4040:4040 p 6080:6080 p 8042:8042 p 8088:8088 p 8188:8188 p 8888:8888 p 995:9995 p 11000:11000 p 15000:15000 p 16010:16010 p 18081:18081 p 19888:19888 p 21000:21000 p 21050:21050 p 50010:50010 p 50020:50020 p 50070:50070 p 50075:50075 p 50111:50111 p 8081:8081 p 2182:2182 p 2202:2202 p 4557:4557 p 6627:6627 p 6667:6667 p 9090:9090 p 9091:9091 p 15500:15500 p 1100:1100 p 1111:1111 p 1988:1988 p 100:2100 p 2181:2181 p 2201:2201 p 2222:2222 p 3000:3000 p 4242:4242 p 5007:5007 p 5011:5011 p 6001:6001 p 6003:6003 p 6008:6008 p 6188:6188 p 8005:8005 p 020:8020 p 8032:8032 p 8040:8040 p 8082:8082 p 8086:8086 p 8090:8090 p 8091:8091 p 8443:8443 p 8765:8765 p 8889:8889 p 8983:8983 p 8993:8993 p 9000:9000 p 996:9996 p 10000:10000 p 10001:10001 p 10015:10015 p 10016:10016 p 10500:10500 p 10502:10502 p 12049:12049 p 12200:12200 p 15002:15002 p 16000:16000 p 16020:16020 p 16030:16030 p 18080:18080 p 33553:33553 p 39419:39419 p 42111:42111 p 50079:50079 p 50095:50095 p 60000:60000 p 60080:60080 cdh:6.3.1 /bin/bash c '/usr/bin/ockerquickstart && /home/cloudera/clouderamanager express && service ntpd start';
说明: --name 容器名 – memory-swap 指定可使用的swap -1表示不限制 –hostname 指定容器的主机名 -p 指定一堆web portal的端口映射,可以自己选择 --privileged=true 必要参数:HBase, MySQL-backed Hive metastore, Hue, Oozie, Sentry, 和 Cloudera Manager 的权限开关
执行“exit”:退出容器
2) 启动容器:
通过命令找到容器
docker ps a 查找该容器ID 查看是否启动,未启动则通过容器id启动容器 docker start CONTAINER ID(容器ID)
3) 进入容器配置
docker exec it CONTAINER ID /bin/bash 步时间 ervice ntpd start 同步时间后还与当前时间相差8小时,原因是时区不同 /etc/profile文件中增加一行 export TZ='CST8' 文件立即生效 source /etc/profile 或者 . /etc/profile
4)启动Cloudera Manager
/home/cloudera/clouderamanager enterprise
通过web访问宿主机:7180端口,可以访问则配置成功,登录用户密码为cloudera/cloudera
![](https://oscimg.oschina.net/oscnet/up9fc08a4cd9215fcda62c596a75d3763cac8.JPEG)
大数据
2019-12-17 17:34:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
简介
Flink本身为了保证其高可用的特性,以及保证作用的Exactly Once的快速恢复,进而提供了一套强大的Checkpoint机制。 Checkpoint机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保 证应用流图状态的一致性。Flink的Checkpoint机制原理来自“Chandy-Lamport algorithm”算法 (分布式快照算法)。
Checkpoint的执行流程
每个需要checkpoint的应用在启动时,Flink的JobManager为其创建一个 CheckpointCoordinator,CheckpointCoordinator全权负责本应用的快照制作。
CheckpointCoordinator周期性的向该流应用的所有source算子发送barrier; 当某个source算子收到一个barrier时,便暂停数据处理过程,然后将自己的当前状 态制作成快照,并保存到指定的持久化存储中,最后向CheckpointCoordinator报告 自己快照制作情况,同时向自身所有下游算子广播该barrier,恢复数据处理; 下游算子收到barrier之后,会暂停自己的数据处理过程,然后将自身的相关状态制作成快照,并保存到指定的持久化存储中,最后向CheckpointCoordinator报告自身 快照情况,同时向自身所有下游算子广播该barrier,恢复数据处理; 每个算子按照步骤3不断制作快照并向下游广播,直到最后barrier传递到sink算子,快照制作完成。 当CheckpointCoordinator收到所有算子的报告之后,认为该周期的快照制作成功; 否则,如果在规定的时间内没有收到所有算子的报告,则认为本周期快照制作失败 ;
Checkpoint常用设置 // start a checkpoint every 1000 ms env.enableCheckpointing(1000); // advanced options: // set mode to exactly-once (this is the default) env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); // checkpoints have to complete within one minute, or are discarded env.getCheckpointConfig().setCheckpointTimeout(60000); // make sure 500 ms of progress happen between checkpoints env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500); // allow only one checkpoint to be in progress at the same time env.getCheckpointConfig().setMaxConcurrentCheckpoints(1); // enable externalized checkpoints which are retained after job cancellation env.getCheckpointConfig().enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION); // This determines if a task will be failed if an error occurs in the execution of the task’s checkpoint procedure. env.getCheckpointConfig().setFailOnCheckpointingErrors(true); 使用StreamExecutionEnvironment.enableCheckpointing方法来设置开启checkpoint;具体可以使用enableCheckpointing(long interval),或者enableCheckpointing(long interval, CheckpointingMode mode);interval用于指定checkpoint的触发间隔(单位milliseconds),而CheckpointingMode默认是CheckpointingMode.EXACTLY_ONCE,也可以指定为CheckpointingMode.AT_LEAST_ONCE 也可以通过StreamExecutionEnvironment.getCheckpointConfig().setCheckpointingMode来设置CheckpointingMode,一般对于超低延迟的应用(大概几毫秒)可以使用CheckpointingMode.AT_LEAST_ONCE,其他大部分应用使用CheckpointingMode.EXACTLY_ONCE就可以 checkpointTimeout用于指定checkpoint执行的超时时间(单位milliseconds),超时没完成就会被abort掉 minPauseBetweenCheckpoints用于指定checkpoint coordinator上一个checkpoint完成之后最小等多久可以出发另一个checkpoint,当指定这个参数时,maxConcurrentCheckpoints的值为1 maxConcurrentCheckpoints用于指定运行中的checkpoint最多可以有多少个,用于包装topology不会花太多的时间在checkpoints上面;如果有设置了minPauseBetweenCheckpoints,则maxConcurrentCheckpoints这个参数就不起作用了(大于1的值不起作用) enableExternalizedCheckpoints用于开启checkpoints的外部持久化,但是在job失败的时候不会自动清理,需要自己手工清理state;ExternalizedCheckpointCleanup用于指定当job canceled的时候externalized checkpoint该如何清理,DELETE_ON_CANCELLATION的话,在job canceled的时候会自动删除externalized state,但是如果是FAILED的状态则会保留;RETAIN_ON_CANCELLATION则在job canceled的时候会保留externalized checkpoint state failOnCheckpointingErrors用于指定在checkpoint发生异常的时候,是否应该fail该task,默认为true,如果设置为false,则task会拒绝checkpoint然后继续运行
flink-conf.yaml相关配置 #============================================================================== # Fault tolerance and checkpointing #============================================================================== # The backend that will be used to store operator state checkpoints if # checkpointing is enabled. # # Supported backends are 'jobmanager', 'filesystem', 'rocksdb', or the # . # # state.backend: filesystem # Directory for checkpoints filesystem, when using any of the default bundled # state backends. # # state.checkpoints.dir: hdfs://namenode-host:port/flink-checkpoints # Default target directory for savepoints, optional. # # state.savepoints.dir: hdfs://namenode-host:port/flink-checkpoints # Flag to enable/disable incremental checkpoints for backends that # support incremental checkpoints (like the RocksDB state backend). # # state.backend.incremental: false state.backend用于指定checkpoint state存储的backend,默认为none state.backend.async用于指定backend是否使用异步snapshot(默认为true),有些不支持async或者只支持async的state backend可能会忽略这个参数 state.backend.fs.memory-threshold,默认为1024,用于指定存储于files的state大小阈值,如果小于该值则会存储在root checkpoint metadata file state.backend.incremental,默认为false,用于指定是否采用增量checkpoint,有些不支持增量checkpoint的backend会忽略该配置 state.backend.local-recovery,默认为false state.checkpoints.dir,默认为none,用于指定checkpoint的data files和meta data存储的目录,该目录必须对所有参与的TaskManagers及JobManagers可见 state.checkpoints.num-retained,默认为1,用于指定保留的已完成的checkpoints个数 state.savepoints.dir,默认为none,用于指定savepoints的默认目录 taskmanager.state.local.root-dirs,默认为none
增量式的检查点- Checkpoint设置的奇技淫巧
增量式检查点
Flink的检查点是一个全局的、异步的程序快照,它周期性的生成并送到持久化存储(一般使用分布式系统)。当发生故障时,Flink使用最新的检查点进行重启。一些Flink的用户在程序“状态”中保存了GB甚至TB的数据。这些用户反馈在大量 的状态下,创建检查点通常很慢并且耗资源,这也是为什么Flink在 1.3版本开始引入“增量式的检查点”。
在引入“增量式的检查点”之前,每一个Flink的检查点都保存了程序完整的状态。后来我们意识到在大部分情况下这是不必要的,因为上一次和这次的检查点之前 ,状态发生了很大的变化,所以我们创建了“增量式的检查点”。增量式的检查点仅保存过去和现在状态的差异部分。
增量式的检查点可以为拥有大量状态的程序带来很大的提升。在早期的测试中,一个拥有TB级别“状态”程序将生成检查点的耗时从3分钟以上降低 到了30秒左右。因为增量式的检查点不需要每次把完整的状态发送到存储中。
现在只能通过RocksDB state back-end来获取增量式检查点的功能,Flink使用RocksDB内置的备份机制来合并检查点数据。这样Flink增量式检查点的数据不会无限制的增大,它会自动合并老的检查点数据并清理掉。
要启用这个机制,可以如下设置: RocksDBStateBackend backend = new RocksDBStateBackend(filebackend, true);
增量式检查点如何工作
Flink 增量式的检查点以“RocksDB”为基础,RocksDB是一个基于 LSM树的KV存储,新的数据保存在内存中,称为memtable。如果Key相同,后到的数据将覆盖之前的数据,一旦memtable写满了,RocksDB将数据压缩并写入到磁盘。memtable的数据持久化到磁盘后,他们就变成了不可变的sstable。
RocksDB会在后台执行compaction,合并sstable并删除其中重复的数据。之后RocksDB删除原来的sstable,替换成新合成的ssttable,这个sstable包含了之前的sstable中的信息。
在这个基础之上,Flink跟踪前一个checkpoint创建和删除的RocksDB sstable文件,因为sstable是不可变的,Flink可以因此计算出 状态有哪些改变。为了达到这个目标,Flink在RocksDB上触发了一个刷新操作,强制将memtable刷新到磁盘上。这个操作在Flink中是同步的,其他的操作是异步的,不会阻塞数据处理。
Flink 的checkpoint会将新的sstable发送到持久化存储(例如HDFS,S3)中,同时保留引用。Flink不会发送所有的sstable, 一些数据在之前的checkpoint存在并且写入到持久化存储中了,这样只需要增加引用次数就可以了。因为compaction的作用,一些sstable会合并成一个sstable并删除这些sstable,这也是为什么Flink可以减少checkpoint的历史文件。
为了分析checkpoint的数据变更,而上传整理过的sstable是多余的(这里的意思是之前已经上传过的,不需要再次上传)。Flink处理这种情况,仅带来一点点开销。这个过程很重要,因为在任务需要重启的时候,Flink只需要保留较少的历史文件。
假设有一个子任务,拥有一个keyed state的operator,checkpoint最多保留2个。上面的图片描述了每个checkpoint对应的RocksDB 的状态,它引用到的文件,以及在checkpoint完成后共享状态中的count值。
checkpoint ‘CP2’,本地的RocksDB目录有两个sstable文件,这些文件是新生成的,于是Flink将它们传到了checkpoint 对应的存储目录。当checkpoint完成后,Flink在共享状态中创建两个实体,并将count设为1。在这个共享状态中,这个key 由operator、subtask,原始的sstable名字组成,value为sstable实际存储目录。
checkpoint‘CP2’,RocksDB有2个老的sstable文件,又创建了2个新的sstable文件。Flink将这两个新的sstable传到 持久化存储中,然后引用他们。当checkpoint完成后,Flink将所有的引用的相应计数加1。
checkpoint‘CP3’,RocksDB的compaction将sstable-(1), sstable-(2), sstable-(3) 合并成 sstable-(1,2,3),然后删除 原始的sstable。这个合并后的文件包含了和之前源文件一样的信息,并且清理掉了重复的部分。sstable-(4)还保留着,然后有一个 新生成的sstable-(5)。Flink将新的 sstable-(1,2,3)以及 sstable-(5)传到持久化存储中, sstable-(4)仍被‘CP2’引用,所以 将计数增加1。现在有了3个checkpoint,'CP1','CP2','CP3',超过了预设的保留数目2,所以CP1被删除。作为删除的一部分, CP1对应的文件(sstable-(1)、sstable-(2)) 的引用计数减1。
checkpoint‘CP4’,RocksDB将sstable-(4), sstable-(5), 新的 sstable-(6) 合并成 sstable-(4,5,6)。Flink将新合并 的 sstable-(4,5,6)发送到持久化存储中,sstable-(1,2,3)、sstable-(4,5,6) 的引用计数增加1。由于再次到达了checkpoint的 保留数目,‘CP2’将被删除,‘CP2’对应的文件(sstable-(1)、sstable-(2)、sstable(3) )的引用计数减1。由于‘CP2’对应 的文件的引用计数达到0,这些文件将被删除。
需要注意的地方
如果使用增量式的checkpoint,那么在错误恢复的时候,不需要考虑很多的配置项。一旦发生了错误,Flink的JobManager会告诉 task需要从最新的checkpoint中恢复,它可以是全量的或者是增量的。之后TaskManager从分布式系统中下载checkpoint文件, 然后从中恢复状态。
增量式的checkpoint能为拥有大量状态的程序带来较大的提升,但还有一些trade-off需要考虑。总的来说,增量式减少了checkpoint操作的时间,但是相对的,从checkpoint中恢复可能更耗时,具体情况需要根据应用程序包含的状态大小而定。相对的,如果程序只是部分失败,Flink TaskManager需要从多个checkpoint中读取数据,这时候使用全量的checkpoint来恢复数据可能更加耗时。同时,由于新的checkpoint可能引用到老的checkpoint,这样老的checkpoint就不能被删除,这样下去,历史的版本数据会越来越大。需要考虑使用分布式来存储checkpoint,另外还需要考虑读取带来的带宽消耗。
声明:本号所有文章除特殊注明,都为原创,公众号读者拥有优先阅读权,未经作者本人允许不得转载,否则追究侵权责任。
关注我的公众号,后台回复【JAVAPDF】获取200页面试题! 5万人关注的大数据成神之路,不来了解一下吗? 5万人关注的大数据成神之路,真的不来了解一下吗? 5万人关注的大数据成神之路,确定真的不来了解一下吗?
欢迎您关注 《大数据成神之路》
大数据
2019-12-15 19:35:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一. 网站结构
1. 网站截图说明
采集昵图网某页面中所有图片及链接地址
【昵图网某页面】
2. 采集结果截图
【检索列表链接】
【检索数据结果】
二. 配置模板
1.新建任务
点击【下一步】,需要采集全部检索结果,所以此处需要勾选【链接列表】和【普通翻页】,如图:
【新建采集任务】
2.过滤链接列表
①用定位过滤链接,过滤关键词检索出的列表链接。
【定位过滤列表链接】
②采集预览,看是否过滤成功。
采集预览出现链接均为网页中关键词检索列表中的链接,过滤成功。
【采集预览】
3. 过滤翻页链接
①用定位过滤链接,过滤翻页链接。
【定位过滤翻页链接】
②采集预览,看是否过滤成功
采集预览出现链接均为翻页的链接,过滤成功。
【采集预览】
4.新建数据抽取
5.添加示例地址
6.创建/选择表单
表单创建后可以重复选择使用,如果已有建好的表单,选择对应数据表单即可。如果没有,点击创建表单。
7.配置表单
根据所需内容,配置表单字段,此处配置了包括主键、网页地址、图片三个字段。=>(字段各属性介绍)
方式一:快速建表。(点击【创建表单】出现弹窗。)
【配置表单】
方式二:自由建表。(在【数据建表】界面。)
【字段取值】
8.关联表单

9.字段取值
①f_id:主键字段,自动取值。=>(能够自动取值的字段有哪些?)
②url:当前页面网址,自动取值。
③name1:通过字段定位取值,按ctrl+单击标题,确认选取。
【name1】
④fujian:通过字段定位取值,按ctrl+单击图片,确认选取。
10.关联数据表
先选择对应表单,然后再创建关联数据表,如图所示。
【创建关联数据表】
【定义表名称】
【勾选数据表】
11.模板预览
通过预览,可以了解配置是否能够正确地采集到所需数据。
方式一:点击【采集预览】按钮,可以从入口页逐层预览各个模板的数据。
点击任意一条链接,看看是否可以得到和网页对应的规整的数据。
方式二:右键后选择【模板预览】,可以单独预览某个模板的数据。
【模板预览】
【预览结果】
三.数据采集
1. 运行设置
运行设置处可以设置采集速度、采集策略、任务装载等。

【运行设置】
2. 选择采集任务
在【任务列表】中勾选需要采集的任务,可勾选多个任务,同时采集。
【选择采集任务】
3. 开始采集
点击【开始采集】,系统开始进行采集。剩余任务数为0时,系统自动停止采集。用户也可以自己暂停任务或停止任务(停止任务会释放任务,再次启动时重新装载任务)。
【开始采集】
4.数据浏览
采集一段时间以后,点击【数据浏览】,在数据列表中选中对应的数据表,即可浏览采集到的数据,点击【刷新】按钮可以同步显示数据。
【数据浏览】
5.导出数据
点击【导出】按钮,选择导出文件格式后保存。
【导出数据】
【导出数据】
大数据
2019-12-11 17:07:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在本博客系列的第3部分,我将仍然关注查询计划( Query Plan )和执行摘要( Execution Summary ),但是将使用真实数据( Kaggle’s Flights Delay database ),来执行一个更复杂的查询。
测试用的数据库有三张表: flights.csv airlines.csv airports.csv
查询语句如下: SELECT a.airline as airline_name, COUNT(IF(cancelled = 1, 1, NULL)) AS cancelled, COUNT(1) AS total, CONCAT(CAST(CAST(COUNT(IF(cancelled = 1, 1, NULL)) / COUNT(1) AS DECIMAL(8,4)) * 100 AS STRING), "%") AS cancelled_rate FROM flights f JOIN airlines a ON (f.airline = a.iata_code) GROUP BY a.airline ORDER BY a.airline
该查询将连接航班( flights )表和机场( airports )表生成一个报表,该报表可以告诉我们 2015 年期间每个航空公司的航班取消率,报表结果如下: +------------------------------+-----------+---------+----------------+ | airline_name | cancelled | total | cancelled_rate | +------------------------------+-----------+---------+----------------+ | Alaska Airlines Inc. | 669 | 172521 | 0.3800% | | American Airlines Inc. | 10919 | 725984 | 1.5000% | | American Eagle Airlines Inc. | 15025 | 294632 | 5.0900% | | Atlantic Southeast Airlines | 15231 | 571977 | 2.6600% | | Delta Air Lines Inc. | 3824 | 875881 | 0.4300% | | Frontier Airlines Inc. | 588 | 90836 | 0.6400% | | Hawaiian Airlines Inc. | 171 | 76272 | 0.2200% | | JetBlue Airways | 4276 | 267048 | 1.6000% | | Skywest Airlines Inc. | 9960 | 588353 | 1.6900% | | Southwest Airlines Co. | 16043 | 1261855 | 1.2700% | | Spirit Air Lines | 2004 | 117379 | 1.7000% | | US Airways Inc. | 4067 | 198715 | 2.0400% | | United Air Lines Inc. | 6573 | 515723 | 1.2700% | | Virgin America | 534 | 61903 | 0.8600% | +------------------------------+-----------+---------+----------------+
查询计划和执行概要的详细信息如下: F03:PLAN FRAGMENT [UNPARTITIONED] hosts=1 instances=1 | Per-Host Resources: mem-estimate=0B mem-reservation=0B PLAN-ROOT SINK | mem-estimate=0B mem-reservation=0B | 08:MERGING-EXCHANGE [UNPARTITIONED] | order by: a.airline ASC | mem-estimate=0B mem-reservation=0B | tuple-ids=3 row-size=52B cardinality=14 | F02:PLAN FRAGMENT [HASH(a.airline)] hosts=4 instances=4 Per-Host Resources: mem-estimate=22.00MB mem-reservation=13.94MB 04:SORT | order by: a.airline ASC | mem-estimate=12.00MB mem-reservation=12.00MB spill-buffer=2.00MB | tuple-ids=3 row-size=52B cardinality=14 | 07:AGGREGATE [FINALIZE] | output: count:merge(if(cancelled = 1, 1, NULL)), count:merge(*) | group by: a.airline | mem-estimate=10.00MB mem-reservation=1.94MB spill-buffer=64.00KB | tuple-ids=2 row-size=52B cardinality=14 | 06:EXCHANGE [HASH(a.airline)] | mem-estimate=0B mem-reservation=0B | tuple-ids=2 row-size=52B cardinality=14 | F00:PLAN FRAGMENT [RANDOM] hosts=4 instances=4 Per-Host Resources: mem-estimate=187.94MB mem-reservation=3.94MB 03:AGGREGATE [STREAMING] | output: count(if(cancelled = 1, 1, NULL)), count(*) | group by: a.airline | mem-estimate=10.00MB mem-reservation=2.00MB spill-buffer=64.00KB | tuple-ids=2 row-size=52B cardinality=14 | 02:HASH JOIN [INNER JOIN, BROADCAST] | hash predicates: f.airline = a.iata_code | fk/pk conjuncts: f.airline = a.iata_code | runtime filters: RF000 <- a.iata_code | mem-estimate=1.94MB mem-reservation=1.94MB spill-buffer=64.00KB | tuple-ids=0,1 row-size=73B cardinality=5819079 | |--05:EXCHANGE [BROADCAST] | | mem-estimate=0B mem-reservation=0B | | tuple-ids=1 row-size=54B cardinality=14 | | | F01:PLAN FRAGMENT [RANDOM] hosts=1 instances=1 | Per-Host Resources: mem-estimate=32.00MB mem-reservation=0B | 01:SCAN HDFS [flight_delay.airlines a, RANDOM] | partitions=1/1 files=1 size=341B | stats-rows=14 extrapolated-rows=disabled | table stats: rows=14 size=341B | column stats: all | mem-estimate=32.00MB mem-reservation=0B | tuple-ids=1 row-size=54B cardinality=14 | 00:SCAN HDFS [flight_delay.flights f, RANDOM] partitions=1/1 files=1 size=564.96MB runtime filters: RF000 -> f.airline stats-rows=5819079 extrapolated-rows=disabled table stats: rows=5819079 size=564.96MB column stats: all mem-estimate=176.00MB mem-reservation=0B tuple-ids=0 row-size=19B cardinality=5819079 ---------------- Estimated Per-Host Mem: 253689856 Per Host Min Reservation: host-10-17-100-140.coe.cloudera.com:22000(17.88 MB) host-10-17-100-141.coe.cloudera.com:22000(17.88 MB) host-10-17-100-143.coe.cloudera.com:22000(17.88 MB) host-10-17-100-147.coe.cloudera.com:22000(17.88 MB) Request Pool: root.hive Admission result: Admitted immediately ExecSummary: Operator #Hosts Avg Time Max Time #Rows Est. #Rows Peak Mem Est. Peak Mem Detail ------------------------------------------------------------------------------------------------------------------------- 08:MERGING-EXCHANGE 1 4s122ms 4s122ms 14 14 0 0 UNPARTITIONED 04:SORT 4 249.999us 999.996us 14 14 12.02 MB 12.00 MB 07:AGGREGATE 4 2.750ms 4.000ms 14 14 1.99 MB 10.00 MB FINALIZE 06:EXCHANGE 4 4s100ms 4s137ms 55 14 0 0 HASH(a.airline) 03:AGGREGATE 4 280.499ms 339.002ms 55 14 10.11 MB 10.00 MB STREAMING 02:HASH JOIN 4 177.749ms 184.999ms 5.82M 5.82M 10.05 MB 1.94 MB INNER JOIN, BROADCAST |--05:EXCHANGE 4 0.000ns 0.000ns 14 14 0 0 BROADCAST | 01:SCAN HDFS 1 97.000ms 97.000ms 14 14 177.00 KB 32.00 MB flight_delay.airlines a 00:SCAN HDFS 4 2s052ms 3s278ms 5.82M 5.82M 40.06 MB 176.00 MB flight_delay.flights f
这次我们先跳到执行摘要部分,因为它更容易看到,而且通常是我在帮助 CDH 用户排查 Impala 查询相关问题时首先要检查的部分。从上面的执行摘要信息中,我们可以看到在查询执行期间发生了什么:
1、从 HDFS 扫描上 flight_delay.flights 表的数据平均花费 2 秒时间( 2s052ms )
2、 Impala 估算到 flight_delay.flights 表的数据为 582 万行,和实际返回的行数相同,这表明表统计信息是最新的
3、 Impala 估算到扫描 flight_delay.flights 表需要 176MB 内存,但是实际上只用到 40MB ,这符合预期,因为估计内存不可能和实际使用的内存相同,我们的想法是尽可能地接近
4、由于数据量大, Impala 对文件进行了分割,并在 4 台主机上执行扫描操作,从而分散负载
5、当 flight_delay.flights 表扫描完成之后, Impala 开始扫描另一张表 flight_delay.airlines 。该表的估计行数和实际返回行数相同,说明表统计信息也是最新的。由于该表只有 14 行,所以只需要 97 毫秒就可以扫描它
6、由于表很小,只有 14 行, Impala 只使用 1 台主机来执行扫描操作
7、下一步是广播( broadcast )较小的表 flight_delay 到执行查询的所有节点(在我的示例中是 4 台主机(广播))
8、广播完成之后, Impala 对 flight_delay.airlines 和 flight_delay.flights 表执行 Hash Join 操作,花费 177ms 、 10MB 内存
9、由于我们调用了 COUNT 聚合函数, Impala 被要求执行聚合操作,该操作在 4 台主机上运行,花费 280ms 、 10MB 内存并返回 55 行
10、因为上面的步骤是在 4 个工作节点上执行的,所以 Impala 需要合并( merge )来自它们的结果,这是通过内部交换数据( exchanging the data internally )实现的,然后对中间结果( intermediate result )执行最后的聚合
11、因为我们的查询中有 ORDER BY ,因此在第 10 步完成后执行排序操作
你可以将 Summary 部分中的操作编号(比如 00 、 01 、 02 等)与查询计划( Query Plan )部分中的编号相匹配,查询计划部分将告诉你相关操作的更多细节。我在第 2 部分中提到的细节,如果你需要参考,请参考前面的文章。
现在,让我们看看 Profile 的 Planner Timeline 和 Query Timeline 部分: Planner Timeline Analysis finished: 3ms (3389346) Equivalence classes computed: 3ms (3600838) Single node plan created: 4ms (4625920) Runtime filters computed: 4ms (4734686) Distributed plan created: 5ms (5120630) Lineage info computed: 13ms (13666462) Planning finished: 15ms (15712999) Query Timeline Query submitted: 0ns (0) Planning finished: 16ms (16999947) Submit for admission: 17ms (17999944) Completed admission: 17ms (17999944) Ready to start on 4 backends: 18ms (18999941) All 4 execution backends (10 fragment instances) started: 28ms (28999909) Rows available: 4.28s (4280986646) First row fetched: 4.31s (4308986559)
每行的信息都很容易理解,我们可以看到运行查询计划花费了 15ms ,从 17ms 开始向 admission 提交查询计划,从 28ms 开始在工作节点上执行查询计划,在 4.28s 时准备好最后一行数据并在 4.31s 时第一行数据被客户端获取( fetch )。这使你可以很清楚地了解每个阶段所花的时间,如果任何阶段都很慢,那将是非常明显的,然后我们可以开始进一步深入研究,以了解可能发生了什么。
因为我的查询很快,所以在这里看到它不是很有趣,让我们看看另一个真实的生产 Impala query profile : Query Compilation: 16.268ms - Metadata of all 1 tables cached: 1.786ms (1.786ms) - Analysis finished: 6.162ms (4.376ms) - Value transfer graph computed: 6.537ms (374.918us) - Single node plan created: 7.955ms (1.417ms) - Runtime filters computed: 8.274ms (318.815us) - Distributed plan created: 8.430ms (156.307us) - Lineage info computed: 9.664ms (1.234ms) - Planning finished: 16.268ms (6.603ms) Query Timeline: 35m46s - Query submitted: 0.000ns (0.000ns) - Planning finished: 22.001ms (22.001ms) - Submit for admission: 23.001ms (1.000ms) - Completed admission: 23.001ms (0.000ns) - Ready to start on 2 backends: 24.001ms (1.000ms) - All 2 execution backends (2 fragment instances) started: 36.001ms (12.000ms) - Rows available: 5m51s (5m51s) - First row fetched: 5m52s (950.045ms) - Last row fetched: 35m46s (29m53s) - Released admission control resources: 35m46s (1.000ms) - Unregister query: 35m46s (30.001ms) - ComputeScanRangeAssignmentTimer: 0.000ns
这取自一个真实案例, Impala 查询运行了很长时间,客户想要找出原因。从查询时间轴( Query Timeline )中,我们可以清楚地看到,从开始执行(一共两个执行后端( All 2 execution backends ))到数据可用(可用行( Rows available ))几乎花费了 6 分钟( 5m51s )。这 6 分钟的执行可能是正常的,就像有很多大数据集的连接( join )一样,查询运行几分钟是很常见的。
但是,我们可以注意到 Impala 花了 30 分钟将数据传递回客户端,因为第一行在第 6 分钟获取,而最后一行在第 36 分钟获取。因此,从这里,我们可以怀疑 Impala 协调器( coordinator )和客户端之间可能存在一些网络问题(当从客户端,如 impala-shell 或 Hue ,到 Impala 协调器主机获取数据时)。另一种可能性是客户端可能在获取结果时也在执行其他操作,如在屏幕上打印,因为返回的数据可能很大,该操作可能很耗时。
因此,这部分概要信息可以指引我们找到寻找瓶颈的正确方向。
这是 Impala profile 系列的第3部分,详细介绍了如何将查询计划部分中显示的操作号与概要文件部分的最后部分联系起来,概要文件部分显示了每个操作的详细度量,包括平均操作和每个主机上的单独操作。
编译自: IMPALA QUERY PROFILE EXPLAINED – PART 3
大数据
2019-12-08 11:57:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
solr:
优点 Solr有一个更大、更成熟的用户、开发和贡献者社区。 支持添加多种格式的索引,如:HTML、PDF、微软 Office 系列软件格式以及 JSON、XML、CSV 等纯文本格式。 Solr比较成熟、稳定。 不考虑建索引的同时进行搜索,速度更快。
缺点
建立索引时,搜索效率下降,实时索引搜索效率不高。
Elasticsearch
优点 Elasticsearch是分布式的。不需要其他组件,分发是实时的,被叫做”Push replication”。 Elasticsearch 完全支持 Apache Lucene 的接近实时的搜索。 处理多租户(multitenancy)不需要特殊配置,而Solr则需要更多的高级设置。 Elasticsearch 采用 Gateway 的概念,使得完备份更加简单。 各节点组成对等的网络结构,某些节点出现故障时会自动分配其他节点代替其进行工作。
缺点
还不够自动,不适合当前新的Index Warmup API (参考: http://zhaoyanblog.com/archives/764.html )
总结: 当单纯的对已有数据进行搜索时,Solr更快。 当实时建立索引时, Solr会产生io阻塞,查询性能较差, Elasticsearch具有明显的优势。 随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。 Solr的架构不适合实时搜索的应用。 Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式 Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用
大数据
2019-12-06 22:01:04
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
让 etcd 变得更强
本节主要介绍 etcd 在性能方面的升级工作。首先我们来理解一下 etcd 的性能背景。
性能背景
这里先庖丁解牛,将 etcd 分为如下几个部分,如下图所示:
每一部分都有各自的性能影响,让我们逐层分解: raft 层:raft 是 etcd 节点之间同步数据的基本机制,它的性能受限于网络 IO、节点之间的 rtt 等, WAL 受到磁盘 IO 写入延迟; 存储层:负责持久化存储底层 kv, 它的性能受限于磁盘 IO,例如:fdatasync 延迟、内存 treeIndex 索引层锁的 block、boltdb Tx 锁的 block 以及 boltdb 本身的性能; 其他还有诸如宿主机内核参数、grpc api 层等性能影响因子。
服务端优化
了解完背景后,这里介绍一下性能优化手段,主要由服务端和客户端两个方面组成,这里先介绍服务端优化的一些手段。
硬件部署
etcd 是一款对 cpu、内存、磁盘要求较高的软件。随着内部存储数据量的增加和对并发访问量的增大,我们需要使用不同规格的硬件设备。这里我们推荐 etcd 至少使用 4 核 cpu、8GB 内存、SSD 磁盘、高速低延迟网络、独立宿主机部署等(具体硬件的 配置信息 )。在阿里巴巴,由于有超大规模的容器集群,因此我们运行 etcd 的硬件也较强。
软件优化
etcd 是一款开源的软件,集合了全世界优秀软件开发者的智慧。最近一年在软件上有很多贡献者更新了很多性能优化,这里分别从几个方面来介绍这些优化,最后介绍一个由阿里巴巴贡献的 etcd 存储优化。 内存索引层。由于索引层大量使用锁机制同步对性能影响较大,通过优化锁使用,提升了读写性能,具体参考: github pr ; lease 规模化使用。lease 是 etcd 支持 key 使用 ttl 过期的机制。在之前的版本中 scalability 较差,当有大量 lease 时性能下降的较为严重,通过优化 lease revoke 和过期失效的算法,解决了 lease 规模性的问题,具体参考: github pr ; 后端 boltdb 使用优化。etcd 使用 boltdb 作为底层数据库存储 kv, 它的使用优化对整体性能影响很大。
通过调节不同的 batch size 和 interval, 使我们可以根据不同硬件和工作负载优化性能,具体参考: github pr 。
除此之外,新的完全并发读特性也优化了 boltdb tx 读写锁性能,大幅度地提升了读写性能,具体参考: github pr 。
最后介绍一个由阿里巴巴自主研发并贡献开源社区的优化:基于 segregated hashmap 的 etcd 内部存储 freelist 分配回收算法。
下图是一个 etcd 节点的架构,etcd 使用 boltdb 持久化存储所有 kv,它的性能好坏对 etcd 性能起着非常重要的作用。
在阿里巴巴内部大规模使用 etcd 用于存储元数据,在使用中我们发现了 boltdb 的性能问题。这里给大家分享一下:
上图是 etcd 内部存储分配回收的核心算法。etcd 内部默认以 4kB 为一个页面大小存储数据。图中的数字表示页面 id, 红色表示该页面正在使用, 白色表示没有。当用户删除数据时 etcd 不会把存储空间还给系统,而是内部先留存起来维护一个页面池,以提升再次使用的性能,这个页面池专业术语叫 freelist。当 etcd 需要存储新数据时,普通 etcd 会线性扫描内部 freelist,时间复杂度 o(n),当数据量超大或是内部碎片严重的情况下,性能会急剧下降。
因此我们重新设计并实现了基于 segregated hashmap 的 etcd 内部存储 freelist 分配回收新算法,该优化算法将内部存储分配算法时间复杂度从 o(n) 降为 o(1), 回收从 o(nlgn) 也降为 o(1), 使 etcd 性能有了质的飞跃,极大地提高了 etcd 存储数据的能力,使得 etcd 存储容量提升 50 倍,从推荐的 2GB 提升到 100GB;读写性能提升 24 倍。 CNCF 官方博客 收录了此次更新,感兴趣的读者可以读一下。
客户端优化
性能优化除了服务端要做的事情外,还需要客户端的帮助。保持客户端使用最佳实践将保证 etcd 集群稳定高效地运行,这里我们分享 3 个最佳实践: put 数据时避免大的 value, 大的 value 会严重影响 etcd 性能,例如:需要注意 Kubernetes 下 crd 的使用; 避免创建频繁变化的 key/value, 例如:Kubernetes 下 node 数据上传更新; 避免创建大量 lease 对象,尽量选择复用过期时间接近的 lease, 例如 Kubernetes 下 event 数据的管理。
让 etcd 管理更高效
作为基于 raft 协议的分布式键值数据库,etcd 是一个有状态的应用。管理 etcd 集群状态、运维 etcd 节点、冷热备份、故障恢复等过程均有一定复杂性,且需要具备 etcd 内核相关的专业知识,想高效地运维 etcd 有不小的挑战。
目前在业界里已经有一些 etcd 运维的工具,例如开源的 etcd-operator 等,但是这些工具往往比较零散,功能通用性不强,集成度比较差,学习这些工具的使用也需要一定的时间,关键是这些工具不是很稳定,存在稳定性风险等。
面对这些问题,我们根据阿里巴巴内部场景,基于开源 etcd-operator 进行了一系列修改和加强,开发了 etcd 运维管理平台 Alpha。利用它,运维人员可以高效地运维管理 etcd,之前要前后操作多个工具完成的任务,现在只要操作它就可以完成,一个人就可以管理成百上千的 etcd 集群。
下图展示了 Alpha 的基础功能:
如上图所示,Alpha 分为 etcd 生命周期管理和数据管理两大部分。
其中生命周期管理功能依托于 operator 中声明式的 CustomResource 定义,将 etcd 的集群创建、销毁的过程流程化、透明化,用户不再需要为每个 etcd 成员单独制定繁琐的配置,仅需要指定成员数量、成员版本、性能参数配置等几个简单字段。除此之外,我们还提供了 etcd 版本升级、故障节点替换、集群实例启停等功能,将 etcd 常用的运维操作自动化,同时也在一定程度上保证了 etcd 变更的稳定性。
其次,数据作为 etcd 的核心内容,我们也开发了一系列功能进行重点保障。在备份上,数据管理工具支持定期冷备及实时热备,且保持本地盘和云上 OSS 两类备份,同时也支持从备份上快速恢复出一个新的 etcd 集群。此外,数据管理工具支持对 etcd 进行扫描分析,发现当前集群的热点数据键值数和存储量,弥补了业界无法提供数据管理的空白,同时该拓展也是 etcd 支持多租户的基础。最后,数据管理工具还支持对 etcd 进行垃圾数据清理、跨集群数据腾挪传输等功能。
这些丰富的功能为上层 Kubernetes 集群的管理提供了很多灵活的帮助,例如用户 A 原来在某云厂商或自建 Kubernetes 集群,我们可以通过迁移 etcd 内部的账本数据的功能,将用户的核心数据搬移至另外一个集群,方便地实现用户的 K8s 集群跨云迁移。
利用 Alpha,我们可以做到透明化、自动化、白屏化,减少人肉黑屏操作,让 etcd 运维管理更高效。
让 etcd 变得更稳
本节主要介绍一些 etcd 稳定建设的技巧。大家知道 etcd 是容器云平台的底层依赖核心,它的服务质量、稳定程度决定了整个容器云的稳定程度,其重要性无需赘述。这里先介绍一下 etcd 常见的问题和风险分析,如下图所示,主要分三个方面: etcd 自身:例如 OOM、代码 bug、panic 等; 宿主机环境:例如宿主机故障、网络故障、同一台宿主机其他进程干扰; 客户端:例如客户端 bug、运维误操作、客户端滥用 ddos 等。
针对这些风险点,我们从以下几方面入手: 建立完善的监控告警机制,覆盖客户端输入,etcd 自身以及宿主机环境状态; 客户操作审计,高危操作如删除数据做风控限流; 数据治理,分析客户端滥用,引导最佳实践; 定期数据冷备,通过热备实现异地多活,保证数据安全; 常态化故障演练,做好故障恢复预案。
总结展望:让 etcd 变得更智能
本文分别从性能、稳定性、生态工具三个部分享了 etcd 变得更强、更快、更高效的技巧。在未来我们还将为让 etcd 变得更智能而努力。如何让 etcd 变得更智能是一个比较高级的话题,这里简单做一下展望。更智能的意思是指可以使 etcd 的管理更加地聪明,更少的人为干预,例如遇到一些故障,系统可以自行修复等。
大数据
2019-12-06 10:37:02
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
  好程序员大数据教程分享HadoopHDFS操作命令总结

  1.列出根目录下所有的目录或文件

  hadoopfs-ls/

  2.列出/logs目录下的所有目录和文件

  hadoopfs-ls/logs

  3.列出/user目录及其子目录下的所有文件(谨慎使用)

  hadoopfs-ls-R/user

  4.创建/soft目录

  hadoopfs-mkdir/soft

  5.创建多级目录

  hadoopfs-mkdir-p/apps/windows/2017/01/01

  6.将本地的wordcount.jar文件上传到/wordcount目录下

  hadoopfs-putwordcount.jar/wordcount

  7.将/stu/students.txt文件拷贝到本地

  hadoopfs-copyToLocal/stu/students.txt

  8.将word.txt文件拷贝到/wordcount/input/目录

  hadoopfs-copyFromLocalword.txt/wordcount/input

  9.将word.txt文件从本地移动到/wordcount/input/目录下

  hadoopfs-moveFromLocalword.txt/wordcount/input/

  10.将/stu/students.txt拷贝一份为/stu/students.txt.bak

  hadoopfs-cp/stu/students.txt/stu/students.txt.bak

  11.将/flume/tailout/目录下的子目录或文件都拷贝到/logs目录(如果此目录不存在会创建)下

  hadoopfs-cp/flume/tailout//logs

  12.将/word.txt文件重命名为/words.txt

  hadoopfs-mv/word.txt/words.txt

  13.将/words.txt文件移动到/wordcount/input/目录下

  hadoopfs-mv/words.txt/wordcount/input/

  14.将/ws目录以及子目录和文件都删除(谨慎使用)

  hadoopfs-rm-r/ws

  15.将/wordcount/output2/目录下的a.txt文件删除

  hadoopfs-rm/wordcount/output2/a.txt

  16.将/wordcount/input/目录下面的所有文件都删除

  hadoopfs-rm/wordcount/input/*

  17.查看HDFS集群的磁盘空间使用情况

  hadoopfs-df-h

  18.查看/word.txt文件的内容

  hadoopfs-cat/word.txt

  19.将name.txt文件中的内容添加到/wordcount/input/words.txt文件中

  hadoopfs-appendToFilename.txt/wordcount/words.txt

  20.动态查看/wordcount/input/words.txt文件的内容

  hadoopfs-tail-f/wordcount/words.txt

  21.统计/flume目录总大小

  hadoopfs-du-s-h/flume

  22.分别统计/flume目录下各个子目录(或文件)大小

  hadoopfs-du-s-h/flume/*
大数据
2019-11-12 19:24:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一、定时任务触发条件
1、在 Application 启动类上添加:@EnableScheduling
2、含定时方法的类上添加注解: @Component ,该注解将定时任务类纳入 spring bean 管理。
3、在定时方法上写上:@Scheduled(cron = "0 0/1 * * * ?"),该 cron 表达式为每一分钟执行一次方法。
二、@Scheduled用法
1、fixedDelay @Scheduled(fixedDelay = 5000) public void testFixedDelay(){ try { log.info("当前时间:" + DateUtil.now()); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }
每个任务延迟3秒,然后打印当前时间。
fixedDelay规律总结:
前一个任务执行结束后,再等待5秒,然后执行第二个任务。
2、fixedRate @Scheduled(fixedRate = 5000) public void testFixedRate(){ try { log.info("当前时间:" + DateUtil.now()); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }
任务启动后,每隔5秒执行一次任务。
如果将延时时间修改为8秒,则输出变为8秒,如下图所示:
fixedRate规律总结:
假如设置定时任务每5秒一执行,如果前一个任务用时超过了5秒,则等前一个任务完成后就立刻执行第二次任务。如果前一个任务用时小于5秒,则等满足5秒以后,再执行第二次任务。
3、Corn表达式详解(常用)
Corn 表达式可用 秒、分、时、天、周、月、年 来表示: 秒 分 时 天 周 月 年 0 * 14 * * ? * : 代表每天从14点开始,每一分钟执行一次。 0 0 14 * * ? * : 代表每天的14点执行一次任务。
可使用 Corn 在线生成表达式: http://cron.qqe2.com/ ,来检测 Cron 的合理性。
**Corn 示例:**每2分钟执行一次。 @Scheduled(cron = "0 0/2 * * * ?") public void test() { int j = 0; for (int i = 0; i < 10; i++) { log.info("Scheduled测试"); j++; log.info("j的值为:" + j); try { Thread.sleep(1000 * 20); } catch (InterruptedException e) { e.printStackTrace(); } } }
效果:
总结 :
如上述代码所示,设置 test() 方法每2分钟执行一次。但如果前一个任务执行时长超过了2分钟,则第二个任务会等待前一个任务完成后的一段时间后再执行第二个任务。
本文来自: 微信公众号【大数据实战演练】。阅读更多精彩好文,欢迎关注微信公众号【大数据实战演练】。
大数据
2019-10-09 01:21:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Flume是Cloudera提供的一个 高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统 ,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。
Flume最主要作用就是实时读取服务器本地磁盘的数据,将数据写入到HDFS。
Flume组成架构
Agent
Agent是一个JVM进程,它以事件的形式将数据从源头送至目的地, 是Flume数据传输的基本单元 。Agent主要有3个部分组成:Source、Channel、Sink。
Source
Source是负责接收数据到Flume Agent的组件 。Source组件可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling director、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从自己的内部缓存区删除事件。
Event
传输单元,Flume数据传输的基本单元,以事件的形式将数据从源头送至目的地。
Flume Agent内部原理
大数据
2020-02-20 19:39:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
造概念,在IT行业可不是一件陌生的事儿,中文博大精深,新名词、新概念往往简单准确,既可以被大众接受,又可以被专家把玩,真正做到雅俗共赏、各有趣味。近年来,数据中台之火爆,什么数据平台、数据中台、数据湖、数据集市等等,不同的叫法把大家绕的云里雾里,概念混淆不清,着实让人摸不着头脑……
正如我们清楚的知道企业要进行数字化驱动架构之前,必须要建立统一的数据标准和规范,用统一的、大家都了解的语言描述一件事情是多么重要。同样的道理,在理解“大数据”“数据中台”相关知识之前,我们有必要先将常遇到的包括数据仓库、数据集市、数仓湖、大数据平台、数据中台等概念一次性说清,以便在今后的学习与建设中能够清楚的区别开来。
在回答上述问题之前,我们先来看看数据中台应该怎么理解?阿里认为数据中台其三项核心能力分别为:OneModel负责统一数据构建及管理,OneID负责将核心商业要素资产化,OneService负责向上提供统一的数据服务。
小编认为,数据中台的核心能力是数据能力的抽象、共享与复用,两者对数据中台的定义看似差异巨大,但仔细分析,相差无几。换言之,“抽象”是为了达成“OneModel”、“共享”则是为了“OneID”、“复用”才能让“OneService”更有意义。
数字化运营不同阶段,运营手段各尽所能
随着大数据技术的不断更新与迭代,数据管理工具得到了飞速的发展,从数据库、数据仓库、数据集市与数据湖,再到大数据平台与如今的数据中台,其实将它们比喻成一场“数据的旅程”就不难理解在数字化运营的不同阶段,各运营手段并不一定是谁替代了谁,准确的讲,它们都有自己的功能、特点所在,技术之间的互补,每个手段都各尽所能的为自己的用例服务。下面我们就来简明扼要的归纳一下数字化运营不同阶段中各运营手段的功能与亮点。
1、数据库: 传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。
2、数据仓库: 数据仓库系统的主要应用是OLAP,支持复杂的数据分析,侧重决策支持,并且提供直观易懂的查询结果,可做到业务的历史快照,总结性数据以及高纬度分析。
3、数据集市: 可以理解为是一种"小型数据仓库",只包含单个主题,且关注范围也非全局,数据从企业范围的数据库、数据仓库中抽取出来,迎合专业用户群体的特殊需求,其面向部门级业务或某一个特定的主题,良好地解决了灵活性和性能之间的矛盾。
4、数据湖: 存储企业各种各样原始数据的大型仓库,其中的数据可供存取、处理、分析及传输,主要解决的是“看见数据”的问题,作为全局数据汇总及处理的一个核心功能,数据湖在数据中台建设中必不可少,除了为数据仓库提供原始数据之外,数据湖也可以直接为上层的数据应用提供服务。
5、大数据平台: 个性化、多样化数据,以处理海量数据存储、计算及流数据实时计算等场景为主的一套基础设施,使用大数据平台,企业可以比竞争对手更快地作出数据驱动的决策,更快地推出适应客户需求的产品。
6、数据中台: 我们知道所有关于数据工具的建设,其目的都是为了从数据中提取价值来支持更有效的数据运营,那么不能指导实际行动,创造实际价值的数据以及从数据中产生的知识是无用的,那花大价钱来做这个系统也没有必要。
说到底,数据工具的建设还是要以 ROI(Return On Investment)来支持,数据中台概念的出现,很大程度上是原来的大数据系统建设的ROI 不如人意,企业投入了大量的物力、财力和人力建设了大数据平台,却发现并没有给企业带来应用的价值,大数据平台更多的沦为“形象工程“,甚至产生了新的数据孤岛,更不用说实现数据能力的全局抽象、复用和共享了,而数据中台可以说是为此类大数据平台了个“补丁”,其全局的数据仓库、大数据协调共享等能力,真正解决了重复开发、数据标准不统一、数据孤岛等问题,从而提高了数据价值实现效率和ROI。
常见混淆概念梳理:传统大数据平台、硅谷大数据平台、数据中台
其实,数字化运营不同阶段的运营手段相对来说是比较好理解的,但是我们常常能听到一些字面意思相近的概念,尤其是当我们了解到原来在美国硅谷“中台”其实早已有之,只不过这种方法论在被引入到国内之后,被冠以“中台”之名时混淆的概念常常让我们不知所措。
那么,在硅谷所谓的“中台”叫什么?国外的大数据平台与国内的大数据平台又有什么区别?接下来就让我们统一相关概念并梳理其关系,一次性说清让大家一目了然。
1、大数据平台1.0
大数据平台1.0=传统大数据平台
大数据平台1.0时期,其实就是我们通常所看到的国内“传统大数据平台”的概念,此时的大数据平台是以处理海量数据存储、计算及流数据实时计算等场景为主的一套基础设施,以Hadoop、Spark、Hive等作为大数据基础能力层,在大数据组件上搭建包括数据分析,机器学习程序等ETL流水线,以及包括数据治理系统、数据仓库系统、数据可视化系统等核心功能。
但是在大数据平台1.0时期,硬件投资与软件开发投入量巨大,极大增加了研发的难度、调试部署的周期、运维的复杂度,且经常由于架构的缺陷,数据应用开发运维的困难,多租户资源隔离的复杂度等原因造成数据孤岛、应用孤岛的问题。
传统大数据平台
2、大数据平台2.0
大数据平台2.0=新一代大数据平台=大数据平台1.0+数据中台的功能+数据运营的功能
大数据平台2.0时期充分诠释了硅谷“中台”早已有之的说法,但为什么硅谷没有“数据中台”概念?原因是硅谷公司从起步开始,管理层就将打造数据驱动需要的基础架构作为必须的功课之一,公司内部都有一个 Data Platform(数据平台)部门负责建设公司的数据平台,其大数据平台建设绝大多数是需求驱动,且后续发展都是由这个大数据平台能产生多少价值来决定的。
也就是说,在硅谷大家其实也并没有刻意的去打造什么中台,但是“避免重复造轮子”“快速迭代”“数据驱动”“业务驱动”是硅谷工程师文化的一些核心概念,也是硅谷高效创新的一个核心,大部分公司在起始架构设计时,就将“数据中台”所包括的数据抽象、复用与共享的能力,以及一些数据运营的功能设计在内了,其建设目的是一样的,所以没有必要在概念上过度纠结。
3、数据中台
数据中台建设的目标可简单归纳为通过提供工具、流程和方法论,实现数据能力的抽象、复用和共享,赋能业务部门,提高实现数据价值的效率。阿里提出数据中台的概念,只是为了强调和国内现有的大数据平台加以区别,强调解决数据孤岛、重复开发的问题,突出数据共享和复用的概念。
深入探究:数据中台与Ta的关系
想必现在你已经从傻傻分不清的状态中走出来,接下来,我们再深入具体地了解下数据中台与之相对应的关系,看看你是不是已经游刃有余的掌握了相关概念呢?
数据中台与传统数据仓库、数据集市、数据湖的关系
数据仓库与数据集市的出现,就是为了解决信息化阶段OLTP(联机事务处理过程)在分析场景下的局限性,它们将OLTP中的数据采集过来,做成面向历史、主题、分析的一些数据集,从而可以轻松地做出OLTP难以做出的分析。
但是,随着互联网时代的到来,数据仓库的数据来源只在业务系统功能中,提供一些汇聚的业务信息,无法提供个性化的信息以及一些非传统业务数据源的信息。另外,一些非传统业务数据源的信息一般存储在服务器日志中,那么大量且无效的数据如果都存储到数据仓库中,其效率之低和限制是无法想象的。
此时,数据湖和大数据平台的出现改变了上述局面,在这个阶段的数据仓库和数据集市,则基于大数据技术取得了进化,也就是说数据仓库不能解决的问题,我们用大数据数仓(基于大数据技术实现的数据仓库)来解决,大数据数仓解决不了的,我们用大数据平台来解决,大数据平台解决不了的问题,就需要数据中台来解决。
应该说数据中台是建立在数据仓库和数据平台之上的,让业务部门可以更好,更有效率的使用数据的运营管理层,并强调从工具和机制上支持对数据能力的抽象、共享和复用。
数据中台与大数据平台1.0、2.0的关系
很多人会疑惑大数据平台1.0与数据中台的差别在哪里呢?其实,两者的建设目的都是发掘数据价值,高效实现数字化运营,区别则在于数据中台是具备业务属性的,输入的是原始数据,输出的是业务部门可以直接使用的数据能力。如果必须要将数据中台和大数据平台1.0区分开来, 可以说数据中台是建立在大数据平台1.0的基础层之上,强调提供相应的工具和机制来实现数据能力的全局抽象、共享和复用。
在国内,为什么很多企业面临着数据孤岛与应用孤岛的困局?而在硅谷,大多数企业并没有数据孤岛、应用孤岛的烦恼?因为硅谷每个公司在建设大数据平台的时候,大数据平台的运营效率和使用效率,都是必须要考虑的关键问题。在起始架构设计与后续迭代的时候,如何最大化投入产出比,并让业务部门真正发挥数据的作用都是关键所在。在这个过程中,也有很多的尝试和迭代,但是最终的结果是, 绝大部分的大数据平台自然的就会提供所谓的“数据中台”的功能,成为公司内部的一个核心价值驱动引擎。
大数据平台1.0与2.0关系图
而大数据平台2.0作为新一代大数据平台,则是在大数据平台1.0基础上,增加了数据中台的功能,以及数据运营的功能。对于“各个部门数据重复开发,浪费存储与计算资源”、“数据标准不统一,数据使用成本高”、“业务数据孤岛问题严重,数据利用效率低”,这些需要在大数据平台1.0阶段解决的问题,并没有在国内企业的大数据平台阶段得到考虑和解决。因此,需要一个新的平台来为这个大数据平台“打补丁”,而这个新平台,就是所谓的“数据中台”。
总结
本文从数字化运营不同阶段对数据仓库、数据湖、大数据平台、数据中台等内涵作了详细说明,便于读者更好的理解和掌握数据领域相关概念,并帮助大家更好地了解大数据带给我们的能力与作用。需要强调的是,除了了解数据中台的概念外,其方法论更为重要,数据中台建设为我们企业数据服务和共享奠定了重要的基础,是企业从“数据”迈向“价值”的强大助推器。
本文来源: 智领云科技

点击关注,第一时间了解华为云新鲜技术~
大数据
2020-08-13 19:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
声明 :此篇文章主要是观看静觅教学视频后做的笔记,原教程地址 https://cuiqingcai.com/
普通代理
因为之前都是学习测试,不需要对网站频繁的搜索爬取,所以代理使用似乎关系不大,不过为了防止IP被封,也是一个很重要的知识点。之前使用代理也都是查找一些代理网站,手动将IP设置添加到proxies参数中,比较麻烦,而且代理IP也有可能出现无法正常访问的情况 import requests proxies = { "http": "http://127.0.0.1:9743", "https": "https://127.0.0.1:9743", } response = requests.get("https://www.taobao.com", proxies=proxies)
维护代理池
1.为什么要用代理池许多网站有专门的反爬虫措施,可能遇到封IP等问题
①互联网上公开了大量免费代理,利用好资源
②通过定时的检测维护同样可以得到多个可用代理
2.代理池的要求
①多站抓取,异步检测
②定时筛选,持续更新
③提供接口,易于提取
3.代理池的架构

4.代理池的获取途径
现在,新增一个解决思路,那就是通过Flask+Redis维护代理池。免费代理的质量可能不是太好,可能一个代理无数个人在用也说不定,主要实现就是从免费代理网站大量抓取这些免费代理,然后筛选出其中可用的代理存储起来供我们使用,不可用的进行剔除,以西刺免费代理IP为例
可以看到网页里提供了一些免费代理列表,包括服务器地址、端口、代理种类、地区、更新时间等等信息。当前我们需要的就是代理服务器和端口信息,将其爬取下来即可
5.维护代理
代理被爬取下来之后,就要解决代理的保存问题。首先我们需要确保的目标是可以边取边存,另外还需要定时检查队列中不可用的代理将其剔除,所以需要易于存取。
另外怎样区分哪些是最新的可用的,哪些是旧的,如果用修改时间来标注是可以的,不过更简单的方法就是维护一个队列,只从一端存入,例如右端,这样就能确保最新的代理在队列右端,而在左端则是存入时间较长的代理,如果要取一个可用代理,从队列右端取一个就好了。那么对于队列的左端,不能让它一直老化下去,还需要做的操作就是定时从队列左端取出代理,然后进行检测,如果可用,重新将其加入右端
通过以上操作,就保证了代理一直是最新可用的。所以目前来看,既能高效处理,又可以做到队列动态维护,合适的方法就是利用Redis数据库的队列。可以定义一个类来维护一个Redis队列,比如get方法是批量从左端取出代理,put方法是从右端放入可用代理,pop方法是从右端取出最新可用代理 import redis from proxypool.error import PoolEmptyError from proxypool.setting import HOST, PORT ​​​​​​​ class RedisClient(object): def __init__(self, host=HOST, port=PORT): self._db = redis.Redis(host, port) def get(self, count=1): proxies = self._db.lrange("proxies", 0, count - 1) self._db.ltrim("proxies", count, -1) return proxies def put(self, proxy): self._db.rpush("proxies", proxy) def pop(self): try: return self._db.rpop("proxies").decode('utf-8') except: raise PoolEmptyError
6.检测代理
可以使用这个代理来请求某个站点,比如百度,如果获得正常的返回结果,那证明代理可用,否则代理不可用 import requests conn = RedisClient() proxies = {'http': proxy} r = requests.get('https://www.baidu.com', proxies=proxies) if r.status_code == 200: conn.put(proxy)
7.获取可用代理
通过redis-desktop可以看到一个proxies的键,其内容就是一些可用的IP代理,也就是所维护的代理池

维护了一个代理池,那么这个代理池需要是可以公用的。比如现在有多个爬虫项目都需要用到代理,而代理池的维护作为另外的一个项目,他们之间如果要建立连接,最恰当的方式就是接口。所以可以利用Web服务器来实现一个接口,其他的项目通过请求这个接口得到内容获取到一个可用代理,这样保证了代理池的通用性。所以要实现这个还需要一个Web服务器,例如Flask,Tornado等等。例如使用Flask,定义一个路由,然后调用RedisClient的pop方法,返回结果即可 @app.route('/') def get_proxy(): conn = RedisClient() return conn.pop()
这样一来,整个程序运行起来后,请求网页就可以看到一个可用代理了
8.使用代理
使用代理时只需要请求这个站点,就可以拿到可使用的代理了。
def get_proxy(): r = requests.get('http://127.0.0.1:5000') return r.text def crawl(url, proxy): proxies = {'http': get_proxy()} r = requests.get(url, proxies=proxies) # do something
可以定义一个简单的方法,返回网页内容即代理,然后在爬取方法里设置代理使用即可
10.代理池功能测试
在了解了前面内容之后,现学现用,马上用代理池测试访问了我的站点
11.代理池完整源码
我是从崔家华老师的github上下载下来的,源码可参考: https://github.com/Germey/ProxyPool
下载后发现源码不能正常安装,发现是在setup.py文件中有个参数配置错误 # 将 packages=[ 'proxy-pool' ], # 改为 packages=[ 'proxypool' ],
修改后的项目地址: https://github.com/XiaoFei-97/ProxyPool
原文出处: https://www.jzfblog.com/detail/68 ,文章的更新编辑以此链接为准。欢迎关注源站文章!
大数据
2018-10-10 14:17:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hive2.x
hive2.x特性 LLAP(Live Long and Process)Hive2.1进行了极大的性能优化。在Hive2.x开启LLAP与Apache Hive1.x进行对比测试,其性能提升约25倍。 支持使用HPL/SQL的存储过程,Hive2.0.0推出的Hive Hybrid Procedural SQL On Hadoop (HPL/SQL) 是一个在Hive上执行过程SQL的工具,它可以表达复杂的业务规则。 更智能的成本优化器CBO,Hive2.0开始持续不断地优化成本优化器CBO,尤其是在BI业务关注的TPC-DS查询上。 提供全面详尽的监控和诊断工具,可以通过新的Hive Server2 Web UI,LLAP Web UI和Tez Web UI查看Hive相关的HQL查询以及关联的作业状态和日志。丰富了Hive用户的运维和排错的手段。
HiveServer 在hive的机器上启动一个server:客户端可以通过ip + port的方式对其进行访问之后,就可以有很多客户端连到这个server上面去干活可以采用jdbc、odbc、beeline的方式进行连接 HiveServer是一种可选服务,允许远程客户端可以使用各种编程语言向Hive提交请求并检索结果。HiveServer无法处理来自多个客户端的并发请求.从Hive 0.11.0版本开始。建议使用HiveServer2。
HiveServer2 HiveServer2(HS2)是一种能使客户端执行Hive查询的服务。 HiveServer2是HiveServer1的改进版,HiveServer1已经被废弃。HiveServer2可以支持多客户端并发和身份认证。旨在为开放API客户端(如JDBC和ODBC)提供更好的支持。 远程HiveServer2模式是Hive产品使用的推荐模式,它更加安全并且不需要直接为用户对HDFS/metastore进行赋权。
Beeline 是hive的新hiveCLI
CLI hive -vs- Beeline # Beeline操作结果 0: jdbc:hive2://node225:10000/movie> select * from t_user limit 5; OK +----------------+----------------+-------------+--------------------+-----------------+--+ | t_user.userid | t_user.gender | t_user.age | t_user.occupation | t_user.zipcode | +----------------+----------------+-------------+--------------------+-----------------+--+ | 1 | F | 1 | 10 | 48067 | | 2 | M | 56 | 16 | 70072 | | 3 | M | 25 | 15 | 55117 | | 4 | M | 45 | 7 | 02460 | | 5 | M | 25 | 20 | 55455 | +----------------+----------------+-------------+--------------------+-----------------+--+ # hive 操作结果 hive> select * from t_user limit 5; OK 1 F 1 10 48067 2 M 56 16 70072 3 M 25 15 55117 4 M 45 7 02460 5 M 25 20 55455 Time taken: 2.662 seconds, Fetched: 5 row(s) hive>
要使用Beeline前需要先启动HiveServer2,启动过程中可以通过hiveconf设置相应的自定义参数和值,直接启动会占据当前连接会话,第一次可以直接启动,正常启动后可以切换至后台运行方式启动。 # 直接启动 [root@node225 ~]# /usr/local/hive-2.1.1/bin/hiveserver2 --hiveconf hive.server2.thrift.prot=10000 # 后台运行方式启动 [root@node225 ~]# /usr/local/hive-2.1.1/bin/hiveserver2 --hiveconf hive.server2.thrift.prot=10000 2>&1 >> /dev/null & #或者 #分别记录标准日志输出和错误日志 nohup /usr/local/hive-2.1.1/bin/hiveserver2 --hiveconf hive.server2.thrift.prot=10000 1>/usr/local/hive-2.1.1/hivelog/hiveserver.log 2>/usr/local/hive-2.1.1/hivelog/hiveserver.err & #不记录日志 nohup /usr/local/hive-2.1.1/bin/hiveserver2 --hiveconf hive.server2.thrift.prot=10000 1>/dev/null 2>/dev/null & nohup /usr/local/hive-2.1.1/bin/hiveserver2 --hiveconf hive.server2.thrift.prot=10000 >/dev/null 2>&1 &
beeline的使用 -n 指定机器登陆的名字,当前机器的登陆用户名 -u 指定一个连接串
每成功运行一个命令,hiveserver2启动的那个窗口,只要在启动beeline的窗口中执行成功一条命令,另外个窗口随即打印一个OK如果命令错误,hiveserver2那个窗口就会抛出异常 # beeline连接hive [root@node225 ~]# /usr/local/hive-2.1.1/bin/beeline -u jdbc:hive2://node225:10000/movie -n root which: no hbase in (.:/usr/local/jdk1.8.0_66//bin:/usr/local/zookeeper-3.4.10/bin:ZK_HOME/sbin:ZK_HOME/lib:/usr/local/hadoop-2.6.5//bin:/usr/local/hadoop-2.6.5//sbin:/usr/local/hadoop-2.6.5//lib:/usr/local/hive-2.1.1/bin:/usr/local/mongodb/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin) Connecting to jdbc:hive2://node225:10000/movie Connected to: Apache Hive (version 2.1.1) Driver: Hive JDBC (version 2.1.1) 18/10/09 14:03:00 [main]: WARN jdbc.HiveConnection: Request to set autoCommit to false; Hive does not support autoCommit=false. Transaction isolation: TRANSACTION_REPEATABLE_READ Beeline version 2.1.1 by Apache Hive 0: jdbc:hive2://node225:10000/movie> select current_database(); +----------------+--+ | database_name | +----------------+--+ | db_hive_edu | | default | | movie | +----------------+--+ 3 rows selected (1.293 seconds)
可以在相同局域网内的其他部署hive的节点上通过Beelin连接指定的HiveServer2服务,进行多用户操作。
退出beeline连接用!quit
大数据
2018-10-10 11:45:00