数据专栏

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

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
1.概述
ElasticSearch提供了丰富的参数对文档字段进行定义,比如字段的分词器、字段权重、日期格式、检索模型等等。可以查看官网每个参数的定义及使用:https://www.elastic.co/guide/en/elasticsearch/reference/6.1/mapping-params.html。
2.analyzer
分词器对索引和查询有效:https://www.elastic.co/guide/en/elasticsearch/reference/6.1/analyzer.html
我们要测试分词器参数使用首先要安装分词器组件,从https://github.com/medcl/elasticsearch-analysis-ik/releases下载和elasticsearch相匹配的组件版本,这里下载 elasticsearch-analysis-ik-6.2.3.zip 文件,拷贝到elasticsearch安装目录的plugins文件夹下面,解压,删除zip文件,重启elasticsearch(一定要重启才生效)。
定义索引: DELETE my_index PUT my_index
使用ik_smart分词 GET my_index/_analyze { "analyzer": "ik_smart", "text": "安徽省长江流域" }
结果 { "tokens": [ { "token": "安徽省", "start_offset": 0, "end_offset": 3, "type": "CN_WORD", "position": 0 }, { "token": "长江流域", "start_offset": 3, "end_offset": 7, "type": "CN_WORD", "position": 1 } ] }
定义mapping,指定字段分词器 PUT my_index/fulltext/_mapping { "properties": { "content":{ "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word" } } }
添加文档 PUT my_index/fulltext/1 { "content":"软件测试是非常复杂的工作" } PUT my_index/fulltext/2 { "content":"发改委表示,上半年审核批准固定资产项目102个" } PUT my_index/fulltext/3 { "content":"全球最大资产管理公司贝莱德成立区块链研究组" } PUT my_index/fulltext/4 { "content":"资本投资疯狂,工业产能过剩" }
通过关键字查询 GET my_index/fulltext/_search { "query": { "match": { "content": "资产" } } }
查询结果 { "took": 11, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0.5897495, "hits": [ { "_index": "my_index", "_type": "fulltext", "_id": "2", "_score": 0.5897495, "_source": { "content": "发改委表示,上半年审核批准固定资产项目102个" } }, { "_index": "my_index", "_type": "fulltext", "_id": "3", "_score": 0.2876821, "_source": { "content": "全球最大资产管理公司贝莱德成立区块链研究组" } } ] } }
3.normalizer
normalizer用于解析前的标准化配置,比如把所有的字符转化为小写等。
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/normalizer.html
定义映射 DELETE my_index PUT my_index { "settings": { "analysis": { "normalizer": { "my_normalizer": { "type": "custom", "char_filter": [], "filter": ["lowercase", "asciifolding"] } } } }, "mappings": { "my_type": { "properties": { "foo": { "type": "keyword", "normalizer": "my_normalizer" } } } } }
索引文档 PUT my_index/my_type/1 { "foo": "BÀR" } PUT my_index/my_type/2 { "foo": "bar" } PUT my_index/my_type/3 { "foo": "baz" } POST my_index/_refresh GET my_index/_search { "query": { "match": { "foo": "BAR" } } }
由于设置foo字段索引时会进行标准化,保存是“BAR”会被转化为“bar”进行保存,在搜索时也会将搜索条件中的“BAR”转化为“bar”进行匹配。 { "took": 7, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0.2876821, "hits": [ { "_index": "my_index", "_type": "my_type", "_id": "2", "_score": 0.2876821, "_source": { "foo": "bar" } }, { "_index": "my_index", "_type": "my_type", "_id": "1", "_score": 0.2876821, "_source": { "foo": "BÀR" } } ] } }
通过查询可以统计字段“foo”被反向索引个数 GET my_index/_search { "size": 0, "aggs": { "foo_terms": { "terms": { "field": "foo" } } } }
可以看到"bar"被索引2个,"baz"被索引1个 { "took": 14, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 0, "hits": [] }, "aggregations": { "foo_terms": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "bar", "doc_count": 2 }, { "key": "baz", "doc_count": 1 } ] } } }
4.boost
可以通过指定一个boost值来控制每个查询子句的相对权重,该值默认为1。一个大于1的boost会增加该查询子句的相对权重。
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/mapping-boost.html#mapping-boost DELETE my_index PUT my_index PUT my_index/my_type/1 { "title":"quick brown fox" } GET my_index/_search { "query": { "match" : { "title": { "query": "quick brown fox", "boost":2 } } } }
设定权重2,默认1 { "took": 5, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 1.7260926, "hits": [ { "_index": "my_index", "_type": "my_type", "_id": "1", "_score": 1.7260926, "_source": { "title": "quick brown fox" } } ] } }
5.coerce
数据并不总是干净的,在json中有些熟悉的值的类型不一定就是该数据格式定义的类型,例如json中一个字符串类型"5"表示的意思有可能就是数字类型5。coerce默认为true,elasticsearch会自动将"5"转化为5保存。
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/coerce.html#coerce
创建索引,定义文档结构:该文档中包含两个字段,都是integer类型,一个关闭coerce DELETE my_index PUT my_index { "mappings": { "my_type":{ "properties": { "number_one":{ "type": "integer" }, "number_tow":{ "type": "integer", "coerce":false } } } } }
保存数据 PUT my_index/my_type/1 { "number_one":"5" } PUT my_index/my_type/2 { "number_tow":"5" }
第一个保存成功,第二个保存失败 { "error": { "root_cause": [ { "type": "mapper_parsing_exception", "reason": "failed to parse [number_tow]" } ], "type": "mapper_parsing_exception", "reason": "failed to parse [number_tow]", "caused_by": { "type": "illegal_argument_exception", "reason": "Integer value passed as String" } }, "status": 400 }
6.copy_to
copy_to属性用于配置自定义的_all字段。换言之,就是多个字段可以合并成一个超级字段。比如,first_name和last_name可以合并为full_name字段。
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/copy-to.html
创建索引,定义文档结构,包含三个字段"first_name"、"last_name"、"full_name",将first_name和last_name的值 赋给full_name。 DELETE my_index PUT my_index { "mappings": { "my_type":{ "properties": { "first_name":{ "type": "text", "copy_to": "full_name" }, "last_name":{ "type": "text", "copy_to": "full_name" }, "full_name":{ "type": "text" } } } } }
保存数据 PUT my_index/my_type/1 { "first_name":"John", "last_name":"Smith" } GET my_index/my_type/_search { "query": { "match": { "full_name": "John Smith" } } }
查询时可以通过first_name对应的值,或者last_name对应的值也可以通过full_name查询同时对应first_name或者last_name。 { "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.5753642, "hits": [ { "_index": "my_index", "_type": "my_type", "_id": "1", "_score": 0.5753642, "_source": { "first_name": "John", "last_name": "Smith" } } ] } }
7.doc_values
doc_values是为了加快排序、聚合操作,在建立倒排索引的时候,额外增加一个列式存储映射,是一个空间换时间的做法。默认是开启的,对于确定不需要 聚合或者排序 的字段可以关闭。
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/doc-values.html#doc-values DELETE my_index PUT my_index { "mappings": { "my_type":{ "properties": { "status_code":{ "type": "keyword" }, "session_id":{ "type": "keyword", "doc_values":false } } } } }
8.dynamic
https://www.elastic.co/guide/en/elasticsearch/reference/6.1/dynamic.html
属性用于检测新发现的字段,有三个取值:
true:新发型的字段添加到映射中(默认)。
false:新检测的字段被忽略,必须显示添加新字段。
strict:如果检测到新字段就会触发异常,并拒绝保存。
定义索引 DELETE my_index PUT my_index { "mappings": { "my_type": { "dynamic":"strict", "properties": { "title":{ "type": "text" } } } } }
保存文档数据 PUT my_index/my_type/2 { "title":"this is a test", "content":"上半年上海市货币信贷运行平稳 个人住房贷款增速回落" }
因为content字段没有在mapping中定义,且设置dynamic为strict。保存是异常 { "error": { "root_cause": [ { "type": "strict_dynamic_mapping_exception", "reason": "mapping set to strict, dynamic introduction of [content] within [my_type] is not allowed" } ], "type": "strict_dynamic_mapping_exception", "reason": "mapping set to strict, dynamic introduction of [content] within [my_type] is not allowed" }, "status": 400 }
9.enabled
ELasticseaech默认会索引所有的字段,enabled设为false的字段,es会跳过字段内容,该字段只能从_source中获取,但是不可搜。
如下创建索引,插入数据 DELETE my_index PUT my_index { "mappings": { "my_type":{ "properties": { "name":{ "enabled":false } } } } } PUT my_index/my_type/1 { "name":"sean", "title":"this is a test" }
搜索name GET /my_index/_search { "query": { "match": { "name": "sean" } } }
因为name字段设置enabled为false,所以不能作为条件搜索 { "took": 8, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 0, "max_score": null, "hits": [] } }


大数据
2018-07-17 17:10:03
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hive命令行接口(CLI)提供了执行Hive QL、设置参数等功能,要启用CLI只需要在命令行下执行$HIVE_HOME/bin/hive命令。在命令下执行hive –H可以查看CLI选项,如下表所示:
-d,--define 应用于Hive命令的变量替换,如-d A=B或者--define A=B
--database 指定所使用的数据库
-e 执行命令行指定SQL
-f 执行文件中的SQL
-H,--help 打印帮助信息
-h 连接远程主机上的Hive服务器
--hiveconf 使用给定变量的值
--hivevar 应用于Hive命令的变量替换,如--hivevar A=B
-i 初始化SQL文件
-p 在指定端口上连接Hive服务器
-S,--silent
-v,--verbose
在交互式shell下启动Silent模式
Verbose模式,在控制台回显执行的SQL
当使用参数-e或者-f时,hive以batch模式执行SQL语句,当hive不带-e或者-f时,hive进入交互式模式。下面先看一些batch模式的例子,然后再学习交互式模式下的一些命令。
1. 第一个例子是在命令行下执行SQL语句。
[hadoop@hadoop ~]$ hive -e 'show tables' Logging initialized using configuration injar:file:/home/hadoop/hive-0.13.0/lib/hive-common-0.13.0.jar!/hive-log4j.properties OK page_view pokes Time taken: 1.619 seconds, Fetched: 2 row(s)

2. 第二个例子是在Silent模式下将数据转储到指定的文件中。
[hadoop@hadoop~]$ hive -S -e 'show tables' > a.txt [hadoop@hadoop~]$ cat a.txt page_view pokes

当hive不带-e或者-f时,hive进入交互式模式,在交互式模式下使用分号终止命令的输入,开始执行命令,脚本中的命令可以使用--前缀指定。交互模式下的常见命令如下表所示:
命令
描述 quit
exit 使用quit或者exit命令离开交互模式。
reset 重置配置参数为默认值
set = 设置配置参数为指定的值,注意如果拼错参数名,CLI不提示错误。
set 打印被覆盖默认值的参数。
set -v 打印所有Hadoop和Hive的配置参数。
add FILE[S] *
add JAR[S] *
add ARCHIVE[S] *
添加一个或者多个文件、jar或者归档到分布式缓存中的资源列表中。
list FILE[S]
list JAR[S]
list ARCHIVE[S]
列出已经添加到分布式缓存中的资源。
list FILE[S] *
list JAR[S] *
list ARCHIVE[S] *
检查指定的资源是否已经添加到分布式缓存中。
delete FILE[S] *
delete JAR[S] *
delete ARCHIVE[S] *
删除分布式缓存中的资源。
! 在CLI中执行shell命令。
dfs 在CLI中执行HDFS命令。
source FILE
执行Hive语句并打印结果到标准输出。
在CLI中执行脚本文件。
下面是上述命令的一下例子:
hive> setmapred.reduce.tasks=32; hive> setmapred.reduce.tasks; mapred.reduce.tasks=32 hive> dfs -ls/user/hive/; Found 1 items drwxr-xr-x - hadoop supergroup 0 2014-05-23 16:43/user/hive/warehouse hive> select* from page_view; OK Time taken: 8.06seconds hive> listfiles; /home/hadoop/a.txt hive> reset; hive> setmapred.reduce.tasks; mapred.reduce.tasks=-1

上面提到的add、list、delete命令分别向分布式缓存添加、列出和删除普通文件、jar文件和归档文件,那这些文件有什么用途呢?这些文件作为会话的额外资源在查询执行时可以使用,任何本地可访问的文件都可以添加到会话中。一旦文件被加入到会话中,Hive查询可以通过文件资源的名称引用它(在map/reduce/transform子句中)并且在执行时该资源文件在整个Hadoop集群上都是本地可用的。Hive在查询执行时使用Hadoop的分布式缓存将资源分布到集群中的所有机器上。
普通文件资源通常是像转换脚本一类的文件,该类型的文件仅仅被加入到分布式缓存中。JAR文件也会加入到classpath中,这就要求依次引用包含诸如UDF的对象。归档文件在分布时自动解压缩。
大数据
2018-07-17 16:02:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
大数据这个词也许几年前你听着还会觉得陌生,但我相信你现在听到hadoop这个词的时候你应该都会觉得“熟悉”!越来越发现身边从事hadoop开发或者是正在学习hadoop的人变多了。作为一个hadoop入门级的新手,你会觉得哪些地方很难呢?运行环境的搭建恐怕就已经足够让新手头疼。如果每一个发行版hadoop都可以做到像大快DKHadoop那样把各种环境搭建集成到一起,一次安装搞定所有,那对于新手来说将是件多么美妙的事情!
闲话扯得稍微多了点,回归整体。这篇准备给大家hadoop新入门的朋友分享一些hadoop的基础知识——hadoop家族产品。通过对hadoop家族产品的认识,进一步帮助大家学习好hadoop!同时,也欢迎大家提出宝贵意见!
一、Hadoop定义
Hadoop是一个大家族,是一个开源的生态系统,是一个分布式运行系统,是基于Java编程语言的架构。不过它最高明的技术还是HDFS和MapReduce,使得它可以分布式处理海量数据。

二、Hadoop产品

HDFS(分布式文件系统):
它与现存的文件系统不同的特性有很多,比如高度容错(即使中途出错,也能继续运行),支持多媒体数据和流媒体数据访问,高效率访问大型数据集合,数据保持严谨一致,部署成本降低,部署效率提高等,如图是HDFS的基础架构。
MapReduce/ S park/ S torm (并行计算架构):
1、数据处理方式来说分离线计算和在线计算:
角色 描述
MapReduce MapReduce常用于离线的复杂的大数据计算
Storm
Spark
Storm用于在线的实时的大数据计算,Storm的实时主要是一条一条数据处理;
可以用于离线的也可用于在线的实时的大数据计算,Spark的实时主要是处理一个个时间区域的数据,所以说Spark比较灵活。

2、数据存储位置来说分磁盘计算和内存计算:
角色 描述
MapReduce
Spark和Strom
数据存在磁盘中
数据存在内存中

Pig/Hive (Hadoop编程):
角色 描述
Pig
Hive
是一种高级编程语言,在处理半结构化数据上拥有非常高的性能,可以帮助我们缩短开发周期。
是数据分析查询工具,尤其在使用类SQL查询分析时显示出极高的性能。可以在分分钟完成ETL要一晚上才能完成的事情,这就是优势,占了先机!

HBase/Sqoop/Flume (数据导入与导出) :
角色 描述
HBase 是运行在HDFS架构上的列存储数据库,并且已经与Pig/Hive很好地集成。通过Java API可以近无缝地使用HBase。
Sqoop
Flume
设计的目的是方便从传统数据库导入数据到Hadoop数据集合(HDFS/Hive)。
设计的目的是便捷地从日志文件系统直接把数据导入到Hadoop数据集合(HDFS)中。
以上这些数据转移工具都极大地方便了使用的人,提高了工作效率,把精力专注在业务分析上。

ZooKeeper/Oozie (系统管理架构):
角色 描述
ZooKeeper
Oozie
是一个系统管理协调架构,用于管理分布式架构的基本配置。它提供了很多接口,使得配置管理任务简单化。
Oozie服务是用于管理工作流。用于调度不同工作流,使得每个工作都有始有终。这些架构帮助我们轻量化地管理大数据分布式计算架构。

Ambari/Whirr (系统部署管理):
角色 描述
Ambari
Whirr
帮助相关人员快捷地部署搭建整个大数据分析架构,并且实时监控系统的运行状况。
Whirr的主要作用是帮助快速地进行云计算开发。

Mahout (机器学习):
Mahout旨在帮助我们快速地完成高智商的系统。其中已经实现了部分机器学习的逻辑。这个架构可以让我们快速地集成更多机器学习的智能。
大数据
2018-07-17 16:01:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
XML数据 Java编程基础 88 Java高级应用 99
解析代码 import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; /** * Created by Administrator on 2018/7/7 17:14 in Beijing. */ public class DOMTest { @Test public void demo1() throws ParserConfigurationException, IOException, SAXException { //构造工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //通过工厂获得解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); //使用解析器加载xml文档 Document document = builder.parse("./src/main/resources/books.xml"); NodeList nodeList = document.getElementsByTagName("name"); for(int i = 0 ; i < nodeList.getLength() ; i++) { Node node = nodeList.item(i); //这里每个 Node 都是元素 Element element = (Element) node; //将节点转换为子类型节点 System.out.println(element.getNodeName()); System.out.println(element.getNodeType()); System.out.println(element.getNodeValue()); System.out.println(element.getTextContent()); System.out.println("---------------"); } } }
输出 name 1 null Java编程基础 --------------- name 1 null Java高级应用 --------------- Process finished with exit code 0
读取查询(Retrieve) @Test public void demo2() throws ParserConfigurationException, IOException, SAXException { //构造工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //通过工厂获得解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); //使用解析器加载xml文档 Document document = builder.parse("./src/main/resources/books.xml"); NodeList nodeList = document.getElementsByTagName("name"); for(int i = 0; i < nodeList.getLength(); i++) { Element name = (Element) nodeList.item(i); if (name.getTextContent().equals("Java编程基础")) { Element price = (Element) name.getNextSibling().getNextSibling(); System.out.println("price\t"+price.getTextContent()); } } }
输出 price 88 Process finished with exit code 0
增加(Create) @Test public void demo3() throws ParserConfigurationException, IOException, SAXException, TransformerException { //构造工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //通过工厂获得解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); //使用解析器加载xml文档 Document document = builder.parse("./src/main/resources/books.xml"); //添加节点 Element newBook = document.createElement("book"); Element newName = document.createElement("name"); newName.setTextContent("葵花宝典"); newBook.appendChild(newName); Element root = document.getDocumentElement(); root.appendChild(newBook); //回写XML TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource domSource = new DOMSource(document); //用 dom 构造数据源 StreamResult streamResult = new StreamResult(new java.io.File("./src/main/resources/books_bak.xml")); transformer.transform(domSource, streamResult); }
输出 Java编程基础 88 Java高级应用 99 葵花宝典
更新(Update) @Test public void demo4() throws ParserConfigurationException, IOException, SAXException, TransformerException { //构造工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //通过工厂获得解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); //使用解析器加载xml文档 Document document = builder.parse("./src/main/resources/books.xml"); NodeList nodeList = document.getElementsByTagName("name"); for(int i =0 ; i < nodeList.getLength() ; i++) { Element name = (Element) nodeList.item(i); if(name.getTextContent().equals("Java高级应用")) { Element price = (Element) name.getNextSibling().getNextSibling(); double money = Double.parseDouble(price.getTextContent()); money *= 1.2 ; price.setTextContent(String.valueOf(money)); } } //回写XML TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource domSource = new DOMSource(document); //用 dom 构造数据源 StreamResult streamResult = new StreamResult(new java.io.File("./src/main/resources/books_bak.xml")); transformer.transform(domSource, streamResult); }
输出 Java编程基础 88 Java高级应用 118.8
删除(Delete) @Test public void demo5() throws ParserConfigurationException, IOException, SAXException, TransformerException { //构造工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //通过工厂获得解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); //使用解析器加载xml文档 Document document = builder.parse("./src/main/resources/books.xml"); NodeList nodeList = document.getElementsByTagName("name"); for(int i =0 ; i < nodeList.getLength() ; i++) { Element name = (Element) nodeList.item(i); if(name.getTextContent().contains("Java")) { Element book = (Element) name.getParentNode(); //删除必须通过父节点 book.getParentNode().removeChild(book); i--; //修复List长度 } } //回写XML TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource domSource = new DOMSource(document); //用 dom 构造数据源 StreamResult streamResult = new StreamResult(new java.io.File("./src/main/resources/books_bak.xml")); transformer.transform(domSource, streamResult); }
输出
大数据
2018-07-19 14:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hive应用:外部表链接内部表
我们知道,Hive的外部表可以连接HDFS中的任何目录的数据,那么Hive的外部表是否可以连接本身的内部表的数据呢?
答案是肯定,当然可以连接,因为Hive本身的数据就是存放在HDFS特定的目录中的,在Hive中创建外部表,关联内部表,方式和关联HDFS的目录是一样的。此处的目录只要换成Hive内部表在HDFS中的存储位置即可,注意目录的路径不要写错了。
例如:如下图的Hive目录结构,创建一个tbl_custom的外部表。
在另一个库中使用如下的建表语句: create external table Tbl_Custom(CustomID int,AreaID int,Name string,Gender int) row format delimited fields terminated by '\t' location 'hdfs://hadoop01:9000/user/hive/warehouse/beijing.db/tbl_custom';
这样就可以在另外一个库中使用内部表的数据了。
如果你有一个业务场景,是需要连接多个MySQL数据库进行数据查询,那么你就可以备份这几个数据库到Hive或者HDFS中,然后利用外部表,将需要表格数据连接到一个库中进行操作。
下一篇: Hive应用:外部分区表
大数据
2018-07-19 14:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在互联网场景下,经常会有各种实时的数据处理,这种处理方式也就是流式计算,延迟通常也在毫秒级或者秒级,比较有代表性的几个开源框架,分别是Storm,Spark Streaming和Filnk。
伦理片 http://www.dotdy.com/
曾经在一个项目里面用过阿里改造后的JStrom,整体感受就是编程略复杂,在不使用Trident Api的时候是不能保证准确一次的数据处理的,但是能保证不丢数据,但是不保证数据重复,我们在使用期间也出现过几次问题,bolt或者worker重启时候会导致大量数据重复计算,这个问没法解决,如果想解决就得使用Trident来保证,使用比较繁琐。

最近在做一个实时流计算的项目,采用的是Spark Steaming,主要是对接Spark方便,当然后续有机会也会尝试非常具有潜力的Filnk,大致流程,就是消费kafka的数据,然后中间做业务上的一些计算,中间需要读取redis,计算的结果会落地在Hbase中,Spark2.x的Streaming能保证准确一次的数据处理,通过spark本身维护kafka的偏移量,但是也需要启用checkpoint来支持,因为你没法预料到可能出现的故障,比如断电,系统故障,或者JVM崩溃等等。
鉴于上面的种种可能,Spark Streaming需要通过checkpoint来容错,以便于在任务失败的时候可以从checkpoint里面恢复。
在Spark Streaming里面有两种类型的数据需要做checkpoint:
A :元数据信息checkpoint 主要是驱动程序的恢复
(1)配置 构建streaming应用程序的配置
(2)Dstream操作 streaming程序中的一系列Dstream操作
(3)没有完成的批处理 在运行队列中的批处理但是没有完成
B:消费数据的checkpoint
保存生成的RDD到一个可靠的存储系统中,常用的HDFS,通常有状态的数据横跨多个batch流的时候,需要做checkpoint

总结下:
元数据的checkpoint是用来恢复当驱动程序失败的场景下
而数据本身或者RDD的checkpoint通常是用来容错有状态的数据处理失败的场景

大多数场景下没有状态的数据或者不重要的数据是不需要激活checkpoint的,当然这会面临丢失少数数据的风险(一些已经消费了,但是没有处理的数据)

如何在代码里面激活checkpoint?

Java代码 // 通过函数来创建或者从已有的checkpoint里面构建StreamingContext def functionToCreateContext(): StreamingContext = { val ssc = new StreamingContext(...) // new context val rdds = ssc.socketTextStream(...) // create DStreams ... ssc.checkpoint("/spark/kmd/checkpoint") // 设置在HDFS上的checkpoint目录 //设置通过间隔时间,定时持久checkpoint到hdfs上 rdds.checkpoint(Seconds(batchDuration*5)) rdds.foreachRDD(rdd=>{ //可以针对rdd每次调用checkpoint //注意上面设置了,定时持久checkpoint下面这个地方可以不用写 rdd.checkpoint() } ) //返回ssc ssc } def main(args:Array){ // 创建context val context = StreamingContext.getOrCreate(checkpointDirectory, functionToCreateContext _) // 启动流计算 context.start() context.awaitTermination() }

启动项目之后,我们能在HDFS上看到对应目录下面的checkpoint内容


这里有有两个坑:

(1)处理的逻辑必须写在functionToCreateContext函数中,你要是直接写在main方法中,在首次启动后,kill关闭,再启动就会报错
关闭命令
Java代码 yarn application -kill application_1482996264071_34284
再次启动后报错信息
Java代码 has not been initialized when recovery from checkpoint

解决方案:将逻辑写在函数中,不要写main方法中,
(2)首次编写Spark Streaming程序中,因为处理逻辑没放在函数中,全部放在main函数中,虽然能正常运行,也能记录checkpoint数据,但是再次启动先报(1)的错误,然后你解决了,打包编译重新上传服务器运行,会发现依旧报错,这次的错误和(1)不一样:
Java代码 xxxx classs ClassNotFoundException
但令你疑惑的是明明打的jar包中包含了,这个类,上一次还能正常运行这次为啥就不能了,问题就出在checkpoint上,因为checkpoint的元数据会记录jar的序列化的二进制文件,因为你改动过代码,然后重新编译,新的序列化jar文件,在checkpoint的记录中并不存在,所以就导致了上述错误,如何解决:
也非常简单,删除checkpoint开头的的文件即可,不影响数据本身的checkpoint
Java代码 hadoop fs -rm /spark/kmd/check_point/checkpoint*
然后再次启动,发现一切ok,能从checkpoint恢复数据,然后kill掉又一次启动
就能正常工作了。

最后注意的是,虽然数据可靠性得到保障了,但是要谨慎的设置刷新间隔,这可能会影响吞吐量,因为每隔固定时间都要向HDFS上写入checkpoint数据,spark streaming官方推荐checkpoint定时持久的刷新间隔一般为批处理间隔的5到10倍是比较好的一个方式。
大数据
2018-07-19 13:43:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
7 月 6 日上午,在 ArchSummit 2018 深圳站 | 全球架构师峰会上,七牛云工程效率部技术专家宫静分享了《基于容器和大数据平台的持续交付平台》为题的演讲。本文是对演讲内容的整理。


本次分享的主要内容是基于 容器 和 大数据平台 去构建的持续交付系统,是七牛云工程效率部在持续交付、容器化方面去做的技术实践。将从以下两个方向展开:一个是容器化方向,一个是持续交付的平台。主要会结合在七牛云的实践来介绍这个持续集成、持续部署在容器化方向的探索和思考,以及未来方向的考虑。

01 业务场景的介绍
七牛云业务场景:
上图的数字其实是七牛云的业务场景的缩影,七牛云现在有六大产品线,围绕这六大核心业务,我们有 500 多个组件和服务,这个数字可能还在持续地变化,不断地上升,不断地发展。我们外部的业务需求是这样的,因为市场在快速变化,它对我们业务需求要求是有一个快速迭代的能力,快速发布的能力。工程效率部的目标是在保证质量的前提下来做到一个快速的验证和有效的发布的能力。而产品研发人员和工程效率这边是这样的一个人员比,在这样的一个人员比下,我们会遇到哪些问题呢?
02 研发团队在质量、工程效率所面临的问题总结
以下是我们研发团队面临的一个真实的问题的总结。当一个团队中的开发人员面对的是怎么样一个开发场景,开发人员要面对的是多样化的编译运行环境,要保证从代码开发到编译到运行到调试自测这样一个完整的路径覆盖,当他完成这个路径过长的时候,就会影响他的开发效率。覆盖这样一个完整的路径目前有一个什么样的挑战呢?
第一部分是我们的服务比较多的,产品结构是比较复杂的,在各种情况下调试自测的难度是增加的。首先我们开发人员并不是去独立开发,通常情况下是一个很大的团队,需要一个团队合作,这个团队中可能有多个人在并行进行开发,然后如何去有效把这些并行的开发高效的集成在一起,做到一个快速的迭代,同时合并不会对主干代码的稳定性和质量造成一个不稳定的风险。
第二部分是质量方面的问题。质量的同学面对的是复杂的测试场景,对业务产品在不同的测试环境下做多样的验证,保证我们的产品能在各种各样的复杂场景下都能正确稳定的运行。第二点,通常情况下测试资源是有限的,我们要合理地做一个规划,用有限的资源应对集成测试、性能测试等各种情况的验证需求。第三点是在面对这么复杂的场景,这么多样的业务,然后我们又怎么去把我们验证效率做提升,来满足我们快速迭代,快速发布的需求。第四点是我们现在经常提到的一点,我们如何在产品研发的整个流程,对代码状态做有效追踪,这是我们要回答的问题,也是我们日常的工作一个重要组成部分。
第三部分是微服务带来的新挑战。我们也观察到说,比如说像刚刚的服务数量的量级是因为微服务模式的实施,微服务架构给我们带来一些好处,但任何事情都是有两面性的,不能先看到它所有带来的好处,而要看到另外一面。
首先是服务拆分之后带来的架构复杂度,服务拆分以后,开发人员可能只需要去关注拆分后的服务,这部分服务它的部署运行,它的功能。但是这个服务它是服务于整个产品的,我们产品的整体由于服务拆分,它的架构变得更复杂了。我们是不是需要有一个架构人员或者是技术人员做整体的把控?当产品在做微服务模式实施的时候,我的架构复杂度不能有一个线性的或者指数性的提升。
第二点就是说,当服务拆分以后,给开发人员带来了一些便利,但是微服务模式它是否是面对整个运维部署的流程的?我们的产品虽然服务拆分了,但仍然要作为一个整体去部署运维。对于测试人员来说,我们要面对的是这样的一个整体的方面,所以我们要考虑的是产品去整体部署运维的难度。
接下来是七牛云工程效率部,在解决这些问题方面,提出的一些方向和思路。
第一个是比较容易考虑到的一点,就是要去保证我们的工具链是完备的,当开发人员从第一行代码开始,就有一个完备的工具链去支持他完成开发,完成整个的调试自测。而研发团队的一些其它角色,比如说测试人员或者运维人员,会遇到其他各种各样的需求,这都需要工具链的支持。
第二个是容器化方向,因为在这样的主题下,容器化方向是大家都在考虑的,它能给我们带来什么好处,首先我们的资源可以大幅提升它的有效利用率。第二点可以做到环境的隔离,然后保证从代码开始就在一致的环境上编译运行,它的可移植性是增强的。
第三点是主要在质量效率上要保证有持续演进的 CICD 系统,这个系统保证我们做快速的迭代,有效的验证。
第四、第五点其实是我们现在一直考虑的问题,如何把一个产品从代码开始,到最终上线去做一个有效的追踪和分析,在不同的开发流程环节中,都需要去追踪,这个功能在开发的时候,它的整体实现是什么样的、它的代码质量什么样的,不同的人有不同的代码实现质量,有的人做了有效的单测覆盖、有的人可能没有单测。对整个研发流程都需要去做打点、追踪、分析,全流程去保证作为一个团队的研发阶段到最终交付的内容是符合我们效率上、质量上的要求。
03 持续交付平台 SPOCK 介绍
下面是对前三点的实践做主要的介绍,也就是持续交付系统 SPOCK 平台。
SPOCK 平台的定位是基于容器和大数据平台的持续交付系统。
第一点是容器化方向的实施,SPOCK 平台可以做到什么。它可以做到的我们容器化测试环境的管理,当用户对测试环境有任何需求的时候可以一键部署,这个一键部署听起来是比较容易的,但是实际上我们有很多的开发维护的细节。
第二点是把容器化服务做到了模板化,它可以自由编排,当我面对的是复杂产品的结构的时候,自由编排会保证创建产品环境的灵活性。   
第三点是工作流模块化,不同角色对集成的需求是不一样的,应对这样不同的需求,把工作流各个环节做到了模块化,也就是说研发人员和测试人员,他可以根据他的需求来定制他的工作流,扩展他的工作流,用户可以通过配置来按照需要对这个工作流路径做扩展。
第四点是持续交付及系统的基本,也就是说我们需要跟研发流程中所涉及到的各个系统,比如 jira 系统、github 系统发布系统集成起来,达到代码的持续集成持续发布的目的。
第五点是统一日志服务,它是基于大数据平台构建的。
最后一点就是在持续交付流程的各个环节,我们做数据的打点和收集。这样做是为了去构建持续交付流程上的质量效率体系,数据是可以收集,可以度量的,这些收集和度量可以帮助去优化和改进研发流程,去对质量效率提出下一步的发展空间。
上图现在的持续交付流程示意图,在这个里面可以看到,我们的研发团队的不同角色,都可以去使用 SPOCK 平台,去进行它的持续集成。开发人员可能的使用流程是这样的,当开发完成一部分功能的开发,可以去手动地触发的工作流,这个工作流是在 SPOCK 平台中管理的,它可以做到基于容器化的统一构建,SPOCK 平台可以把构建镜像自动部署到容器云环境中,提供给开发同学进行调试和自测。当代码开发调试自测完成迭代之后,代码提交到代码仓库中去进行管理。在这个提交之后,我们会进入测试环节,测试人员是怎么用这个环境的呢?首先是我们的自动持续集成能力,也就是说我们这个平台集成各种系统,做到代码的自动集成和部署。
测试人员需要对整个产品或者是几个服务做一个集成的测试或者是联调的测试的时候,可以根据自己的需求,对他所维护的集成环境进行一键部署,然后通过工作流来进行集成测试的自动回归验证。在验证通过之后,就可以走到一个持续发布的流程,发布到我们的预上线系统环境。
这是 SPOCK 架构的示意图,SPOCK 平台的两个特性,第一个是容器环境的管理,第二个是在容器环境之上,做到的持续交付的实现,从右边来讲,是容器化方向的实现。SPOCK 平台中把产品抽象成产品模板进行管理,基于这些模板,可以去部署产品实例。这些部署和模板是通过我们的 K8S CTL 模块去实施的,从而在容器集群中去生效,底层就是七牛的容器云服务平台 Kirk Cluster。
上文提到,基于模块化的工作流配置做到了可定制化的交付流程,Reaper 模块执行统一的容器化构建,Warpdrive 对工作流各模块做实际的任务执行。底层还有 SPOCK 涉及到的一些其它服务,比如我们的镜像仓库和对象存储,基于这些基础服务对整个持续交付过程都做到了的软件管理,所以对整个过程都是可追踪的,任何构建的交付对象都在我们的对象存储中去进行跟踪、存储和管理。中间的 Pandora 服务是七牛大数据平台,基于大数据平台对我们整个持续交付流程和测试环境集群做数据的统一收集和分析,最终可以在 Pandora 大数据平台上进行数据分析。
04 产品实施
下面介绍测试环境容器化具体的实施,换句话就是怎么对测试环境做到一键部署。
当我去问一个技术人员说,你怎么把你的服务容器化的时候,技术人员可能他的回答是比较简单的:写一个 Dockerfile 为服务编译容器镜像,底层是 kubernetes 容器编排系统,写一个 yml 文件,描述这个容器怎么在我的集群里面去部署、如何启动运行、服务的伸缩、服务的更新、服务的维护、它的策略是什么样的。并且我去声明服务在这个容器集群里面需要的资源,它的网络和存储。然后通过工具脚本来自动创建,自动更新维护。
当一个服务,或者是几个服务的时候,事情好像就是这么简单,但是当要面临的是几十个服务、上百个服务的时候,事情就变得不那么简单了。我要面对的不仅是服务量级上的变化,我的服务在不同的环境下,不同的场景下,它的配置可能是不一样的,它的运行环境可能是不一样的,它所需要的资源也不一样,比如说我的存储声明在测试环境和在生产环境下是不一样的,同时不同的研发人员需要部署产品结构也是不一样的。要怎么把这些差异在我们平台中去有效地管理和维护起来呢?这个就是 SPOCK 在容器化方向做的一些探索。
首先可以看到的是这个场景,这些小的圆圈圈可能是一个容器服务,或者是我们比较熟悉 K8S 的,认为它是一个服务的最小单元 POD,单个服务就是一个容器镜像加一个部署描述 yml 文件,再加一些配置文件。我们怎么把这些零散的服务把它给组织起来,做到根据需求的差异化部署,提供给开发人员呢?不同的开发人员对环境的要求差异很大,开发只需要关注他负责开发的那一块就够了。它可能有共同的依赖或者是类似的服务需求,又要部署自己正在开发调试的服务。
第二个场景,我们可能看到的是产品结构层级上更复杂一点,要部署的服务之间,有先后启动顺序关系,有服务的依赖,最终作为一个整体的产品去运维和部署。这个主要是在调研的时候,发现的是测试人员的集成测试环境,它对这样的需求会比较多一些。
应对这样的不同环境,我们是怎么去实施的呢?
首先我们把这些单个的服务,给抽取出来,定义一个服务的模板。一个服务的模板包括的是描述这个服务的模板文件,以及这个服务它自身所需要的一些配置文件。简单来说就是一个 YAML 文件,加一些 Config 文件,服务可以按照业务逻辑进行组织成服务组,一个服务组里面的服务,它是没有先后启动顺序的,它是并行的,只是把一个服务放到了一个服务组单元,我们可以对同一个单元下的服务做统一管理。但是当我有层级关系,依赖关系的时候,有先后启动顺序的时候,在服务组织上又有个整体的产品结构概念,这个产品结构把不同的服务组做一个层级的管理。比如说我先起第一级别的服务组,再起第二级别的服务组,再起第三层级的服务组,服务组之间是按照顺序去先后启动,它是有一个依赖关系的。
对应到我们真正的产品上,现实是什么样的呢?一个实际案例是要去部署一个带数据库服务的架构的时候。首先我可能要去定义这个产品结构,我把产品中的服务做一个抽象,做一个隔离,做了一个拆分。抽象出前端的服务、后端服务、数据库服务,我们把这些前端服务、后端服务、数据库服务都模块化,作为服务模块去管理。
编排的时候把服务按照逻辑编排到服务组,服务组内部都是平行的,同一服务组没有先后的启动顺序。但是当我要整体的去部署这个产品的时候,我可能先去部署数据库服务组,然后再去部署我的后端服务组,最后再去部署我的前端服务组,当我的所有服务组都部署完成之后,我们整个产品就可以对外提供一个可访问域名,然后作为一个整体去运行起来。
另外是我们在实施中发现可能还会遇到一些实际问题,比我们想象的更要复杂,是不是所有的服务都在容器化的环境下去运行。我们首先要明确一点,我们把服务做容器化的时候,前面描述的是服务怎么去运行,怎么去部署,但是实际我们在做容器化方案的时候,我们要考虑的一点就是容器化不等于把一个服务从物理级直接迁移容器上面去部署运行,因为我们在物理级部署和容器部署这两个不同的部署架构上,其实在服务场景是有差异化的。
什么样的服务适合在物理级上部署,什么样的服务适合在容器化的环境下部署,在目前的这个容器服务上或者是现有的解决方案上,一些特殊服务是不是还不能做到在容器环境中提供等同的服务支持。所以我们在实际做这个容器化方案的时候,我们需要去做一个整体的评估和审核,拿到一个产品去做容器化的时候,要去进行设计,我这个容器化环境提供的是什么样的业务场景、提供的是什么样的服务能力、它是否是跟我们物理级部署提供的一样的目的和一样的场景支持。同时我们要对容器化的范围也做一个界定,什么样的服务做容器化,什么样的服务不做容器化,这个是要根据我们实践和方案评估来确定的。
在实践中遇到了这样的情况,也就是说一些服务我们判断它在现有的方案下不适合做容器化,那么怎么做到说,去提供一个可以一键部署的测试环境呢,这里是 SPOCK 的解决方案,我们把物理级部署和容器部署做一个打通,也就是说在 SPOCK 去进行一键部署的时候,物理机部署和容器服务部署同时支持的,服务之间可能有物理级部署和服务部署两部分,然后它们之间可以互相依赖,这样的方案有一个前提,然后也有很多的技术细节。首先 SPOCK 平台需要把已有的物理级部署方式在平台上实现,并且对接。然后我们需要去梳理我们的服务依赖关系,通过统一的配置管理和统一的规划来进行部署实施。
接下来就是我们实施中遇到的一个日志方面的实际问题,比如说当我的 容器服务 去重启伸缩的时候,它的日志,到底是怎么管理去收集的,这个我们现在的实施是基于大数据平台做一个统一的日志收集和分析的服务,也就是说我跑在这个容器集群上面的所有测试环境,我都基于这个大数据平台它的数据源收集的组件去进行数据收集,数据收集之后,我们的日志数据其实不是存储在容器集群的存储当中的,我们现在把它统一放在数据平台上做日志管理,通过这个日志管理我们也发现可以有一些新的创新点,当日志统一收集起来之后,它可以做到的是,它可以做更高效的分析,可以定制化的搜索,业务报警,这是我们现在发现,通过这个解决方案给我们带来的一些好处。
这个大数据平台也不只是去收集我们的统一日志服务,前面可以看到我们的系统定位是一个持续交付的系统,持续交付也就是在代码的初始阶段就进入了这个系统,但是在这个流程持续运行的时候,各个环节它的代码质量到底是什么样的,我们需要去观察、去收集、去度量,也就是说在代码进入到持续交付系统之后,就对每个环节做一个数据收集,在代码的构建阶段、单测阶段、部署阶段、集成测试、分发阶段,都去收集各种有效数据,去把它给放到我们数据平台。
这个数据做什么用呢?这个数据作为我们一个质量效率体系的数据基础,这个数据基础可以去有效度量代码在进入持续交付、持续迭代的流程中,它到底是处于一个什么状态的,是否在某一个环节,或者是某几个环节,有一个比较好的可改进的空间。
这里给了一个图,是我们持续交付流程中收集的系统测试的覆盖率。我们能评估到每一次开发提交代码,系统测试是否有效覆盖到了代码,它有一个整体的评估。代码提交后自动触发这些自动进行持续集成和持续构建的流程,在这个过程中,这些数据就自动进入我们的质量效率体系,生成度量指标用于评估。
05 关于未来的挑战和思考
最后介绍的是我们未来考虑的一个方向。
这个方向比较大,就是我们希望把测试服务化,对研发团队进行开放。现在做到的是测试环境,作为一个一键部署的服务去提供开放,之后希望把所有的这些持续交付的流程环节都把它给模块化、服务化,作为测试服务提供出去。这样所有开发人员都是可以通过服务化的方式,去使用我们的测试体系、质量体系。
以上是今天全部的内容分享。
关注公众号【七牛云】,了解更多信息哦~
大数据
2018-07-19 11:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
JupyterLab安装地图插件
(本文所述软件还在发展之中,欢迎加入开源项目,提供建议、测试和开发。)
在 Jupyter 中进行数据分析时,往往需要将数据叠加到地图上。简单的地图需求可以利用matplotlib/echarts/bokeh的地图显示功能,更为复杂的可以使用SuperMap/Leaflet/GMaps提供的地图插件,实现交互的地图操作,可以显示和操作大比例尺的专业级地图,甚至调用专业GIS服务进行空间分析,当然这操作起来也会更复杂一些。
上图来自于Kubernetes和JupyterLab支持, https://github.com/openthings/iclient-python
1、iClientPy
iClientPy是由超图软件公司(http://www.supermap.com)开发的一个开源项目,可以将SuperMap iServer和Online服务通过python API访问,并支持Jupyter的编程和集成化分析。目前该项目还在开发之中,尚未形成发布版本(预计2018年底发布)。项目地址如下: 文档, http://iclientpy.supermap.io/geticlientpy.html 源码, https://github.com/SuperMap/iclient-python

由于conda安装国内访问较慢,可以加入镜像站,配置第三方库的依赖源。这里使用清华大学的源(也可是国内其他镜像源): conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --set show_channel_urls yes
执行下面命令: conda install -y -c http://iclientpy.supermap.io/conda/channel iclientpy
目前安装后地图显示不出来,还需要改进。
2、ipyleaflet
ipyleaflet是基于leaflet的地图显示模块,提供了jupyterlab支持。 ipyleaflet文档, https://ipyleaflet.readthedocs.io/en/latest/installation.html#jupyterlab-extension GeoJson绘制, https://github.com/jupyterlab/jupyter-renderers/tree/master/packages/geojson-extension 流体地图可视化, https://github.com/benbovy/jupyterlab_velocity
安装到jupyterlab,执行: conda install -c conda-forge nodejs=8.10.0 conda install -c conda-forge ipyleaflet jupyter labextension install jupyter-leaflet
一些用户发现需要 jupyterlab-manager,才能显示地图。 查看 issue 173 和 issue 168 了解更多细节。
使用命令 jupyter labextension list 查看 jupyter-widgets 是否安装。如果没有,安装方法如下: jupyter labextension install @jupyter-widgets/jupyterlab-manager
目前我在Kubernetes里安装jupyterlab,使用的all-in-one-spark的jupyter镜像,但是安装后地图显示不出来,还需要改进。
倒腾了一番后,运行nodejs降级到10以下,运行 jupyter lab build ,然后就可以显示了。不过发现,Ubuntu上的 FireFox还是显示不出来,通过MacOS上的FireFox却是可以的。
3、gmaps
是基于Google Maps的地图显示插件,需要google maps API的访问token。 文档, https://jupyter-gmaps.readthedocs.io/en/latest/ 源码, https://github.com/pbugnion/gmaps
为了在JupyterLab中使用jupyter-gmaps需要安装 jupyter widgets 扩展。 $ jupyter labextension install @jupyter-widgets/jupyterlab-manager
然后通过pip安装jupyter-gmaps: $ pip install gmaps
下次启动 JupyterLab时,会提示需要 “rebuild JupyterLab: this is necessary to include the jupyter-gmaps frontend code into your JupyterLab installation”。
在命令行运行: $ jupyter lab build
因为国内访问网络原因和需要token,暂时还没有测试过。
4、jupyter-renderer
jupyter-renderer是jupyter官方的一个渲染架构,用于绘制更多类型的cell输出。 https://github.com/jupyterlab/jupyter-renderers
目前GeoJSON已经可以支持用于绘图了,主要是基于leaflet的。 GeoJSON extension https://github.com/jupyterlab/jupyter-renderers/tree/master/packages/geojson-extension jupyter labextension install @jupyterlab/geojson-extension 流体地图显示 https://github.com/benbovy/jupyterlab_velocity https://github.com/benbovy/xvelmap jupyter labextension install jupyterlab_velocity
同样的问题,在JupyterLab里图显示不出来。
5、MapBox
将MapBox嵌入到Notebook中进行地图显示。参见: https://www.ryanbaumann.com/blog/2016/4/3/embedding-mapbox-plots-in-jupyter-notebooks
6、查看安装信息
启用nbextension扩展: !jupyter nbextension enable --py widgetsnbextension --sys-prefix !jupyter nbextension enable --py --sys-prefix ipyleaflet
查看nbextension扩展列表: !jupyter nbextension list
查看labextension扩展列表: !jupyter labextension list
最后需要运行 jupyter lab build,才能真正启用扩展。
不过,我在这一步运行时,一直处于运行状态,然后显示地图时也显示不出来,好像提示webpack的“_.extend is not a function”,后面再慢慢研究。
尝试修复bug: npm i -y leaflet@~1.0.3 npm i -y jupyter-leaflet
将“parallel: true,”改为“parallel: false,”,如下: ovyan@jupyter-supermap:/opt/conda/share/jupyter/lab/staging$ cat webpack.prod.config.js var UglifyJSPlugin = require('uglifyjs-webpack-plugin'); var merge = require('webpack-merge'); var webpack = require('webpack'); var common = require('./webpack.config'); module.exports = merge(common, { devtool: 'source-map', plugins: [ new UglifyJSPlugin({ sourceMap: true, parallel: true, uglifyOptions: { beautify: false, ecma: 6, mangle: true, compress: false, comments: false, } }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }) ] }); jovyan@jupyter-supermap:/opt/conda/share/jupyter/lab/staging$ nano webpack.prod.config.js
对于上面的问题,在互联网上搜索一番无果,然后到 https://github.com/jupyter-widgets/ipyleaflet 上提了个issue,晚上得到了 jasongrout 的回答,说 “Use node version < 10. JupyterLab does not work with node 10 currently.”,然后执行 conda install nodejs=8.10.0 从 10.4.0 降级下来,再安装就ok了。真是“踏遍世界无觅处,得来也需费功夫”,难者不会、会者不难啊!
大数据
2018-07-18 22:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
The Spark Streaming integration for Kafka 0.10 is similar in design to the 0.8 Direct Stream approach . It provides simple parallelism, 1:1 correspondence between Kafka partitions and Spark partitions, and access to offsets and metadata. However, because the newer integration uses the new Kafka consumer API instead of the simple API, there are notable differences in usage. This version of the integration is marked as experimental, so the API is potentially subject to change.
Linking
For Scala/Java applications using SBT/Maven project definitions, link your streaming application with the following artifact (see Linking section in the main programming guide for further information). groupId = org.apache.spark artifactId = spark-streaming-kafka-0-10_2.11 version = 2.3.1
Do not manually add dependencies on org.apache.kafka artifacts (e.g. kafka-clients ). The spark-streaming-kafka-0-10 artifact has the appropriate transitive dependencies already, and different versions may be incompatible in hard to diagnose ways.
Creating a Direct Stream
Note that the namespace for the import includes the version, org.apache.spark.streaming.kafka010 Scala Java import org.apache.kafka.clients.consumer.ConsumerRecord import org.apache.kafka.common.serialization.StringDeserializer import org.apache.spark.streaming.kafka010._ import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe val kafkaParams = Map[String, Object]( "bootstrap.servers" -> "localhost:9092,anotherhost:9092", "key.deserializer" -> classOf[StringDeserializer], "value.deserializer" -> classOf[StringDeserializer], "group.id" -> "use_a_separate_group_id_for_each_stream", "auto.offset.reset" -> "latest", "enable.auto.commit" -> (false: java.lang.Boolean) ) val topics = Array("topicA", "topicB") val stream = KafkaUtils.createDirectStream[String, String]( streamingContext, PreferConsistent, Subscribe[String, String](topics, kafkaParams) ) stream.map(record => (record.key, record.value))
Each item in the stream is a ConsumerRecord
For possible kafkaParams, see Kafka consumer config docs . If your Spark batch duration is larger than the default Kafka heartbeat session timeout (30 seconds), increase heartbeat.interval.ms and session.timeout.ms appropriately. For batches larger than 5 minutes, this will require changing group.max.session.timeout.ms on the broker. Note that the example sets enable.auto.commit to false, for discussion see Storing Offsets below.
LocationStrategies
The new Kafka consumer API will pre-fetch messages into buffers. Therefore it is important for performance reasons that the Spark integration keep cached consumers on executors (rather than recreating them for each batch), and prefer to schedule partitions on the host locations that have the appropriate consumers.
In most cases, you should use LocationStrategies.PreferConsistent as shown above. This will distribute partitions evenly across available executors. If your executors are on the same hosts as your Kafka brokers, use PreferBrokers , which will prefer to schedule partitions on the Kafka leader for that partition. Finally, if you have a significant skew in load among partitions, use PreferFixed . This allows you to specify an explicit mapping of partitions to hosts (any unspecified partitions will use a consistent location).
The cache for consumers has a default maximum size of 64. If you expect to be handling more than (64 * number of executors) Kafka partitions, you can change this setting via spark.streaming.kafka.consumer.cache.maxCapacity .
If you would like to disable the caching for Kafka consumers, you can set spark.streaming.kafka.consumer.cache.enabled to false . Disabling the cache may be needed to workaround the problem described in SPARK-19185. This property may be removed in later versions of Spark, once SPARK-19185 is resolved.
The cache is keyed by topicpartition and group.id, so use a separate group.id for each call to createDirectStream .
ConsumerStrategies
The new Kafka consumer API has a number of different ways to specify topics, some of which require considerable post-object-instantiation setup. ConsumerStrategies provides an abstraction that allows Spark to obtain properly configured consumers even after restart from checkpoint.
ConsumerStrategies.Subscribe , as shown above, allows you to subscribe to a fixed collection of topics. SubscribePattern allows you to use a regex to specify topics of interest. Note that unlike the 0.8 integration, using Subscribe or SubscribePattern should respond to adding partitions during a running stream. Finally, Assign allows you to specify a fixed collection of partitions. All three strategies have overloaded constructors that allow you to specify the starting offset for a particular partition.
If you have specific consumer setup needs that are not met by the options above, ConsumerStrategy is a public class that you can extend.
Creating an RDD
If you have a use case that is better suited to batch processing, you can create an RDD for a defined range of offsets. Scala Java // Import dependencies and create kafka params as in Create Direct Stream above val offsetRanges = Array( // topic, partition, inclusive starting offset, exclusive ending offset OffsetRange("test", 0, 0, 100), OffsetRange("test", 1, 0, 100) ) val rdd = KafkaUtils.createRDD[String, String](sparkContext, kafkaParams, offsetRanges, PreferConsistent)
Note that you cannot use PreferBrokers , because without the stream there is not a driver-side consumer to automatically look up broker metadata for you. Use PreferFixed with your own metadata lookups if necessary.
Obtaining Offsets Scala Java stream.foreachRDD { rdd => val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges rdd.foreachPartition { iter => val o: OffsetRange = offsetRanges(TaskContext.get.partitionId) println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}") } }
Note that the typecast to HasOffsetRanges will only succeed if it is done in the first method called on the result of createDirectStream , not later down a chain of methods. Be aware that the one-to-one mapping between RDD partition and Kafka partition does not remain after any methods that shuffle or repartition, e.g. reduceByKey() or window().
Storing Offsets
Kafka delivery semantics in the case of failure depend on how and when offsets are stored. Spark output operations are at-least-once . So if you want the equivalent of exactly-once semantics, you must either store offsets after an idempotent output, or store offsets in an atomic transaction alongside output. With this integration, you have 3 options, in order of increasing reliability (and code complexity), for how to store offsets.
Checkpoints
If you enable Spark checkpointing , offsets will be stored in the checkpoint. This is easy to enable, but there are drawbacks. Your output operation must be idempotent, since you will get repeated outputs; transactions are not an option. Furthermore, you cannot recover from a checkpoint if your application code has changed. For planned upgrades, you can mitigate this by running the new code at the same time as the old code (since outputs need to be idempotent anyway, they should not clash). But for unplanned failures that require code changes, you will lose data unless you have another way to identify known good starting offsets.
Kafka itself
Kafka has an offset commit API that stores offsets in a special Kafka topic. By default, the new consumer will periodically auto-commit offsets. This is almost certainly not what you want, because messages successfully polled by the consumer may not yet have resulted in a Spark output operation, resulting in undefined semantics. This is why the stream example above sets “enable.auto.commit” to false. However, you can commit offsets to Kafka after you know your output has been stored, using the commitAsync API. The benefit as compared to checkpoints is that Kafka is a durable store regardless of changes to your application code. However, Kafka is not transactional, so your outputs must still be idempotent. Scala Java stream.foreachRDD { rdd => val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges // some time later, after outputs have completed stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges) }
As with HasOffsetRanges, the cast to CanCommitOffsets will only succeed if called on the result of createDirectStream, not after transformations. The commitAsync call is threadsafe, but must occur after outputs if you want meaningful semantics.
Your own data store
For data stores that support transactions, saving offsets in the same transaction as the results can keep the two in sync, even in failure situations. If you’re careful about detecting repeated or skipped offset ranges, rolling back the transaction prevents duplicated or lost messages from affecting results. This gives the equivalent of exactly-once semantics. It is also possible to use this tactic even for outputs that result from aggregations, which are typically hard to make idempotent. Scala Java // The details depend on your data store, but the general idea looks like this // begin from the the offsets committed to the database val fromOffsets = selectOffsetsFromYourDatabase.map { resultSet => new TopicPartition(resultSet.string("topic"), resultSet.int("partition")) -> resultSet.long("offset") }.toMap val stream = KafkaUtils.createDirectStream[String, String]( streamingContext, PreferConsistent, Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets) ) stream.foreachRDD { rdd => val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges val results = yourCalculation(rdd) // begin your transaction // update results // update offsets where the end of existing offsets matches the beginning of this batch of offsets // assert that offsets were updated correctly // end your transaction }
SSL / TLS
The new Kafka consumer supports SSL . To enable it, set kafkaParams appropriately before passing to createDirectStream / createRDD . Note that this only applies to communication between Spark and Kafka brokers; you are still responsible for separately securing Spark inter-node communication. Scala Java val kafkaParams = Map[String, Object]( // the usual params, make sure to change the port in bootstrap.servers if 9092 is not TLS "security.protocol" -> "SSL", "ssl.truststore.location" -> "/some-directory/kafka.client.truststore.jks", "ssl.truststore.password" -> "test1234", "ssl.keystore.location" -> "/some-directory/kafka.client.keystore.jks", "ssl.keystore.password" -> "test1234", "ssl.key.password" -> "test1234" )
Deploying
As with any Spark applications, spark-submit is used to launch your application.
For Scala and Java applications, if you are using SBT or Maven for project management, then package spark-streaming-kafka-0-10_2.11 and its dependencies into the application JAR. Make sure spark-core_2.11 and spark-streaming_2.11 are marked as provided dependencies as those are already present in a Spark installation. Then use spark-submit to launch your application (see Deploying section in the main programming guide).
大数据
2018-07-18 20:48:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
Kafka 0.10的Spark Streaming集成设计与0.8 Direct Stream方法类似。 它提供了简单的并行性,Kafka分区和Spark分区之间的1:1对应关系,以及对偏移量和元数据的访问。 但是,由于较新的集成使用新的Kafka消费者API而不是简单的API,所以在使用上存在显着差异。 这个版本的集成被标记为实验,所以API可能会有变化。
LINK(依赖)
对于使用SBT / Maven项目定义的Scala / Java应用程序,请将您的流应用程序与以下工件链接起来。 1 groupId = org.apache.spark 2 artifactId = spark-streaming-kafka-0-10_2.11 3 version = 2.2.0
不要在org.apache.kafka构件上手动添加依赖项(例如kafka-clients)。 spark-streaming-kafka-0-10已经具有适当的传递依赖性,不同的版本可能在诊断的方式上不兼容。
Creating a Direct Stream
请注意,导入的名称空间包含版本org.apache.spark.streaming.kafka010
1 import org.apache.kafka.clients.consumer.ConsumerRecord 2 import org.apache.kafka.common.serialization.StringDeserializer 3 import org.apache.spark.streaming.kafka010._ 4 import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent 5 import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe 6 7 val kafkaParams = Map[String, Object]( 8 "bootstrap.servers" -> "localhost:9092,anotherhost:9092", 9 "key.deserializer" -> classOf[StringDeserializer], 10 "value.deserializer" -> classOf[StringDeserializer], 11 "group.id" -> "use_a_separate_group_id_for_each_stream", 12 "auto.offset.reset" -> "latest", 13 "enable.auto.commit" -> (false: java.lang.Boolean) 14 ) 15 16 val topics = Array("topicA", "topicB") 17 val stream = KafkaUtils.createDirectStream[String, String]( 18 streamingContext, 19 PreferConsistent, 20 Subscribe[String, String](topics, kafkaParams) 21 ) 22 23 stream.map(record => (record.key, record.value))
流中的每个项目都是一个ConsumerRecord(消费者记录)
有关可能的kafkaParams,请参阅Kafka使用者配置文档。 如果您的Spark批处理持续时间大于默认Kafka心跳会话超时(30秒),请适当增加heartbeat.interval.ms和session.timeout.ms。 对于大于5分钟的批次,这将需要更改代理上的group.max.session.timeout.ms。 请注意,该示例将enable.auto.commit设置为false,有关讨论,请参阅下面的“存储偏移量”。
LocationStrategies(位置策略)
新的Kafka消费者API将预取消息到缓冲区中。因此,性能方面的原因是Spark集成将缓存的消费者保留在执行者上(而不是为每个批次重新创建它们),并且倾向于在具有相应使用者的主机位置上调度分区。
在大多数情况下,您应该使用LocationStrategies.PreferConsistent,如上所示。这会将分区平均分配给可用的执行者。如果您的执行者(executors )与您的Kafka经纪人(brokers)位于同一个主机上,请使用PreferBrokers,它将优先为该分区的Kafka领导安排分区。最后,如果分区间负载存在明显偏移,请使用PreferFixed。这允许您指定分区到主机的明确映射(任何未指定的分区将使用一致的位置)。
消费者缓存的默认最大大小为64.如果您希望处理多于(执行者数量为64 *)的Kafka分区,则可以通过spark.streaming.kafka.consumer.cache.maxCapacity更改此设置。
如果您想禁用Kafka使用者的缓存,则可以将spark.streaming.kafka.consumer.cache.enabled设置为false。可能需要禁用缓存来解决SPARK-19185中描述的问题。 SPARK-19185解决后,该属性可能会在更高版本的Spark中删除。
缓存由topicpartition和group.id键入,因此每次调用createDirectStream时都要使用一个单独的group.id。
ConsumerStrategies(消费者策略)
新的Kafka消费者API有许多不同的方式来指定主题,其中一些需要大量的后对象实例化设置。 ConsumerStrategies提供了一个抽象,允许Spark从检查点重新启动后即可获得正确配置的使用者。
ConsumerStrategies.Subscribe,如上所示,允许您订阅固定的主题集合。 SubscribePattern允许您使用正则表达式来指定感兴趣的主题。 请注意,与0.8集成不同,使用Subscribe或SubscribePattern应该在正在运行的流中添加分区。 最后,Assign允许你指定一个固定的分区集合。 所有这三种策略都有重载的构造函数,允许您指定特定分区的起始偏移量。
如果您具有以上选项不能满足的特定消费者设置需求,则ConsumerStrategy是可以扩展的公共类。
Creating an RDD
如果您有一个更适合批处理的用例,则可以为已定义的偏移范围创建一个RDD。
1 // Import dependencies and create kafka params as in Create Direct Stream above 2 3 val offsetRanges = Array( 4 // topic, partition, inclusive starting offset, exclusive ending offset 5 OffsetRange("test", 0, 0, 100), 6 OffsetRange("test", 1, 0, 100) 7 ) 8 9 val rdd = KafkaUtils.createRDD[String, String](sparkContext, kafkaParams, offsetRanges, PreferConsistent)
请注意,您不能使用PreferBrokers,因为如果没有流,则不存在驱动程序方面的消费者为您自动查找代理元数据。 如有必要,请使用PreferFixed与您自己的元数据查找。
Obtaining Offsets
1 stream.foreachRDD { rdd => 2 val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges 3 rdd.foreachPartition { iter => 4 val o: OffsetRange = offsetRanges(TaskContext.get.partitionId) 5 println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}") 6 } 7 }
请注意,HasOffsetRanges的类型转换只会在createDirectStream的结果中调用的第一个方法中完成,而不是稍后向下的一系列方法。 请注意,RDD分区与Kafka分区之间的一对一映射在任何混洗或重新分区的方法(例如, reduceByKey()或window()。
Storing Offsets
Kafka交付语义在失败的情况下取决于如何以及何时存储偏移量。 spark输出操作至少一次。 所以,如果你想要相当于一次的语义,你必须在幂等输出之后存储偏移量,或者在输出中存储原子事务的偏移量。 通过这种集成,您可以选择3个选项,以提高可靠性(和代码复杂度),以便如何存储偏移量。
Checkpoints
如果启用了Spark检查点,偏移量将被存储在检查点中。 这很容易启用,但也有缺点。 你的输出操作必须是幂等的,因为你将得到重复的输出; 转换不是一种选择。 此外,如果应用程序代码已更改,则无法从检查点恢复。 对于计划升级,可以通过在旧代码的同时运行新代码来缓解这种情况(因为无论如何输出需要是幂等的,它们不应该发生冲突)。 但是对于需要更改代码的意外故障,除非您有另外的方法来识别已知的良好起始偏移量,否则将会丢失数据。
Kafka itself
Kafka有一个偏移提交API,用于在特殊的Kafka主题中存储偏移量。 默认情况下,新的使用者将定期自动提交偏移量。 这几乎肯定不是你想要的,因为由消费者成功轮询的消息可能还没有导致Spark输出操作,导致未定义的语义。 这就是为什么上面的流示例将“enable.auto.commit”设置为false的原因。 但是,在知道输出已存储之后,可以使用commitAsync API将偏移量提交给Kafka。 与检查点相比,好处在于,无论应用程序代码如何变化,Kafka都是耐用的商店。 然而,Kafka不是转换型的,所以你的输出仍然是幂等的。 1 stream.foreachRDD { rdd => 2 val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges 3 4 // some time later, after outputs have completed 5 stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges) 6 }
和HasOffsetRanges一样,如果调用createDirectStream的结果,而不是在转换之后,转换为CanCommitOffsets将会成功。 commitAsync调用是线程安全的,但是如果你想要有意义的语义,则必须在输出之后进行。
Your own data store
对于支持事务的数据存储,即使在出现故障的情况下,也可以在同一个事务中保存偏移量,以保持两者同步。 如果仔细检测重复或跳过的偏移量范围,回滚事务将防止重复或丢失的消息影响结果。 这给出了相当于一次的语义。 甚至可以使用这种策略,即使对于通常难以产生幂等性的聚合产生的输出也是如此。
1 // The details depend on your data store, but the general idea looks like this 2 3 // begin from the the offsets committed to the database 4 val fromOffsets = selectOffsetsFromYourDatabase.map { resultSet => 5 new TopicPartition(resultSet.string("topic"), resultSet.int("partition")) -> resultSet.long("offset") 6 }.toMap 7 8 val stream = KafkaUtils.createDirectStream[String, String]( 9 streamingContext, 10 PreferConsistent, 11 Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets) 12 ) 13 14 stream.foreachRDD { rdd => 15 val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges 16 17 val results = yourCalculation(rdd) 18 19 // begin your transaction 20 21 // update results 22 // update offsets where the end of existing offsets matches the beginning of this batch of offsets 23 // assert that offsets were updated correctly 24 25 // end your transaction 26 }
SSL / TLS
新的Kafka使用者支持SSL。 要启用它,请在传递给createDirectStream / createRDD之前适当地设置kafkaParams。 请注意,这仅适用于Spark和Kafkabroker之间的沟通; 您仍负责单独确保Spark节点间通信。
1 val kafkaParams = Map[String, Object]( 2 // the usual params, make sure to change the port in bootstrap.servers if 9092 is not TLS 3 "security.protocol" -> "SSL", 4 "ssl.truststore.location" -> "/some-directory/kafka.client.truststore.jks", 5 "ssl.truststore.password" -> "test1234", 6 "ssl.keystore.location" -> "/some-directory/kafka.client.keystore.jks", 7 "ssl.keystore.password" -> "test1234", 8 "ssl.key.password" -> "test1234" 9 )
Deploying
与任何Spark应用程序一样,spark-submit用于启动您的应用程序。
对于Scala和Java应用程序,如果您使用SBT或Maven进行项目管理,请将spark-streaming-kafka-0-10_2.11及其依赖项打包到应用程序JAR中。 确保spark-core_2.11和spark-streaming_2.11被标记为提供的依赖关系,因为这些已经存在于Spark安装中。 然后使用spark-submit启动您的应用程序(请参阅主编程指南中的部署)。
大数据
2018-07-18 20:33:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
昨天在写一个java消费kafka数据的实例,明明设置auto.offset.reset为earliest,但还是不从头开始消费,官网给出的含义太抽象了。
earliest : automatically reset the offset to the earliest offset,自动将偏移量置为最早的。难道不是topic中各分区的开始?结果还真不是,具体含义如下:
auto.offset.reset值含义解释 earliest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
latest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
none
topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
以下为测试详细:
1.同分组下测试
1.1测试一
1.1.1测试环境
Topic为lsztopic7,并生产30条信息。lsztopic7详情:
创建组为“testtopi7”的consumer,将enable.auto.commit设置为false,不提交offset。依次更改auto.offset.reset的值。此时查看offset情况为:
1.1.2测试结果
earliest
客户端读取30条信息,且各分区的offset从0开始消费。
latest
客户端读取0条信息。
none
抛出NoOffsetForPartitionException异常。
1.1.3测试结论 新建一个同组名的消费者时,auto.offset.reset值含义:
earliest 每个分区是从头开始消费的。
none 没有为消费者组找到先前的offset值时,抛出异常
1.2测试二
1.2.1测试环境
测试场景一下latest时未接受到数据,保证该消费者在启动状态,使用生产者继续生产10条数据,总数据为40条。
1.2.2测试结果
latest
客户端取到了后生产的10条数据
1.2.3测试结论 当创建一个新分组的消费者时,auto.offset.reset值为latest时,表示消费新的数据(从consumer创建开始,后生产的数据),之前产生的数据不消费。
1.3测试三
1.3.1测试环境
在测试环境二,总数为40条,无消费情况下,消费一批数据。运行消费者消费程序后,取到5条数据。
即,总数为40条,已消费5条,剩余35条。
1.3.2测试结果
earliest
消费35条数据,即将剩余的全部数据消费完。
latest
消费9条数据,都是分区3的值。
offset:0 partition:3
offset:1 partition:3
offset:2 partition:3
offset:3 partition:3
offset:4 partition:3
offset:5 partition:3
offset:6 partition:3
offset:7 partition:3
offset:8 partition:3
none
抛出NoOffsetForPartitionException异常。
1.3.3测试结论 earliest 当分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费。
latest 当分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据。
none 当该topic下所有分区中存在未提交的offset时,抛出异常。
1.4测试四
1.4.1测试环境
再测试三的基础上,将数据消费完,再生产10条数据,确保每个分区上都有已提交的offset。
此时,总数为50,已消费40,剩余10条
1.4.2测试结果
none
消费10条信息,且各分区都是从offset开始消费
offset:9 partition:3
offset:10 partition:3
offset:11 partition:3
offset:15 partition:0
offset:16 partition:0
offset:17 partition:0
offset:18 partition:0
offset:19 partition:0
offset:20 partition:0
offset:5 partition:2
1.4.3测试结论 值为none时,topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常。
2.不同分组下测试
2.1测试五
2.1.1测试环境
在测试四环境的基础上:总数为50,已消费40,剩余10条,创建不同组的消费者,组名为testother7
2.1.2 测试结果
earliest
消费50条数据,即将全部数据消费完。
latest
消费0条数据。
none
抛出异常
2.1.3测试结论 组与组间的消费者是没有关系的。
topic中已有分组消费数据,新建其他分组ID的消费者时,之前分组提交的offset对新建的分组消费不起作用。
大数据
2018-07-18 20:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
2016年12月,我们开始研究Ambar——一个文档搜索系统。Ambar使用ElasticSearch作为核心搜索引擎。
在Ambar开发的过程中,我们处理了很多与ES相关的问题,我们想分享我们得到的宝贵经验。让我们从每个搜索系统的一个重要功能开始——高亮显示搜索结果。
在任何搜索系统的可用性中,适当的结果高亮显示是最有价值的部分,首先,它为用户提供了关于内部搜索逻辑的必要信息,以及为什么显示该结果。此外,它也使我们能够仅仅通过快速浏览重点而不是下载和浏览整个文档来估计结果。
因为Ambar是一个文档搜索系统,我说的文档也是指文件,所以它必须处理非常大的文件(就全文搜索而言),大小大于100Mb。本文介绍了在利用ElasticSearch高亮显示大型文档时如何达到高性能。
定义问题
Ambar使用ES作为搜索引擎,搜索经过解析的文件/文档内容及其元数据。下面是Ambar在ES中存储一个文档的例子: { sha256: "1a4ad2c5469090928a318a4d9e4f3b21cf1451c7fdc602480e48678282ced02c", meta: [ { id: "21264f64460498d2d3a7ab4e1d8550e4b58c0469744005cd226d431d7a5828d0", short_name: "quarter.pdf", full_name: "//winserver/store/reports/quarter.pdf", source_id: "crReports", extension: ".pdf", created_datetime: "2017-01-14 14:49:36.788", updated_datetime: "2017-01-14 14:49:37.140", extra: [], indexed_datetime: "2017-01-16 18:32:03.712" } ], content: { size: 112387192, indexed_datetime: "2017-01-16 18:32:33.321", author: "John Smith", processed_datetime: "2017-01-16 18:32:33.321", length: "", language: "", state: "processed", title: "Quarter Report (Q4Y2016)", type: "application/pdf", text: ".... laaaaaarge text here ...." } }
上面的JSON文档是一个解析后的.pdf文件,里面有财务报告,文件大小约为100Mb。content.text字段包含报告的解析文本,其大小也约为100Mb。
让我们做一个简单的实验。索引1000个文档,如我以前指定的文档,而不定义任何索引调优或自定义映射。然后看看ES会多快地搜索它们,并高亮显示content.text字段中的检索关键字。
结果如下: 在content.text字段中进行match_phrase搜索会耗费5-30秒 突出显示content.text字段中的文本内容,每次命中平均需要10秒
这种结果是不能接受的。任何使用搜索系统的用户都希望在点击“搜索”按钮后立即得到搜索结果,而不需要等待半分钟就会出现第一个结果。让我们来看看高亮显示这个缓慢突出的问题并解决它。
选择高亮策略
ES 和 Lucene底层有三种高亮策略可供选择,这是 官方文档 链接,三种策略如下: Plain - ES中默认的高亮显示,它是最慢的,但它做了最精确的高亮显示,几乎完全匹配Lucene的搜索逻辑。要高亮显示检索关键字,它必须将整个文档加载到内存中并重新分析它。 Postings - 更快的一个。它将文档的字段分割成句子,并使用 BM25 算法对匹配的结果进行标记,从而对结果进行排序,但它需要在索引中额外存储句子的位置。 Fast Vector Highlighting (FVH) - 似乎是最快的,特别是对于大型文档。需要为索引中的每个令牌存储位置偏移量。在本例中,要对检索词进行高亮显示,它不需要检索整个文档,只需检索接近命中的令牌,由于每个令牌的位置是已知的,因此这个速度非常快。
因此,现在你可以猜到为什么ES可以开箱即用地对大文档中的检索关键字高亮显示。对于每次命中检索整个文档并重新分析它的性能非常昂贵,尤其是对于大于1Mb的文档。
由于我们绝对不能使用普通的高亮显示方式,我们测试了Postings和FVH。最后的选择是FVH,原因如下: 如果使用FVH,一个100Mb的文档高亮显示大约需要10-20毫秒,Postings大约需要一秒钟 Postings并不总是正确地将文档的字段划分为句子,这就是为什么高亮显示的大小会有很大的差异(在某些情况下,从50个单词到数千个单词)。FVH没有这种问题,因为它检索固定数量的令牌,而不是句子。 Postings以任何顺序突出显示令牌,在复杂查询中不能正常工作。对于引用,它不会正确地突出显示具有指定slop值的match_phrase查询的结果。它将把它解释为bool查询,高亮显示整个文档字段中的每个匹配令牌。
在FVH测试中,我们发现了一个非常棘手的问题。它确实解释了match_phrase查询,而不是Lucene的搜索。它只按查询中指定的顺序突出显示令牌,但Lucene的搜索将令牌按任意顺序解释为命中。如果您正在搜索“John Smith”短语,但是文档在其字段中有“Smith John”值,ES将检索该文档作为命中结果,但FVH不会高亮显示它。解决这个问题的办法是短语置换。我们提交不同的查询以搜索和高亮显示,Search获取默认查询,高亮显示通过修改源短语中所有单词位置的变化而构建查询。
总结
ES实际上可以处理大型文档,并且仍然能够提供相当好的性能,重要的是正确地设置索引并记住所有与ES相关的问题。
编译自: Highlighting Large Documents in ElasticSearch
大数据
2018-07-18 19:36:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.词项查询介绍
全文查询将在执行之前分析查询字符串,但词项级别查询将按照存储在倒排索引中的词项进行精确操作。这些查询通常用于数字,日期和枚举等结构化数据,而不是全文本字段。 或者,它们允许您制作低级查询,并在分析过程之前进行。
2.term查询
term查询用于词项搜索,前一章已经介绍过这里不再重复。
3.terms查询
term查询对于查找单个值非常有用,但通常我们可能想搜索多个值。我们只要用单个 terms 查询(注意末尾的 s ), terms 查询好比是 term 查询的复数形式(以英语名词的单复数做比)。
如下查询”title“中包含”河北“,”长生“,”碧桂园“三个词组。 GET telegraph/_search { "query": { "terms": { "title": ["河北","长生","碧桂园"] } } } { "took": 7, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "A5etp2QBW8hrYY3zGJk7", "_score": 1, "_source": { "title": "碧桂园集团副主席杨惠妍", "content": "杨惠妍分别于7月10日、11日买入碧桂园1000万股、1500万股", "author": "小财注", "pubdate": "2018-07-17T16:12:55" } }, { "_index": "telegraph", "_type": "msg", "_id": "Apetp2QBW8hrYY3zGJk7", "_score": 1, "_source": { "title": "长生生物再次跌停 三机构抛售近1000万元", "content": "长生生物再次一字跌停,报收19.89元,成交1432万元", "author": "长生生物", "pubdate": "2018-07-17T10:03:11" } }, { "_index": "telegraph", "_type": "msg", "_id": "BJetp2QBW8hrYY3zGJk7", "_score": 1, "_source": { "title": "河北聚焦十大行业推进国际产能合作", "content": "河北省政府近日出台积极参与“一带一路”建设推进国际产能合作实施方案", "author": "财联社", "pubdate": "2018-07-17T14:14:55" } } ] } }
4. terms_set查询
查找与一个或多个指定词项匹配的文档,其中必须匹配的术语数量取决于指定的最小值,应匹配字段或脚本。
5.range查询
range查询用于匹配数值型、日期型或字符串型字段在某一范围内的文档。
日期类型范围查询
上面例子查询发布时间“pubdate”在“2018-07-17T12:00:00”和“2018-07-17T16:30:00”之间的文档数据。 GET telegraph/_search { "query": { "range": { "pubdate": { "gte": "2018-07-17T12:00:00", "lte": "2018-07-17T16:30:00" } } } }
查询结果 { "took": 6, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "AZetp2QBW8hrYY3zGJk7", "_score": 1, "_source": { "title": "周五召开董事会会议 审议及批准更新后的一季报", "content": "以审议及批准更新后的2018年第一季度报告", "author": "中兴通讯", "pubdate": "2018-07-17T12:33:11" } }, { "_index": "telegraph", "_type": "msg", "_id": "A5etp2QBW8hrYY3zGJk7", "_score": 1, "_source": { "title": "碧桂园集团副主席杨惠妍", "content": "杨惠妍分别于7月10日、11日买入碧桂园1000万股、1500万股", "author": "小财注", "pubdate": "2018-07-17T16:12:55" } }, { "_index": "telegraph", "_type": "msg", "_id": "BJetp2QBW8hrYY3zGJk7", "_score": 1, "_source": { "title": "河北聚焦十大行业推进国际产能合作", "content": "河北省政府近日出台积极参与“一带一路”建设推进国际产能合作实施方案", "author": "财联社", "pubdate": "2018-07-17T14:14:55" } } ] } }
数值类型范围查询
新建索引添加数据 DELETE my_person PUT my_person PUT my_person/stu/1 { "name":"sean", "age":20 } PUT my_person/stu/2 { "name":"sum", "age":25 } PUT my_person/stu/3 { "name":"dean", "age":30 } PUT my_person/stu/4 { "name":"kastel", "age":35 }
查询“age”范围在20到30之间的人员 GET my_person/_search { "query": { "range": { "age": { "gte": 20, "lte": 30 } } } }
查询结果 { "took": 6, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "my_person", "_type": "stu", "_id": "2", "_score": 1, "_source": { "name": "sum", "age": 25 } }, { "_index": "my_person", "_type": "stu", "_id": "1", "_score": 1, "_source": { "name": "sean", "age": 20 } }, { "_index": "my_person", "_type": "stu", "_id": "3", "_score": 1, "_source": { "name": "dean", "age": 30 } } ] } }
6.exists查询
查询文档中的字段至少包含一个非空值。
创建索引添加数据 DELETE my_person PUT my_person PUT my_person/stu/1 { "name":"sean", "hobby":"running" } PUT my_person/stu/2 { "name":"Jhon", "hobby":"" } PUT my_person/stu/3 { "name":"sum", "hobby":["swimming",null] } PUT my_person/stu/4 { "name":"lily", "hobby":[null,null] } PUT my_person/stu/5 { "name":"lucy" }
查询“hobby”不为空的文档 GET my_person/_search { "query": { "exists":{ "field":"hobby" } } }
查询结果 { "took": 12, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "my_person", "_type": "stu", "_id": "2", "_score": 1, "_source": { "name": "Jhon", "hobby": "" } }, { "_index": "my_person", "_type": "stu", "_id": "1", "_score": 1, "_source": { "name": "sean", "hobby": "running" } }, { "_index": "my_person", "_type": "stu", "_id": "3", "_score": 1, "_source": { "name": "sum", "hobby": [ "swimming", null ] } } ] } }
匹配说明: "hobby":"running"------值不为空(可以匹配) "hobby":""------值为空字符串,不是空值(可以匹配) "hobby":["swimming",null]------数组中有非空值(可以匹配) "hobby":[null,null]------数组中值都为null(不可以匹配) "name":"lucy"------没有hobby字段(不可以匹配)
7.prefix查询
查询以匹配字符串开头的文档,如下查询”hobby“中以”sw“开头的文档 GET my_person/_search { "query": { "prefix": { "hobby": { "value": "sw" } } } }
查询结果 { "took": 11, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "my_person", "_type": "stu", "_id": "6", "_score": 1, "_source": { "name": "deak", "hobby": "swimming" } }, { "_index": "my_person", "_type": "stu", "_id": "3", "_score": 1, "_source": { "name": "sum", "hobby": [ "swimming", null ] } } ] } }
8.wildcard查询
通配符查询,如下查询hobby匹配”*ing“的文档 GET my_person/_search { "query": { "wildcard": { "hobby": { "value": "*ing" } } } }
查询结果 { "took": 27, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "my_person", "_type": "stu", "_id": "6", "_score": 1, "_source": { "name": "deak", "hobby": "swimming" } }, { "_index": "my_person", "_type": "stu", "_id": "1", "_score": 1, "_source": { "name": "sean", "hobby": "running" } }, { "_index": "my_person", "_type": "stu", "_id": "3", "_score": 1, "_source": { "name": "sum", "hobby": [ "swimming", null ] } } ] } }
9.regexp查询
正则表达式查询的性能很大程度上取决于所选的正则表达式。 类似 .* 的匹配任何内容的正则表达式非常缓慢,并且使用了lookaround正则表达式。 如果可以的话,请尝试在正则表达式开始之前使用长前缀。 像 .*?+ 这样的通配符匹配器大多会降低性能。大多数正则表达式引擎允许您匹配字符串的任何部分。 如果你想让正则表达式模式从字符串的开头开始,或者在字符串的末尾完成,那么你必须明确地定位它,使用 ^ 表示开始或 $ 表示结束。
元字符 语义 说明 例子 . Match any character The period “.” can be used to represent any character
匹配任何一个字符 ab. 匹配abc、ab1
+ One-or-more The plus sign “+” can be used to repeat the preceding shortest pattern once or more times.
加号“+”可以用来重复上一个最短的模式一次或多次。
“aaabbb”匹配a+b+
* Zero-or-more The asterisk “*” can be used to match the preceding shortest pattern zero-or-more times. “aaabbb”匹配a*b*
? Zero-or-one The question mark “?” makes the preceding shortest pattern optional. It matches zero or one times. “aaabbb”匹配aaa?bbbb?
{m} , {m,n} Min-to-max Curly brackets “{}” can be used to specify a minimum and (optionally) a maximum number of times the preceding shortest pattern can repeat. “aaabbb”匹配a{3}b{3}和a{2,4}b{2,4}
() Grouping Parentheses “()” can be used to form sub-patterns. “ababab”匹配 (ab)+
| Alternation The pipe symbol “|” acts as an OR operator. “aabb”匹配 aabb|bbaa
[] Character classes Ranges of potential characters may be represented as character classes by enclosing them in square brackets “[]”. A leading ^ negates the character class. [abc]匹配 ‘a’ or ‘b’ or ‘c’
~ Complement The shortest pattern that follows a tilde “~” is negated(否定).“ab~cd”的意思是:以a开头,后跟b,后面跟一个任意长度的字符串,但不是c,以d结尾 “abcdef”匹配ab~df或a~(cb)def,不匹配ab~cdef和a~(bc)def
<>
& @
Interval间隔
Intersection Any string
The interval option enables the use of numeric ranges, enclosed by angle brackets “<>”.
The ampersand “&” joins two patterns in a way that both of them have to match. The at sign “@” matches any string in its entirety.
“foo80”匹配 foo<1-100>
“aaabbb”匹配aaa.+&.+bbb @&~(foo.+) 匹配除了以“foo”开头的字符串 “foo”
查询”hobby“字段值与”sw.+“正则匹配的文档 GET my_person/_search { "query": { "regexp":{ "hobby":"sw.+" } } }
查询结果 { "took": 5, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "my_person", "_type": "stu", "_id": "6", "_score": 1, "_source": { "name": "deak", "hobby": "swimming" } }, { "_index": "my_person", "_type": "stu", "_id": "3", "_score": 1, "_source": { "name": "sum", "hobby": [ "swimming", null ] } } ] } }
10.fuzzy查询
模糊查询 GET telegraph/_search { "query": { "fuzzy": { "title": "十大" } } }
查询结果 { "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.99277425, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "BJetp2QBW8hrYY3zGJk7", "_score": 0.99277425, "_source": { "title": "河北聚焦十大行业推进国际产能合作", "content": "河北省政府近日出台积极参与“一带一路”建设推进国际产能合作实施方案", "author": "财联社", "pubdate": "2018-07-17T14:14:55" } } ] } }
11.ids查询
根据跟定的文档id列表查询文档。 GET my_person/_search { "query": { "ids": { "values": ["1","3","5"] } } }
查询结果 { "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "my_person", "_type": "stu", "_id": "5", "_score": 1, "_source": { "name": "lucy" } }, { "_index": "my_person", "_type": "stu", "_id": "1", "_score": 1, "_source": { "name": "sean", "hobby": "running" } }, { "_index": "my_person", "_type": "stu", "_id": "3", "_score": 1, "_source": { "name": "sum", "hobby": [ "swimming", null ] } } ] } }
大数据
2018-07-18 16:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
高级别全文检索通常用于在全文本字段(如电子邮件正文)上运行全文检索。 他们了解如何分析被查询的字段,并在执行之前将每个字段的分析器(或search_analyzer)应用于查询字符串。
1.term查询
term是代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词,所以我们的搜索词必须是文档分词集合中的一个。
例如我们可以通过指定分词器对”周五召开董事会会议 审议及批准更新后的一季报“进行分词。 GET telegraph/_analyze { "analyzer": "ik_max_word", "text": "周五召开董事会会议 审议及批准更新后的一季报" }
分词结果集合中共有15个 { "tokens": [ { "token": "周五", "start_offset": 0, "end_offset": 2, "type": "CN_WORD", "position": 0 }, { "token": "五", "start_offset": 1, "end_offset": 2, "type": "TYPE_CNUM", "position": 1 }, { "token": "召开", "start_offset": 2, "end_offset": 4, "type": "CN_WORD", "position": 2 }, { "token": "董事会", "start_offset": 4, "end_offset": 7, "type": "CN_WORD", "position": 3 }, { "token": "董事", "start_offset": 4, "end_offset": 6, "type": "CN_WORD", "position": 4 }, { "token": "会会", "start_offset": 6, "end_offset": 8, "type": "CN_WORD", "position": 5 }, { "token": "会议", "start_offset": 7, "end_offset": 9, "type": "CN_WORD", "position": 6 }, { "token": "审议", "start_offset": 10, "end_offset": 12, "type": "CN_WORD", "position": 7 }, { "token": "及", "start_offset": 12, "end_offset": 13, "type": "CN_CHAR", "position": 8 }, { "token": "批准", "start_offset": 13, "end_offset": 15, "type": "CN_WORD", "position": 9 }, { "token": "更新", "start_offset": 15, "end_offset": 17, "type": "CN_WORD", "position": 10 }, { "token": "后", "start_offset": 17, "end_offset": 18, "type": "CN_CHAR", "position": 11 }, { "token": "的", "start_offset": 18, "end_offset": 19, "type": "CN_CHAR", "position": 12 }, { "token": "一季", "start_offset": 19, "end_offset": 21, "type": "CN_WORD", "position": 13 }, { "token": "一", "start_offset": 19, "end_offset": 20, "type": "TYPE_CNUM", "position": 14 }, { "token": "季报", "start_offset": 20, "end_offset": 22, "type": "CN_WORD", "position": 15 } ] }
我们用term进行搜索”会议“ GET telegraph/_search { "query": { "term": { "title": { "value": "会议" } } } }
由于搜索字段”会议“属于分词集合,可以搜索到结果 { "took": 9, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.2876821, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "AZetp2QBW8hrYY3zGJk7", "_score": 0.2876821, "_source": { "title": "周五召开董事会会议 审议及批准更新后的一季报", "content": "以审议及批准更新后的2018年第一季度报告", "author": "中兴通讯", "pubdate": "2018-07-17T12:33:11" } } ] } }
如果我们搜索”董事会会议“ GET telegraph/_search { "query": { "term": { "title": { "value": "董事会会议" } } } }
”董事会会议“虽然属于文档文本中的一部分,但是由于没有在分词集合中,所以也是搜索不到的 { "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 0, "max_score": null, "hits": [] } }
2.match搜索
match查询会先对搜索词进行分词,分词完毕后再逐个对分词结果进行匹配,因此相比于term的精确搜索,match是分词匹配搜索。
当我们搜索”河北会议“时,搜索词首先会被分解为”河北“、”会议“,只要文档中包含”河北“、”会议“任意一个就会被搜索到。当然我们也可以通过”operator“来指定被分解词匹配逻辑关系,比如我们可以指定”operator“为”and“时,只有文档的分词集合中同时含有”河北“和”会议“才会被搜索到。默认”operator“为”or“,也就是只要文档分词集合中只要含有任意一个就会被搜索到。 GET telegraph/_search { "query": { "match": { "title": { "query": "河北会议" } } } }
搜索结果 { "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0.99277425, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "BJetp2QBW8hrYY3zGJk7", "_score": 0.99277425, "_source": { "title": "河北聚焦十大行业推进国际产能合作", "content": "河北省政府近日出台积极参与“一带一路”建设推进国际产能合作实施方案", "author": "财联社", "pubdate": "2018-07-17T14:14:55" } }, { "_index": "telegraph", "_type": "msg", "_id": "AZetp2QBW8hrYY3zGJk7", "_score": 0.2876821, "_source": { "title": "周五召开董事会会议 审议及批准更新后的一季报", "content": "以审议及批准更新后的2018年第一季度报告", "author": "中兴通讯", "pubdate": "2018-07-17T12:33:11" } } ] } }
如果我们指定”operator“为”and“进行搜索 GET telegraph/_search { "query": { "match": { "title": { "query": "河北会议", "operator": "and" } } } }
因为所有文档中没有一个的分词集合中既包含”河北“又包含”会议“,所以搜索结果为空。 { "took": 8, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 0, "max_score": null, "hits": [] } }
3.match_phrase查询
match_phrase查询会将查询内容分词,分词器可以自定义,文档中同时满足以下三个条件才会被检索到: 分词后所有词项都要出现在该字段中 字段中的词项顺序要一致 各搜索词之间必须 紧邻
同样上面的例子,我们搜索”董事会会议“,文档会被搜索到。如果分词顺序不一致或者没有紧密相邻都不能被搜索到。 GET telegraph/_search { "query": { "match_phrase": { "title":{ "query": "董事会会议" } } } } { "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 1.1507283, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "AZetp2QBW8hrYY3zGJk7", "_score": 1.1507283, "_source": { "title": "周五召开董事会会议 审议及批准更新后的一季报", "content": "以审议及批准更新后的2018年第一季度报告", "author": "中兴通讯", "pubdate": "2018-07-17T12:33:11" } } ] } }
4.match_phrase_prefix
match_phrase_prefix与match_phrase比较相近,只是match_phrase_prefix允许搜索词的最后一个分词的前缀匹配上即可。
上面的例子中文档的分词集合中有”召开“、”董事会“这两个紧邻的分词。我们使用match_phrase_prefix搜索时只需要搜索词中包含”召开“以及”董事会“的前缀就能匹配上。 GET telegraph/_search { "query": { "match_phrase_prefix": { "title": { "query": "召开董" } } } } { "took": 10, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.8630463, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "AZetp2QBW8hrYY3zGJk7", "_score": 0.8630463, "_source": { "title": "周五召开董事会会议 审议及批准更新后的一季报", "content": "以审议及批准更新后的2018年第一季度报告", "author": "中兴通讯", "pubdate": "2018-07-17T12:33:11" } } ] } }
5.multi_match
当我们想对多个字段进行匹配,其中一个字段包含分词就被文档就被搜索到时,可以用multi_match。
我们搜索”聚焦成交“,只要”title“、”content“任意一个字段中包含 GET telegraph/_search { "query": { "multi_match": { "query": "聚焦成交", "fields": ["title","content"] } } } { "took": 9, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1.0806551, "hits": [ { "_index": "telegraph", "_type": "msg", "_id": "Apetp2QBW8hrYY3zGJk7", "_score": 1.0806551, "_source": { "title": "长生生物再次跌停 三机构抛售近1000万元", "content": "长生生物再次一字跌停,报收19.89元,成交1432万元", "author": "长生生物", "pubdate": "2018-07-17T10:03:11" } }, { "_index": "telegraph", "_type": "msg", "_id": "BJetp2QBW8hrYY3zGJk7", "_score": 0.99277425, "_source": { "title": "河北聚焦十大行业推进国际产能合作", "content": "河北省政府近日出台积极参与“一带一路”建设推进国际产能合作实施方案", "author": "财联社", "pubdate": "2018-07-17T14:14:55" } } ] } }
6.common_terms

7.query_string

8.simple_query_string

大数据
2018-07-18 12:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hadoop对于从事互联网工作的朋友来说已经非常熟悉了,相信在我们身边有很多人正在转行从事hadoop开发的工作,理所当然也会有很多hadoop入门新手。Hadoop开发太过底层,技术难度远比我们想象的要大,对新手而言选择一个合适的hadoop版本就意味着上手更快!
Hadoop是由Apache基金会所开发的分布式系统基础架构,它最核心的设计就是HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。国内互联网的飞速发展催生了大数据技术的快速成长,海量的数据急切需要一种合适的处理方式。Hadoop正值风口,所以迎来了爆发式的发展。国内的hadoop商业发行版比较多,可以说是外资背景的占据了主导地位,毕竟hadoop最早也是由国外提出的,相应的玩法规则都已经制定好了,而我们在很大程度上也只能被动接收了。即便如此,我们也看到了一些国产发行版hadoop。比如:阿里云在做大数据、华为云、以及腾讯云等。
但今天想要给hadoop新手入门推荐的最新发行稳定版hadoop则是dkhadoop。Dkhadoop是大快推出的商业发行版,集成了整个hadoop生态系统的全部组件并且做了深度优化,重新编译成了一个完整的更高性能的大数据通用计算平台,实现了各部件的有机协调。大快Hadoop相比开源的大数据平台,在计算性能上有了非常大的提升。
DKHadoop也是我目前正在使用的。Dkhadoop当前版本主要有:DKH标准版、DKH-分布式SQL版、DK.HADOOP发行版。DKH标准版有三个不同的子版本:用于开发调试的单机版;支持三节点的学习版;支持5节点以上的标准服务器版。DKH-分布式SQL版有两个子版本:学习版、服务器版。
对于hadoop新手入门来说,个人建议选择一个三节点的就可以满足学习需求了。可以下载一个DKHadoop三节点的标准版本学习研究。DKHadoop的三节点标准版本是可以自己去免费申请下载的,所以对于新手来说更合适,因为不需要考虑支付高昂费用的问题!想要DKHadoop三节点标准版本的朋友可以自己去找一下,当然也可以留言给我向我索要!
大数据
2018-07-18 11:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
AirFlow-Tutorial https://airflow.incubator.apache.org/tutorial.html
This tutorial walks you through some of the fundamental Airflow concepts, objects, and their usage while writing your first pipeline.
Example Pipeline definition
Here is an example of a basic pipeline definition. Do not worry if this looks complicated, a line by line explanation follows below. """ Code that goes along with the Airflow tutorial located at: https://github.com/airbnb/airflow/blob/master/airflow/example_dags/tutorial.py """ from airflow import DAG from airflow.operators.bash_operator import BashOperator from datetime import datetime, timedelta default_args = { 'owner': 'airflow', 'depends_on_past': False, 'start_date': datetime(2015, 6, 1), 'email': ['airflow@example.com'], 'email_on_failure': False, 'email_on_retry': False, 'retries': 1, 'retry_delay': timedelta(minutes=5), # 'queue': 'bash_queue', # 'pool': 'backfill', # 'priority_weight': 10, # 'end_date': datetime(2016, 1, 1), } dag = DAG('tutorial', default_args=default_args) # t1, t2 and t3 are examples of tasks created by instantiating operators t1 = BashOperator( task_id='print_date', bash_command='date', dag=dag) t2 = BashOperator( task_id='sleep', bash_command='sleep 5', retries=3, dag=dag) templated_command = """ {% for i in range(5) %} echo "{{ ds }}" echo "{{ macros.ds_add(ds, 7)}}" echo "{{ params.my_param }}" {% endfor %} """ t3 = BashOperator( task_id='templated', bash_command=templated_command, params={'my_param': 'Parameter I passed in'}, dag=dag) t2.set_upstream(t1) t3.set_upstream(t1)
It’s a DAG definition file
One thing to wrap your head around (it may not be very intuitive for everyone at first) is that this Airflow Python script is really just a configuration file specifying the DAG’s structure as code. The actual tasks defined here will run in a different context from the context of this script. Different tasks run on different workers at different points in time, which means that this script cannot be used to cross communicate between tasks. Note that for this purpose we have a more advanced feature called XCom .
People sometimes think of the DAG definition file as a place where they can do some actual data processing - that is not the case at all! The script’s purpose is to define a DAG object. It needs to evaluate quickly (seconds, not minutes) since the scheduler will execute it periodically to reflect the changes if any.
Importing Modules
An Airflow pipeline is just a Python script that happens to define an Airflow DAG object. Let’s start by importing the libraries we will need. # The DAG object; we'll need this to instantiate a DAG from airflow import DAG # Operators; we need this to operate! from airflow.operators.bash_operator import BashOperator
Default Arguments
We’re about to create a DAG and some tasks, and we have the choice to explicitly pass a set of arguments to each task’s constructor (which would become redundant), or (better!) we can define a dictionary of default parameters that we can use when creating tasks. from datetime import datetime, timedelta default_args = { 'owner': 'airflow', 'depends_on_past': False, 'start_date': datetime(2015, 6, 1), 'email': ['airflow@example.com'], 'email_on_failure': False, 'email_on_retry': False, 'retries': 1, 'retry_delay': timedelta(minutes=5), # 'queue': 'bash_queue', # 'pool': 'backfill', # 'priority_weight': 10, # 'end_date': datetime(2016, 1, 1), }
For more information about the BaseOperator’s parameters and what they do, refer to the :py:class: airflow.models.BaseOperator documentation.
Also, note that you could easily define different sets of arguments that would serve different purposes. An example of that would be to have different settings between a production and development environment.
Instantiate a DAG
We’ll need a DAG object to nest our tasks into. Here we pass a string that defines the dag_id , which serves as a unique identifier for your DAG. We also pass the default argument dictionary that we just defined and define a schedule_interval of 1 day for the DAG. dag = DAG( 'tutorial', default_args=default_args, schedule_interval=timedelta(1))
Tasks
Tasks are generated when instantiating operator objects. An object instantiated from an operator is called a constructor. The first argument task_id acts as a unique identifier for the task. t1 = BashOperator( task_id='print_date', bash_command='date', dag=dag) t2 = BashOperator( task_id='sleep', bash_command='sleep 5', retries=3, dag=dag)
Notice how we pass a mix of operator specific arguments ( bash_command ) and an argument common to all operators ( retries ) inherited from BaseOperator to the operator’s constructor. This is simpler than passing every argument for every constructor call. Also, notice that in the second task we override the retries parameter with 3 .
The precedence rules for a task are as follows: Explicitly passed arguments Values that exist in the default_args dictionary The operator’s default value, if one exists
A task must include or inherit the arguments task_id and owner , otherwise Airflow will raise an exception.
Templating with Jinja
Airflow leverages the power of Jinja Templating and provides the pipeline author with a set of built-in parameters and macros. Airflow also provides hooks for the pipeline author to define their own parameters, macros and templates.
This tutorial barely scratches the surface of what you can do with templating in Airflow, but the goal of this section is to let you know this feature exists, get you familiar with double curly brackets, and point to the most common template variable: {{ ds }} (today’s “date stamp”). templated_command = """ {% for i in range(5) %} echo "{{ ds }}" echo "{{ macros.ds_add(ds, 7) }}" echo "{{ params.my_param }}" {% endfor %} """ t3 = BashOperator( task_id='templated', bash_command=templated_command, params={'my_param': 'Parameter I passed in'}, dag=dag)
Notice that the templated_command contains code logic in {% %} blocks, references parameters like {{ ds }} , calls a function as in {{ macros.ds_add(ds, 7)}} , and references a user-defined parameter in {{ params.my_param }} .
The params hook in BaseOperator allows you to pass a dictionary of parameters and/or objects to your templates. Please take the time to understand how the parameter my_param makes it through to the template.
Files can also be passed to the bash_command argument, like bash_command='templated_command.sh' , where the file location is relative to the directory containing the pipeline file ( tutorial.py in this case). This may be desirable for many reasons, like separating your script’s logic and pipeline code, allowing for proper code highlighting in files composed in different languages, and general flexibility in structuring pipelines. It is also possible to define your template_searchpath as pointing to any folder locations in the DAG constructor call.
Using that same DAG constructor call, it is possible to define user_defined_macros which allow you to specify your own variables. For example, passing dict(foo='bar') to this argument allows you to use {{ foo }} in your templates. Moreover, specifying user_defined_filters allow you to register you own filters. For example, passing dict(hello=lambda name: 'Hello %s' % name) to this argument allows you to use {{ 'world' | hello }} in your templates. For more information regarding custom filters have a look at the Jinja Documentation
For more information on the variables and macros that can be referenced in templates, make sure to read through the Macros section
Setting up Dependencies
We have two simple tasks that do not depend on each other. Here’s a few ways you can define dependencies between them: t2.set_upstream(t1) # This means that t2 will depend on t1 # running successfully to run # It is equivalent to # t1.set_downstream(t2) t3.set_upstream(t1) # all of this is equivalent to # dag.set_dependency('print_date', 'sleep') # dag.set_dependency('print_date', 'templated')
Note that when executing your script, Airflow will raise exceptions when it finds cycles in your DAG or when a dependency is referenced more than once.
Recap
Alright, so we have a pretty basic DAG. At this point your code should look something like this: """ Code that goes along with the Airflow located at: http://airflow.readthedocs.org/en/latest/tutorial.html """ from airflow import DAG from airflow.operators.bash_operator import BashOperator from datetime import datetime, timedelta default_args = { 'owner': 'airflow', 'depends_on_past': False, 'start_date': datetime(2015, 6, 1), 'email': ['airflow@example.com'], 'email_on_failure': False, 'email_on_retry': False, 'retries': 1, 'retry_delay': timedelta(minutes=5), # 'queue': 'bash_queue', # 'pool': 'backfill', # 'priority_weight': 10, # 'end_date': datetime(2016, 1, 1), } dag = DAG( 'tutorial', default_args=default_args, schedule_interval=timedelta(1)) # t1, t2 and t3 are examples of tasks created by instantiating operators t1 = BashOperator( task_id='print_date', bash_command='date', dag=dag) t2 = BashOperator( task_id='sleep', bash_command='sleep 5', retries=3, dag=dag) templated_command = """ {% for i in range(5) %} echo "{{ ds }}" echo "{{ macros.ds_add(ds, 7)}}" echo "{{ params.my_param }}" {% endfor %} """ t3 = BashOperator( task_id='templated', bash_command=templated_command, params={'my_param': 'Parameter I passed in'}, dag=dag) t2.set_upstream(t1) t3.set_upstream(t1)
Testing
Running the Script
Time to run some tests. First let’s make sure that the pipeline parses. Let’s assume we’re saving the code from the previous step in tutorial.py in the DAGs folder referenced in your airflow.cfg . The default location for your DAGs is ~/airflow/dags . python ~/airflow/dags/tutorial.py
If the script does not raise an exception it means that you haven’t done anything horribly wrong, and that your Airflow environment is somewhat sound.
Command Line Metadata Validation
Let’s run a few commands to validate this script further. # print the list of active DAGs airflow list_dags # prints the list of tasks the "tutorial" dag_id airflow list_tasks tutorial # prints the hierarchy of tasks in the tutorial DAG airflow list_tasks tutorial --tree
Testing
Let’s test by running the actual task instances on a specific date. The date specified in this context is an execution_date , which simulates the scheduler running your task or dag at a specific date + time: # command layout: command subcommand dag_id task_id date # testing print_date airflow test tutorial print_date 2015-06-01 # testing sleep airflow test tutorial sleep 2015-06-01
Now remember what we did with templating earlier? See how this template gets rendered and executed by running this command: # testing templated airflow test tutorial templated 2015-06-01
This should result in displaying a verbose log of events and ultimately running your bash command and printing the result.
Note that the airflow test command runs task instances locally, outputs their log to stdout (on screen), doesn’t bother with dependencies, and doesn’t communicate state (running, success, failed, …) to the database. It simply allows testing a single task instance.
Backfill
Everything looks like it’s running fine so let’s run a backfill. backfill will respect your dependencies, emit logs into files and talk to the database to record status. If you do have a webserver up, you’ll be able to track the progress. airflow webserver will start a web server if you are interested in tracking the progress visually as your backfill progresses.
Note that if you use depends_on_past=True , individual task instances will depend on the success of the preceding task instance, except for the start_date specified itself, for which this dependency is disregarded.
The date range in this context is a start_date and optionally an end_date , which are used to populate the run schedule with task instances from this dag. # optional, start a web server in debug mode in the background # airflow webserver --debug & # start your backfill on a date range airflow backfill tutorial -s 2015-06-01 -e 2015-06-07
What’s Next?
That’s it, you’ve written, tested and backfilled your very first Airflow pipeline. Merging your code into a code repository that has a master scheduler running against it should get it to get triggered and run every day.
Here’s a few things you might want to do next: Take an in-depth tour of the UI - click all the things! Keep reading the docs! Especially the sections on: Command line interface Operators Macros Write your first pipeline!
大数据
2018-07-10 13:43:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
AirFlow-Installation https://airflow.incubator.apache.org/installation.html
Getting Airflow
The easiest way to install the latest stable version of Airflow is with pip : pip install apache-airflow
You can also install Airflow with support for extra features like s3 or postgres : pip install "apache-airflow[s3, postgres]"
Extra Packages
The apache-airflow PyPI basic package only installs what’s needed to get started. Subpackages can be installed depending on what will be useful in your environment. For instance, if you don’t need connectivity with Postgres, you won’t have to go through the trouble of installing the postgres-devel yum package, or whatever equivalent applies on the distribution you are using.
Behind the scenes, Airflow does conditional imports of operators that require these extra dependencies.
Here’s the list of the subpackages and what they enable:
subpackage install command enables all pip install apache-airflow[all] All Airflow features known to man
all_dbs pip install apache-airflow[all_dbs] All databases integrations
async pip install apache-airflow[async] Async worker classes for gunicorn
devel pip install apache-airflow[devel] Minimum dev tools requirements
devel_hadoop pip install apache-airflow[devel_hadoop] Airflow + dependencies on the Hadoop stack
celery pip install apache-airflow[celery] CeleryExecutor
crypto pip install apache-airflow[crypto] Encrypt connection passwords in metadata db
druid pip install apache-airflow[druid] Druid.io related operators & hooks
gcp_api pip install apache-airflow[gcp_api] Google Cloud Platform hooks and operators (using google-api-python-client )
jdbc pip install apache-airflow[jdbc] JDBC hooks and operators
hdfs pip install apache-airflow[hdfs] HDFS hooks and operators
hive pip install apache-airflow[hive] All Hive related operators
kerberos pip install apache-airflow[kerberos] kerberos integration for kerberized hadoop
ldap pip install apache-airflow[ldap] ldap authentication for users
mssql pip install apache-airflow[mssql] Microsoft SQL operators and hook, support as an Airflow backend
mysql pip install apache-airflow[mysql] MySQL operators and hook, support as an Airflow backend
password pip install apache-airflow[password] Password Authentication for users
postgres pip install apache-airflow[postgres] Postgres operators and hook, support as an Airflow backend
qds pip install apache-airflow[qds] Enable QDS (qubole data services) support
rabbitmq pip install apache-airflow[rabbitmq] Rabbitmq support as a Celery backend
s3 pip install apache-airflow[s3] S3KeySensor , S3PrefixSensor
samba pip install apache-airflow[samba] Hive2SambaOperator
slack pip install apache-airflow[slack] SlackAPIPostOperator
vertica pip install apache-airflow[vertica] Vertica hook support as an Airflow backend
cloudant
redis
pip install apache-airflow[cloudant]
pip install apache-airflow[redis]
Cloudant hook
Redis hooks and sensors
大数据
2018-07-10 13:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1、sqoop使用
在学习sqoop使用之前,我们需要查看sqoop都是可以完成什么任务,通过键入:sqoop help,我们就可以看到sqoop可以提供的服务。在项目中,我们主要使用的是sqoop import服务,在使用的过程中,我们还会经历很多定制修改,讲逐一讲解。
1.1将数据从数据库导入到hadoop中
导入指令:sqoop import –connect jdbc:mysql://hostname:port/database –username root –password 123456 –table example –m 1。在这里讲解一下指令的构成,如下:
1、--connect jdbc:mysql://hostname:port/database指定mysql数据库主机名和端口号和数据库名;
2、--username root 指定数据库用户名
3、--password 123456 指定数据库密码
4、--table example mysql中即将导出的表
5、-m 1 指定启动一个map进程,如果表很大,可以启动多个map进程
6、导入到HDFS中的路径 默认:/user/grid/example/part-m-00000
注意:默认情况下,Sqoop会将我们导入的数据保存为逗号分隔的文本文件。如果导入数据的字段内容存在分隔符,则我们可以另外指定分隔符、字段包围字符和转义字符。使用命令行参数可以指定分隔符、文件格式、压缩以及对导入过程进行更细粒度的控制。
1.2、生成代码
除了能够将数据库表的内容写到HDFS,Sqoop还生成了一个Java源文件(example.java)保存在当前的本地目录中。在运行了前面的sqoop import命令之后,可以通过ls example.java命令看到这个文件。代码生成时Sqoop导入过程的必要组成部分,他是在Sqoop将源数据库的表数据写到HDFS之前,首先用生成的代码对其进行反序列化。
生成的类中能够保存一条从被导入表中取出的记录。该类可以在MapReduce中使用这条记录,也可以将这条记录保存在HDFS中的一个SequenceFile文件中。在导入过程中,由Sqoop生成的SequenceFile文件会生成的类,将每一个被导入的行保存在其键值对格式中“值”的位置。
也许你不想将生成的类命名为example,因为每一个类的实例只对应与一条记录。我们可以使用另外一个Sqoop工具来生成源代码,并不执行导入操作,这个生成的代码仍然会检查数据库表,以确定与每个字段相匹配的数据类型:
Sqoop codegen –connect jdbc:mysql://localhost/yidong –table example –class-name example
Codegen工具只是简单的生成代码,他不执行完整的导入操作。我们指定希望生成一个名为example的类,这个类将被写入到example.java文件中。在之前执行的导入过程中,我们还可以指定—class-name和其他代码生成参数。如果你意外的删除了生成的源代码,或希望使用不同于导入过程的设定来生成代码,都可以用这个工具来重新生成代码。
如果计划使用导入到SequenceFile文件中的记录,你将不可避免的用到生成的类(对SequenceFile文件中的数据进行反序列化)。在使用文本文件中的记录时,不需要用生成的代码。
1.3、深入了解数据库导入
在深入理解之前,我们需要先想一个问题:Sqoop是通过一个MapReduce作业从数据库中导入一个表,这个作业从表中抽取一行行记录,然后写入到HDFS。MapReduce是如何记录的?
下图是Sqoop从数据库中导入到HDFS的原理图:
在导入开始之前,Sqoop使用JDBC来检查将要导入的表。他检索出表中所有的列以及列的SQL数据类型。这些SQL类型(VARCHAR、INTEGER)被映射到Java数据类型(String、Integer等),在MapReduce应用中将使用这些对应的java类型来保存字段的值。Sqoop的代码生成器使用这些信息来创建对应表的类,用于保存从表中抽取的记录。例如前面提到过的example类。
对于导入来说,更关键的是DBWritable接口的序列化方法,这些方法能使Widget类和JDBC进行交互:
Public void readFields(resultSet _dbResults)throws SQLException;
Public void write(PreparedStatement _dbstmt)throws SQLException;
JDBC的ResultSet接口提供了一个用户从检查结果中检索记录的游标;这里的readFields()方法将用ResultSet中一行数据的列来填充Example对象的字段。
Sqoop启动的MapReduce作业用到一个InputFormat,他可以通过JDBC从一个数据库表中读取部分内容。Hadoop提供的DataDriverDBInputFormat能够为几个Map任务对查询结果进行划分。为了获取更好的导入性能,查询会根据一个“划分列”来进行划分的。Sqoop会选择一个合适的列作为划分列(通常是表的主键)。
在生成反序列化代码和配置InputFormat之后,Sqoop将作业发送到MapReduce集群。Map任务将执行查询并将ResultSet中的数据反序列化到生成类的实例,这些数据要么直接保存在SequenceFile文件中,要么在写到HDFS之前被转换成分割的文本。
Sqoop不需要每次都导入整张表,用户也可以在查询中加入到where子句,以此来限定需要导入的记录:Sqoop –query
导入和一致性:在向HDFS导入数据时,重要的是要确保访问的是数据源的一致性快照。从一个数据库中并行读取数据的MAP任务分别运行在不同的进程中。因此,他们不能共享一个数据库任务。保证一致性的最好方法就是在导入时不允许运行任何进行对表中现有数据进行更新。
1.4、使用导入的数据
一旦数据导入HDFS,就可以供定制的MapReduce程序使用。导入的文本格式数据可以供Hadoop Streaming中的脚本或者TextInputFormat为默认格式运行的MapReduce作业使用。
为了使用导入记录的个别字段,必须对字段分割符进行解析,抽取出的字段值并转换为相应的数据类型。Sqoop生成的表类能自动完成这个过程,使你可以将精力集中在真正的要运行的MapReduce作业上。
1.5、导入的数据与hive
Hive和sqoop共同构成一个强大的服务于分析任务的工具链。Sqoop能够根据一个关系数据源中的表来生成一个hive表。既然我们已经将表的数据导入到HDFS中,那么就可以直接生成相应hive表的定义,然后加载保存在HDFS中的数据,例如:
Sqoop create-hive-table –connect jdbc:mysql://localhoust/yidong –table example –fields-terminated-by “,”
Load data inpath ‘example’ into table example
注:在为一个特定的已导入数据集创建相应的hive表定义时,我们需要指定该数据集所使用的分隔符。否则,sqoop将允许hive使用自己默认的风格符。
如果想直接从数据库将数据导入到hive,可以将上述三个步骤(将数据导入HDFS,创建hive表,将hdfs中的数据导入hive)缩短为一个步骤。在进行导入时,sqoop可以生成hive表的定义,然后直接将数据导入hive表:
Sqoop import –connect jdbc:mysql://localhost/hadoopguide –table widgets –m 1 –hive-import
1.6、导入大对象
很多数据库都具有在一个字段中保存大量数据的能力。取决于数据是文本还是二进制类型,通常这些类型为CLOB或BLOB。数据库一般会对这些“大对象”进行特殊处理。Sqoop将导入的大对象数据存储在LobFile格式的单独文件中,lobfile格式能够存储非常大的单条记录。Lobfile文件中的每条记录保存一个大对象。
在导入一条记录时,所有的“正常”字段会在一个文本文件中一起物化,同时还生成一个指向保存CLOB或BLOB列的lobfile文件的引用。
2、执行导出
在sqoop中,导出是将hdfs作为一个数据源,而将一个远程的数据库作为目标。将一张表从hdfs导出到数据库时,我们必须在数据库中创建一张用于接收数据的目标表。虽然sqoop可以推断出那个java类型适合存储sql数据类型,但反过来确实行不通。因此,必须由用户来确定哪些类型是最合适的。
例如:我们打算从hive中导出zip_profits表到mysql数据库中。
①先在mysql中创建一个具有相同序列顺序及合适sql表型的目标表:
Create table sales_by_sip(volume decimal(8,2),zip integer);
②接着运行导出命令:
Sqoop export –connect jdbc:mysql://localhost/hadoopguide –m 1 –table sales_by_zip –export-dir /user/hive/warehouse/zip_profits –input-fields-terminated-by “\0001”
③过mysql来确认导出成功:
mysql hadoopguide –e ‘select * from sales_by_zip’
注意:在hive中创建zip_profits表时,我们没有指定任何分隔符。因此hive使用了自己的默认分隔符;但是直接从文件中读取这张表时,我们需要将所使用的分隔符告知sqoop。Sqoop默认记录是以换行符作为分隔符。因此,可在sqoop export命令中使用—input-fields-terminated-by参数来指定字段分隔符。
2.1、深入了解导出
Sqoop导出功能的架构与其导入功能非常相似,在执行导出操作之前,sqoop会根据数据库连接字符串来选择一个导出方法。一般为jdbc。然后,sqoop会根据目标表的定义生成一个java类。这个生成的类能够从文本文件中解析记录,并能够向表中插入类型合适的值。接着会启动一个MapReduce作业,从HDFS中读取源数据文件,使用生成的类解析记录,并且执行选定的导出方法。
基于jdbc的导出方法会产生一批insert语句,每条语句都会向目标表中插入多条记录。多个单独的线程被用于从HDFS读取数据并与数据库进行通信,以确保涉及不同系统的I/O操作能够尽可能重叠执行。
虽然HDFS读取数据的MapReduce作业大多根据所处理文件的数量和大小来选择并行度(map任务的数量),但sqoop的导出工具允许用户明确设定任务的数量。由于导出性能会受并行的数据库写入线程数量的影响,所以sqoop使用combinefileinput类将输入文件分组分配给少数几个map任务去执行。
2.2、导出与事务
进程的并行特性,导致导出操作往往不是原子操作。Sqoop会采用多个并行的任务导出,并且数据库系统使用固定大小的缓冲区来存储事务数据,这时一个任务中的所有操作不可能在一个事务中完成。因此,在导出操作进行过程中,提交过的中间结果都是可见的。在导出过程完成前,不要启动那些使用导出结果的应用程序,否则这些应用会看到不完整的导出结果。
更有问题的是,如果任务失败,他会从头开始重新导入自己负责的那部分数据,因此可能会插入重复的记录。当前sqoop还不能避免这种可能性。在启动导出作业前,应当在数据库中设置表的约束(例如,定义一个主键列)以保证数据行的唯一性。
2.3、导出与SequenceFile
Sqoop还可以将存储在SequenceFile中的记录导出到输出表,不过有一些限制。SequenceFile中可以保存任意类型的记录。Sqoop的导出工具从SequenceFile中读取对象,然后直接发送到OutputCollector,由他将这些对象传递给数据库导出OutputFormat。为了能让Sqoop使用,记录必须被保存在SequenceFile键值对格式的值部分,并且必须继承抽象类com.cloudera.sqoop.lib.SqoopRecord。
3、项目案例
在我们移动项目中,有些数据是通过web页面维护,这些数据都是通过管理员手动添加到分析系统中的。为了使hive可以更好的进行分析,所以需要将这些服务数据,定期导入到我们的hive数据仓储中,这时sqoop就需要发挥作用了。
需要通过sqoop将数据从mysql导入到hive数据仓储的服务有:应用管理、渠道管理、自定义事件管理、里程碑管理。他们对应的数据库(10.6.219.86)表分别是:base_app、base_channel、base_event、base_milestone。
3.1、采用sqoop指令导入
在本次实践中,我们仅以导入应用管理表(base_app)为例进行说明。
1、执行sqoop指令将数据从mysql导入到hive中,指令为:
Sqoop import --connect jdbc:mysql://10.1.11.78:3306/video --table base_event --username root --password 123456 -m 1 --hive-import --hive-database video --hive-table base_event --hive-overwrite --fields-terminated-by "\t"--lines-terminated-by “\n”--as-textfile
指令详解: sqoop import ---执行sqoop导入指令; --connect jdbc:mysql://hostname:port/database ---要连接的数据库地址、端口号、数据库database; --table base_app ----- 要操作的数据库表; --username root ----- 连接数据库的用户名; --password 123456 --- 连接数据库的密码; -m 1 ------ 要启动的map数量 --hive-import --- 采用hive方式导入 [--create-hive-table] --- 如果导入的表在hive中不存在的话,sqoop自动在hive中创建该表。但是当表存在的情况下,添加该选项会导致指令报错。所以在实际操作中,不建议使用,并且在实际的操作过程中,即使我们不添加该项辅助指令,sqoop也会在hive中创建导入的表。 --hive-database yidong --- 要将数据库表导入到hive的那个database中; --hive-table base_app --- 要将数据库表导入到hive的那个表中; --hive-overwrite ---如果hive的表中已经存在数据,添加该项操作后,会将原有的数据覆盖掉。 --fields-terminated-by “\t” --- hive存储到hdfs中的文件中字段间的分隔符; --lines-terminated-by “\n”– hive存储到hdfs中的文件中每行间的分隔符; --as-textfile ---hive存储到hdfs中的文件格式,采用文本存储;
常用辅助指令详解:
1、通过help指令查看sqoop导入帮助:
Sqoop help import;
2、Sqoop导入行辅助操作详解:
3、Sqoop hive导入辅助操作详解:
3.2、通过oozie编写workflow定时将同步数据
在本次项目实践中,我们需要用到oozie的sqoop action。在这章节中,我会演示给大家如何编写workflow.xml实现定时数据同步的。
在项目运作的过程中发现,为了能更好的进行数据同步,建议首先把需要同步的数据库表在环境初始化的过程中,在hive数据仓储中创建出来。这样项目运作的过程中,会很轻松的执行。
编写workflow.xml进行同步工作,workflow.xml内容如下:

在该例子中,我们是对四个需要同步的表进行,通过sqoop进行了同步。大家在编写oozie sqoop action脚本的工程中,需要特别注意:sqoop支持两种方式配置指令,一种是command,另一种是arg的方式。不管是采用哪种方式,导入导出的过程的命令中,一定不要出现sqoop,而是从sqoop需要执行的指令以后的内容开始(从import或者export开始),请查看截图中的红色标注。
3.3、项目实践总结归纳
① 首先在初始化同步工作流环境的过程中,先在script.hql文件中编写建表语句,在hive数据仓储中创建出需要进行同步的table。
② 编写workflow.xml文件,定义需要同步表的sqoop工作流,且工作流需要执行的命令中,不会出现“sqoop”关键字,而是从该关键字以后的指令开始,如:在cli情况下我们需要同步数据库,执行的命令为:sqoop import jdbc:mysql://……;而在workflow.xml配置的command命令中为:import jdbc:mysql://…..,在这里不用再填写sqoop,否则会报错,这是初学人员常遇到的错误,谨记!!!!
③ 在sqoop导入mysql数据到hive时,--fields-terminated-by "\t",自定义的分隔符要为双引号。否则指定的分隔符无效!
  这些内容是从sqoop的官网整理出来的,是1.4.3版本的Document,如果有错误,希望大家指正。
1.使用sqoop导入数据
     sqoop import --connect jdbc:mysql: // localhost/db --username foo --table TEST
  2.账号密码
  
   sqoop import --connect jdbc:mysql: // database.example.com/employees \ --username aaron --password 12345
  3.驱动
   sqoop import --driver com.microsoft.jdbc.sqlserver.SQLServerDriver --connect ...
  
   4.写sql语句导入的方式
   sqoop import --query ' SELECT a.*, b.* FROM a JOIN b on (a.id == b.id) WHERE $CONDITIONS ' -- split -by a. id --target- dir /user/foo/joinresults
  
  如果是顺序导入的话,可以只开一个线程
sqoop import --query ' SELECT a.*, b.* FROM a JOIN b on (a.id == b.id) WHERE $CONDITIONS ' -m 1 --target- dir /user/foo/joinresults
  如果where语句中有要用单引号的,就像这样子写就可以啦"SELECT * FROM x WHERE a='foo' AND \$CONDITIONS"
  5. 1.4.3版本的sqoop不支持复杂的sql语句,不支持or语句
  
  6. --split-by
  默认是主键,假设有100行数据,它会执行那个SELECT * FROM sometable WHERE id >= lo AND id < hi,
  with (lo, hi) 会分为4次导入(0,250),(250,500),(500,750),(750,1001)
  如果这个字段不能达到实际的划分区域的效果,可以用别的字段。如果没有索引列或者是组合主键的表,需要手动设置一个划分列
  
  7. --direct 是为了利用某些数据库本身提供的快速导入导出数据的工具,比如mysql的mysqldump
  性能比jdbc更好,但是不知大对象的列,使用的时候,那些快速导入的工具的客户端必须的shell脚本的目录下
  
  8.导入数据到hdfs目录,这个命令会把数据写到/shared/foo/ 目录
   sqoop import --connnect --table foo --warehouse- dir /shared \
  
  或者
   sqoop import --connnect --table foo --target- dir /dest \
  
  9.传递参数给快速导入的工具,使用--开头,下面这句命令传递给mysql默认的字符集是latin1
   sqoop import --connect jdbc:mysql: // server.foo.com/db --table bar \ --direct -- --default-character-set=latin1
  
  10.转换为对象
  --map-column-java 转换为java数据类型
  --map-column-hive 转转为hive数据类型

  11.增加导入
  --check-column (col) Specifies the column to be examined when determining which rows to import.
  --incremental (mode) Specifies how Sqoop determines which rows are new. Legal values for mode include append and lastmodified.
  --last-value (value) Specifies the maximum value of the check column from the previous import.
  增加导入支持两种模式append和lastmodified,用--incremental来指定

  12.在导入大对象,比如BLOB和CLOB列时需要特殊处理,小于16MB的大对象可以和别的数据一起存储,超过这个值就存储在_lobs的子目录当中
  它们采用的是为大对象做过优化的存储格式,最大能存储2^63字节的数据,我们可以用--inline-lob-limit参数来指定每个lob文件最大的限制是多少
  如果设置为0,则大对象使用外部存储

  13.分隔符、转移字符
  下面的这句话
  Some string, with a comma.
  Another "string with quotes"
  使用这句命令导入$ sqoop import --fields-terminated-by , --escaped-by \\ --enclosed-by '\"' ...
  会有下面这个结果
  "Some string, with a comma.","1","2","3"...
  "Another \"string with quotes\"","4","5","6"...
  使用这句命令导入$ sqoop import --optionally-enclosed-by '\"' (the rest as above)...
  "Some string, with a comma.",1,2,3...
  "Another \"string with quotes\"",4,5,6...

  14.hive导入参数
  --hive-home 重写$HIVE_HOME
  --hive-import 插入数据到hive当中,使用hive的默认分隔符
  --hive-overwrite 重写插入
  --create-hive-table 建表,如果表已经存在,该操作会报错!
  --hive-table 设置到hive当中的表名
  --hive-drop-import-delims 导入到hive时删除 \n, \r, and \01
  --hive-delims-replacement 导入到hive时用自定义的字符替换掉 \n, \r, and \01
  --hive-partition-key hive分区的key
  --hive-partition-value hive分区的值
  --map-column-hive 类型匹配,sql类型对应到hive类型

  15.hive空值处理
  sqoop会自动把NULL转换为null处理,但是hive中默认是把\N来表示null,因为预先处理不会生效的
  我们需要使用 --null-string 和 --null-non-string来处理空值 把\N转为\\N
   sqoop import ... -- null - string ' \\N ' -- null -non- string ' \\N '
  
  
  
  16.导入数据到hbase
  导入的时候加上--hbase-table,它就会把内容导入到hbase当中,默认是用主键作为split列
  也可以用--hbase-row-key来指定,列族用--column-family来指定,它不支持--direct。
  如果不想手动建表或者列族,就用--hbase-create-table参数

  17.代码生成参数,没看懂
  --bindir Output directory for compiled objects
  --class-name Sets the generated class name. This overrides --package-name. When combined with --jar-file, sets the input class.
  --jar-file Disable code generation; use specified jar
  --outdir Output directory for generated code
  --package-name Put auto-generated classes in this package
  --map-column-java Override default mapping from SQL type to Java type for configured columns.

  18.通过配置文件conf/sqoop-site.xml来配置常用参数
   < property > < name > property.name < value > property.value
  如果不在这里面配置的话,就需要像这样写命令
   sqoop import -D property.name=property.value ...
  
  
  19.两个特别的参数
  sqoop.bigdecimal.format.string 大decimal是否保存为string,如果保存为string就是 0.0000007,否则则为1E7
  sqoop.hbase.add.row.key 是否把作为rowkey的列也加到行数据当中,默认是false的
  
  20.例子
  
#指定列 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ --columns " employee_id,first_name,last_name,job_title " #使用8个线程 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ -m 8 #快速模式 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ --direct #使用sequencefile作为存储方式 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ --class-name com.foocorp.Employee --as-sequencefile #分隔符 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ --fields-terminated-by ' \t ' --lines-terminated-by ' \n ' --optionally-enclosed-by ' \" ' #导入到hive $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ --hive-import #条件过滤 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ --where " start_date > '2010-01-01' " #用dept_id作为分个字段 $ sqoop import --connect jdbc:mysql: // db.foo.com/corp --table EMPLOYEES \ -- split -by dept_id #追加导入 $ sqoop import --connect jdbc:mysql: // db.foo.com/somedb --table sometable \ --where " id > 100000 " --target- dir /incremental_dataset --append
  
   
  21.导入所有的表sqoop-import-all-tables
  每个表都要有主键,不能使用where条件过滤
   sqoop import-all-tables --connect jdbc:mysql: // db.foo.com/corp
  
  
  22.export
  我们采用sqoop-export插入数据的时候,如果数据已经存在了,插入会失败
  如果我们使用--update-key,它会认为每个数据都是更新,比如我们使用下面这条语句
   sqoop-export --table foo --update-key id --export- dir /path/to/data --connect …   UPDATE foo SET msg= ' this is a test ' , bar= 42 WHERE id = 0 ;   UPDATE foo SET msg= ' some more data ' , bar= 100 WHERE id = 1 ;    ...
  这样即使找不到它也不会报错
  23.如果存在就更新,不存在就插入
  加上这个参数就可以啦--update-mode allowinsert
  
  
  24.事务的处理
  它会一次statement插入100条数据,然后每100个statement提交一次,所以一次就会提交10000条数据
  
  
  25.例子
  
$ sqoop export --connect jdbc:mysql: // db.example.com/foo --table bar \ --export- dir /results/bar_data $ sqoop export --connect jdbc:mysql: // db.example.com/foo --table bar \ --export- dir /results/bar_data --validate $ sqoop export --connect jdbc:mysql: // db.example.com/foo --call barproc \ --export- dir /results/bar_data
sqoop中文参考手册 :
https://wenku.baidu.com/view/7f3651ac58fb770bf68a5504.html

Sqoop用户指南(v1.3.0-cdh3u6):
http://archive.cloudera.com/cdh/3/sqoop/SqoopUserGuide.html
sqoop参数详解
Sqoop-1.4.6工具import和export使用详解(官网)
(MySQL里的数据)通过Sqoop Import Hive 里 和 通过Sqoop Export Hive 里的数据到(MySQL)

(MySQL里的数据)通过Sqoop Import HDFS 里 和 通过Sqoop Export HDFS 里的数据到(MySQL)
使用sqoop从MySQL导入数据到HBase:
https://blog.csdn.net/Magic_Ninja/article/details/80555254
http://blog.5ibc.net/p/116730.html
https://www.2cto.com/net/201708/673854.html
使用sqoop从hive中导入数据到hbase:
https://blog.csdn.net/Magic_Ninja/article/details/80555254
  
大数据
2018-07-10 13:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
今天博主主要分享Linux系统中的一种软件安装方式YUM,主要针对人群包括运维、开发、项目经理等等。当然,如果你对这块知识已经非常熟悉,那请移步博主其它文章。
一、YUM相关概念
Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器。基于RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包,无须繁琐地一次次下载、安装。
在Linux上使用源码的方式安装软件非常麻烦,使用yum可以简化安装的过程。
二、YUM相关命令
安装软件(以foo-x.x.x.rpm为例):yum install foo-x.x.x.rpm
删除软件:yum remove foo-x.x.x.rpm或者yum erase foo-x.x.x.rpm
升级软件:yum upgrade foo或者yum update foo
查询信息:yum info foo
搜索软件(以包含foo字段为例):yum search foo
显示软件包依赖关系:yum deplist foo
-q 静默执行
-t 忽略错误
-R[分钟] 设置等待时间
-y 自动应答yes
--skip-broken 忽略依赖问题
--nogpgcheck 忽略GPG验证
check-update 检查可更新的包
clean all 清除全部
clean packages 清除临时包文件(/var/cache/yum 下文件)
clean headers 清除rpm头文件
clean oldheaders 清除旧的rpm头文件
deplist 列出包的依赖
list 可安装和可更新的RPM包
list installed 已安装的包
list extras 已安装且不在资源库的包
info 可安装和可更新的RPM包 信息
info installed 已安装包的信息(-qa 参数相似)
install[RPM包] 安装包
localinstall 安装本地的 RPM包
update[RPM包] 更新包
upgrade 升级系统
search[关键词] 搜索包
provides[关键词] 搜索特定包文件名
reinstall[RPM包] 重新安装包
repolist 显示资源库的配置
resolvedep 指定依赖
remove[RPM包] 卸载包

三、常用命令
安装httpd并确认安装
yum instll -y httpd
列出所有可用的package和package组
yum list
清除所有缓冲数据
yum clean all
列出一个包所有依赖的包
yum deplist httpd
删除httpd
yum remove httpd

四、搭建本地YUM源服务器
a.为什么要搭建YUM源服务器?
YUM源虽然可以简化我们在Linux上安装软件的过程,但是生成环境通常无法上网,不能连接外网的YUM源,说以接就无法使用yum命令安装软件了。为了在内网中也可以使用yum安装相关的软件,就要配置yum源。
b.YUM源服务器的原理
YUM源其实就是一个保存了多个RPM包的服务器,可以通过http的方式来检索、下载并安装相关的RPM包。
c.制作本YUM源
(1)将插入到lInux系统中的本地光盘挂载到一个目录mount -t iso9660 -o ro /dev/cdrom /mnt/cdrom/
(2)进入YUM配置文件目录/etc/yum.repos.d/,vi centos-local.repo新建yum配置文件,并且加入内容

(3)清理YUM缓存yum clean all
(4)列出可用yum repolist
(5)安装http(此处建议大家讲其它YUM源关闭后测试local)
(6)启动httpd服务:service httpd start
(7)查看服务端口占用情况
(8)访问httpd服务器:http://192.168.29.133/,暂时无法访问
(9)配置防火墙允许80端口数据进出 iptables -I INPUT -p tcp -m multiport --dport 22,80,3306 -j ACCEPT service iptables save service iptables restart
(10)再次访问httpd,成功

最后总结:今天的分享就到这里,如果你对YUM这块技术或者其它服务器技术感兴趣,请联系点赞并欢迎同博主交流。
大数据
2018-07-09 23:07:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在阿里云创建Kubernetetes-1.11.0镜像服务(高速)
Kubernetetes镜像在国内无法直接访问,导致安装费时、容易出错、难以排查。可以将其抓取到阿里云上面,提前拉取(docker pull)到本地,然后再运行kubeadm安装程序,就要快很多了。 脚本代码在 https://github.com/openthings/kubernetes-tools/tree/master/kubeadm/2-docker-image-tool/ aliyun-get-kubeimages-1.11.0.sh
1、拉取镜像 这里(registry.cn-hangzhou.aliyuncs.com/openthings)有拉取好的,可以运行下面的脚本(上面的链接)就直接pull到本地(速度很快): echo "" echo "==========================================================" echo "Pull Kubernetes 1.11.0 Images from aliyuncs.com ......" echo "==========================================================" echo "" MY_REGISTRY=registry.cn-hangzhou.aliyuncs.com/openthings ## 拉取镜像 docker pull ${MY_REGISTRY}/k8s-gcr-io-kube-apiserver-amd64:v1.11.0 docker pull ${MY_REGISTRY}/k8s-gcr-io-kube-controller-manager-amd64:v1.11.0 docker pull ${MY_REGISTRY}/k8s-gcr-io-kube-scheduler-amd64:v1.11.0 docker pull ${MY_REGISTRY}/k8s-gcr-io-kube-proxy-amd64:v1.11.0 docker pull ${MY_REGISTRY}/k8s-gcr-io-etcd-amd64:3.2.18 docker pull ${MY_REGISTRY}/k8s-gcr-io-pause-amd64:3.1 docker pull ${MY_REGISTRY}/k8s-gcr-io-coredns:1.1.3 ## 添加Tag docker tag ${MY_REGISTRY}/k8s-gcr-io-kube-apiserver-amd64:v1.11.0 k8s.gcr.io/kube-apiserver-amd64:v1.11.0 docker tag ${MY_REGISTRY}/k8s-gcr-io-kube-scheduler-amd64:v1.11.0 k8s.gcr.io/kube-scheduler-amd64:v1.11.0 docker tag ${MY_REGISTRY}/k8s-gcr-io-kube-controller-manager-amd64:v1.11.0 k8s.gcr.io/kube-controller-manager-amd64:v1.11.0 docker tag ${MY_REGISTRY}/k8s-gcr-io-kube-proxy-amd64:v1.11.0 k8s.gcr.io/kube-proxy-amd64:v1.11.0 docker tag ${MY_REGISTRY}/k8s-gcr-io-etcd-amd64:3.2.18 k8s.gcr.io/etcd-amd64:3.2.18 docker tag ${MY_REGISTRY}/k8s-gcr-io-pause-amd64:3.1 k8s.gcr.io/pause-amd64:3.1 docker tag ${MY_REGISTRY}/k8s-gcr-io-coredns:1.1.3 k8s.gcr.io/coredns:1.1.3 echo "" echo "==========================================================" echo "Pull Kubernetes 1.11.0 Images FINISHED." echo "into registry.cn-hangzhou.aliyuncs.com/openthings, " echo " by openthings@https://my.oschina.net/u/2306127." echo "==========================================================" echo ""
保存为aliyun-get-kubeimages-1.11.0.sh,复制上面内容进去,然后运行即可。
2、安装Kubernetes
安装Kubernetes1.11.0(下面以Ubuntu16.04.03LTS为准),如下:
第一步,安装Kubeadm: echo "添加Kubernetes安装源认证key:" sudo curl -sSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add echo "添加Kubernetes安装源:" sudo echo deb http://apt.kubernetes.io/ kubernetes-xenial main > /etc/apt/sources.list.d/kubernetes.list echo "更新系统软件包列表:" sudo apt update echo "查看Kubernetes的可用版本:" apt-cache madison kubeadm echo "安装kubeadm 1.11.0: " apt-get install -y kubeadm=1.11.0-00 kubectl=1.11.0-00 kubelet=1.11.0-00
第二步,通过kubeadm安装Kubernetes1.11.0: echo "" echo "==========================================================" echo "Setup Kubernetes Cluster 1.11.0 using kubeadm......" echo "" echo "Please visit https://my.oschina.net/u/2306127/blog/1628082" echo "==========================================================" echo "" echo "Close Swap, K8S required." sudo swapoff -a echo "请sudo nano /etc/fstab中永久删除或关闭swap分区,使用 # 注释掉即可。" echo "" echo "Close IPTable,K8S required." sudo iptables -F echo "" echo "" echo "Setup Kubernetes Cluster using kubeadm..." sudo kubeadm init --kubernetes-version=v1.11.0 –pod-network-cidr 10.244.0.0/16 --feature-gates CoreDNS=true echo "" echo "Install flannel CNI network driver..." sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml echo "" echo "Kubernetes status..." kubectl get pod --all-namespaces echo "" echo "==========================================================" echo "Please visit https://my.oschina.net/u/2306127/blog/1628082" echo "==========================================================" echo ""
3、构建自己的镜像库 创建aliyun上的k8s镜像脚本如下,如果想自己做一个镜像库,可以拷下去自己修改: echo "" echo "==========================================================" echo "Push Kubernetes 1.11.0 Images into aliyuncs.com ......" echo "==========================================================" echo "" echo "docker tag to openthings ..." ## 添加Tag for registry.cn-hangzhou.aliyuncs.com/openthings MY_REGISTRY=registry.cn-hangzhou.aliyuncs.com/openthings docker tag k8s.gcr.io/kube-apiserver-amd64:v1.11.0 ${MY_REGISTRY}/k8s-gcr-io-kube-apiserver-amd64:v1.11.0 docker tag k8s.gcr.io/kube-scheduler-amd64:v1.11.0 ${MY_REGISTRY}/k8s-gcr-io-kube-scheduler-amd64:v1.11.0 docker tag k8s.gcr.io/kube-controller-manager-amd64:v1.11.0 ${MY_REGISTRY}/k8s-gcr-io-kube-controller-manager-amd64:v1.11.0 docker tag k8s.gcr.io/kube-proxy-amd64:v1.11.0 ${MY_REGISTRY}/k8s-gcr-io-kube-proxy-amd64:v1.11.0 docker tag k8s.gcr.io/etcd-amd64:3.2.18 ${MY_REGISTRY}/k8s-gcr-io-etcd-amd64:3.2.18 docker tag k8s.gcr.io/pause-amd64:3.1 ${MY_REGISTRY}/k8s-gcr-io-pause-amd64:3.1 docker tag k8s.gcr.io/coredns:1.1.3 ${MY_REGISTRY}/k8s-gcr-io-coredns:1.1.3 echo "" echo "==========================================================" echo "" ## Push镜像 echo "" echo "1.k8s-kube-apiserver-amd64" docker push ${MY_REGISTRY}/k8s-gcr-io-kube-apiserver-amd64:v1.11.0 echo "" echo "2.k8s-kube-controller-manager-amd64" docker push ${MY_REGISTRY}/k8s-gcr-io-kube-controller-manager-amd64:v1.11.0 echo "" echo "3.k8s-kube-scheduler-amd64" docker push ${MY_REGISTRY}/k8s-gcr-io-kube-scheduler-amd64:v1.11.0 echo "" echo "4.k8s-kube-proxy-amd64" docker push ${MY_REGISTRY}/k8s-gcr-io-kube-proxy-amd64:v1.11.0 echo "" echo "5.k8s-etcd-amd64" docker push ${MY_REGISTRY}/k8s-gcr-io-etcd-amd64:3.2.18 echo "" echo "6.k8s-pause-amd64" docker push ${MY_REGISTRY}/k8s-gcr-io-pause-amd64:3.1 echo "" echo "7.k8s-coredns" docker push ${MY_REGISTRY}/k8s-gcr-io-coredns:1.1.3 echo "" echo "==========================================================" echo "Push Kubernetes 1.11.0 Images FINISHED." echo "into registry.cn-hangzhou.aliyuncs.com/openthings, " echo " by openthings@https://my.oschina.net/u/2306127." echo "==========================================================" echo ""
上面的脚本需要在能够同时访问gcr.io和阿里云的registry服务的机器上运行。镜像仓库服务我在这里用的阿里云的{华东1}的容器仓库服务(registry.cn-hangzhou.aliyuncs.com,需要注意:阿里云的每个区都有registry服务区,是相互独立的),你可以改成自己的地址。
大数据
2018-07-09 21:36:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.元字段概述
官方解释:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-fields.html#_document_source_meta_fields
mapping元字段是mapping映射中描述文档本身的字段,大致可以分为文档属性元数据、文档元数据、索引元数据、路由元数据和自定义元数据。
2.主要字段解读
_index
多索引查询时,有时候只需要在特地索引名上进行查询,_index字段提供了便利,也就是说可以对索引名进行term查询、terms查询、聚合分析、使用脚本和排序。
_index是一个虚拟字段,不会真的加到Lucene索引中,对_index进行term、terms查询(也包括match、query_string、simple_query_string),但是不支持prefix、wildcard、regexp和fuzzy查询。
_type
在6.0.0中弃用,此doc的mapping type名, 自动被索引,可被查询,聚合,排序使用,或者脚本里访问
_id
doc的id,建索引时候传入 ,不被索引, 可通过_uid被查询,脚本里使用,不能参与聚合或排序 PUT my_index PUT my_index/my_type/1 { "text":"this is a doc" } PUT my_index/my_type/2 { "text": "Document with ID 2" } GET my_index/_search { "query": { "terms": { "_id": ["1","2"] } } }
创建索引,添加文档,通过_id查询文档 { "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "my_index", "_type": "my_type", "_id": "2", "_score": 1, "_source": { "text": "Document with ID 2" } }, { "_index": "my_index", "_type": "my_type", "_id": "1", "_score": 1, "_source": { "text": "this is a doc" } } ] } }
6.0之前的版本并不是这样的,因为它们支持多种类型,所以_type和_id被合并为一个名为_uid的复合主键。
_uid
在6.0.0中弃用。现在,类型已被删除,文档由_id唯一标识,_uid字段仅作为查看_id字段以保持向后兼容。
_source
_source字段包含在索引时传递的原始JSON文档正文。 _source字段本身没有编入索引(因此不可搜索),但它被存储,以便在执行获取请求(如get或search)时可以返回它。
默认_source字段是开启的,也就是说,默认情况下存储文档的原始值。
如果某个字段内容非常多(比如一篇小说),或者查询业务只需要对该字段进行搜索,返回文档id,然后通过其他途径查看文档原文,则不需要保留_source元字段。可以通过禁用_source元字段,在ElasticSearch 中只存储倒排索引,不保留字段原始值。
_source禁用 DELETE my_index PUT my_index { "mappings": { "my_type":{ "_source": {"enabled": false} } } } PUT my_index/my_type/1 { "text":"this is a doc" }
通过id查询文档 GET my_index/my_type/1
结果中并没有_source字段内容 { "_index": "my_index", "_type": "my_type", "_id": "1", "_version": 1, "found": true }
_source包含或者排除字段 DELETE my_index PUT my_index { "mappings": { "blog": { "_source": { "includes": [ "title", "url" ], "excludes": [ "content" ] }, "properties": { "title": { "type": "text" }, "content": { "type": "text" }, "url": { "type": "text" } } } } }
定义my_index索引blog文档结构包含三个属性:title、content、url。设置_source属性包含title和url不包含content。 PUT my_index/blog/1 { "title":"百度搜索", "content":"搜索查询的内容有哪些", "url":"http://www.baidu.com" } GET my_index/blog/1
查询结果只能看到title和url两个字段 { "_index": "my_index", "_type": "blog", "_id": "1", "_version": 1, "found": true, "_source": { "title": "百度搜索", "url": "http://www.baidu.com" } }
_field_names
_field_names字段索引文档中每个字段的名称,其中包含除null以外的任何值。

_routing
使用以下公式将文档路由到索引中的特定分片。 shard_num = hash(_routing) % num_primary_shards
自定义路由模式可以通过指定每个文档的自定义路由值来实现。 PUT my_index/my_type/3?routing=user1 { "title":"this is 3", "body":"this is 3 body" } GET my_index/my_type/3?routing=user1
查询结果 { "_index": "my_index", "_type": "my_type", "_id": "3", "_version": 2, "_routing": "user1", "found": true, "_source": { "title": "this is 3", "body": "this is 3 body" } }
查询所有“user1”路由下的文档 GET my_index/_search { "query": { "term": { "_routing": { "value": "user1" } } } }
查询结果 { "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.2876821, "hits": [ { "_index": "my_index", "_type": "my_type", "_id": "3", "_score": 0.2876821, "_routing": "user1", "_source": { "title": "this is 3", "body": "this is 3 body" } } ] } }






















大数据
2018-07-09 20:33:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
数仓框架: 商业系统 InfoBright Greenplum(已开源)、HP Vertica、TeraData、Palo、ExaData、RedShift、BigQuery(Dremel) 开源实现 Impala、Presto、Spark SQL、Drill、Hawq Druid、Pinot Kylin
大体分为三类:
1.基于hbase预聚合的,比如Opentsdb,Kylin,Druid等,需要指定预聚合的指标,在数据接入的时候根据指定的指标进行聚合运算,适合相对固定的业务报表类需求,只需要统计少量维度即可满足业务报表需求
2.基于Parquet列式存储的,比如Presto, Drill,Impala等,基本是完全基于内存的并行计算,Parquet系能降低存储空间,提高IO效率,以离线处理为主,很难提高数据写的实时性,超大表的join支持可能不够好。spark sql也算类似,但它在内存不足时可以spill disk来支持超大数据查询和join
3.基于lucene外部索引的,比如ElasticSearch和Solr,能够满足的的查询场景远多于传统的数据库存储,但对于日志、行为类时序数据,所有的搜索请求都也必须搜索所有的分片,另外,对于聚合分析场景的支持也是软肋
开源大数据查询分析引擎现状 2015-06-03 14:39:42 | 编辑:hely | 查看: 6758 | 评论: 0
大数据查询分析是云计算中核心问题之一,自从Google在2006年之前的几篇论文奠定云计算领域基础,尤其是GFS、Map-Reduce、 Bigtable被称为云计算底层技术三大基石。
文|叶蓬
引言
大数据查询分析 是 云计算 中核心问题之一,自从Google在2006年之前的几篇论文奠定云计算领域基础,尤其是GFS、Map-Reduce、 Bigtable被称为云计算底层技术三大基石。GFS、Map-Reduce技术直接支持了Apache Hadoop项目的诞生。Bigtable和Amazon Dynamo直接催生了NoSQL这个崭新的数据库领域,撼动了RDBMS在商用数据库和数据仓库方面几十年的统治性地位。FaceBook的Hive项目是建立在Hadoop上的数据仓库基础构架,提供了一系列用于存储、查询和分析大规模数据的工具。当我们还浸淫在GFS、Map-Reduce、 Bigtable等Google技术中,并进行理解、掌握、模仿时,Google在2009年之后,连续推出多项新技术,包括:Dremel、 Pregel、Percolator、Spanner和F1。其中,Dremel促使了实时计算系统的兴起,Pregel开辟了图数据计算这个新方向,Percolator使分布式增量索引更新成为文本检索领域的新标准,Spanner和F1向我们展现了跨数据中心数据库的可能。在Google的第二波技术浪潮中,基于Hive和Dremel,新兴的大数据公司Cloudera开源了大数据查询分析引擎Impala,Hortonworks开源了 Stinger,Fackbook开源了Presto。类似Pregel,UC Berkeley AMPLAB实验室开发了Spark图计算框架,并以Spark为核心开源了大数据查询分析引擎Shark。由于某电信运营商项目中大数据查询引擎选型需求,本文将会对Hive、Impala、Shark、Stinger和Presto这五类主流的开源大数据查询分析引擎进行简要介绍以及性能比较,最后进行总结与展望。Hive、Impala、Shark、Stinger和Presto的进化图谱如图1所示。


图1. Impala、Shark、Stinger和Presto的进化图谱
当前主流引擎简介
基于Map-Reduce模式的Hadoop擅长数据批处理,不是特别符合即时查询的场景。实时查询一般使用MPP (Massively Parallel Processing)的架构,因此用户需要在Hadoop和MPP两种技术中选择。在Google的第二波技术浪潮中,一些基于Hadoop架构的快速 SQL访问技术逐步获得人们关注。现在有一种新的趋势是MPP和Hadoop相结合提供快速SQL访问框架。最近有四个很热门的开源工具出来:Impala、Shark、Stinger和Presto。这也显示了大数据领域对于Hadoop生态系统中支持实时查询的期望。总体来说,Impala、Shark、Stinger和Presto四个系统都是类SQL实时大数据查询分析引擎,但是它们的技术侧重点完全不同。而且它们也不是为了替换Hive而生,Hive在做数据仓库时是非常有价值的。这四个系统与Hive都是构建在Hadoop之上的数据查询工具,各有不同的侧重适应面,但从客户端使用来看它们与Hive有很多的共同之处,如数据表元数据、Thrift接口、ODBC/JDBC驱动、SQL语法、灵活的文件格式、存储资源池等。Hive与Impala、Shark、Stinger、Presto在Hadoop中的关系如图2所示。Hive适用于长时间的批处理查询分析,而Impala、Shark、Stinger和Presto适用于实时交互式SQL查询,它们给数据分析人员提供了快速实验、验证想法的大数据分析工具。可以先使用Hive进行数据转换处理,之后使用这四个系统中的一个在Hive处理后的结果数据集上进行快速的数据分析。下面,从问题域出发简单介绍 Hive、Impala、Shark、Stinger和Presto:
1) Hive, 披着SQL外衣的Map-Reduce。Hive是为方便用户使用Map-Reduce而在外面封装了一层SQL,由于Hive采用了SQL,它的问题域比Map-Reduce更窄,因为很多问题,SQL表达不出来,比如一些数据挖掘算法,推荐算法、图像识别算法等,这些仍只能通过编写Map-Reduce完成。
2) Impala: Google Dremel的开源实现(Apache Drill类似),因为交互式实时计算需求,Cloudera推出了Impala系统,该系统适用于交互式实时处理场景,要求最后产生的数据量一定要少。
3) Shark/Spark:为 了提高Map-Reduce的计算效率,Berkeley的AMPLab实验室开发了Spark,Spark可看做基于内存的Map-Reduce实现,此外,伯克利还在Spark基础上封装了一层SQL,产生了一个新的类似Hive的系统Shark。
4) Stinger Initiative(Tez optimized Hive): Hortonworks开源了一个DAG计算框架Tez,Tez可以理解为Google Pregel的开源实现,该框架可以像Map-Reduce一样,可以用来设计DAG应用程序,但需要注意的是,Tez只能运行在YARN上。Tez的一个重要应用是优化Hive和PIG这种典型的DAG应用场景,它通过减少数据读写IO,优化DAG流程使得Hive速度提供了很多倍。
5) Presto :FaceBook于2013年11月份开源了Presto,一个分布式SQL查询引擎,它被设计为用来专门进行高速、实时的数据分析。它支持标准的ANSI SQL,包括复杂查询、聚合(aggregation)、连接(join)和窗口函数(window functions)。Presto设计了一个简单的数据存储的抽象层,来满足在不同数据存储系统(包括HBase、HDFS、Scribe等)之上都可以使用SQL进行查询。

图2. Hive与Impala、Shark、Stinger、Presto在Hadoop中的关系
当前主流引擎架构

Hive
Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供完整的SQL查询功能,可以将SQL语句转换为 Map-Reduce任务进行运行,十分适合数据仓库的统计分析。其架构如图3所示,Hadoop和Map-Reduce是Hive架构的根基。Hive 架构包括如下组件:CLI(Command Line Interface)、JDBC/ODBC、Thrift Server、Meta Store和Driver(Complier、Optimizer和Executor)。


图3. Hive架构
Impala架构
Impala是Cloudera在受到Google的Dremel启发下开发的实时交互SQL大数据查询工具,它可以看成是Google Dremel架构和MPP (Massively Parallel Processing)结构的结合体。Impala没有再使用缓慢的Hive&Map-Reduce批处理,而是通过使用与商用并行关系数据库中类似的分布式查询引擎(由Query Planner、Query Coordinator和Query Exec Engine三部分组成),可以直接从HDFS或HBase中用SELECT、JOIN和统计函数查询数据,从而大大降低了延迟,其架构如图4所示,Impala主要由Impalad,State Store和CLI组成。Impalad与DataNode运行在同一节点上,由Impalad进程表示,它接收客户端的查询请求(接收查询请求的 Impalad为Coordinator,Coordinator通过JNI调用java前端解释SQL查询语句,生成查询计划树,再通过调度器把执行计划分发给具有相应数据的其它Impalad进行执行),读写数据,并行执行查询,并把结果通过网络流式的传送回给Coordinator,由 Coordinator返回给客户端。同时Impalad也与State Store保持连接,用于确定哪个Impalad是健康和可以接受新的工作。Impala State Store跟踪集群中的Impalad的健康状态及位置信息,由state-stored进程表示,它通过创建多个线程来处理Impalad的注册订阅和与各Impalad保持心跳连接,各Impalad都会缓存一份State Store中的信息,当State Store离线后,因为Impalad有State Store的缓存仍然可以工作,但会因为有些Impalad失效了,而已缓存数据无法更新,导致把执行计划分配给了失效的Impalad,导致查询失败。 CLI提供给用户查询使用的命令行工具,同时Impala还提供了Hue,JDBC,ODBC,Thrift使用接口。


图4. Impala架构
Shark架构
Shark是UC Berkeley AMPLAB开源的一款数据仓库产品,它完全兼容Hive的HQL语法,但与Hive不同的是,Hive的计算框架采用Map-Reduce,而 Shark采用Spark。所以,Hive是SQL on Map-Reduce,而Shark是Hive on Spark。其架构如图4所示,为了最大程度的保持和Hive的兼容性,Shark复用了Hive的大部分组件,如下所示:
1) SQL Parser&Plan generation: Shark完全兼容Hive的HQL语法,而且Shark使用了Hive的API来实现query Parsing和 query Plan generation,仅仅最后的Physical Plan execution阶段用Spark代替Hadoop Map-Reduce;
2) metastore:Shark采用和Hive一样的meta信息,Hive里创建的表用Shark可无缝访问;
3) SerDe: Shark的序列化机制以及数据类型与Hive完全一致;
4) UDF: Shark可重用Hive里的所有UDF。通过配置Shark参数,Shark可以自动在内存中缓存特定的RDD(Resilient Distributed Dataset),实现数据重用,进而加快特定数据集的检索。同时,Shark通过UDF用户自定义函数实现特定的数据分析学习算法,使得SQL数据查询和运算分析能结合在一起,最大化RDD的重复使用;
5) Driver:Shark在Hive的CliDriver基础上进行了一个封装,生成一个SharkCliDriver,这是shark命令的入口;
6) ThriftServer:Shark在Hive的ThriftServer(支持JDBC/ODBC)基础上,做了一个封装,生成了一个SharkServer,也提供JDBC/ODBC服务。


图5. Shark架构
Spark是UC Berkeley AMP lab所开源的类Hadoop Map-Reduce的通用的并行计算框架,Spark基于Map-Reduce算法实现的分布式计算,拥有Hadoop Map-Reduce所具有的优点;但不同于Map-Reduce的是Job中间输出和结果可以保存在内存中,从而不再需要读写HDFS,因此Spark 能更好地适用于数据挖掘与机器学习等需要迭代的Map-Reduce的算法。其架构如图6所示:


图6. Spark架构
与Hadoop的对比,Spark的中间数据放到内存中,对于迭代运算效率更高,因此Spark适用于需要多次操作特定数据集的应用场合。需要反复操作的次数越多,所需读取的数据量越大,受益越大,数据量小但是计算密集度较大的场合,受益就相对较小。Spark比Hadoop更通用,Spark提供的数据集操作类型有很多种(map, filter, flatMap, sample, groupByKey, reduceByKey, union, join, cogroup, mapValues, sort,partionBy等),而Hadoop只提供了Map和Reduce两种操作。Spark可以直接对HDFS进行数据的读写,同样支持 Spark on YARN。Spark可以与Map-Reduce运行于同集群中,共享存储资源与计算,数据仓库Shark实现上借用Hive,几乎与Hive完全兼容。
Stinger架构
Stinger是Hortonworks开源的一个实时类SQL即时查询系统,声称可以提升较Hive 100倍的速度。与Hive不同的是,Stinger采用Tez。所以,Hive是SQL on Map-Reduce,而Stinger是Hive on Tez。Tez的一个重要作用是优化Hive和PIG这种典型的DAG应用场景,它通过减少数据读写IO,优化DAG流程使得Hive速度提供了很多倍。其架构如图7所示, Stinger是在Hive的现有基础上加了一个优化层Tez(此框架是基于Yarn),所有的查询和统计都要经过它的优化层来处理,以减少不必要的工作以及资源开销。虽然Stinger也对Hive进行了较多的优化与加强,Stinger总体性能还是依赖其子系统Tez的表现。而Tez是 Hortonworks开源的一个DAG计算框架,Tez可以理解为Google Pregel的开源实现,该框架可以像Map-Reduce一样,用来设计DAG应用程序,但需要注意的是,Tez只能运行在YARN上。


图7. Stinger架构
Presto架构
2013年11月Facebook开源了一个分布式SQL查询引擎Presto,它被设计为用来专门进行高速、实时的数据分析。它支持标准的 ANSI SQL子集,包括复杂查询、聚合、连接和窗口函数。其简化的架构如图8所示,客户端将SQL查询发送到Presto的协调器。协调器会进行语法检查、分析和规划查询计划。调度器将执行的管道组合在一起,将任务分配给那些里数据最近的节点,然后监控执行过程。客户端从输出段中将数据取出,这些数据是从更底层的处理段中依次取出的。Presto的运行模型与Hive有着本质的区别。Hive将查询翻译成多阶段的Map-Reduce任务,一个接着一个地运行。每一个任务从磁盘上读取输入数据并且将中间结果输出到磁盘上。然而Presto引擎没有使用Map-Reduce。它使用了一个定制的查询执行引擎和响应操作符来支持SQL的语法。除了改进的调度算法之外,所有的数据处理都是在内存中进行的。不同的处理端通过网络组成处理的流水线。这样会避免不必要的磁盘读写和额外的延迟。这种流水线式的执行模型会在同一时间运行多个数据处理段,一旦数据可用的时候就会将数据从一个处理段传入到下一个处理段。这样的方式会大大的减少各种查询的端到端响应时间。同时,Presto设计了一个简单的数据存储抽象层,来满足在不同数据存储系统之上都可以使用SQL进行查询。存储连接器目前支持除Hive/HDFS外,还支持HBase、Scribe和定制开发的系统。


图8. Presto架构
性能评测总结

通过对Hive、Impala、Shark、Stinger和Presto的评测和分析,总结如下:
1) 列存储一般对查询性能提升明显,尤其是大表是一个包含很多列的表。例如,从Stinger(Hive 0.11 with ORCFile)VS Hive,以及Impala的Parquet VS Text file;
2) 绕开MR计算模型,省去中间结果的持久化和MR任务调度的延迟,会带来性能提升。例如,Impala,Shark,Presto要好于Hive和Stinger,但这种优势随着数据量增加和查询变复杂而减弱;
3) 使用MPP数据库技术对连接查询有帮助。例如,Impala在两表,多表连接查询中优势明显;
4) 充分利用缓存的系统在内存充足的情况下性能优势明显。例如,Shark,Impala在小数据量时性能优势明显;内存不足时性能下降严重,Shark会出现很多问题;
5) 数据倾斜会严重影响一些系统的性能。例如,Hive、Stinger、Shark对数据倾斜比较敏感,容易造成倾斜;Impala受这方面的影响似乎不大;
对于Hive、Impala、Shark、Stinger和Presto这五类开源的分析引擎,在大多数情况下,Imapla的综合性能是最稳定的,时间性能也是最好的,而且其安装配置过程也相对容易。其他分别为Presto、Shark、Stinger和Hive。在内存足够和非Join操作情况下,Shark的性能是最好的。
总结与展望
对大数据分析的项目来说,技术往往不是最关键的,关键在于谁的生态系统更强,技术上一时的领先并不足以保证项目的最终成功。对于Hive、 Impala、Shark、Stinger和Presto来讲,最后哪一款产品会成为事实上的标准还很难说,但我们唯一可以确定并坚信的一点是,大数据分析将随着新技术的不断推陈出新而不断普及开来,这对用户永远都是一件幸事。举个例子,如果读者注意过下一代Hadoop(YARN)的发展的话就会发现,其实YARN已经支持Map-Reduce之外的计算范式(例如Shark,Impala等),因此将来Hadoop将可能作为一个兼容并包的大平
greenplum安装配置:
http://www.cnblogs.com/zzjhn/p/5912386.html
http://www.cnblogs.com/liuyungao/p/5689588.html
https://blog.csdn.net/u013181216/article/details/72605362
https://blog.csdn.net/gnail_oug/article/details/46945283
Greenplum 入门——基础知识、安装、常用函数
greenplum数据库学习笔记
Greenplum 详解
详解开源大数据引擎 Greenplum 的架构和技术特点
开源大数据引擎: Greenplum 数据库架构分析
Greenplum 优化--数据库配置篇
Greenplum 优化--SQL调优篇
聊聊Greenplum的那些事
Greenplum概述及架构
impala安装配置:
http://lxw1234.com/archives/2017/06/862.htm
presto安装配置:
http://lxw1234.com/archives/2017/09/879.htm
Presto、Impala性能比较
2018年01月22日 01:32:09
阅读数:4347
下面是Presto、Impala这两种典型的内存数据库的简单测试比较,当然这种内存数据库类似的还有spark sql,这种数据库在大数据量,多表关联查询时,会展现出自己的优势,下面是一组impala和presto的性能对比图:

环境准备:1台32G内存、2台16G内存,没有完全把内存配置饱和
测试数据:hive中3张2000W数据量的表
集群:impala和presro部署在3台机器上

presto版本:presto-server-0.191 (presto安装:http://blog.csdn.net/u012551524/article/details/79013194)
impala版本:2.8.0-cdh5.11.0

1、单表的聚合操作

Presto:count


1s(presto目前只精确到整数,所以小于1s也是显示1s)


Impala:count

0.24s


Presto:count、distinct

取了3次,分别是:4、3、3 (s)


Impala:count、distinct

取了3次:0.74、0.75、0.76(s)


2、单值查询

Presto :查询一个ID的记录
3次:6、5、6(s)


Impala:

3次都在1.7s左右


3、两表关联(2张2000W的表做join)
Presto:

3次结果:9、11、9


Impala:

3次结果在7s左右


4、3表关联(3张2000W的表做join)
Presto:

4次结果:13、11、15、12(s)


Impala:


3次结果在8.9s左右


总结:这是一些场景下的查询效率的比较,数据量不是很大,但是能看出一些问题,他们的共同点就是吃内存,当然在内存充足的情况下,并且有规模适当的集群,性能应该会更可观,从上图可以看出Impala性能稍领先于presto,但是presto在数据源支持上非常丰富,包括hive、图数据库、传统关系型数据库、Redis等

缺点:这两种对hbase支持的都不好,presto 不支持,但是对hdfs、hive兼容性很好,其实这也是顺理成章的,所以数据源的处理很重要,针对hbase的二级索引查询可以用phoenix,效果也不错

作者:Greg Rahn和Mostafa Mokhtar
与传统的分析数据库(Greenplum)相比,未经修改的基于TPC-DS的性能基准测试表现出了Impala的领导地位,特别是对于多用户并发工作负载而言。此外,基准测试还进一步证明了分析数据库与Hive LLAP、Spark SQL和Presto等SQL-on-Hadoop引擎之间存在的显著性能差距。
过去一年是Apache Impala(正在孵化中)发展变化最大的一年。Impala团队不仅继续努力不断扩大其规模和稳定性,而且还推出了一系列的关键功能,进一步巩固了Impala作为高性能商务智能(BI)和SQL分析的开放标准地位。对于云计算和混合部署而言,Impala现在可以提供云端-本地部署弹性、灵活性,以及直接从Amazon S3对象存储中(以及为未来一年制定的其他对象存储)读取/写入的能力。随着Apache Kudu的GA,用户现在可以使用Impala对接收到或更新的数据立即进行高性能分析。另外,也很容易将现有的商务智能(BI)工作负载从传统分析数据库或数据仓库迁移至由Impala构建的Cloudera分析数据库中,同时可以使用Navigator Optimizer优化其性能。而且如同以往一样,对于更大的并发性工作负载的性能改进仍然是全年工作的重中之重。
除了这些性能改进之外,随着越来越多的企业机构(例如纽约证券交易所(NYSE)和奎斯特诊断公司(Quest Diagnostics))已经注意到Cloudera现代分析数据库(而不是传统分析数据库)的灵活性、可扩展性和支持SQL及非SQL工作负载(例如数据科学、机器学习和操作性工作负载)的开放式架构,Impala的采用率也在不断增长。
对于该基准测试而言,我们使用未经修改的多用户TPC-DS查询对具有Impala的Cloudera现代分析数据库与传统分析数据库(Greenplum)进行了性能比较。我们还研究了分析数据库与SQL-on-Hadoop引擎,例如:Hive LLAP、Spark SQL和Presto的对比。总的来说,我们发现:
● Impala相对于传统分析数据库而言性能更为先进,包括超过8倍的高并发工作负载性能。
● 分析数据库和其他SQL-on-Hadoop引擎之间存在显著的性能差异,使用Impala可以使多用户工作负载的性能提高近22倍。
● 其他SQL-on-Hadoop引擎也无法完成大规模基准测试来与分析数据库进行比较,因此需要一个简化的、规模较小的基准测试(Hive甚至还需要修改,Presto无法完成多用户测试)。
比较集分析数据库(采用10TB和1TB级别的数据进行测试,未经修改的查询)。● Impala 2.8 from CDH 5.10;
● Greenplum Database 4.3.9.1。
附加的SQL-on-Hadoop引擎(采用1TB级别的数据进行测试,并对Hive进行了一些查询修改)。
● Spark SQL 2.1;
● Presto 0.160;
● Hive 2.1 with LLAP from HDP 2.5。
配置每一个集群由七个工作节点组成,每个节点采用以下配置:● CPU:2 块E5-2698 v4 @ 2.20GHz;
● 存储器:8 块2TB硬盘;
● 内存:256GB内存。
我们配置了三个由相同硬件组成的集群,其中一个用于Impala、Spark和Presto(负责运行CDH),另一个用于Greenplum,还有一个用于具有LLAP(负责运行HDP)的Hive。每个集群都装载了相同的TPC-DS数据:针对Impala和Spark的Parquet/Snappy,以及针对Hive和Presto的ORCFile/Zlib,而Greenplum使用内部的柱状格式与QuickLZ压缩文件。
查询工作负载:● 数据:TPC-DS 10TB和1TB(比例系数);
● 查询:TPC-DS v2.4查询模板(未经修改的TPC-DS)。
我们运行了77个查询,所有引擎的运行都具有语言支持,无需修改TPC-DS规范(Hive除外)。1其中22个已排除的查询都使用以下几个不常见的SQL功能:
● 使用ROLLUP进行的11个查询(TPC-DS允许的变体在本测试中未使用);
● 3个INTERSECT或EXCEPT查询;
● 8个具有高级子查询位置的查询(例如HAVING子句中的子查询等)。
由于Hive对子查询位置的更大限制,我们被迫进行了一些修改以创建语义上等同的查询。我们针对Hive运行了这些经过修改的查询。
虽然Greenplum、Presto和Spark SQL也声称支持所有99个未经修改的查询,但是即使没有并发执行,Spark SQL和Presto也无法成功完成10TB级别的99个查询。Greenplum随着多用户并发性的增加而出现越来越多的查询失败(详见下文)。
分析数据库基准测试结果10 TB级别上Impala与Greenplum的比较我们使用常见的77个未修改的TPC-DS查询在10TB级别数据下对Impala和Greenplum进行了测试。在单用户测试和更实际的多用户测试集上比较了两个、四个和八个并发流。总结如下:● 总体来说,Impala在单用户和多用户并发测试方面优于Greenplum。
● 相比Greenplum而言,Impala 线性扩展表现更优异,随着并发度增加,Impala与Greenplum的性能比率从2倍上升到了8.3倍,,同时保持了更高的成功率。
在单用户测试中,当比较查询中的几何平均值时,Impala的性能是Greenplum的2.8倍;完成查询流的总时间是Greenplum的1.8倍:
对于多用户吞吐量比较,我们使用TPC-DS dsqgen工具运行同一组77个未修改的查询来生成并发查询流。每个查询流由随机排序的77个通用查询组成,并且每个查询流使用不同的查询替换值。我们运行了多个测试,增加了超过系统饱和点的查询流数量,并且测量了各个并发级别的所有后续查询的吞吐量。
如下图所示,与Greenplum相比,Impala的性能指标随着并发速度从2个查询流2倍的速度提升加速到8个查询流8.3倍的速度提升。
鉴于集群的规模与数据集大小和并发性相比较而言较小,对于Impala和Greenplum这两个系统而言,预期在并发性增加时会发现一些查询失败。Impala和Greenplum这两个系统在两个查询流测试中达到了100%的成功率。对于四个和八个查询流测试,Impala系统的平均成功率为97%,而Greenplum系统的成功率下降到50%。如果这些测试在大于7节点集群的集群上运行,则可以预期这两个系统的成功率都会相应提高。分析数据库与SQL-on-Hadoop引擎1TB基准测试我们已经尝试针对SQL-on-Hadoop引擎使用相同的77个查询和10TB级别基准测试,但是,Hive、Presto和Spark SQL都无法成功完成77个未修改查询中的大多数查询,甚至仅仅是单用户结果也未能成功,因此无法在10TB级上进行比较。因此,我们在1TB规模下运行了单独的比较,将分析数据库引擎与其余的SQL-on-Hadoop引擎进行比较。除了Hive之外,所有的引擎都使用相同的77个TPC-DS查询,但是需要进行一些修改,以寻找方法去绕过这些限制条件,从而解决无法解析的子查询。
通过这些简化的标准(对于其他SQL-on-Hadoop引擎来说是非常必要的),我们再次对所有五个引擎进行了单用户测试和更为真实环境的多用户测试。测试结果汇总如下:
● 分析数据库 – Impala和Greenplum系统在各个并发级别展现出的性能都优于所有的SQL-on-Hadoop引擎。
● 随着并发性的提高,再次看到Impala在性能方面拔得头筹,是其他引擎的8.5倍 – 21.6倍。
● 在所有引擎中,Presto在单用户测试中表现出最慢的性能,甚至无法完成多用户测试。
在该单用户测试中,我们再次看到,在几何平均值方面相较而言Impala仍然保持了其性能优势,但是,Greenplum在总时间上略有下降。这两个分析数据库的性能显着优于其他引擎,与其他SQL-on-Hadoop引擎相比,Impala在几何平均值方面性能优势在3.6倍至13倍之间,在总时间方面性能优势在2.8倍-8.3倍之间。
Presto对除了过滤、分组和聚合的简单单表扫描之外的其他常见的 SQL 查询表现的很挣扎。对于非常简单的查询类型,它更符合Spark SQL的性能,但是如上所述,对于使用更多标准SQL(包括连接)的更典型的商务智能(BI)查询,是执行效果最差的SQL-on-Hadoop集群。
使用TPC-DS通过四个、八个和十六个并发流运行更具代表性的多用户比较测试,以生成与上述的10TB分析数据库比较一样的随机查询流。除Presto之外,所有引擎都能够在三个并发级别的1TB级别下完成流,而不会出现任何查询失败。即使只运行四个并发查询,Presto也可能由于内存不足错误而使大多数查询失败。
对于能够成功完成多用户并发测试的引擎,分析数据库组群和SQL-on-Hadoop组群之间的性能差异变得更加明显。Impala在每一个并发级别上都展示出了优异的吞吐量 – 不仅比Greenplum快1.3-2.8倍,与Spark SQL相比,其速度快达6.5-21.6倍,并且比Hive快8.5-19.9倍。
结论各企业机构越来越期待现代化改造其系统架构,但是不愿意牺牲重要商务智能(BI)和SQL分析所需的交互式、多用户性能。Impala作为Cloudera公司平台的一部分,能够独特地提供一个现代化分析数据库。通过设计,Impala可以灵活地支持更多种类的数据和使用案例,而无需任何前期建模工作;Impala可以有弹性地和成本高效地在公司内部部署和云端部署方式下按需进行扩展;并且,作为共享平台的一部分,这些相同的数据可用于其他团队和工作负载,而不仅仅只是SQL分析,因此可以进一步拓展其价值。此外,从上述基准测试结果可以看出,与传统分析数据库相比,Impala还提供了领先的性能。无论整体性能还是大规模运算以及不断激增的并发性工作负载能力方面,分析数据库群(Impala、Greenplum)和SQL-on-Hadoop组群(Hive,Presto,Spark)之间的差异也变得非常明显。虽然其他SQL-on-Hadoop引擎不能满足分析数据库工作负载的要求,但这并不意味着对其他工作负载没有价值。事实上,绝大多数Cloudera客户充分利用平台的开放架构,通过Hive准备数据,通过Spark建立和测试模型,通过Impala运行商务智能(BI)并提供报告,而无需在不同的孤岛中复制数据。
在接下来的一年中,我们将以Impala为核心继续推动现代化分析数据库的重大性能改进,包括增加商务智能(BI)体验的智能化和自动化,并且不断扩大云计算支持,进一步提高多租户能力和可扩展性。请点击此博客了解更多详情。
像往常一样,我们鼓励您通过基于开放式基准测试工具包运行您自己的基准测试以独立验证这些结果。
via: Cloudera中国
End.
大数据
2018-07-09 20:13:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
本文转自Apache Kylin公众号apachekylin.
Superset 是一个数据探索和可视化平台,设计用来提供直观的,可视化的,交互式的分析体验。
Superset 提供了两种分析数据源的方式:
1. 用户可以以单表形式直接查询多种数据源,包括 Presto、Hive、Impala、SparkSQL、MySQL、Postgres、Oracle、Redshift、SQL Server、Druid 等。本文后续内容也会详细介绍Superset如何支持Kylin数据源。
2. 一个 SQL 的 IDE 供高级分析师使用 SQL 查询定义所需要分析的数据集,这种方法使用户在一个查询中实现用 Superset 查询数据源的多表,并立即对查询进行可视化分析。
Superset 的前世今生
Superset 起源于 2015 年初黑客马拉松项目,曾经使用过 Caravel 和 Panoramix 作为项目名。现在主要维护小组是 Airbnb 数据科学组,代码托管在 Github。作为 Apache 软件基金会孵化项目,Superset 目标是要做成数据可视化平台。
Superset 对于数据源端通过一个成熟的 OR-Mapping 方案对接了几乎市面上所有数据库产品,数据的分析和建模再使用 Pandas 统一加工序列化后由前端渲染展示. 进而前端渲染出众多富有表现力的可视化图表,这些可视化技术包括但不限于: D3,react stack,mapbox,deck.gl。
笔者在使用 Superset 过程中也感觉到一些不足,例如无法通过权限隔离不同用户可访问的数据源,数据查询暂时不支持下钻操作,多数据源不容易做交互查询等。但是瑕不掩瑜,Superset 依然是现在这个星球上最好的开源 BI 平台。
Apache Kylin 与 Superset 集成
交互式分析是 Apache Kylin 与 Superset 共同的产品目标,使用 Kylin 作为 Superset 查询,数据经过 Kylin Cube 的预计算处理,在 Superset 前端进行可视化分析想必是快到飞起,真可谓是强强联合。
Kyligence 数据科学小组开源了 kylinpy 项目完成了 Kylin 与 Superset 数据源的集成。现在我们就来手把手教读者实现 Kylin 和 Superset 的集成,并实现交互式的可视化分析。
准备工作
1. 安装 Apache Kylin
请参考 Apache Kylin installation guide:http://kylin.apache.org/docs23/
2. Apache Kylin 提供了样例 Cube,方便大家学习使用。Kylin 启动成功后,可以在 Kylin 安装路径下运行以下命令生成样例数据 Cube:
./${KYLIN_HOME}/bin/sample.sh
运行后,使用默认的 Kylin 账号 ADMIN / KYLIN 登陆界面,在 System 页面点击 Reload Metadata 即可看到样例项目 Learn_kylin。

选择样例 Cube “Kylin_sales_cube”,点击 Action -> Build。选择日期不要晚于 2014-01-01 来进行全量构建。
点击前往 Monitor 页面查看 Cube 构建的进程,知道100%完成,Cube 就可以进行查询了。
前往 Insight 页面执行一个查询验证 Cube 能够返回结果。
select part_dt ,
sum(price) as total_selled ,
count(distinct seller_id) as sellers
from kylin_sales
group by part_dt
order by part_dt
查询会击中新构建的 Kylin_sales_cube。
3. 下面我们安装 Superset,并初始化。
强烈建议使用虚拟环境来安装所有的依赖包(virtualenv/virtualenvwrapper)
通过 PyPi 仓库安装 superset
pip install superset
创建初始超级用户: admin/admin
fabmanager create-admin –app superset –username admin –password admin –firstname admin –lastname admin –email admin@fab.org
使用默认 sqllite metadata,位于 $HOME/.superset/superset.db,并且根据 migrate 创建表结构
superset db upgrade
初始化 role 等
superset init
执行如上4条命令便可以在 POSIX 操作系统上部署 Superset,如想加载 Superset提供的例子数据,可以再执行
superset load_examples
4. 安装 kylinpy
pip install kylinpy
5. 安装验证,如果一切顺利,Superset daemon应该可以跑起来了
-d 选项可以打开 debug 模式
superset runserver -d

Starting server with command:
gunicorn -w 2 –timeout 60 -b 0.0.0.0:8088 –limit-request-line 0 –limit-request-field_size 0 superset:app
[2018-01-03 15:54:03 +0800] [73673] [INFO] Starting gunicorn 19.7.1
[2018-01-03 15:54:03 +0800] [73673] [INFO] Listening at: http://0.0.0.0:8088 (73673)
[2018-01-03 15:54:03 +0800] [73673] [INFO] Using worker: sync
[2018-01-03 15:54:03 +0800] [73676] [INFO] Booting worker with pid: 73676
[2018-01-03 15:54:03 +0800] [73679] [INFO] Booting worker with pid: 73679
….

建立连接
现在所有的准备工作已经完毕,我们来试试在 Superset 中创建一个 Apache Kylin 数据源。
1. 浏览器打开 http://localhost:8088 帐号密码是刚才 fabmanager 创建的 admin/admin。
点击 Source —> Datasource,如下配置,注意如下几点: SQLAlchemy URI 格式为:
kylin://:@:/ 勾选 Expose in SQL Lab 后这个数据源便可以在 SQL Lab 中展示出来。 点击 Test Connection 可以测试链接是否成功。
创建 Kylin 数据源
测试连接
查询 Kylin 表单
连接成功后页面最下会展示这个 Kylin 项目内所有的表。
1. 点击 Source —> Tables,添加 Table,此处需要手动输入需要添加的表名。
2. 在所有列表中选定相应的表,就可以开始查询之旅啦。
使用 SQL Lab 查询 Apache Kylin 多表
熟悉 Kylin 的读者都知道,Kylin Cube 通常都是以多表关联建模为基础生成的,因此分析 Kylin Cube 的数据时,使用多表进行查询对于 Kylin 来说是非常常见的场景。在使用 Superset 分析 Kylin 数据时,我们可以使用 Superset 中的 SQL Lab 功能来查询多表,并对其进行可视化分析。
在这里我们以一个可以击中 Kylin 中的 sample cube ‘kylin_sales_cube’ 的查询为例。
查询返回后点击可视化按键即可针对当前查询进行可视化分析。
你可以复制下面的完整查询来体验 SQL Lab 查询 Kylin Cube 的功能。
select YEAR_BEG_DT,
MONTH_BEG_DT ,
WEEK_BEG_DT ,
META_CATEG_NAME ,
CATEG_LVL2_NAME,
CATEG_LVL3_NAME,
OPS_REGION,
NAME as BUYER_COUNTRY_NAME,
sum(PRICE) as GMV,
sum(ACCOUNT_BUYER_LEVEL) ACCOUNT_BUYER_LEVEL,
count(*) as CNT
from KYLIN_SALES
join KYLIN_CAL_DT
on CAL_DT=PART_DT
join KYLIN_CATEGORY_GROUPINGS
on SITE_ID=LSTG_SITE_ID
and KYLIN_CATEGORY_GROUPINGS.LEAF_CATEG_ID=KYLIN_SALES.LEAF_CATEG_ID
join KYLIN_ACCOUNT
on ACCOUNT_ID=BUYER_ID
join KYLIN_COUNTRY
on ACCOUNT_COUNTRY=COUNTRY
group by YEAR_BEG_DT,
MONTH_BEG_DT ,
WEEK_BEG_DT ,
META_CATEG_NAME ,
CATEG_LVL2_NAME,
CATEG_LVL3_NAME,
OPS_REGION,
NAME
使用 Superset 的多种功能查询 Apache Kylin
根据很多 Apache Kylin 用户在对接可视化及报表分析前端时,所提出的一些常见需求,我们对Superset 的相应功能也做了一些测试,可以说企业对于报表分析及可视化展现所需要的绝大部分功能,Superset 都已经可以提供了。
排序
Superset 支持使用任意数据源上定义的度量进行排序,不论这个度量是否在图表上。

过滤功能
在 Superset 中有多种过滤功能都可以使用在对 Kylin 的查询中。
1. 日期过滤
在 Superset 中你可以对定义为时间列的维度进行日期和时间的过滤。
2. 维度过滤
对于其他非时间维度,Superset 也提供了维度的筛选器,支持 SQL 中的 in,not in,等于,不等于,大于等于,小于等于,小于,大于,like 等多种过滤方式。
3. 报表内搜索
你可以在报表返回后使用 搜索框功能 对数据进行筛选。
4. 度量过滤
对于度量 Superset 支持用户直接写入 SQL 的having 表达式。
5. 联动过滤
使用 Superset 中提供的过滤框可视化组件,可以实现一个过滤器联动过滤多个可视化图形的效果。
如下图,过滤框组件可以联动控制仪表盘上的所有可视化图形。
6. Top N
你可以通过对数据进行排序和设置返回行数限制来实现展示 Top 10/Bottom 10 等功能。
7. 分页
在返回的数据量较大时,Superset 支持设置每页数据行数实现数据的分页。
8. 多种可视化
Superset 提供多样的可视化图表选择,这里仅以世界地图和气泡图为例作为展示。

9. 其它功能

另外 Superset 还支持数据导出 CSV,报表分享,查看报表 SQL 等功能。
10. 中文支持

最重要的是,Superset 由于社区的贡献已提供了中文版本!

Superset 使用了 Flask 的翻译扩展工具 Flask-Babel(http://packages.python.org/Flask-Babel/) ,使用了这个扩展包后,每个对应的语言版本只需要在翻译文件中将对应的 Superset 文字翻译成中文即可,这使得 Superset 社区的中文用户可以很容易的贡献翻译内容。

总结
多个开源项目的结合往往能产生1+1>2的效果,Kylin 专注于 OLAP 计算引擎,Superset 专注于数据可视化展现. 分析师手中的双剑合璧实现交互式分析,让企业使用大数据技术显著提升生产力。
大数据
2018-07-09 17:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
《阿里技术参考图册》(算法篇)下载:https://102.alibaba.com/downloadFile.do?file=1523848064814/AliTech101_Algorithms.pdf

《阿里技术参考图册》(研发篇)下载:https://102.alibaba.com/downloadFile.do?file=1523849261680/AliTech101_RD.pdf
大数据
2018-07-09 15:43:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
目前我们的业务需要多个spark job串行一起执行,每个spark job所需的参数配置各不相同;之所以分开多个spark job,是我们想保留每个任务独立执行的能力,提供独立的服务能力,又想在任务需要时,将多个任务作为一个执行链条串行执行;这里主要介绍我在任务串行时想通过创建多个spark context来实现不同参数配置的实践,资源管理是yarn。
实验过程
测试代码 SparkSession sparkSession = SparkSession.builder()//.master("local[1]") .config("spark.driver.allowMultipleContexts", true) .config("spark.executor.cores", 4) .appName("Java Spark SQL basic example").getOrCreate(); sparkSession.sparkContext().stop(); SparkSession newSession = sparkSession.newSession(); newSession.conf().set("spark.executor.cores","2"); sparkSession.setDefaultSession(newSession);
结果: [2018-07-11 09:39:29.879] [ERROR] [main] [org.apache.spark.deploy.yarn.ApplicationMaster] >>> Uncaught exception: org.apache.spark.SparkException: Exception thrown in awaitResult: at org.apache.spark.util.ThreadUtils$.awaitResult(ThreadUtils.scala:194) at org.apache.spark.deploy.yarn.ApplicationMaster.runDriver(ApplicationMaster.scala:401) at org.apache.spark.deploy.yarn.ApplicationMaster.run(ApplicationMaster.scala:254) at org.apache.spark.deploy.yarn.ApplicationMaster$$anonfun$main$1.apply$mcV$sp(ApplicationMaster.scala:766) at org.apache.spark.deploy.SparkHadoopUtil$$anon$1.run(SparkHadoopUtil.scala:67) at org.apache.spark.deploy.SparkHadoopUtil$$anon$1.run(SparkHadoopUtil.scala:66) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1548) at org.apache.spark.deploy.SparkHadoopUtil.runAsSparkUser(SparkHadoopUtil.scala:66) at org.apache.spark.deploy.yarn.ApplicationMaster$.main(ApplicationMaster.scala:764) at org.apache.spark.deploy.yarn.ApplicationMaster.main(ApplicationMaster.scala) Caused by: java.lang.IllegalStateException: SparkContext has been shutdown at org.apache.spark.SparkContext.runJob(SparkContext.scala:1910) at org.apache.spark.SparkContext.runJob(SparkContext.scala:1931) at org.apache.spark.SparkContext.runJob(SparkContext.scala:1944) at org.apache.spark.sql.execution.SparkPlan.executeTake(SparkPlan.scala:333) at org.apache.spark.sql.execution.CollectLimitExec.executeCollect(limit.scala:38) at org.apache.spark.sql.Dataset$$anonfun$org$apache$spark$sql$Dataset$$execute$1$1.apply(Dataset.scala:2371) at org.apache.spark.sql.execution.SQLExecution$.withNewExecutionId(SQLExecution.scala:57) at org.apache.spark.sql.Dataset.withNewExecutionId(Dataset.scala:2765) at org.apache.spark.sql.Dataset.org$apache$spark$sql$Dataset$$execute$1(Dataset.scala:2370) at org.apache.spark.sql.Dataset.org$apache$spark$sql$Dataset$$collect(Dataset.scala:2377) at org.apache.spark.sql.Dataset$$anonfun$head$1.apply(Dataset.scala:2113) at org.apache.spark.sql.Dataset$$anonfun$head$1.apply(Dataset.scala:2112) at org.apache.spark.sql.Dataset.withTypedCallback(Dataset.scala:2795) at org.apache.spark.sql.Dataset.head(Dataset.scala:2112) at org.apache.spark.sql.Dataset.take(Dataset.scala:2327) at org.apache.spark.sql.Dataset.showString(Dataset.scala:248) at org.apache.spark.sql.Dataset.show(Dataset.scala:636) at org.apache.spark.sql.Dataset.show(Dataset.scala:595) at org.apache.spark.sql.Dataset.show(Dataset.scala:604) at com.vip.spark.api.SparkSimpleTest01.main(SparkSimpleTest01.java:41) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.spark.deploy.yarn.ApplicationMaster$$anon$2.run(ApplicationMaster.scala:637)
spark context 已停止,囧...
查看源码,说要创建一个新的context就要先停掉旧的啊... /** * Main entry point for Spark functionality. A SparkContext represents the connection to a Spark * cluster, and can be used to create RDDs, accumulators and broadcast variables on that cluster. * * Only one SparkContext may be active per JVM. You must `stop()` the active SparkContext before * creating a new one. This limitation may eventually be removed; see SPARK-2243 for more details. * * @param config a Spark Config object describing the application configuration. Any settings in * this config overrides the default configs as well as system properties. */ class SparkContext(config: SparkConf) extends Logging with ExecutorAllocationClient { // The call site where this SparkContext was constructed. private val creationSite: CallSite = Utils.getCallSite() // If true, log warnings instead of throwing exceptions when multiple SparkContexts are active private val allowMultipleContexts: Boolean = config.getBoolean("spark.driver.allowMultipleContexts", false) ... 省了n行代码
无解,无法删除旧context就无法创建新的配置的context,这就意味着第一个context 在yarn里申请的资源将会是所有任务的共享资源,并且无法改变...
再看一下spark on yarn的资源申请过程,确实是这样,第一个container就是spark context的配置,后续的container都在这个基础上向resource manager 申请,如果不杀掉这个application master的container,就无法重现申请新的container,而一旦使用sparkContext.stop 方法杀掉Context,则整个应用会死掉,囧....
看到这里,我还不死心,想在spark Context 源码上看有没有入口可以切换不同的context,或者让多个context并存 /** * Called to ensure that no other SparkContext is running in this JVM. * * Throws an exception if a running context is detected and logs a warning if another thread is * constructing a SparkContext. This warning is necessary because the current locking scheme * prevents us from reliably distinguishing between cases where another context is being * constructed and cases where another constructor threw an exception. */ private def assertNoOtherContextIsRunning( sc: SparkContext, allowMultipleContexts: Boolean): Unit = { SPARK_CONTEXT_CONSTRUCTOR_LOCK.synchronized { Option(activeContext.get()).filter(_ ne sc).foreach { ctx => val errMsg = "Only one SparkContext may be running in this JVM (see SPARK-2243)." + " To ignore this error, set spark.driver.allowMultipleContexts = true. " + s"The currently running SparkContext was created at:\n${ctx.creationSite.longForm}" val exception = new SparkException(errMsg) if (allowMultipleContexts) { logWarning("Multiple running SparkContexts detected in the same JVM!", exception) } else { throw exception } } contextBeingConstructed.filter(_ ne sc).foreach { otherContext => // Since otherContext might point to a partially-constructed context, guard against // its creationSite field being null: val otherContextCreationSite = Option(otherContext.creationSite).map(_.longForm).getOrElse("unknown location") val warnMsg = "Another SparkContext is being constructed (or threw an exception in its" + " constructor). This may indicate an error, since only one SparkContext may be" + " running in this JVM (see SPARK-2243)." + s" The other SparkContext was created at:\n$otherContextCreationSite" logWarning(warnMsg) } } }
看到这里,spark context 使用一个同步锁确保在master端同个jvm下只能创建一个context,并且所有切换当前context方法都是私有方法,我....
再看看源码里面说的 SPARK-2243 issues https://issues.apache.org/jira/browse/SPARK-2243
Won't Fix,呵呵,不想看了,路已被堵死,只能找别的出路了。
解决方法
在进程级别控制 spark job,通过脚本串行执行,即多个 spark job submit 的命令在一起的脚本,这个不用我写了吧 囧。。
大数据
2018-07-11 14:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
活动内容
HBase —Hadoop Database是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”。HBase的特点是高可靠性、高性能、面向列、可伸缩的分布式存储系统,如今HBase已经广泛应用于各互联网行业。那么我们如何熟练掌握HBase技术及应用呢?
2018年7月21号 ,由中国HBase技术社区、DataFun社区主办、贝壳找房联合主办的中国第二届HBase Meetup继续在北京举行,来自阿里、小米、贝壳找房等公司的各位HBase的PMC、committer共聚一堂,为大家分享HBase技术解析及应用实践。
报名链接:
http://www.huodongxing.com/event/4447209379400
直播链接 :
http://www.itdks.com/eventlist/detail/2382
主办方:中国HBase技术社区、DataFun社区
联合主办方:贝壳找房
视频支持:IT大咖说
时间:2018.07.21,13:00-18:00
地点:北京海淀区上地开拓路11号福道大厦
议程安排:
时间 议程安排
13:00-14:00 签到
14:00-14:10 主持人开场
14:10-15:00
HBase应用实践
——邓钫元 贝壳找房 资深研发工程师
15:20-16:10
Procedure V2介绍
——张铎 小米人工智能与云平台 研发工程师
16:30-17:20
17:20-18:00

HBase in Practise: 性能、监控和问题排查
——李钰 阿里巴巴计算平台事业部高级技术专家
自由交流时间
嘉宾介绍:

邓钫元 贝壳找房 资深研发工程师
现就职于贝壳找房,主要负责贝壳大数据基础引擎建设。毕业于浙江大学,曾就职于百度商业平台部-风控平台研发,现负责贝壳找房大数据集群及基础引擎建设,专注于hadoop生态组件,热爱开源,为社区贡献多个patch,有丰富的性能调优经验。
分享主题:HBase应用实践
内容概要:待定

张铎 小米人工智能与云平台 研发工程师

张铎,HBase PMC member,目前在小米人工智能与云平台负责HBase的研发工作。
分享主题:Procedure V2介绍
内容概要:主要介绍一下Procedure V2的设计和结构,以及为什么用Procedure V2能比较容易实现出正确的AssignmentManager。最后介绍一下最近在2.1分支上对一些Procedure实现修正和改进。
李钰 阿里巴巴计算平台事业部高级技术专家
李钰(社区ID:Yu Li),阿里巴巴计算平台事业部高级技术专家,HBase开源社区PMC&committer。开源技术爱好者,主要关注分布式系统设计、大数据基础平台建设等领域。连续4年基于HBase/HDFS设计和开发存储系统应对双十一访问压力,具备丰富的大规模集群生产实战经验
分享主题:HBase in Practise: 性能、监控和问题排查
内容概要:HBase在不同版本(1.x, 2.x, 3.0)中针对不同类型的硬件(以IO为例,HDD/SATA-SSD/PCIe-SSD/Cloud)和场景(single/batch, get/scan)做了(即将做)各种不同的优化,这些优化都有哪些?如何针对自己的生产业务和硬件环境选择和使用合适的版本/功能?
在生产环境可能出现各种问题,而监控系统是发现并解决问题的关键。目前HBase提供了大量的metrics用于监控,其中有哪些是要特别关注的?线上不同类型的问题应该重点查看哪些metrics来定位问题?如何结合metrics和客户端/服务端日志快速定位问题?

了解更多HBase相关话题,请关注中国HBase技术社区:
最新活动,更多分享,请关注DataFunTalk公众号:
主办方介绍:
中国HBase技术社区:为了让众多HBase相关从业人员及爱好者有一个自由交流HBase相关技术的社区,由阿里巴巴、小米、网易、滴滴、知乎等公司的HBase技术研究人员共同发起了组建:中国HBase技术社区(Chinese HBase Technical Community 简称CHTC)。
我们非常欢迎对HBase有技术激情的同学一起加入探讨HBase技术,同时诚邀广大HBase技术爱好者加入。大家在工作学习遇到HBase技术问题,可以把问题发布到中国HBase技术社区论坛http://hbase.group,欢迎大家论坛上面提问留言讨论。想了解更多HBase技术,关注HBase技术社区公众号(微信号:hbasegroup),邀请进社区的讨论群。
DataFunTalk是一家关注大数据、人工智能技术主题的社区,主要形式以组织线下的技术沙龙活动为主、线上运营为辅。希望将行业内资深从业者拉到大家面前,和大家进行一对一的面对面交流,促进同行间的沟通交流,推动大数据、人工智能技术在不同场景下的交流融合、共同进步。DataFun的愿景是:为广大数据从业者和爱好者打造一个公益免费的分享、交流、学习、成长的平台。
目前我们的活动已经覆盖了北京、深圳、上海等城市,希望大家多多支持,后期持续为大家推出更多干货的线下沙龙活动。
联合主办方:
贝壳找房创立于2018年初,定位于技术驱动的品质居住服务平台。贝壳找房致力于聚合和赋能全行业的优质服务者,打造开放合作的行业生态,为消费者提供包括二手房、新房、租赁和装修等全方位的居住服务。
贝壳找房将继承和升级链家网在产品技术、品质控制和数据挖掘等方面的优势能力,继承和持续迭代大数据产品“楼盘字典”,研发和应用VR看房等创新技术手段,为消费者提供更好的服务体验。通过线上交易流程的可视化、线下的闭环服务和平台承诺,为消费者提供安全保障。
贝壳找房还将搭建服务者的信用评价体系,为消费者甄选优质服务商,通过营销、经营、供应链、技术、数据、金融、交易等各角度的赋能,让优质服务者和品牌在平台生态中得以更好的发展。
未来几年,进入全中国超过300个城市,赋能超过100个品牌,连接10万家门店和100万职业经纪人,服务超过2亿社区家庭,推动行业正循环。
视频支持:


大家工作学习遇到HBase技术问题,把问题发布到HBase技术社区论坛 http://hbase.group ,欢迎大家论坛上面提问留言讨论。想了解更多HBase技术关注HBase技术社区公众号(微信号: hbasegroup ),非常欢迎大家积极投稿。

长按下面的二维码加入HBase技术社区微信群
大数据
2018-07-10 21:52:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Mdx
select {[MEASURES].[UNITPRICE],[MEASURES].[NUMBER]} on columns, {[CUSTOMER].[GENDER].members} on rows from saleinfo
问题
问题1: mondrian转换过来的sql kylin不支持:Cartesian Join is not supported. while executing SQL: "select "CUSTOMER"."GENDER" as "c0", sum("SALE"."UNITPRICE") as "m0", sum("SALE"."NUMBER") as "m1" from "SALE" as "SALE", "CUSTOMER" as "CUSTOMER" where "SALE"."CUSID" = "CUSTOMER"."CUSID" group by "CUSTOMER"."GENDER" 不支持笛卡儿积的写法=》对应源码修改1,2,3
问题2:No realization found for OLAPContext, MODEL UNMATCHED JOIN, rel#993:OLAPTableScan.OLAP.[](table=[PUBLIC, SALE],ctx=,fields=[0, 1, 2, 3, 4, 5, 6, 7]), JoinDesc [type=INNER, primary key=[CUSID], foreign key=[CUSID]] while executing SQL: "select "CUSTOMER"."GENDER" as "c0", sum("SALE"."UNITPRICE") as "m0", sum("SALE"."NUMBER") as "m1" from "SALE" as "SALE" join "CUSTOMER" as "CUSTOMER" on "SALE"."CUSID" = "CUSTOMER"."CUSID" group by "CUSTOMER"."GENDER 源码修改1,2,3执行完之后,转换出的sql 中join 没有带left,kylin不支持。对应解决方法是修改4。
源码修改
1.Dialect.java的枚举类型DatabaseProduct中新增KYLIN.
2.JdbcDialectImpl.java的getProduct中新增对kyin的支持:else if (upperProductName.equals("KYLIN")){ return DatabaseProduct.KYLIN; }
3.新增KylinDialect.java类 @author wangzhou @date 2018年7月10日 增加kylin支持 package mondrian.spi.impl;
import java.sql. ; import mondrian.spi. ;
public class KylinDialect extends JdbcDialectImpl { public static final JdbcDialectFactory FACTORY; public KylinDialect(final Connection connection) throws SQLException { super(connection); } public boolean allowsCountDistinct() { return false; } public boolean allowsJoinOn() { return true; } static { FACTORY = new JdbcDialectFactory(KylinDialect.class, Dialect.DatabaseProduct.KYLIN) { protected boolean acceptsConnection(final Connection connection) { return super.acceptsConnection(connection); } }; }
}
4.修改SqlQuery.java的toBuffer()中的三目运算String fromSep = joinCount > 0 ? " join " : ", ";修改为String fromSep = joinCount > 0 ? " left join " : ", ";
大数据
2018-07-10 19:38:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Spark单机运行时,都是跑Main方法,那如何集成到Spring Boot实现http调用呢?
接下实现一个从一个文本里排序出频次最高的前10名
项目环境:
JDK:1.8;
Spark:2.2.0
项目搭建:
pom.xml 依赖: org.springframework.boot spring-boot-starter-parent 1.5.3.RELEASE UTF-8 UTF-8 1.8 2.11 2.2.0 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-web org.apache.spark spark-core_${scala.version} ${spark.version} org.slf4j slf4j-log4j12 log4j log4j provided org.apache.spark spark-streaming_${scala.version} ${spark.version} provided org.apache.spark spark-sql_${scala.version} ${spark.version} org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-configuration-processor true
配置类: @Configuration @ConfigurationProperties(prefix = "spark") public class SparkContextBean { private String sparkHome = "."; private String appName = "sparkTest"; private String master = "local"; @Bean @ConditionalOnMissingBean(SparkConf.class) public SparkConf sparkConf() throws Exception { SparkConf conf = new SparkConf().setAppName(appName).setMaster(master); return conf; } @Bean @ConditionalOnMissingBean(JavaSparkContext.class) public JavaSparkContext javaSparkContext() throws Exception { return new JavaSparkContext(sparkConf()); } public String getSparkHome() { return sparkHome; } public void setSparkHome(String sparkHome) { this.sparkHome = sparkHome; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getMaster() { return master; } public void setMaster(String master) { this.master = master; } }
实现类: @Service public class SparkTestService { private static final Logger logger = LoggerFactory.getLogger(SparkTestService.class); private static final Pattern SPACE = Pattern.compile(" "); @Autowired private JavaSparkContext sc; public Map calculateTopTen() { Map result = new HashMap(); JavaRDD lines = sc.textFile("src/test/java/test.txt").cache(); System.out.println(); System.out.println("-------------------------------------------------------"); System.out.println(lines.count()); JavaRDD words = lines.flatMap(str -> Arrays.asList(SPACE.split(str)).iterator()); JavaPairRDD ones = words.mapToPair(str -> new Tuple2(str, 1)); JavaPairRDD counts = ones.reduceByKey((Integer i1, Integer i2) -> (i1 + i2)); JavaPairRDD temp = counts.mapToPair(tuple -> new Tuple2(tuple._2, tuple._1)); JavaPairRDD sorted = temp.sortByKey(false).mapToPair(tuple -> new Tuple2(tuple._2, tuple._1)); System.out.println(); System.out.println("-------------------------------------------------------"); System.out.println(sorted.count()); //List> output = sorted.collect(); //List> output = sorted.take(10); List> output = sorted.top(10); for (Tuple2 tuple : output) { result.put(tuple._1(), tuple._2()); } return result; } /** * 练习demo,熟悉其中API */ public void sparkExerciseDemo() { List data = Lists.newArrayList(1,2,3,4,5,6); JavaRDD rdd01 = sc.parallelize(data); rdd01 = rdd01.map(num ->{ return num * num; }); //data map :1,4,9,16,25,36 logger.info("data map :{}",Joiner.on(",").skipNulls().join(rdd01.collect()).toString()); rdd01 = rdd01.filter(x -> x < 6); //data filter :1,4 logger.info("data filter :{}",Joiner.on(",").skipNulls().join(rdd01.collect()).toString()); rdd01 = rdd01.flatMap( x ->{ Integer[] test = {x,x+1,x+2}; return Arrays.asList(test).iterator(); }); //flatMap :1,2,3,4,5,6 logger.info("flatMap :{}",Joiner.on(",").skipNulls().join(rdd01.collect()).toString()); JavaRDD unionRdd = sc.parallelize(data); rdd01 = rdd01.union(unionRdd); //union :1,2,3,4,5,6,1,2,3,4,5,6 logger.info("union :{}",Joiner.on(",").skipNulls().join(rdd01.collect()).toString()); List result = Lists.newArrayList(); result.add(rdd01.reduce((Integer v1,Integer v2) -> { return v1+v2; })); //reduce :42 logger.info("reduce :{}",Joiner.on(",").skipNulls().join(result).toString()); result.forEach(System.out::print); JavaPairRDD> groupRdd = rdd01.groupBy(x -> { logger.info("======grouby========:{}",x); if (x > 10) return 0; else return 1; }); List>> resul = groupRdd.collect(); //group by key:1 value:1,2,3,4,5,6,1,2,3,4,5,6 resul.forEach(x -> { logger.info("group by key:{} value:{}",x._1,Joiner.on(",").skipNulls().join(x._2).toString()); }); } /** * spark streaming 练习 */ public void sparkStreaming() throws InterruptedException { JavaStreamingContext jsc = new JavaStreamingContext(sc,Durations.seconds(10));//批间隔时间 JavaReceiverInputDStream lines = jsc.receiverStream(new CustomReceiver(StorageLevel.MEMORY_AND_DISK_2())); JavaDStream count = lines.count(); count = count.map(x -> { logger.info("这次批一共多少条数据:{}",x); return x; }); count.print(); jsc.start(); jsc.awaitTermination(); jsc.stop(); } } /** * 自定义接收streaming类 */ public class CustomReceiver extends Receiver{ private static Logger logger = LoggerFactory.getLogger(CustomReceiver.class); /** * * @author hz15041240 * @date 2018年1月18日 下午4:37:22 * @version */ private static final long serialVersionUID = 5817531198342629801L; public CustomReceiver(StorageLevel storageLevel) { super(storageLevel); } @Override public void onStart() { new Thread(this::doStart).start(); logger.info("开始启动Receiver..."); //doStart(); } public void doStart() { while(!isStopped()) { int value = RandomUtils.nextInt(100); if(value <20) { try { Thread.sleep(1000); }catch (Exception e) { logger.error("sleep exception",e); restart("sleep exception", e); } } store(String.valueOf(value)); } } @Override public void onStop() { logger.info("即将停止Receiver..."); } } @RestController public class DemoController { @Autowired private SparkTestService sparkTestService; @RequestMapping("/demo/top10") public Map calculateTopTen() { return sparkTestService.calculateTopTen(); } @RequestMapping("/demo/exercise") public void exercise() { sparkTestService.sparkExerciseDemo(); } @RequestMapping("/demo/stream") public void streamingDemo() throws InterruptedException { sparkTestService.sparkStreaming(); } }
application.yml: server: port: 8054 spark: spark-home: . app-name: sparkTest master: local[4]
在项目的 src/test/java 目录下新建一个test.txt文件,立面随便一堆随机的字符就可以了。
启动项目,访问:http://localhost:8054/demo/top10 就能得到前10频率词汇了。
大数据
2018-07-10 17:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
摘要: 阿里云负载均衡SLB可以对多台云服务器(ECS)进行流量分发,支持TCP的四层负载均衡和基于HTTP/HTTPS的七层负载均衡。使用SLB可以降低单台ECS异常时对业务的冲击,提升系统可用性。同时,结合弹性伸缩服务(ESS)动态扩容、缩容后端服务器可以快速应对业务流量的变化。
阿里云 负载均衡SLB 可以对多台云服务器(ECS)进行流量分发,支持TCP的四层负载均衡和基于HTTP/HTTPS的七层负载均衡。使用SLB可以降低单台ECS异常时对业务的冲击,提升系统可用性。同时,结合弹性伸缩服务(ESS)动态扩容、缩容后端服务器可以快速应对业务流量的变化。
SLB七层访问日志内容丰富,提供近30个字段,例如:收到请求的时间、客户端的IP地址、处理Latency、请求URI、后端RealServer(阿里云ECS)地址、返回状态码等。在您开启 SLB七层访问日志功能 后,SLB会记录对应实例上所有访问日志到 日志服务 。本文以两个主题向大家介绍如何通过日志服务来发掘SLB访问日志背后蕴含的一些价值。
请求从哪里来
这是一个关于client_ip的问题,直接看访问日志的client_ip字段就可以回答。但有时会发现client_ip总是那么几个值,直觉告诉我们不大对劲:
一个客户端的请求从最初的ip到SLB负载均衡,如果不经过代理,那么client_ip记录的就是原始客户端ip。而假如请求经过proxy多次转发,这种情况下访问日志记录的client_ip就不能真实反应请求来源了。
好在SLB访问日志中有另两个字段可以帮助我们解决真实client_ip问题: http_x_forwarded_for,取自HTTP扩展头X-Forwarded-For字段,是 RFC7293 标准。假设客户端在client_0发出请求,到达服务端之前依次经过了三个代理proxy_1、proxy_2、proxy_3,其中proxy_3直连负载均衡器,那么proxy_3会在X-Forwarded-For上追加proxy_2的ip表示是在替proxy_2转发请求。这样多层级联后形成一个用逗号连接的字符串"client_0_ip,proxy_1_ip,proxy_2_ip",字符串中的第一个即是原客户端ip。 http_x_real_ip,取自HTTP自定义头X-Real-IP字段,非正式标准但在业内普遍使用。在各层代理始终坚持记录原始客户端ip的前提下,这是最方便且正确的取值。
值得注意的是,X-Forwarded-For和X-Real-IP字段都有可能出现不准确的情况,感兴趣的同学可以读一下这篇文章: HTTP请求头中的X-Forwarded-For 。
本文按照 X-Real-IP优先策略 计算真实的请求来源ip,算法用如下决策树来表达:
当http_x_forwarded_for、http_x_real_ip字段取值为字符串"-"时,表示该字段值不是有效内容。那么通过SQL的case/when语法把上图的计算方法翻译如下: select (case when http_x_real_ip = '-' then (case when http_x_forwarded_for = '-' then client_ip when split_part(http_x_forwarded_for, ',', 1) = '-' then client_ip else split_part(http_x_forwarded_for, ',', 1) end) else http_x_real_ip end) as real_client_ip
real_client_ip是通过算法得到的优化版真实客户端ip:
在real_client_ip基础上,可以使用 日志服务IP地理函数 计算访问来源的地理(国家、省市、运营商、经纬度)信息。例如按照省维度统计PV分布:
HTTP状态码说明了什么
408 Request Timeout
现象
客户端请求部署在SLB上的服务,但经常出现网络超时情况。
排查过程
首先用SQL统计是否有异常的状态码: not (status : 200) | select status, count(*) as pv group by status order by pv desc
分析发现在最近15分钟的访问日志中有些408返回的请求:
关于408状态码,它表示服务端在一定时间内没有收到完整的请求,这个时候服务端决定不再等待,在响应中将Connection首部值设置为close并主动关闭连接。
发生408错误的时候,表现为Request Timeout。最大可能的两个原因有:客户端没有在超时时间内把数据包发到服务端;或者是因为服务端负载很重,没有及时处理请求。如果通过监控可以排除服务端负载原因,那么可以将更多关注点转移到客户端身上。
统计408状态的client_ip来源: status : 408 | select client_ip, count(*) as pv group by client_ip order by pv desc
如果client_ip集中在几个特定来源上,那么,个别客户端网络流量导致问题的可能性就比较大。
同时,查看408状态码的日志发现,异常请求的upstream_addr、upstream_status都没有记录,这说明请求没有到达后端real server。这个时候可以认为,客户端问题导致网络超时的可能性是很大了。
接下来,就请到客户端上查看网路监控或抓包调查吧。
499 Client Closed Request
现象
SLB负载均衡上的流量出现下跌,同时后端服务器上没有看到5xx错误。
排查过程
经典开局,先看异常状态码分布,但这次我们怀疑是499导致的:
499状态码表示服务端Nginx正在处理请求过程中,客户端主动关闭了连接。
通过异常的访问日志加以印证,upstream_addr记录了请求在real server上进行处理,但是没有记录响应的后端状态码upstream_status,说明后端服务器没有完成请求的处理。并且,整个请求的处理时间request_time用了10秒多,也许正是因为长时间的等待导致用户停止了下载任务。
原文链接
本文为云栖社区原创内容,未经允许不得转载。
大数据
2018-07-10 16:16:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Kudu vs HBase
网易云

已认证的官方帐号
9 人赞了该文章
本文由 网易云 发布

背景
Cloudera在2016年发布了新型的分布式存储系统——kudu,kudu目前也是apache下面的开源项目。Hadoop生态圈中的技术繁多,HDFS作为底层数据存储的地位一直很牢固。而HBase作为Google BigTable的开源产品,一直也是Hadoop生态圈中的核心组件,其数据存储的底层采用了HDFS,主要解决的是在超大数据集场景下的随机读写和更新的问题。Kudu的设计有参考HBase的结构,也能够实现HBase擅长的快速的随机读写、更新功能。那么同为分布式存储系统,HBase和Kudu二者有何差异?两者的定位是否相同?我们通过分析HBase与Kudu整体结构和存储结构等方面对两者的差异进行比较。
整体结构Hbase的整体结构

HBase的主要组件包括Master,zookeeper服务,RegionServer,HDFS。
(1)Master:用来管理与监控所有的HRegionServer,也是管理HBase元数据的模块。
(2)zookeeper:作为分布式协调服务,用于保存meta表的位置,master的位置,存储RS当前的工作状态。
(3)RegionServer:负责维护Master分配的region,region对应着表中一段区间内的内容,直接接受客户端传来的读写请求。
(4)HDFS:负责最终将写入的数据持久化,并通过多副本复制实现数据的高可靠性。
Kudu的整体结构

Kudu的主要组件包括TServer和TMaster。
(1) TServer:负责管理Tablet,tablet是负责一张表中某块内容的读写,接收其他TServer中leader tablet传来的同步信息。
(2) TMaster:集群中的管理节点,用于管理tablet的基本信息,表的信息,并监听TServer的状态。多个TMaster之间通过Raft 协议实现数据同步和高可用。
主要区别
Kudu结构看上去跟HBase差别并不大,主要的区别包括:
(1)Kudu将HBase中zookeeper的功能放进了TMaster内,Kudu中TMaster的功能比HBase中的Master任务要多一些。
(2)Hbase将数据持久化这部分的功能交给了Hadoop中的HDFS,最终组织的数据存储在HDFS上。Kudu自己将存储模块集成在自己的结构中,内部的数据存储模块通过Raft协议来保证leader Tablet和replica Tablet内数据的强一致性,和数据的高可靠性。为什么不像HBase一样,利用HDFS来实现数据存储,笔者猜测可能是因为HDFS读小文件时的时延太大,所以Kudu自己重新完成了底层的数据存储模块,并将其集成在TServer中。
数据存储方式
HBase
HBase是一款Nosql数据库,典型的KV系统,没有固定的schema模式,建表时只需指定一个或多个列族名即可,一个列族下面可以增加任意个列限定名。一个列限定名代表了实际中的一列,HBase将同一个列族下面的所有列存储在一起,所以HBase是一种面向列族式的数据库。

HBase将每个列族中的数据分别存储,一个列族中的每行数据中,将rowkey\列族名、列名、timestamp组成最终存取的key值, 另 外 为 了 支 持 修 改 , 删 除 ,增 加 了 一 个 表 征 该 行 数 据 是 否 删 除 的 标 记 。 在 同 一 个 列 族 中 的 所 有 数 据 , 按照rowkey:columnfamily:columnQulifier:timestamp组成的key值大小进行升序排列,其中 rowkey 、 columnfamily 、columnQulifier 采用的是字典顺序,其值越大,Key越大,而timestamp是值越大,Key越小。HBase通过按照列族分开存储,相对于行式存储能够实现更高的压缩比,这也是其比较重要的一个特性。
HBase对一行数据进行更新时,HBase也是相当于插入一行新数据,在读数据时HBase按照timestamp的大小得到经过更新过的最新数据。

Kudu
Kudu是一种完全的列式存储引擎,表中的每一列数据都是存放在一起,列与列之间都是分开的。

为了能够保存一部分历史数据,并实现MVCC,Kudu将数据分为三个部分。一个部分叫做base data,是当前的数据;第二个部分叫做UNDO records,存储的是从插入数据时到形成base data所进行的所有修改操作,修改操作以一定形式进行组织,实现快速查看历史数据;第三个部分是REDO records,存储的是还未merge到当前数据中的更新操作。下图中表示的是在Kudu中插入一条数据、更新数据两个操作的做法,当然做法不唯一,不唯一的原因是Kudu可以选择先不将更新操作合并到base data中。

差异分析
(1)HBase是面向列族式的存储,每个列族都是分别存放的,HBase表设计时,很少使用设计多个列族,大多情况下是一个列族。这个时候的HBase的存储结构已经与行式存储无太大差别了。而Kudu,实现的是一个真正的面向列的存储方式,表中的每一列都是单独存放的;所以HBase与Kudu的差异主要在于类似于行式存储的列族式存储方式与典型的面向列式的存储方式的差异;
(2) HBase是一款NoSQL类型的数据库,对表的设计主要在于rowkey与列族的设计,列的类型可以不指定,因为HBase在实际存储中都会将所有的value字段转换成二进制的字节流。因为不需要指定类型,所以在插入数据的时候可以任意指定列名(列限定名),这样相当于可以在建表之后动态改变表的结构。Kudu因为选择了列式存储,为了更好的提高列式存储的效果,Kudu要求在建表时指定每一列的类型,这样的做法是为了根据每一列的类型设置合适的编码方式,实现更高的数据压缩比,进而降低数据读入时的IO压力;
(3) HBase对每一个cell数据中加入了timestamp字段,这样能够实现记录同一rowkey和列名的多版本数据,另外HBase将数据更新操作、删除操作也是作为一条数据写入,通过timestamp来标记更新时间,type来区分数据是插入、更新还是删除。HBase写入或者更新数据时可以指定timestamp,这样的设置可以完成某些特定的操作;
(4) 相对于HBase允许多版本的数据存在,Kudu为了提高批量读取数据时的效率,要求设计表时提供一列或者多列组成一个主键,主键唯一,不允许多个相同主键的数据存在。这样的设置下,Kudu不能像HBase一样将更新操作直接转换成插入一条新版本的数据,Kudu的选择是将写入的数据,更新操作分开存储;
(5)当然还有一些其他的行式存储与列式存储之间在不同应用场景下的性能差异。
HBase
HBase作为一种非常典型的LSM结构的分布式存储系统,是Google bigtable的apache开源版本。经过近10年的发展,HBase 已经成为了一个成熟的项目,在处理OLTP型的应用如消息日志,历史订单等应用较适用。在HBase中真正接受客户端读写请求的RegionServer的结构如下图所示:

关于HBase的几个关键点:
(1)在HBase中,充当写入缓存的这个结构叫做Memstore,另外会将写入操作顺序写入HLOG(WAL)中以保证数据不丢失;
(2)为了提高读的性能,HBase在内存中设置了blockcache,blockcache采用LRU策略将最近使用的数据块放在内存中;
(3)作为分布式存储系统,为保证数据不因为集群中机器出现故障而导致数据丢失,HBase将实际数据存放在HDFS上,包括storefile与HLOG。HBase与HDFS低耦合,HBase作为HDFS的客户端,向HDFS读写数据。
1. HBase写过程
(1)客户端通过客户端上保存的RS信息缓存或者通过访问zk得到需要读写的region所在的RS信息;
(2)RS接受客户端写入请求,先将写入的操作写入WAL,然后写入Memstore,这时HBase向客户端确认写入成功;
(3)HBase在一定情况下将Memstore中的数据flush成storefile(可能是Memstore大小达到一定阈值或者region占用的内存超过一定阈值或者手动flush之类的),storefile以HFile的形式存放在HDFS上;
(4)HBase会按照一定的合并策略对HDFS上的storefile进行合并操作,减少storefile的数量。
2. Hbase读过程
HBase读数据的过程比较麻烦,原因包括:
(1)HBase采用了LSM-tree的多组件算法作为数据组织方式,这种算法会导致一个region中有多个storefile;
(2)HBase中采用了非原地更新的方式,将更新操作和删除操作转换成插入一条新数据的形式,虽然这样能够较快的实现更新与删除,但是将导致满足指定rowkey,列族、列名要求的数据有多个,并且可能分布在不同的storefile中;
(3)HBase中允许设置插入和删除数据行的timestamp属性,这样导致按顺序落盘的storefile内数据的timestamp可能不是递增的。
下面介绍从HBase中读取一条指定(rowkey,column family,column)的记录:
(1)读过程与HBase客户端写过程第一步一样,先尝试获取需要读的region所在的RS相关信息;
( 2 ) RS 接收读请求, 因为HBase中支持多版本数据( 允许存在rowkey、列族名、列名相同的数据, 不同版本的数据通过
timestamp进行区分),另外更新与删除数据都是通过插入一条新数据实现的。所以要准确的读到数据,需要找到所有可能存储有该条数据的位置,包括在内存中未flush的memstore,已经flush到HDFS上的storefile,所以需要在1 memstore +N storefile中查找;
(3)在找到的所有数据中通过判断timestamp值得到最终的数据。
Kudu

(1)Kudu中的Tablet是负责表中一块内容的读写工作,Tablet由一个或多个Rowset组成。其中有一个Rowset处于内存中,叫做Memrowset,Memrowset主要负责处理新的数据写入请求。DiskRowSet是MemRowset达到一定程序刷入磁盘后生成的,实质上是由一个CFile(Base Data)、多个DeltaFile(UNDO records &REDO records)和位于内存的DeltaMemStore组成。Base data、UNDO records、和REDO records都是不可修改的,DeltaMemStore达到一定大小后会将数据刷入磁盘生成新的REDO records。Kudu后台会有一个类似HBase的compaction线程按照一定的compaction 策略对tablet进行合并处理:
a. 将多个DeltaFile(REDO records)合并成一个大的DeltaFile;
b. 将多个REDO reccords文件与Base data进行合并,并生成新的 UNDO records;
c. 将多个DiskRowset之间进行合并,减少DiskRowset的数量。
(2)Kudu将最终的数据存储在本地磁盘上,为了保证数据可靠性,Kudu为一个tablet设置了多个副本(一般为3或5个)。所以一个tablet会由多个TServer负责维护,其中有个副本称为leader tablet,写入的请求只能通过leader tablet来处理,副本之间通过Raft协议保证其他副本与leader tablet的强一致性。
1. Kudu写过程
Kudu与HBase不同,Kudu将写入操作分为两种,一种是插入一条新数据,一种是对一条已插入数据的更新。Kudu插入一条新数据:
(1)客户端连接TMaster获取表的相关信息,包括分区信息,表中所有tablet的信息;
(2)客户端找到负责处理读写请求的tablet所负责维护的TServer。Kudu接受客户端的请求,检查请求是否符合要求(表结构);
(3) Kudu在Tablet中的所有rowset(memrowset,diskrowset)中进行查找,看是否存在与待插入数据相同主键的数据,如果存在就返回错误,否则继续;
(4) Kudu在MemRowset中写入一行新数据,在MemRowset数据达到一定大小时,MemRowset将数据落盘,并生成一个diskrowset用于持久化数据,还生成一个memrowset继续接收新数据的请求。
Kudu对原有数据的更新
(1)客户端连接TMaster获取表的相关信息,包括分区信息,表中所有tablet的信息;
(2)Kudu接受请求,检查请求是否符合要求;
(3)因为待更新数据可能位于memrowset中,也可能已经flush到磁盘上,形成diskrowset。因此根据待更新数据所处位置不同,kudu有不同的做法:
a. 当待更新数据位于memrowset 时, 找到待更新数据所在行, 然后将更新操作记录在所在行中一个mutation链表中;在memrowset将数据落盘时,Kudu会将更新合并到base data,并生成UNDO records用于查看历史版本的数据和MVCC,UNDO records实际上也是以DeltaFile的形式存放;
b. 当待更新数据位于DiskRowset 时, 找到待更新数据所在的DiskRowset , 每个DiskRowset 都会在内存中设置一个DeltaMemStore,将更新操作记录在DeltaMemStore中,在DeltaMemStore达到一定大小时,flush在磁盘,形成Delta并存在方DeltaFile中。
实际上Kudu提交更新时会使用Raft协议将更新同步到其他replica上去,当然如果在memrowset和diskrowset中都没有找到这条数据,那么返回错误给客户端;另外当DiskRowset中的deltafile太多时,Kudu会采用一定的策略对一组deltafile进行合并。
2. Kudu读过程
(1)客户端连接TMaster获取表的相关信息,包括分区信息,表中所有tablet的信息;
(2) 客户端找到需要读取的数据的tablet所在的TServer,Kudu接受读请求,并记录timestamp信息,如果没有显式指定,那么表示使用当前时间;
(3) Kudu找到待读数据的所有相关信息, 当目标数据处于memrowset时, 根据读取操作中包含的timestamp 信息将该 timestamp前提交的更新操作合并到base data中,这个更新操作记录在该行数据对应的mutation链表中;
(4) 当读取的目标数据位于diskrowset中,在所有DeltaFile中找到所有目标数据相关的UNDO record和REDO records,REDO records可能位于多个DeltaFile中,根据读操作中包含的timestamp信息判断是否需要将base data进行回滚或者利用REDO records将base data进行合并更新。
1.写过程
(1)HBase写的时候,不管是新插入一条数据还是更新数据,都当作插入一条新数据来进行;而Kudu将插入新数据与更新操作分 别看待;
(2)Kudu表结构中必须设置一个唯一键,插入数据的时候必须判断一些该数据的主键是否唯一,所以插入的时候其实有一个读的 过程;而HBase没有太多限制,待插入数据将直接写进memstore;
(3)HBase实现数据可靠性是通过将落盘的数据写入HDFS来实现,而Kudu是通过将数据写入和更新操作同步在其他副本上实现 数据可靠性。
结合以上几点,可以看出Kudu在写的性能上相对HBase有一定的劣势。
2. 读过程
(1)在HBase中,读取的数据可能有多个版本,所以需要结合多个storefile进行查询;Kudu数据只可能存在于一个DiskRowset或 者MemRowset中,但是因为可能存在还未合并进原数据的更新,所以Kudu也需要结合多个DeltaFile进行查询;
(2)HBase写入或者更新时可以指定timestamp,导致storefile之间timestamp范围的规律性降低,增加了实际查询storefile的数 量;Kudu不允许人为指定写入或者更新时的timestamp值,DeltaFile之间timestamp连续,可以更快的找到需要的DeltaFile;
(3)HBase通过timestamp值可以直接取出数据;而Kudu实现多版本是通过保留UNDO records(已经合并过的操作)和REDO records(未合并过的操作)完成的,在一些情况下Kudu需要将base data结合UNDO records进行回滚或者结合REDO records进 行合并然后才能得到真正所需要的数据。
结合以上三点可以得出,不管是HBase还是Kudu,在读取一条数据时都需要从多个文件中搜寻相关信息。相对于HBase,Kudu选 择将插入数据和更新操作分开,一条数据只可能存在于一个DiskRowset或者memRowset中,只需要搜寻到一个rowset中存在指 定数据就不用继续往下找了,用户不能设置更新和插入时的timestamp值,减少了在rowset中DeltaFile的读取数量。这样在scan 的情况下可以结合列式存储的优点实现较高的读性能,特别是在更新数量较少的情况下能够有效提高scan性能。
另外,本文在描述HBase读写过程中没有考虑读写中使用的优化技术如Bloomfilter、timestamp range等。其实Kudu中也有使用 类似的优化技术来提高读写性能,本文只是简单的分析,因此就不再详细讨论读写过程。如有需要了解HBase的详细读写过程,
3. 其它差异
HBase:使用的java,内存的释放通过GC来完成,在内存比较紧张时可能引发full GC进而导致服务不稳定;
Kudu:核心模块用的C++来实现,没有full gc的风险。

总 结
本文主要简单介绍了一下Kudu,并在整体结构、数据存储结构和读写过程等方面上对HBase和Kudu这两款分布式存储系统进行大 体上的比较。Kudu通过要求完整的表结构设置,主键的设定,以列式存储作为数据在磁盘上的组织方式,更新和数据分开等技巧, 使得Kudu能够实现像HBase一样实现数据的随机读写之外,在HBase不太擅长的批量数据扫描(scan)具有较好的性能。而批量 读数据正是olap型应用所关注的重点,正如Kudu官网主页上描述的,Kudu实现的是既可以实现数据的快速插入与实时更新,也可以实现数据的快速分析。Kudu的定位不是取代HBase,而是以降低写的性能为代价,提高了批量读的性能,使其能够实现快速在线分析。
## == 是什么 ==

Kudu是Todd Lipcon@Cloudera带头开发的存储系统,其整体应用模式和HBase比较接近,即支持行级别的随机读写,并支持批量顺序检索功能。

那既然有了HBase,为什么还需要Kudu呢,简单的说,就是嫌弃HBase在OLAP场合,SQL/MR类的批量检索场景中,性能不够好。通常这种海量数据OLAP场景,要不走预处理的路,比如像EBAY麒麟这样走Cube管理的,或者像谷歌Mesa这样按业务需求走预定义聚合操作。再有就是自己构建数据通道,串接实时和批量处理两种系统,发挥各自的特长。

但是OLAP是个复杂的问题,场景众多,必然不可能有完美的通用解决方案,Kudu定位于应对快速变化数据的快速分析型数据仓库,希望靠系统自身能力,支撑起同时需要高吞吐率的顺序和随机读写的应用场景(可能的场景,比如时间序列数据分析,日志数据实时监控分析),提供一个介于HDFS和HBase的性能特点之间的一个系统,在随机读写和批量扫描之间找到一个平衡点,并保障稳定可预测的响应延迟

那为什么不能想办法改进HBase呢?Todd自己做为HBase的重要贡献者之一,没有选择这条路,自然是因为任何系统设计时都有Tradeoff,基于HBase的设计思想很难实现Kudu所定位的目标

相关链接:
http://getkudu.io/kudu.pdf http://getkudu.io/

## == 核心思想 ==

### 数据模型:

数据模型定义上,Kudu管理的是类似关系型数据库的结构化的表,表结构由类Sql的Schema进行定义,相比于HBase这样的NoSql类型的数据库,Kudu的行数据是由固定个数有明确类型定义的列组成,并且需要定义一个由一个或多个列组成的主键来对每行数据进行唯一索引,相比于传统的关系型数据库,kudu在索引上有更多的限制,比如暂时不支持二级索引,不支持主键的更新等等。

尽管表结构类似于关系型数据库,但是Kudu自身并不提供SQL类型的语法接口,而是由上层其他系统实现,比如目前通过Impala提供SQL语法支持。

Kudu底层API,主要面对简单的更新检索操作,Insert/Update/Delete等必须指定一个主键进行,而Scan检索类型的操作则支持条件过滤和投影等能力。

### 集群架构:

Kudu的集群架构基本和HBase类似,采用主从结构,Master节点管理元数据,Tablet节点负责分片管理数据,

和HBase不同的是,Kudu没有借助于HDFS存储实际数据,而是自己直接在本地磁盘上管理分片数据,包括数据的Replication机制,kudu的Tablet server直接管理Master分片和Slave分片,自己通过raft协议解决一致性问题等,多个Slave可以同时提供数据读取服务,相对于HBase依托HDFS进行Region数据的管理方式,自主性会强一些,不过比如Tablet节点崩溃,数据的迁移拷贝工作等,也需要Kudu自己完成。

### 存储结构:

因为数据是有严格Schema类型定义,所以Kudu底层可以使用列式存储的方案来提高存储和投影检索效率(不过,设计kudu时,因果关系我估计是倒过来的,先决定要使用列式存储,再决定需要schema)

和HBase一样,Kudu也是通过Tablet的分区来支持水平扩展,与HBase不同的是,Kudu的分区策略除了支持按照Key Range来分区以外,还支持Hash based的策略,实际上,在主键上,Kudu可以混合使用这两种不同的策略

Hash分区的策略在一些场合下可以更好的做到负载均衡,避免数据倾斜,但是它最大的问题就是分区数一旦确定就很难再调整,所以目前Kudu的分区数必须预先指定(对Range的分区策略也有这个要求,估计是先简单化统一处理),不支持动态分区分裂,合并等,因此表的分区一开始就需要根据负载和容量预先进行合理规划。

在处理随机写的效率问题方面,Kudu的基本流程和HBase的方案差不多,在内存中对每个Tablet分区维护一个MemRowSet来管理最新更新的数据,当尺寸超过一定大小后Flush到磁盘上形成DiskRowSet,多个DiskRowSet在适当的时候进行归并处理

和HBase采用的LSM(LogStructured Merge)方案不同的是,Kudu对同一行的数据更新记录的合并工作,不是在查询的时候发生的(HBase会将多条更新记录先后Flush到不同的Storefile中,所以读取时需要扫描多个文件,比较rowkey,比较版本等),而是在更新的时候进行,在Kudu中一行数据只会存在于一个DiskRowSet中,避免读操作时的比较合并工作。那Kudu是怎么做到的呢? 对于列式存储的数据文件,要原地变更一行数据是很困难的,所以在Kudu中,对于Flush到磁盘上的DiskRowSet(DRS)数据,实际上是分两种形式存在的,一种是Base的数据,按列式存储格式存在,一旦生成,就不再修改,另一种是Delta文件,存储Base数据中有变更的数据,一个Base文件可以对应多个Delta文件,这种方式意味着,插入数据时相比HBase,需要额外走一次检索流程来判定对应主键的数据是否已经存在。因此,Kudu是牺牲了写性能来换取读取性能的提升。


既然存在Delta数据,也就意味着数据查询时需要同时检索Base文件和Delta文件,这看起来和HBase的方案似乎又走到一起去了,不同的地方在于,Kudu的Delta文件与Base文件不同,不是按Key排序的,而是按被更新的行在Base文件中的位移来检索的,号称这样做,在定位Delta内容的时候,不需要进行字符串比较工作,因此能大大加快定位速度。但是无论如何,Delta文件的存在对检索速度的影响巨大。因此Delta文件的数量会需要控制,需要及时的和Base数据进行合并。由于Base文件是列式存储的,所以Delta文件合并时,可以有选择性的进行,比如只把变化频繁的列进行合并,变化很少的列保留在Delta文件中暂不合并,这样做也能减少不必要的IO开销。

除了Delta文件合并,DRS自身也会需要合并,为了保障检索延迟的可预测性(这一点是HBase的痛点之一,比如分区发生Major Compaction时,读写性能会受到很大影响),Kudu的compaction策略和HBase相比,有很大不同,kudu的DRS数据文件的compaction,本质上不是为了减少文件数量,实际上Kudu DRS默认是以32MB为单位进行拆分的,DRS的compaction并不减少文件数量,而是对内容进行排序重组,减少不同DRS之间key的overlap,进而在检索的时候减少需要参与检索的DRS的数量。


以32MB这样小的单位进行拆分,也是为了能够以有限的资源快速的完成compaction的任务,及时根据系统负载调整Compaction行为,而不至于像HBase一样,Major Compaction动作成为导致性能不稳定的一个重要因素。所以对于Kudu来说,IO操作可以是一个持续平缓的过程,这点对响应的可预测性至关重要。

### 其它

Kudu底层核心代码使用C++开发,对外提供Java API接口,没有使用Java开发核心代码,也许有部分原因是希望通过自己管理内存,更好的适应和利用当前服务器上普遍越来越大的内存空间(256G+),另外也便于在关键逻辑中更好的优化代码。

## == 小结 ==

总体来说,个人感觉,Kudu本质上是将性能的优化,寄托在以列式存储为核心的基础上,希望通过提高存储效率,加快字段投影过滤效率,降低查询时CPU开销等来提升性能。而其他绝大多数设计,都是为了解决在列式存储的基础上支持随机读写这样一个目的而存在的。比如类Sql的元数据结构,是提高列式存储效率的一个辅助手段,唯一主键的设定也是配合列式存储引入的定制策略,至于其他如Delta存储,compaction策略等都是在这个设定下为了支持随机读写,降低latency不确定性等引入的一些Tradeoff方案

官方测试结果上,如果是存粹的随机读写,或者单行的检索请求这类场景,由于这些Tradeoff的存在,HBASE的性能吞吐率是要优于Kudu不少的(2倍到4倍),kudu的优势还是在支持类SQL检索这样经常需要进行投影操作的批量顺序检索分析场合。

目前kudu还处在Incubator阶段,并且还没有成熟的线上应用(小米走在了前面,做了一些业务应用的尝试),在数据安全,备份,系统健壮性等方面也还要打个问号,所以是否使用kudu,什么场合,什么时间点使用,是个需要好好考量的问题 ;)
## == 是什么 ==

Kudu是Todd Lipcon@Cloudera带头开发的存储系统,其整体应用模式和HBase比较接近,即支持行级别的随机读写,并支持批量顺序检索功能。

那既然有了HBase,为什么还需要Kudu呢,简单的说,就是嫌弃HBase在OLAP场合,SQL/MR类的批量检索场景中,性能不够好。通常这种海量数据OLAP场景,要不走预处理的路,比如像EBAY麒麟这样走Cube管理的,或者像谷歌Mesa这样按业务需求走预定义聚合操作。再有就是自己构建数据通道,串接实时和批量处理两种系统,发挥各自的特长。

但是OLAP是个复杂的问题,场景众多,必然不可能有完美的通用解决方案,Kudu定位于应对快速变化数据的快速分析型数据仓库,希望靠系统自身能力,支撑起同时需要高吞吐率的顺序和随机读写的应用场景(可能的场景,比如时间序列数据分析,日志数据实时监控分析),提供一个介于HDFS和HBase的性能特点之间的一个系统,在随机读写和批量扫描之间找到一个平衡点,并保障稳定可预测的响应延迟

那为什么不能想办法改进HBase呢?Todd自己做为HBase的重要贡献者之一,没有选择这条路,自然是因为任何系统设计时都有Tradeoff,基于HBase的设计思想很难实现Kudu所定位的目标

相关链接:
http://getkudu.io/kudu.pdf http://getkudu.io/

## == 核心思想 ==

### 数据模型:

数据模型定义上,Kudu管理的是类似关系型数据库的结构化的表,表结构由类Sql的Schema进行定义,相比于HBase这样的NoSql类型的数据库,Kudu的行数据是由固定个数有明确类型定义的列组成,并且需要定义一个由一个或多个列组成的主键来对每行数据进行唯一索引,相比于传统的关系型数据库,kudu在索引上有更多的限制,比如暂时不支持二级索引,不支持主键的更新等等。

尽管表结构类似于关系型数据库,但是Kudu自身并不提供SQL类型的语法接口,而是由上层其他系统实现,比如目前通过Impala提供SQL语法支持。

Kudu底层API,主要面对简单的更新检索操作,Insert/Update/Delete等必须指定一个主键进行,而Scan检索类型的操作则支持条件过滤和投影等能力。

### 集群架构:

Kudu的集群架构基本和HBase类似,采用主从结构,Master节点管理元数据,Tablet节点负责分片管理数据,

和HBase不同的是,Kudu没有借助于HDFS存储实际数据,而是自己直接在本地磁盘上管理分片数据,包括数据的Replication机制,kudu的Tablet server直接管理Master分片和Slave分片,自己通过raft协议解决一致性问题等,多个Slave可以同时提供数据读取服务,相对于HBase依托HDFS进行Region数据的管理方式,自主性会强一些,不过比如Tablet节点崩溃,数据的迁移拷贝工作等,也需要Kudu自己完成。

### 存储结构:

因为数据是有严格Schema类型定义,所以Kudu底层可以使用列式存储的方案来提高存储和投影检索效率(不过,设计kudu时,因果关系我估计是倒过来的,先决定要使用列式存储,再决定需要schema)

和HBase一样,Kudu也是通过Tablet的分区来支持水平扩展,与HBase不同的是,Kudu的分区策略除了支持按照Key Range来分区以外,还支持Hash based的策略,实际上,在主键上,Kudu可以混合使用这两种不同的策略

Hash分区的策略在一些场合下可以更好的做到负载均衡,避免数据倾斜,但是它最大的问题就是分区数一旦确定就很难再调整,所以目前Kudu的分区数必须预先指定(对Range的分区策略也有这个要求,估计是先简单化统一处理),不支持动态分区分裂,合并等,因此表的分区一开始就需要根据负载和容量预先进行合理规划。

在处理随机写的效率问题方面,Kudu的基本流程和HBase的方案差不多,在内存中对每个Tablet分区维护一个MemRowSet来管理最新更新的数据,当尺寸超过一定大小后Flush到磁盘上形成DiskRowSet,多个DiskRowSet在适当的时候进行归并处理

和HBase采用的LSM(LogStructured Merge)方案不同的是,Kudu对同一行的数据更新记录的合并工作,不是在查询的时候发生的(HBase会将多条更新记录先后Flush到不同的Storefile中,所以读取时需要扫描多个文件,比较rowkey,比较版本等),而是在更新的时候进行,在Kudu中一行数据只会存在于一个DiskRowSet中,避免读操作时的比较合并工作。那Kudu是怎么做到的呢? 对于列式存储的数据文件,要原地变更一行数据是很困难的,所以在Kudu中,对于Flush到磁盘上的DiskRowSet(DRS)数据,实际上是分两种形式存在的,一种是Base的数据,按列式存储格式存在,一旦生成,就不再修改,另一种是Delta文件,存储Base数据中有变更的数据,一个Base文件可以对应多个Delta文件,这种方式意味着,插入数据时相比HBase,需要额外走一次检索流程来判定对应主键的数据是否已经存在。因此,Kudu是牺牲了写性能来换取读取性能的提升。


既然存在Delta数据,也就意味着数据查询时需要同时检索Base文件和Delta文件,这看起来和HBase的方案似乎又走到一起去了,不同的地方在于,Kudu的Delta文件与Base文件不同,不是按Key排序的,而是按被更新的行在Base文件中的位移来检索的,号称这样做,在定位Delta内容的时候,不需要进行字符串比较工作,因此能大大加快定位速度。但是无论如何,Delta文件的存在对检索速度的影响巨大。因此Delta文件的数量会需要控制,需要及时的和Base数据进行合并。由于Base文件是列式存储的,所以Delta文件合并时,可以有选择性的进行,比如只把变化频繁的列进行合并,变化很少的列保留在Delta文件中暂不合并,这样做也能减少不必要的IO开销。

除了Delta文件合并,DRS自身也会需要合并,为了保障检索延迟的可预测性(这一点是HBase的痛点之一,比如分区发生Major Compaction时,读写性能会受到很大影响),Kudu的compaction策略和HBase相比,有很大不同,kudu的DRS数据文件的compaction,本质上不是为了减少文件数量,实际上Kudu DRS默认是以32MB为单位进行拆分的,DRS的compaction并不减少文件数量,而是对内容进行排序重组,减少不同DRS之间key的overlap,进而在检索的时候减少需要参与检索的DRS的数量。


以32MB这样小的单位进行拆分,也是为了能够以有限的资源快速的完成compaction的任务,及时根据系统负载调整Compaction行为,而不至于像HBase一样,Major Compaction动作成为导致性能不稳定的一个重要因素。所以对于Kudu来说,IO操作可以是一个持续平缓的过程,这点对响应的可预测性至关重要。

### 其它

Kudu底层核心代码使用C++开发,对外提供Java API接口,没有使用Java开发核心代码,也许有部分原因是希望通过自己管理内存,更好的适应和利用当前服务器上普遍越来越大的内存空间(256G+),另外也便于在关键逻辑中更好的优化代码。

## == 小结 ==

总体来说,个人感觉,Kudu本质上是将性能的优化,寄托在以列式存储为核心的基础上,希望通过提高存储效率,加快字段投影过滤效率,降低查询时CPU开销等来提升性能。而其他绝大多数设计,都是为了解决在列式存储的基础上支持随机读写这样一个目的而存在的。比如类Sql的元数据结构,是提高列式存储效率的一个辅助手段,唯一主键的设定也是配合列式存储引入的定制策略,至于其他如Delta存储,compaction策略等都是在这个设定下为了支持随机读写,降低latency不确定性等引入的一些Tradeoff方案

官方测试结果上,如果是存粹的随机读写,或者单行的检索请求这类场景,由于这些Tradeoff的存在,HBASE的性能吞吐率是要优于Kudu不少的(2倍到4倍),kudu的优势还是在支持类SQL检索这样经常需要进行投影操作的批量顺序检索分析场合。

目前kudu还处在Incubator阶段,并且还没有成熟的线上应用(小米走在了前面,做了一些业务应用的尝试),在数据安全,备份,系统健壮性等方面也还要打个问号,所以是否使用kudu,什么场合,什么时间点使用,是个需要好好考量的问题 ;)
大数据
2018-07-10 16:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
ANSI SQL标准和准则
SQL:ANSI数据库管理标准
结构化查询语言(SQL)是用于访问和管理数据库的数据库管理员(DBA)的标准语言。
多年来,SQL不断发展,许多版本和语言开始出现。为了统一SQL以获得最佳实践,美国国家标准协会(ANSI)为数据库查询语言创建了特定的标准。
SQL时间轴 1970年: 为IBM工作的计算机Edgar Frank Codd博士于1970年6月在计算机协会(ACM)期刊上发表了他的着名论文“大型共享数据库的数据关系模型”。Codd博士的模型仍然是关系数据库管理系统(RDBMS)的权威模型。 1978年: IBM公司在其位于加利福尼亚州的圣何塞研究中心开发数据库系统System / R以及数据库语言结构化英语查询语言(SEQUEL)。它们基于Codd博士的原始模型,他们称之为SEQUEL。 1979年: Relational Software,Inc。发布了第一个关系数据库管理系统,并将其命名为Oracle。他们的RDBMS在一台小型机上运行,​​使用SQL作为主要查询语言。该产品变得如此受欢迎,该公司更名为Oracle。 1982年: IBM发布了第一个基于SQL的商用SQL,它们命名为SQL /数据系统或SQL / DS,并于1985年发布了Database 2系统或DB2。两个系统都在IBM大型计算机上运行。IBM稍后将DB2移植到其他系统,包括在Windows和UNIX操作系统上运行的系统。
ANSI标准逐年 1986: SQL-87最初由ANSI于1986年正式确定。 1989年: 美国国家标准协会(ANSI)发布了第一套数据库查询语言标准,称为SQL-89或FIPS 127-1。 1992年: ANSI发布了修订后的标准ANSI / ISO SQL-92或SQL2,它们比SQLI更严格,增加了一些新功能。这些标准引入了合规水平,表明方言符合ANSI标准的程度。 1999: ANSI发布SQL3或ANSI / ISO SQL:1999,具有新功能,如对对象的支持。取代了核心规范的合规水平,以及另外9个封装的附加规格。 2003: ANSI发布SQL:2003,引入标准化序列,XML相关功能和标识列。第一个RDBMS的创建者EFCodd博士于同年4月18日去世。 2006: ANSI发布SQL:2006,定义如何将SQL与XML结合使用,并使应用程序能够将XQuery集成到现有的SQL代码中。 2008: ANSI发布SQL:2008,引入INSTEAD OF触发器以及TRUNCATE语句。 2011: ANSI 发布SQL:2011或ISO / IEC 9075:2011,ISO(1987)的第七个修订版和SQL数据库查询语言的ANSI(1986)标准。
他们将新的ANSI SQL标准分为九个部分,包括: 第1部分 - ISO / IEC 9075-1:2011 SQL / Framework,它提供了逻辑概念。 第2部分 - ISO / IEC 9075-2:2011 SQL / Foundation,包括中心元素SQL。 第3部分ISO / IEC 9075-3:2008 SQL /调用级接口(CLI)解释了接口组件,例如用于以各种编码语言(如COBOL和C ++)执行SQL语句的过程,结构和变量绑定。 第4部分ISO / IEC 9075-4:2011 SQL /持久存储模块(PSM),概述了SQL的过程扩展标准,包括条件处理和控制流,以及语句条件信号和重新标记,以及局部变量和游标。将表达式分配给参数和变量。解决了持久数据库语言例程(如存储过程)的维护问题。 第9部分ISO / IEC 9075-9:2008 SQL /外部数据管理(MED),包括用于定义数据链接类型的SQL扩展和允许SQL管理外部数据的外部数据包装器。外部数据是可访问的,但不受基于SQL的DBMS管理。 第10部分ISO / IEC 9075-10:2008 SQL /对象语言绑定(OLB)定义了SQLJ的语义和语法。SQLJ是将SQL嵌入到Java中。该标准规定了SQLJ应用程序的二进制可移植性机制。它还定义了几个Java包及其包含的类。 第11部分ISO / IEC 9075-11:2011 SQL /信息和定义Schemata,指定信息模式和定义模式,提供使SQL对象和数据库自我描述的工具。包括SQL对象标识符,安全性和授权规范,以及安全性和完整性约束。支持ISO / IEC 9075的功能和包,以及基于SQL的DBMS实现提供的功能。 第13部分 - ISO / IEC 9075-13:2008 ISO / IEC 9075-13:2008:SQL例程和类型使用Java编程语言(JRT),指定将Java类用作SQL结构化用户定义类型的能力从SQL应用程序中调用静态Java方法作为例程,在数据库中称为Java。 第14部分 - ISO / IEC 9075-14:2011 SQL / XML相关规范,定义了在SQL中使用XML时基于SQL的扩展。它概述了XML数据类型,例程和函数。它还描述了在SQL数据库中存储和管理XML的XML到SQL数据类型映射。
ISO / IEC 13249 SQL多媒体和应用程序包与ISO / IEC 9075一起作为单独但相关的标准,指定基于SQL的各种接口和包。
该软件包的目标是提供对最常见的数据库应用程序的集中访问,例如图片,文本,空间数据和数据挖掘。
如何查找标准副本
由于ISO和ANSI版权限制,SQL标准规范的硬拷贝仅可购买。您可以在 ANSI网站上 找到电子副本,方法是选择“访问标准 - eStandards商店”并搜索“SQL语言”。
每个文档有两种变体,但它们是相同的: INCITS / ISO / IEC 9075 - * - 2011 ISO / IEC 9075 - *:2011
每个数据库创建者都希望遵守这些标准,从而使SQL的各种实现变得相似; 但是,每个实现仍然具有使用SQL的独特方言,包括标准的扩展或添加。
将SQL置于测试中
所有SQL方言的基本SQL命令和语句都是类似的,因此一旦DBA知道如何使用它,他们就可以轻松地学习其他方法。
虽然大多数SQL数据库程序都有自己的专有扩展,但为了符合ANSI标准,它们必须至少以相同的方式支持这五个主要命令: UPDATE 删除 选择 插 哪里
SQL使DBA能够在数据库中执行以下操作: 执行查询 检索数据 插入,更新和删除记录 创建新表和视图 建立新的数据库 生成存储过程 设置视图,表和过程的权限。
为了生成可以从数据库访问数据的网站,关系数据库管理系统(RDBMS)数据库程序是必要的。一些流行的RDBMS程序包括: SQL Server MS Access MySQL的 Postgre SQL Oracle数据库。
为了创建数据库,数据库管理员还需要使用以下内容: 标准的标记语言,如CSS / HTML 服务器端脚本语言,例如ASP或PHP。 了解如何使用SQL检索您请求的数据。
数据库的剖析
关系数据库管理系统(RDBMS)构成了数据库的基础。使用RDBMS允许DBA将数据存储在称为表的数据库对象中。表由按部分组织的列和行相关数据组成。
识别数据库表
大多数数据库包含多个表,因此DBA必须使用名称标识每个表。每个表都包含包含信息和数据的记录行。
例如,企业将使用具有客户表的数据库,该客户表包含每个客户的以下数据: 顾客姓名 联系人姓名 客户ID 客户地址 - 街道,城市,邮政编码和国家 客户联系信息
在这种情况下,该表包含五个记录 - 每个客户一个 - 和八列,每个数据块一个:客户名称,联系人姓名,客户ID,包括城市的客户地址,邮政编码和国家/地区,以及客户联系信息。
与SQL数据库通信
数据库管理员使用SQL语句执行数据库中所需的所有操作。然后将结果存储在结果表中,称为结果集。
SQL通常不区分大小写,并且某些数据库系统需要在每个SQL语句的末尾使用分号。分号已成为在数据库系统中将SQL语句彼此分离的标准方法。
这使DBA能够在一次调用服务器时执行多个SQL语句。
一些SQL命令包括: ALTER TABLE 修改表。 ALTER DATABASE 修改数据库。 CREATE DATABASE 创建新数据库。 CREATE INDEX 创建索引/搜索键。 CREATE TABLE 创建新表。 DELETE 从数据库中删除数据。 DROP INDEX 删除索引。 DROP TABLE 删除表 INSERT INTO将 新数据插入数据库。 SELECT 从数据库中提取数据。 UPDATE 更新数据库中的数据。
Codd的12条规则
当他创建第一个关系数据库管理系统时,Codd博士包含了13条规则,建议如果数据库管理系统满足所有这些规则,那么它就是一个真正的关系数据库管理系统。
因为他从0到12编号,他们被称为 Codd的12条规则 : 规则零:基础 声明基本系统必须满足三个基本要求:它必须是关系型的,包括数据库和管理系统。它还必须专门利用关系设施来管理数据库,才能被视为真正的RDBMS。 规则一:信息表示 以单数形式表示数据库中的所有信息,特别是通过将值放在表行内的列位置。 规则二:保证访问 所有数据都必须是可访问的,就像主键的基本要求一样。通过定义包含的表和列的名称以及包含行的主键值,可以合理地寻址数据库中的每个单独标量值。 规则三:处理空值 DBMS必须让每个字段保持为空或为空。这意味着它必须以系统的方式支持对任何不适用或缺失信息的表示的操纵,该方式不同于所有常规值,并且独立于数据类型。 规则四:活动在线目录 系统必须支持使用其常规查询语言的授权用户可访问的内联,联机,关系结构或目录。用户应该能够使用他们用于访问数据库内部数据的相同关系模型和查询语言来访问数据库的目录。 规则五:综合数据子语言 系统必须支持至少一种具有线性语法的关系语言。用户应该能够在应用程序中以交互方式利用它。它还必须支持数据操作操作(如更新和检索)和数据定义操作(如视图定义)以及事务管理操作(如提交,开始和回滚)。它还应该具有完整性和安全性约束。 规则六:更新 任何理论上可以更新的视图都必须由系统更新。 规则七:高级更新,插入和删除 系统应该支持一次更新,插入和删除操作符,以便用户可以从由多个表和/或行的数据构成的集合中检索关系数据库中的数据。应为任何可检索集启用更新,插入和删除操作,而不是单个表中的单行。 规则八:物理数据独立性 物理级别的更新不应要求基于结构更新应用程序。例如,数据存储方式的更改,例如是否将数据放在数组或链接列表中。 规则九:逻辑数据独立性 对逻辑级别的更新(例如列,表和行)不应要求基于结构更新应用程序。实现逻辑数据独立性比物理数据独立性更难。 规则10:完整性独立性 指定完整性约束必须与应用程序和目录分开存储。必须可以在适当时更新这些约束而不影响任何当前存在的应用程序。 规则11:分发独立性 数据库用户不应看到数据库各部分到各个位置的分布。面对引入DBMS的分布式版本或当前分布式数据在整个系统中重新分配时,所有现有应用程序应能够连续有效地运行。 规则12:非颠覆 如果系统提供一次一个记录或低级别接口,则无法利用它来减少另一个系统。这种情况的一个例子是绕过关系完整性或安全约束的行为。
结构化查询语言或SQL已经走过了漫长的道路,但用于创建它的基础仍然很强大。随着云计算成为常态,数据库管理员可能会发现更多使用它的方式和地点; 但是,ANSI将保留用于指定统一数据库查询语言的标准。
资源:
ANSI.org
SQL的历史
W3Resource
←网站管理员工具的最终列表AZ HTTP状态代码→
大数据
2018-07-10 15:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1. Phoenix定义
Phoenix最早是saleforce的一个开源项目,后来成为Apache基金的顶级项目。
Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs而不是HBase客户端APIs来创建表,插入数据和对HBase数据进行查询。 put the SQL back in NoSQL
Phoenix完全使用Java编写,作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫描,并编排执行以生成标准的JDBC结果集。直接使用HBase API、协同处理器与自定义过滤器,对于简单查询来说,其性能量级是毫秒,对于百万级别的行数来说,其性能量级是秒。
HBase的查询工具有很多,如:Hive、Tez、Impala、Spark SQL、Phoenix等。
Phoenix通过以下方式使我们可以少写代码,并且性能比我们自己写代码更好: 将SQL编译成原生的HBase scans。 确定scan关键字的最佳开始和结束 让scan并行执行 ...
使用Phoenix的公司
Paste_Image.png
2. 历史演进 3.0/4.0 release
ARRAY Type . 支持标准的JDBC数组类型
Sequences . 支持 CREATE/DROP SEQUENCE, NEXT VALUE FOR, CURRENT VALUE FOR也实现了
Multi-tenancy . 同一张HBase物理表上,不同的租户可以创建相互独立的视图
Views . 同一张HBase物理表上可以创建不同的视图 3.1/4.1 release
Apache Pig Loader . 通过pig来处理数据时支持pig加载器来利用Phoenix的性能
Derived Tables . 允许在一个FROM子句中使用SELECT子句来定义一张衍生表
Local Indexing . 后面介绍
Tracing . 后面介绍 3.2/4.2 release
Subqueries 支持在WHERE和FROM子句中的独立子查询和相关子查询
Semi/anti joins . 通过标准的[NOT] IN 和 [NOT] EXISTS关键字来支持半/反连接
Optimize foreign key joins . 通过利用跳跃扫描过滤器来优化外键连接
Statistics Collection . 通过收集表的统计信息来提高并行查询能力 ** 3.3/4.3 release**
Many-to-many joins . 支持两边都太大以至于无法放进内存的连接
Map-reduce Integration . 支持Map-reduce集成
Functional Indexes . 后面介绍 4.4 release
User Defined Functions . 后面介绍 4.5 release
Asynchronous Index Population . 通过一个Map-reduce job,索引可以被异步创建 4.6 release
Time series Optimization . 优化针对时间序列数据的查询 4.7 release
Transaction Support . 后面介绍 ** 4.8 release**
DISTINCT Query Optimization . 使用搜索逻辑来大幅提高 SELECT DISTINCT 和 COUNT DISTINCT的查询性能
Local Index Improvements . Reworked 后面介绍
Hive Integration . 能够在Phoenix内使用Hive来支持大表和大表之间的连接
Namespace Mapping . 将Phoenix schema映射到HBase的命名空间来增强不同schema之间的隔离性
3. 特性
3.1 Transactions (beta) 事务
该特性还处于beta版,并非正式版。通过集成 Tephra ,Phoenix可以支持ACID特性。Tephra也是Apache的一个项目,是事务管理器,它在像HBase这样的分布式数据存储上提供全局一致事务。HBase本身在行层次和区层次上支持强一致性,Tephra额外提供交叉区、交叉表的一致性来支持可扩展性。
要想让Phoenix支持事务特性,需要以下步骤: 配置客户端hbase-site.xml phoenix.transactions.enabled true 配置服务端hbase-site.xml data.tx.snapshot.dir /tmp/tephra/snapshots data.tx.timeout 60 set the transaction timeout (time after which open transactions become invalid) to a reasonable value. 配置$HBASE_HOME并启动Tephra ./bin/tephra
通过以上配置,Phoenix已经支持了事务特性,但创建表的时候默认还是不支持的。如果想创建一个表支持事务特性,需要显示声明,如下: CREATE TABLE my_table (k BIGINT PRIMARY KEY, v VARCHAR) TRANSACTIONAL=true;
就是在建表语句末尾增加 TRANSACTIONAL=true 。
原本存在的表也可以更改成支持事务的,需要注意的是,事务表无法改回非事务的,因此更改的时候要小心。一旦改成事务的,就改不回去了。 ALTER TABLE my_other_table SET TRANSACTIONAL=true;
3.2 User-defined functions(UDFs) 用户定义函数
3.2.1 概述
Phoenix从4.4.0版本开始支持用户自定义函数。
用户可以创建临时或永久的用户自定义函数。这些用户自定义函数可以像内置的create、upsert、delete一样被调用。临时函数是针对特定的会话或连接,对其他会话或连接不可见。永久函数的元信息会被存储在一张叫做SYSTEM.FUNCTION的系统表中,对任何会话或连接均可见。
3.2.2 配置 hive-site.xml phoenix.functions.allowUserDefinedFunctions true fs.hdfs.impl org.apache.hadoop.hdfs.DistributedFileSystem hbase.rootdir ${hbase.tmp.dir}/hbase The directory shared by region servers and into which HBase persists. The URL should be 'fully-qualified' to include the filesystem scheme. For example, to specify the HDFS directory '/hbase' where the HDFS instance's namenode is running at namenode.example.org on port 9000, set this value to: hdfs://namenode.example.org:9000/hbase. By default, we write to whatever ${hbase.tmp.dir} is set too -- usually /tmp -- so change this configuration or else all data will be lost on machine restart. hbase.dynamic.jars.dir ${hbase.rootdir}/lib The directory from which the custom udf jars can be loaded dynamically by the phoenix client/region server without the need to restart. However, an already loaded udf class would not be un-loaded. See HBASE-1936 for more details.
后两个配置需要跟hbse服务端的配置一致。
以上配置完后,在JDBC连接时还需要执行以下语句: Properties props = new Properties(); props.setProperty("phoenix.functions.allowUserDefinedFunctions", "true"); Connection conn = DriverManager.getConnection("jdbc:phoenix:localhost", props);
以下是可选的配置,用于动态类加载的时候把jar包从hdfs拷贝到本地文件系统 hbase.local.dir ${hbase.tmp.dir}/local/ Directory on the local filesystem to be used as a local storage.
3.3 Secondary Indexing 二级索引
在HBase中,只有一个单一的按照字典序排序的rowKey索引,当使用rowKey来进行数据查询的时候速度较快,但是如果不使用rowKey来查询的话就会使用filter来对全表进行扫描,很大程度上降低了检索性能。而Phoenix提供了二级索引技术来应对这种使用rowKey之外的条件进行检索的场景。 Covered Indexes 只需要通过索引就能返回所要查询的数据,所以索引的列必须包含所需查询的列(SELECT的列和WHRER的列) Functional Indexes 从Phoeinx4.3以上就支持函数索引,其索引不局限于列,可以合适任意的表达式来创建索引,当在查询时用到了这些表达式时就直接返回表达式结果 Global Indexes Global indexing适用于多读少写的业务场景。
使用Global indexing的话在写数据的时候会消耗大量开销,因为所有对数据表的更新操作(DELETE, UPSERT VALUES and UPSERT SELECT),会引起索引表的更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。在读数据的时候Phoenix会选择索引表来降低查询消耗的时间。在默认情况下如果想查询的字段不是索引字段的话索引表不会被使用,也就是说不会带来查询速度的提升。 Local Indexes Local indexing适用于写操作频繁的场景。
与Global indexing一样,Phoenix会自动判定在进行查询的时候是否使用索引。使用Local indexing时,索引数据和数据表的数据是存放在相同的服务器中的避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。使用Local indexing的时候即使查询的字段不是索引字段索引表也会被使用,这会带来查询速度的提升,这点跟Global indexing不同。一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。
3.4 Statistics Collection 统计信息收集 UPDATE STATISTICS可以更新某张表的统计信息,以提高查询性能
3.5 Row timestamp 时间戳 从4.6版本开始,Phoenix提供了一种将HBase原生的row timestamp映射到Phoenix列的方法。这样有利于充分利用HBase提供的针对存储文件的时间范围的各种优化,以及Phoenix内置的各种查询优化。
3.6 Paged Queries 分页查询 Phoenix支持分页查询: Row Value Constructors (RVC) OFFSET with limit
3.7 Salted Tables 散步表 如果row key是自动增长的,那么HBase的顺序写会导致region server产生数据热点的问题,Phoenix的Salted Tables技术可以解决region server的热点问题
3.8 Skip Scan 跳跃扫描 可以在范围扫描的时候提高性能
3.9 Views 视图 标准的SQL视图语法现在在Phoenix上也支持了。这使得能在同一张底层HBase物理表上创建多个虚拟表。
3.10 Multi tenancy 多租户 通过指定不同的租户连接实现数据访问的隔离
3.11 Dynamic Columns 动态列 Phoenix 1.2, specifying columns dynamically is now supported by allowing column definitions to included in parenthesis after the table in the FROM clause on a SELECT statement. Although this is not standard SQL, it is useful to surface this type of functionality to leverage the late binding ability of HBase.
3.12 Bulk CSV Data Loading 大量CSV数据加载 加载CSV数据到Phoenix表有两种方式:1. 通过psql命令以单线程的方式加载,数据量少的情况下适用。 2. 基于MapReduce的bulk load工具,适用于数据量大的情况
3.13 Query Server 查询服务器 Phoenix4.4引入的一个单独的服务器来提供thin客户端的连接
3.14 Tracing 追踪 从4.1版本开始Phoenix增加这个特性来追踪每条查询的踪迹,这使用户能够看到每一条查询或插入操作背后从客户端到HBase端执行的每一步。
3.15 Metrics 指标 Phoenix提供各种各样的指标使我们能够知道Phoenix客户端在执行不同SQL语句的时候其内部发生了什么。这些指标在客户端JVM中通过两种方式来收集: Request level metrics - collected at an individual SQL statement
level Global metrics - collected at the client JVM level
4. 架构和组成 Phoenix架构
Phoenix Architecture.png Phoenix在Hadoop生态系统中的位置
位置.png
5. 数据存储 Phoenix将HBase的数据模型映射到关系型世界
Data Model.png
6. 对QL的支持
支持的命令如下: SELECT Example: SELECT * FROM TEST LIMIT 1000; SELECT * FROM TEST LIMIT 1000 OFFSET 100; SELECT full_name FROM SALES_PERSON WHERE ranking >= 5.0 UNION ALL SELECT reviewer_name FROM CUSTOMER_REVIEW WHERE score >= 8.0 UPSERT VALUES Example: UPSERT INTO TEST VALUES('foo','bar',3); UPSERT INTO TEST(NAME,ID) VALUES('foo',123); UPSERT SELECT Example: UPSERT INTO test.targetTable(col1, col2) SELECT col3, col4 FROM test.sourceTable WHERE col5 < 100 UPSERT INTO foo SELECT * FROM bar; DELETE Example: DELETE FROM TEST; DELETE FROM TEST WHERE ID=123; DELETE FROM TEST WHERE NAME LIKE 'foo%'; CREATE TABLE CREATE TABLE my_schema.my_table ( id BIGINT not null primary key, date) CREATE TABLE my_table ( id INTEGER not null primary key desc, date DATE not null,m.db_utilization DECIMAL, i.db_utilization) m.DATA_BLOCK_ENCODING='DIFF' CREATE TABLE stats.prod_metrics ( host char(50) not null, created_date date not null,txn_count bigint CONSTRAINT pk PRIMARY KEY (host, created_date) ) CREATE TABLE IF NOT EXISTS "my_case_sensitive_table" ( "id" char(10) not null primary key, "value" integer) DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000 split on (?, ?, ?) CREATE TABLE IF NOT EXISTS my_schema.my_table (org_id CHAR(15), entity_id CHAR(15), payload binary(1000),CONSTRAINT pk PRIMARY KEY (org_id, entity_id) )TTL=86400 DROP TABLE Example: DROP TABLE my_schema.my_table; DROP TABLE IF EXISTS my_table; DROP TABLE my_schema.my_table CASCADE; CREATE FUNCTION Example: CREATE FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' using jar 'hdfs:/localhost:8080/hbase/lib/myjar.jar' CREATE FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' CREATE FUNCTION my_increment(integer, integer constant defaultvalue='10') returns integer as 'com.mypackage.MyIncrementFunction' using jar '/hbase/lib/myincrement.jar' CREATE TEMPORARY FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' using jar 'hdfs:/localhost:8080/hbase/lib/myjar.jar' DROP FUNCTION Example: DROP FUNCTION IF EXISTS my_reverse DROP FUNCTION my_reverse CREATE VIEW Example: CREATE VIEW "my_hbase_table"( k VARCHAR primary key, "v" UNSIGNED_LONG) default_column_family='a'; CREATE VIEW my_view ( new_col SMALLINT ) AS SELECT * FROM my_table WHERE k = 100; CREATE VIEW my_view_on_view AS SELECT * FROM my_view WHERE new_col > 70; DROP VIEW Example: DROP VIEW my_view DROP VIEW IF EXISTS my_schema.my_view DROP VIEW IF EXISTS my_schema.my_view CASCADE CREATE SEQUENCE Example: CREATE SEQUENCE my_sequence; CREATE SEQUENCE my_sequence START WITH -1000 CREATE SEQUENCE my_sequence INCREMENT BY 10 CREATE SEQUENCE my_schema.my_sequence START 0 CACHE 10 DROP SEQUENCE Example: DROP SEQUENCE my_sequence DROP SEQUENCE IF EXISTS my_schema.my_sequence ALTER Example: ALTER TABLE my_schema.my_table ADD d.dept_id char(10) VERSIONS=10 ALTER TABLE my_table ADD dept_name char(50), parent_id char(15) null primary key ALTER TABLE my_table DROP COLUMN d.dept_id, parent_id; ALTER VIEW my_view DROP COLUMN new_col; ALTER TABLE my_table SET IMMUTABLE_ROWS=true,DISABLE_WAL=true; CREATE INDEX Example: CREATE INDEX my_idx ON sales.opportunity(last_updated_date DESC) CREATE INDEX my_idx ON log.event(created_date DESC) INCLUDE (name, payload) SALT_BUCKETS=10 CREATE INDEX IF NOT EXISTS my_comp_idx ON server_metrics ( gc_time DESC, created_date DESC ) DATA_BLOCK_ENCODING='NONE',VERSIONS=?,MAX_FILESIZE=2000000 split on (?, ?, ?) CREATE INDEX my_idx ON sales.opportunity(UPPER(contact_name)) DROP INDEX Example: DROP INDEX my_idx ON sales.opportunity DROP INDEX IF EXISTS my_idx ON server_metrics ALTER INDEX Example: ALTER INDEX my_idx ON sales.opportunity DISABLE ALTER INDEX IF EXISTS my_idx ON server_metrics REBUILD EXPLAIN Example: EXPLAIN SELECT NAME, COUNT(*) FROM TEST GROUP BY NAME HAVING COUNT(*) > 2; EXPLAIN SELECT entity_id FROM CORE.CUSTOM_ENTITY_DATA WHERE organization_id='00D300000000XHP' AND SUBSTR(entity_id,1,3) = '002' AND created_date < CURRENT_DATE()-1; UPDATE STATISTICS Example: UPDATE STATISTICS my_table UPDATE STATISTICS my_schema.my_table INDEX UPDATE STATISTICS my_index UPDATE STATISTICS my_table COLUMNS UPDATE STATISTICS my_table SET phoenix.stats.guidepost.width=50000000 CREATE SCHEMA Example: CREATE SCHEMA IF NOT EXISTS my_schema CREATE SCHEMA my_schema USE Example: USE my_schema USE DEFAULT DROP SCHEMA Example: DROP SCHEMA IF EXISTS my_schema DROP SCHEMA my_schema
7. 安装部署
7.1 安装预编译的Phoenix 下载并解压最新版的phoenix-[version]-bin.tar包 将phoenix-[version]-server.jar放入服务端和master节点的HBase的lib目录下 重启HBase 将phoenix-[version]-client.jar添加到所有Phoenix客户端的classpath
7.2 使用Phoenix
7.2.1 命令行
若要在命令行执行交互式SQL语句:
1.切换到bin目录
2.执行以下语句 $ sqlline.py localhost
若要在命令行执行SQL脚本 $ sqlline.py localhost ../examples/stock_symbol.sql
Paste_Image.png
7.2.2 客户端
SQuirrel 是用来连接Phoenix的客户端。
SQuirrel安装步骤如下: 1. Remove prior phoenix-[*oldversion*]-client.jar from the lib directory of SQuirrel, copy phoenix-[*newversion*]-client.jar to the lib directory (*newversion* should be compatible with the version of the phoenix server jar used with your HBase installation) 2. Start SQuirrel and add new driver to SQuirrel (Drivers -> New Driver) 3. In Add Driver dialog box, set Name to Phoenix, and set the Example URL to jdbc:phoenix:localhost. 4. Type “org.apache.phoenix.jdbc.PhoenixDriver” into the Class Name textbox and click OK to close this dialog. 5. Switch to Alias tab and create the new Alias (Aliases -> New Aliases) 6. In the dialog box, Name: *any name*, Driver: Phoenix, User Name: *anything*, Password: *anything* 7. Construct URL as follows: jdbc:phoenix: *zookeeper quorum server*. For example, to connect to a local HBase use: jdbc:phoenix:localhost 8. Press Test (which should succeed if everything is setup correctly) and press OK to close. 9. Now double click on your newly created Phoenix alias and click Connect. Now you are ready to run SQL queries against Phoenix.
Paste_Image.png
8. 测试
8.1 Pherf
Pherf是可以通过Phoenix来进行性能和功能测试的工具。Pherf可以用来生成高度定制的数据集,并且测试SQL在这些数据集上的性能。
8.1.1 构建Pherf
Pherf是在用maven构建Phoenix的过程中同时构建的。可以用两种不同的配置来构建: 集群(默认) This profile builds Pherf such that it can run along side an existing cluster. The dependencies are pulled from the HBase classpath. 独立 This profile builds all of Pherf’s dependencies into a single standalone jar. The deps will be pulled from the versions specified in Phoenix’s pom. 构建全部的Phoenix。包含Pherf的默认配置。 mvn clean package -DskipTests 用Pherf的独立配置来构建Phoenix。 mvn clean package -P standalone -DskipTests
8.1.2 安装
用以上的Maven命令构建完Pherf后,会在该模块的目标目录下生成一个zip文件。 将该zip文件解压到合适的目录 配置 env.sh 文件 ./pherf.sh -h 想要在一个真正的集群上测试,运行如下命令: ./pherf.sh -drop all -l -q -z localhost -schemaFile .*user_defined_schema.sql -scenarioFile .*user_defined_scenario.xml
8.1.3 命令示例 列出所有可运行的场景文件 $./pherf.sh -listFiles 删掉全部场景文件中存在的特定的表、加载和查询数据 $./pherf.sh -drop all -l -q -z localhost
8.1.4 参数 -h Help
-l Apply schema and load data
-q Executes Multi-threaded query sets and write results
-z [quorum] Zookeeper quorum
-m Enable monitor for statistics
-monitorFrequency [frequency in Ms] _Frequency at which the monitor will snopshot stats to log file.
-drop [pattern] Regex drop all tables with schema name as PHERF. Example drop Event tables: -drop .(EVENT). Drop all: -drop .* or -drop all*
-scenarioFile Regex or file name of a specific scenario file to run.
-schemaFile Regex or file name of a specific schema file to run.
-export Exports query results to CSV files in CSV_EXPORT directory
-diff Compares results with previously exported results
-hint Executes all queries with specified hint. Example SMALL
-rowCountOverride
-rowCountOverride [number of rows] Specify number of rows to be upserted rather than using row count specified in schema
8.1.5 为数据生成增加规则
8.1.6 定义场景
8.1.7 结果
结果实时写入结果目录中。可以打开.jpg格式文件来实时可视化。
8.1.8 测试 Run unit tests: mvn test -DZK_QUORUM=localhost
Run a specific method: mvn -Dtest=ClassName#methodName test
More to come...
8.2 性能
Phoenix通过以下方法来奉行 把计算带到离数据近的地方 的哲学: 协处理器
在服务端执行操作来最小化服务端和客户端的数据传输 定制的过滤器
为了删减数据使之尽可能地靠近源数据并最小化启动代价,Phoenix使用原生的HBase APIs而不是使用Map/Reduce框架
8.2.1 Phoenix对比相近产品
8.2.1.1 Phoenix vs Hive (running over HDFS and HBase)
Paste_Image.png Query: select count(1) from table over 10M and 100M rows. Data is 5 narrow columns. Number of Region Servers: 4 (HBase heap: 10GB, Processor: 6 cores @ 3.3GHz Xeon)
8.2.1.2 Phoenix vs Impala (running over HBase)
Paste_Image.png Query: select count(1) from table over 1M and 5M rows. Data is 3 narrow columns. Number of Region Server: 1 (Virtual Machine, HBase heap: 2GB, Processor: 2 cores @ 3.3GHz Xeon)
8.2.2 Latest Automated Performance Run
Latest Automated Performance Run | Automated Performance Runs History
8.2.3 Phoenix1.2性能提升 Essential Column Family
Paste_Image.png Skip Scan
Paste_Image.png Salting
Paste_Image.png Top-N
Paste_Image.png
9. 参考资料 http://phoenix.apache.org http://phoenix.apache.org/Phoenix-in-15-minutes-or-less.html http://hadooptutorial.info/apache-phoenix-hbase-an-sql-layer-on-hbase/ http://www.phoenixframework.org/docs/resources https://en.wikipedia.org/wiki/Apache_Phoenix

作者:Jeffbond
链接:https://www.jianshu.com/p/d862337247b1
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
大数据
2018-07-10 14:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
本文主要介绍如何免费采集阿里巴巴批发网商品的批发价格、发货时间、是否代发等信息。
采集字段:
商品标题、商品链接、图片链接、标签1、标签2、标签3、价格、30天成交数、 评价、店铺
功能点目录 :
如何对采集字段进行配置
如何采集列表+详情页类型网页
采集结果预览:
下面我们来详细介绍一下如何免费采集1688批发网的商品数据,我们以“羽绒服女”为例,具体步骤如下:
步骤一:下载安装并注册登录
1、 点此 打开官网,下载并安装爬虫软件
2、点击注册登录,注册新账号然后登录
步骤二:新建采集任务
1、复制 1688羽绒服女 的网页(需要搜索结果页的网址,而不是首页的网址)
点此 了解关于如何正确地输入网址。
2、新建智能模式采集任务
您可以在软件上直接新建采集任务,也可以通过导入规则来创建任务。
点此 了解如何导入和导出采集规则。
步骤三:配置采集规则
1、设置提取数据字段
在智能模式下,我们输入网址后软件即可自动识别出页面上的数据并生成采集结果,每一类数据对应一个采集字段,我们可以右击字段进行相关设置,包括修改字段名称、增减字段、处理数据等。
点此 了解如何对采集字段进行配置。
在列表页上,我们需要采集商品标题、商品链接、价格及标签等信息,字段设置效果如下:
2、使用深入采集功能提取详情页数据
在列表页上只展示出了1688批发网商品的部分信息,如果需要商品的详细信息,我们需要右击商品链接使用“深入采集”功能,跳转到详情页进行采集。
点此 深入了解如何采集列表+详情页类型网页。
在详情页面我们可以看到商品的评论数、30天累计销量及店铺等信息,我们可以点击“添加字段”添加采集字段,字段设置效果如下:
步骤四:设置并启动采集任务
1、设置采集任务
完成了采集数据添加,我们可以开始启动采集任务了。点击开始采集之后跳出任务栏,任务栏界面上有“更多设置”的按钮,我们可以点击进行设置,也可以按照系统默认的设置。
点击“更多设置”按钮,在弹出的运行设置页面中我们可以进行运行设置和防屏蔽设置,系统默认设置“2”秒请求等待时间,防屏蔽设置就按照系统默认设置,然后点击保存。
2、启动采集任务
点击“保存并启动”按钮,可在弹出的页面中进行一些高级设置,包括定时启动、自动入库和下载图片,本次示例中未使用到这些功能,直接点击“启动”运行爬虫工具。
点此 深入了解什么是定时采集。
点此 深入了解什么是自动入库。
点此 深入了解如何下载图片。
【温馨提示】 免费版本可以使用非周期性定时采集功能,下载图片功能是免费的。个人专业版及以上版本可以使用高级定时功能和自动入库功能。
3、运行任务提取数据
任务启动之后便开始自动采集数据,我们从界面上可以直观的看到程序运行过程和采集结果,采集结束之后会有提醒。
步骤五:导出并查看数据
数据采集完成后,我们可以查看和导出数据,软件支持多种导出方式(手动导出到本地、手动导出到数据库、自动发布到数据库、自动发布到网站)和导出文件的格式(EXCEL、CSV、HTML和TXT),我们选择自己需要方式和文件类型,点击“确认导出”。
点此 深入了解如何查看和清空采集数据。
点此 深入了解如何导出采集结果。
【温馨提示】: 所有手动导出功能都是免费的。个人专业版及以上版本可以使用发布到网站功能。

再为您推荐几个关于电商的采集教程:
如何免费采集淘宝商品信息数据
如何免费采集京东商品信息数据
如何免费采集亚马逊商品信息数据
大数据
2018-12-25 20:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
随着智能手机的普及和APP形态的愈发丰富,移动设备的应用安装量急剧上升。用户在每天使用这些APP的过程中,也会产生大量的线上和线下行为数据。这些数据反映了用户的兴趣与需求,如果能够被深入挖掘并且合理利用,可以指导用户的运营。若能提前预测用户下一步的行为,甚至提前得知用户卸载、流失的可能性,则能更好地指导产品的优化以及用户的精细化运营。
大数据服务商个推旗下的应用统计产品“个数”,可以从用户属性、使用行为、行业对比等多指标多维度对APP进行全面统计分析。除了基础统计、渠道统计、埋点统计等功能外, 个数的一大特色能力是——可基于大数据进行用户行为预测,帮助运营者预测用户流失、卸载、付费的可能性,从而助力APP的精细化运营以及全生命周期管理。
开发者在实践的过程中,基于大数据进行用户行为预测会有两大难点: 第一,开发者需要使用多种手段对目标问题进行分解;第二,数据在特定的问题上会有不同的表现。
“个数”利用数据分析建模,对用户行为进行预测的大概流程包括以下几点:
1、目标问题分解
(1)明确需要进行预测的问题;
(2)明确未来一段时间的跨度。
2、分析样本数据
(1)提取出所有用户的历史付费记录,这些付费记录可能仅占所有记录的千分之几,数据量会非常小;
(2)分析付费记录,了解付费用户的构成,比如年龄层次、性别、购买力和消费的产品类别等;
(3)提取非付费用户的历史数据,这里可以根据产品的需求,添加条件、或无条件地进行提取,比如提取活跃并且非付费用户,或者不加条件地直接进行提取;
(4)分析非付费用户的构成。
3、构建模型的特征
(1)原始的数据可能能够直接作为特征使用;
(2)有些数据在变换后,才会有更好的使用效果,比如年龄,可以变换成少年、中年、老年等特征;
(3)交叉特征的生成,比如“中年”和“女性”两种特征,就可以合并为一个特征进行使用。
4、计算特征的相关性
(1)计算特征饱和度,进行饱和度过滤;
(2)计算特征IV、卡方等指标,用以进行特征相关性的过滤。
5、选用相关的模型进行建模
(1)选择适当的参数进行建模;
(2)模型训练好后,统计模型的精确度、召回率、AUC等指标,来评价模型;
(3)如果觉得模型的表现可以接受,就可以在验证集上做验证,验证通过后,进行模型保存和预测。
6、预测
加载上述保存的模型,并加载预测数据,进行预测。
7、监控
最后,运营人员还需要对每次预测的结果进行关键指标监控,及时发现并解决出现的问题,防止出现意外情况,导致预测无效或预测结果出现偏差。
以上就是“个数”对用户行为进行预测的整体流程。总的来说, 分析和建模的关键在于大数据的收集和对大数据细节的处理 。在进行用户行为预测的整个过程中,可供技术人员选择的方法和模型都有很多,而对于实际的应用者来说, 没有最好的选择,只有更合适的选择。
大数据
2018-12-25 17:05:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1. 单独启动和关闭hadoop服务
启动名称节点
#hadoop-daemon.sh start namenode
启动数据节点
#hadoop-daemons.sh start datanode slave
启动secondarynamenode
#hadoop-daemon.sh start secondarynamenode

启动resourcemanager
#yarn-daemon.sh start resourcemanager
启动nodemanager
#bin/yarn-daemons.sh start nodemanager
停止数据节点
#hadoop-daemons.sh stop datanode

2. 常用的命令
创建目录
#hdfs dfs -mkdir /input
查看
#hdfs dfs -ls
递归查看
#hdfs dfs ls -R
上传
#hdfs dfs -put
下载
#hdfs dfs -get
删除
#hdfs dfs -rm

从本地剪切粘贴到hdfs
#hdfs fs -moveFromLocal /input/xx.txt /input/xx.txt
从hdfs剪切粘贴到本地
#hdfs fs -moveToLocal /input/xx.txt /input/xx.txt
追加一个文件到另一个文件到末尾
#hdfs fs -appedToFile ./hello.txt /input/hello.txt

查看文件内容
#hdfs fs -cat /input/hello.txt
显示一个文件到末尾
#hdfs fs -tail /input/hello.txt
以字符串的形式打印文件的内容
#hdfs fs -text /input/hello.txt

修改文件权限
#hdfs fs -chmod 666 /input/hello.txt
修改文件所属
#hdfs fs -chown ruoze.ruoze /input/hello.txt

从本地文件系统拷贝到hdfs里
#hdfs fs -copyFromLocal /input/hello.txt /input/
从hdfs拷贝到本地
#hdfs fs -copyToLocal /input/hello.txt /input/

从hdfs到一个路径拷贝到另一个路径
#hdfs fs -cp /input/xx.txt /output/xx.txt
从hdfs到一个路径移动到另一个路径
#hdfs fs -mv /input/xx.txt /output/xx.txt

统计文件系统的可用空间信息
#hdfs fs -df -h /
统计文件夹的大小信息
#hdfs fs -du -s -h /
统计一个指定目录下的文件节点数量
#hadoop fs -count /aaa

设置hdfs的文件副本数量
#hadoop fs -setrep 3 /input/xx.txt

总结:一定要学会查看命令帮助 hadoop命令直接回车查看命令帮助 hdfs命令、hdfs dfs命令直接回车查看命令帮助 hadoop fs 等价 hdfs dfs命令,和Linux的命令差不多。
大数据
2018-12-25 17:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一、需要安装的软件
# 相关环境:
# jdk-7u80
# hadoop-2.6.0-cdh5.7.1 不支持jdk1.8,因此此处也延续jdk1.7
# apache-maven-3.3.9
# mysql5.1
# 伪分布集群已启动

二、安装jdk
mkdir /usr/java && cd /usr/java/
tar -zxvf /tmp/server-jre-7u80-linux-x64.tar.gz
chown -R root:root /usr/java/jdk1.7.0_80/
echo 'export JAVA_HOME=/usr/java/jdk1.7.0_80'>>/etc/profile
source /etc/profile

三、安装maven
cd /usr/local/
unzip /tmp/apache-maven-3.3.9-bin.zip
chown root: /usr/local/apache-maven-3.3.9 -R
echo 'export MAVEN_HOME=/usr/local/apache-maven-3.3.9'>>/etc/profile
echo 'export MAVEN_OPTS="-Xms256m -Xmx512m"'>>/etc/profile
echo 'export PATH=$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH'>>/etc/profile
source /etc/profile

四、安装mysql
yum -y install mysql-server mysql
/etc/init.d/mysqld start
chkconfig mysqld on
mysqladmin -u root password 123456
mysql -uroot -p123456
use mysql;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'v123456' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' IDENTIFIED BY '123456' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
update user set password=password('123456') where user='root';
delete from user where not (user='root') ;
delete from user where user='root' and password='';
drop database test;
DROP USER ''@'%';
flush privileges;

五、下载hive源码包:
# http://archive.cloudera.com/cdh5/cdh/5/
# 根据cdh版本选择对应hive软件包:
# hive-1.1.0-cdh5.7.1-src.tar.gz
# 解压后使用maven命令编译成安装包

六、编译:
cd /tmp/
tar -xf hive-1.1.0-cdh5.7.1-src.tar.gz
cd /tmp/hive-1.1.0-cdh5.7.1
mvn clean package -DskipTests -Phadoop-2 -Pdist
# 编译生成的包在以下位置:
# packaging/target/apache-hive-1.1.0-cdh5.7.1-bin.tar.gz

七、安装编译生成的Hive包,然后测试
cd /usr/local/
tar -xf /tmp/apache-hive-1.1.0-cdh5.7.1-bin.tar.gz
ln -s apache-hive-1.1.0-cdh5.7.1-bin hive
chown -R hadoop:hadoop apache-hive-1.1.0-cdh5.7.1-bin
chown -R hadoop:hadoop hive
echo 'export HIVE_HOME=/usr/local/hive'>>/etc/profile
echo 'export PATH=$HIVE_HOME/bin:$PATH'>>/etc/profile

八、更改环境变量
su - hadoop
cd /usr/local/hive
cd conf

1、hive-env.sh
cp hive-env.sh.template hive-env.sh&&vi hive-env.sh
HADOOP_HOME=/usr/local/hadoop

2、hive-site.xml
vi hive-site.xml





javax.jdo.option.ConnectionURL
jdbc:mysql://localhost:3306/vincent_hive?createDatabaseIfNotExist=true


javax.jdo.option.ConnectionDriverName
com.mysql.jdbc.Driver


javax.jdo.option.ConnectionUserName
root


javax.jdo.option.ConnectionPassword
vincent




九、拷贝mysql驱动包到$HIVE_HOME/lib
# 上方的hive-site.xml使用了java的mysql驱动包
# 需要将这个包上传到hive的lib目录之下
# 解压 mysql-connector-java-5.1.45.zip 对应的文件到目录即可
cd /tmp
unzip mysql-connector-java-5.1.45.zip
cd mysql-connector-java-5.1.45
cp mysql-connector-java-5.1.45-bin.jar /usr/local/hive/lib/

未拷贝有相关报错:
The specified datastore driver ("com.mysql.jdbc.Driver") was not found in the CLASSPATH.
Please check your CLASSPATH specification,
and the name of the driver.
大数据
2018-12-25 16:58:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
近日,阿里云监控发现,匿名者(Anonymous)组织成员正在发起针对全球中央银行网站的攻击行动, 截止目前,国内有超过2家以上的重要网站被攻击,攻击特征主要为DDoS攻击和CC攻击。
此次事件中,阿里云通过威胁情报发现,该攻击的主要特征是DDoS和CC攻击,目前,攻击已经造成多家网站不间断的无法访问, 阿里云安全专家分析本次攻击有5大特征 : 攻击时间范围在2018年11月12日凌晨6点13分到2018年12月14日,根据目前的活动情况来看,已经攻击了1月之久; 分析其中一次攻击,攻击请求15423249,提取到攻击源IP 1439个,攻击IP分散,攻击源主要分布在境内,攻击源分布如下图:
大量资源型文件(zip、apk、js、png)请求耗尽用户带宽资源,使用随机参数绕过防护设备频率检测、cdn设备缓存,附图:
攻击者会伪造user-agent、referer字段伪装攻击流量; 持续性攻击,部分受害用户持续半个月遭受攻击,攻击者会根据防护策略变换攻击手法,具有较强对抗性。
应急方案: DDoS高防IP+WAF 安全防御应急体系构建 阿里云应对此次高危应急事件,采用的是基于DDoS高防IP和Web应用防火墙构建的防御体系,方案采用域名解析的方式接入,可以适用于云上系统方案,同时也适用于云下环境(非阿里云)的防护。 DDoS高防IP是针对互联网服务器在遭受大流量的DDoS攻击后导致服务不可用的情况下,进行有效抵御DDoS攻击的SaaS安全服务,确保源站的稳定可靠。 Web应用防火墙是基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意访问,避免网站资产数据泄露,保障网站的安全与可用性。
云盾DDoS攻击防御特点和优势: 全面覆盖常见DDoS攻击类型
云盾DDoS清洗系统可帮助用户抵御各类基于网络层、传输层及应用层的各种 DDoS 攻击(包括 CC、SYN Flood、UDP Flood、UDPDNS Query Flood、(M)Stream Flood、ICMP Flood、HTTP Get Flood 等所有 DDoS 攻击方式),并能实时短信通知用户网站防御状态。 快速自动响应,5秒内进入防护状态
云盾DDoS清洗系统采用全球领先的检测和防护技术,可以在5秒钟内完成攻击发现、流量牵引和流量清洗全部动作,大大减少了网络抖动现象。在防护触发条件上不仅仅依赖流量阈值,同时还对网络行为的统计判断,做到精准识别DDoS攻击,保障了在遇到DDoS攻击时客户业务的可用性。 高弹性、高冗余的DDoS防御能力
云盾DDoS清洗系统每个最小单元支持10Gbps的攻击流量过滤。得益于云计算架构的高弹性和大冗余特点,DDoS攻击防御系统可在云环境中无缝扩容,实现DDoS攻击防御能力的高弹性。 双向防护,避免云资源被滥用
云盾DDoS攻击防御系统不仅仅能防护来自于云外的DDoS攻击,同时还能及时发现云内资源被滥用的非法行为。一旦发现云内有服务器被利用向外发起DDoS攻击,云网络流量监控系统会与主机安全防护系统联动,限制被滥用的云服务器的网络访问行为,并产生告警,实现对内部主机的有效管控。
云盾Web应用防火墙特点和优势: 支持协议
支持对网站的HTTP、HTTPS、HTTP2、WebSocket流量进行Web安全防护。 常见Web应用攻击防护
防御OWASP 常见威胁:SQL注入、XSS跨站、Webshell上传、后门隔离保护、命令注入、非法HTTP协议请求、常见Web服务器漏洞攻击、核心文件非授权访问、路径穿越、扫描防护等。
网站隐身:不对攻击者暴露站点地址、避免绕过Web应用防火墙直接攻击。0day补丁定期及时更新:防护规则与淘宝同步,及时更新最新漏洞补丁、第一时间全球同步下发最新补丁,对网站进行安全防护。
友好观察模式:针对网站新上线的业务开启观察模式、对于匹配中防护规则的疑似攻击只告警不阻断、方便统计业务误报状况。 CC恶意攻击防护
对单一源IP的访问频率进行控制、重定向跳转验证、人机识别等。针对海量慢速请求攻击、根据统计响应码及URL请求分布、异常Referer及User-Agent特征识别,结合网站精准防护规则进行综合防护。充分利用阿里云大数据安全优势、建立威胁情报与可信访问分析模型、快速识别恶意流量。 精准访问控制
提供友好的配置控制台界面,支持IP、URL、Referer、User-Agent等HTTP常见字段的条件组合,打造强大的精准访问控制策略,可支持盗链防护、网站后台保护等防护场景。与Web常见攻击防护、CC防护等安全模块打造多层综合保护机制、轻松依据需求,识别可信与恶意流量。 虚拟补丁
在Web应用漏洞补丁发布和修复之前,通过调整Web防护策略实现快速防护。 攻击事件管理
支持对攻击事件、攻击流量、攻击规模的集中管理统计。
阿里云安全专家建议:
此次攻击的复杂度和对抗性都比较强,客户应对的时候需要选择安全服务厂商和服务人员: 综上,阿里云安全防护产品可以通过CC攻击防御功能识别和拦截这类攻击,云端优势可以减少恶意流量回源。 阿里云提供海量的威胁情报库可以实现协同防御。 阿里云安全工程师目前提供7*24小时应急服务分析攻击变种,更新防护策略。
原文链接
大数据
2018-12-25 15:40:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Kafka
Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源 项目。

1.前言
消息队列的性能好坏,其文件存储机制设计是衡量一个消息队列服务技术水平和最关键指标之一。下面将从Kafka文件存储机制和物理结构角度,分析Kafka是如何实现高效文件存储,及实际应用效果。

1.1 Kafka的特性:
- 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。
- 可扩展性:kafka集群支持热扩展
- 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
- 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
- 高并发:支持数千个客户端同时读写

1.2 Kafka的使用场景:
- 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
- 消息系统:解耦和生产者和消费者、缓存消息等。
- 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
- 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
- 流式处理:比如spark streaming和storm
- 事件源

1.3 Kakfa的设计思想
- Kakfa Broker Leader的选举:Kakfa Broker集群受Zookeeper管理。所有的Kafka Broker节点一起去Zookeeper上注册一个临时节点,因为只有一个Kafka Broker会注册成功,其他的都会失败,所以这个成功在Zookeeper上注册临时节点的这个Kafka Broker会成为Kafka Broker Controller,其他的Kafka broker叫Kafka Broker follower。(这个过程叫Controller在ZooKeeper注册Watch)。这个Controller会监听其他的Kafka Broker的所有信息,如果这个kafka broker controller宕机了,在zookeeper上面的那个临时节点就会消失,此时所有的kafka broker又会一起去Zookeeper上注册一个临时节点,因为只有一个Kafka Broker会注册成功,其他的都会失败,所以这个成功在Zookeeper上注册临时节点的这个Kafka Broker会成为Kafka Broker Controller,其他的Kafka broker叫Kafka Broker follower。例如:一旦有一个broker宕机了,这个kafka broker controller会读取该宕机broker上所有的partition在zookeeper上的状态,并选取ISR列表中的一个replica作为partition leader(如果ISR列表中的replica全挂,选一个幸存的replica作为leader; 如果该partition的所有的replica都宕机了,则将新的leader设置为-1,等待恢复,等待ISR中的任一个Replica“活”过来,并且选它作为Leader;或选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader),这个broker宕机的事情,kafka controller也会通知zookeeper,zookeeper就会通知其他的kafka broker。
这里曾经发生过一个bug,TalkingData使用Kafka0.8.1的时候,kafka controller在Zookeeper上注册成功后,它和Zookeeper通信的timeout时间是6s,也就是如果kafka controller如果有6s中没有和Zookeeper做心跳,那么Zookeeper就认为这个kafka controller已经死了,就会在Zookeeper上把这个临时节点删掉,那么其他Kafka就会认为controller已经没了,就会再次抢着注册临时节点,注册成功的那个kafka broker成为controller,然后,之前的那个kafka controller就需要各种shut down去关闭各种节点和事件的监听。但是当kafka的读写流量都非常巨大的时候,TalkingData的一个bug是,由于网络等原因,kafka controller和Zookeeper有6s中没有通信,于是重新选举出了一个新的kafka controller,但是原来的controller在shut down的时候总是不成功,这个时候producer进来的message由于Kafka集群中存在两个kafka controller而无法落地。导致数据淤积。
这里曾经还有一个bug,TalkingData使用Kafka0.8.1的时候,当ack=0的时候,表示producer发送出去message,只要对应的kafka broker topic partition leader接收到的这条message,producer就返回成功,不管partition leader 是否真的成功把message真正存到kafka。当ack=1的时候,表示producer发送出去message,同步的把message存到对应topic的partition的leader上,然后producer就返回成功,partition leader异步的把message同步到其他partition replica上。当ack=all或-1,表示producer发送出去message,同步的把message存到对应topic的partition的leader和对应的replica上之后,才返回成功。但是如果某个kafka controller 切换的时候,会导致partition leader的切换(老的 kafka controller上面的partition leader会选举到其他的kafka broker上),但是这样就会导致丢数据。
- Consumergroup:各个consumer(consumer 线程)可以组成一个组(Consumer group ),partition中的每个message只能被组(Consumer group )中的一个consumer(consumer 线程)消费,如果一个message可以被多个consumer(consumer 线程)消费的话,那么这些consumer必须在不同的组。Kafka不支持一个partition中的message由两个或两个以上的同一个consumer group下的consumer thread来处理,除非再启动一个新的consumer group。所以如果想同时对一个topic做消费的话,启动多个consumer group就可以了,但是要注意的是,这里的多个consumer的消费都必须是顺序读取partition里面的message,新启动的consumer默认从partition队列最头端最新的地方开始阻塞的读message。它不能像AMQ那样可以多个BET作为consumer去互斥的(for update悲观锁)并发处理message,这是因为多个BET去消费一个Queue中的数据的时候,由于要保证不能多个线程拿同一条message,所以就需要行级别悲观所(for update),这就导致了consume的性能下降,吞吐量不够。而kafka为了保证吞吐量,只允许同一个consumer group下的一个consumer线程去访问一个partition。如果觉得效率不高的时候,可以加partition的数量来横向扩展,那么再加新的consumer thread去消费。如果想多个不同的业务都需要这个topic的数据,起多个consumer group就好了,大家都是顺序的读取message,offsite的值互不影响。这样没有锁竞争,充分发挥了横向的扩展性,吞吐量极高。这也就形成了分布式消费的概念。
当启动一个consumer group去消费一个topic的时候,无论topic里面有多个少个partition,无论我们consumer group里面配置了多少个consumer thread,这个consumer group下面的所有consumer thread一定会消费全部的partition;即便这个consumer group下只有一个consumer thread,那么这个consumer thread也会去消费所有的partition。因此,最优的设计就是,consumer group下的consumer thread的数量等于partition数量,这样效率是最高的。
同一partition的一条message只能被同一个Consumer Group内的一个Consumer消费。不能够一个consumer group的多个consumer同时消费一个partition。
一个consumer group下,无论有多少个consumer,这个consumer group一定回去把这个topic下所有的partition都消费了。当consumer group里面的consumer数量小于这个topic下的partition数量的时候,如下图groupA,groupB,就会出现一个conusmer thread消费多个partition的情况,总之是这个topic下的partition都会被消费。如果consumer group里面的consumer数量等于这个topic下的partition数量的时候,如下图groupC,此时效率是最高的,每个partition都有一个consumer thread去消费。当consumer group里面的consumer数量大于这个topic下的partition数量的时候,如下图GroupD,就会有一个consumer thread空闲。因此,我们在设定consumer group的时候,只需要指明里面有几个consumer数量即可,无需指定对应的消费partition序号,consumer会自动进行rebalance。
多个Consumer Group下的consumer可以消费同一条message,但是这种消费也是以o(1)的方式顺序的读取message去消费,,所以一定会重复消费这批message的,不能向AMQ那样多个BET作为consumer消费(对message加锁,消费的时候不能重复消费message)
- Consumer Rebalance的触发条件:(1)Consumer增加或删除会触发 Consumer Group的Rebalance(2)Broker的增加或者减少都会触发 Consumer Rebalance
- Consumer: Consumer处理partition里面的message的时候是o(1)顺序读取的。所以必须维护着上一次读到哪里的offsite信息。high level API,offset存于Zookeeper中,low level API的offset由自己维护。一般来说都是使用high level api的。Consumer的delivery gurarantee,默认是读完message先commmit再处理message,autocommit默认是true,这时候先commit就会更新offsite+1,一旦处理失败,offsite已经+1,这个时候就会丢message;也可以配置成读完消息处理再commit,这种情况下consumer端的响应就会比较慢的,需要等处理完才行。
一般情况下,一定是一个consumer group处理一个topic的message。Best Practice是这个consumer group里面consumer的数量等于topic里面partition的数量,这样效率是最高的,一个consumer thread处理一个partition。如果这个consumer group里面consumer的数量小于topic里面partition的数量,就会有consumer thread同时处理多个partition(这个是kafka自动的机制,我们不用指定),但是总之这个topic里面的所有partition都会被处理到的。。如果这个consumer group里面consumer的数量大于topic里面partition的数量,多出的consumer thread就会闲着啥也不干,剩下的是一个consumer thread处理一个partition,这就造成了资源的浪费,因为一个partition不可能被两个consumer thread去处理。所以我们线上的分布式多个service服务,每个service里面的kafka consumer数量都小于对应的topic的partition数量,但是所有服务的consumer数量只和等于partition的数量,这是因为分布式service服务的所有consumer都来自一个consumer group,如果来自不同的consumer group就会处理重复的message了(同一个consumer group下的consumer不能处理同一个partition,不同的consumer group可以处理同一个topic,那么都是顺序处理message,一定会处理重复的。一般这种情况都是两个不同的业务逻辑,才会启动两个consumer group来处理一个topic)。

如果producer的流量增大,当前的topic的parition数量=consumer数量,这时候的应对方式就是很想扩展:增加topic下的partition,同时增加这个consumer group下的consumer。

- Delivery Mode : Kafka producer 发送message不用维护message的offsite信息,因为这个时候,offsite就相当于一个自增id,producer就尽管发送message就好了。而且Kafka与AMQ不同,AMQ大都用在处理业务逻辑上,而Kafka大都是日志,所以Kafka的producer一般都是大批量的batch发送message,向这个topic一次性发送一大批message,load balance到一个partition上,一起插进去,offsite作为自增id自己增加就好。但是Consumer端是需要维护这个partition当前消费到哪个message的offsite信息的,这个offsite信息,high level api是维护在Zookeeper上,low level api是自己的程序维护。(Kafka管理界面上只能显示high level api的consumer部分,因为low level api的partition offsite信息是程序自己维护,kafka是不知道的,无法在管理界面上展示 )当使用high level api的时候,先拿message处理,再定时自动commit offsite+1(也可以改成手动), 并且kakfa处理message是没有锁操作的。因此如果处理message失败,此时还没有commit offsite+1,当consumer thread重启后会重复消费这个message。但是作为高吞吐量高并发的实时处理系统,at least once的情况下,至少一次会被处理到,是可以容忍的。如果无法容忍,就得使用low level api来自己程序维护这个offsite信息,那么想什么时候commit offsite+1就自己搞定了。

- Topic & Partition:Topic相当于传统消息系统MQ中的一个队列queue,producer端发送的message必须指定是发送到哪个topic,但是不需要指定topic下的哪个partition,因为kafka会把收到的message进行load balance,均匀的分布在这个topic下的不同的partition上( hash(message) % [broker数量] )。物理上存储上,这个topic会分成一个或多个partition,每个partiton相当于是一个子queue。在物理结构上,每个partition对应一个物理的目录(文件夹),文件夹命名是[topicname]_[partition]_[序号],一个topic可以有无数多的partition,根据业务需求和数据量来设置。在kafka配置文件中可随时更高num.partitions参数来配置更改topic的partition数量,在创建Topic时通过参数指定parittion数量。Topic创建之后通过Kafka提供的工具也可以修改partiton数量。
一般来说,(1)一个Topic的Partition数量大于等于Broker的数量,可以提高吞吐率。(2)同一个Partition的Replica尽量分散到不同的机器,高可用。
当add a new partition的时候,partition里面的message不会重新进行分配,原来的partition里面的message数据不会变,新加的这个partition刚开始是空的,随后进入这个topic的message就会重新参与所有partition的load balance
- Partition Replica:每个partition可以在其他的kafka broker节点上存副本,以便某个kafka broker节点宕机不会影响这个kafka集群。存replica副本的方式是按照kafka broker的顺序存。例如有5个kafka broker节点,某个topic有3个partition,每个partition存2个副本,那么partition1存broker1,broker2,partition2存broker2,broker3。。。以此类推(replica副本数目不能大于kafka broker节点的数目,否则报错。这里的replica数其实就是partition的副本总数,其中包括一个leader,其他的就是copy副本)。这样如果某个broker宕机,其实整个kafka内数据依然是完整的。但是,replica副本数越高,系统虽然越稳定,但是回来带资源和性能上的下降;replica副本少的话,也会造成系统丢数据的风险。
(1)怎样传送消息:producer先把message发送到partition leader,再由leader发送给其他partition follower。(如果让producer发送给每个replica那就太慢了)
(2)在向Producer发送ACK前需要保证有多少个Replica已经收到该消息:根据ack配的个数而定
(3)怎样处理某个Replica不工作的情况:如果这个部工作的partition replica不在ack列表中,就是producer在发送消息到partition leader上,partition leader向partition follower发送message没有响应而已,这个不会影响整个系统,也不会有什么问题。如果这个不工作的partition replica在ack列表中的话,producer发送的message的时候会等待这个不工作的partition replca写message成功,但是会等到time out,然后返回失败因为某个ack列表中的partition replica没有响应,此时kafka会自动的把这个部工作的partition replica从ack列表中移除,以后的producer发送message的时候就不会有这个ack列表下的这个部工作的partition replica了。
(4)怎样处理Failed Replica恢复回来的情况:如果这个partition replica之前不在ack列表中,那么启动后重新受Zookeeper管理即可,之后producer发送message的时候,partition leader会继续发送message到这个partition follower上。如果这个partition replica之前在ack列表中,此时重启后,需要把这个partition replica再手动加到ack列表中。(ack列表是手动添加的,出现某个部工作的partition replica的时候自动从ack列表中移除的)
- Partition leader与follower:partition也有leader和follower之分。leader是主partition,producer写kafka的时候先写partition leader,再由partition leader push给其他的partition follower。partition leader与follower的信息受Zookeeper控制,一旦partition leader所在的broker节点宕机,zookeeper会冲其他的broker的partition follower上选择follower变为parition leader。
- Topic分配partition和partition replica的算法:(1)将Broker(size=n)和待分配的Partition排序。(2)将第i个Partition分配到第(i%n)个Broker上。(3)将第i个Partition的第j个Replica分配到第((i + j) % n)个Broker上

- 消息投递可靠性
一个消息如何算投递成功,Kafka提供了三种模式:
- 第一种是啥都不管,发送出去就当作成功,这种情况当然不能保证消息成功投递到broker;
- 第二种是Master-Slave模型,只有当Master和所有Slave都接收到消息时,才算投递成功,这种模型提供了最高的投递可靠性,但是损伤了性能;
- 第三种模型,即只要Master确认收到消息就算投递成功;实际使用时,根据应用特性选择,绝大多数情况下都会中和可靠性和性能选择第三种模型
消息在broker上的可靠性,因为消息会持久化到磁盘上,所以如果正常stop一个broker,其上的数据不会丢失;但是如果不正常stop,可能会使存在页面缓存来不及写入磁盘的消息丢失,这可以通过配置flush页面缓存的周期、阈值缓解,但是同样会频繁的写磁盘会影响性能,又是一个选择题,根据实际情况配置。
消息消费的可靠性,Kafka提供的是“At least once”模型,因为消息的读取进度由offset提供,offset可以由消费者自己维护也可以维护在zookeeper里,但是当消息消费后consumer挂掉,offset没有即时写回,就有可能发生重复读的情况,这种情况同样可以通过调整commit offset周期、阈值缓解,甚至消费者自己把消费和commit offset做成一个事务解决,但是如果你的应用不在乎重复消费,那就干脆不要解决,以换取最大的性能。

- Partition ack:当ack=1,表示producer写partition leader成功后,broker就返回成功,无论其他的partition follower是否写成功。当ack=2,表示producer写partition leader和其他一个follower成功的时候,broker就返回成功,无论其他的partition follower是否写成功。当ack=-1[parition的数量]的时候,表示只有producer全部写成功的时候,才算成功,kafka broker才返回成功信息。这里需要注意的是,如果ack=1的时候,一旦有个broker宕机导致partition的follower和leader切换,会导致丢数据。

- message状态:在Kafka中,消息的状态被保存在consumer中,broker不会关心哪个消息被消费了被谁消费了,只记录一个offset值(指向partition中下一个要被消费的消息位置),这就意味着如果consumer处理不好的话,broker上的一个消息可能会被消费多次。
- message持久化:Kafka中会把消息持久化到本地文件系统中,并且保持o(1)极高的效率。我们众所周知IO读取是非常耗资源的性能也是最慢的,这就是为了数据库的瓶颈经常在IO上,需要换SSD硬盘的原因。但是Kafka作为吞吐量极高的MQ,却可以非常高效的message持久化到文件。这是因为Kafka是顺序写入o(1)的时间复杂度,速度非常快。也是高吞吐量的原因。由于message的写入持久化是顺序写入的,因此message在被消费的时候也是按顺序被消费的,保证partition的message是顺序消费的。一般的机器,单机每秒100k条数据。
- message有效期:Kafka会长久保留其中的消息,以便consumer可以多次消费,当然其中很多细节是可配置的。
- Produer : Producer向Topic发送message,不需要指定partition,直接发送就好了。kafka通过partition ack来控制是否发送成功并把信息返回给producer,producer可以有任意多的thread,这些kafka服务器端是不care的。Producer端的delivery guarantee默认是At least once的。也可以设置Producer异步发送实现At most once。Producer可以用主键幂等性实现Exactly once
- Kafka高吞吐量: Kafka的高吞吐量体现在读写上,分布式并发的读和写都非常快,写的性能体现在以o(1)的时间复杂度进行顺序写入。读的性能体现在以o(1)的时间复杂度进行顺序读取, 对topic进行partition分区,consume group中的consume线程可以以很高能性能进行顺序读。
- Kafka delivery guarantee(message传送保证):(1)At most once消息可能会丢,绝对不会重复传输;(2)At least once 消息绝对不会丢,但是可能会重复传输;(3)Exactly once每条信息肯定会被传输一次且仅传输一次,这是用户想要的。
- 批量发送:Kafka支持以消息集合为单位进行批量发送,以提高push效率。
- push-and-pull : Kafka中的Producer和consumer采用的是push-and-pull模式,即Producer只管向broker push消息,consumer只管从broker pull消息,两者对消息的生产和消费是异步的。
- Kafka集群中broker之间的关系:不是主从关系,各个broker在集群中地位一样,我们可以随意的增加或删除任何一个broker节点。
- 负载均衡方面: Kafka提供了一个 metadata API来管理broker之间的负载(对Kafka0.8.x而言,对于0.7.x主要靠zookeeper来实现负载均衡)。
- 同步异步:Producer采用异步push方式,极大提高Kafka系统的吞吐率(可以通过参数控制是采用同步还是异步方式)。
- 分区机制partition:Kafka的broker端支持消息分区partition,Producer可以决定把消息发到哪个partition,在一个partition 中message的顺序就是Producer发送消息的顺序,一个topic中可以有多个partition,具体partition的数量是可配置的。partition的概念使得kafka作为MQ可以横向扩展,吞吐量巨大。partition可以设置replica副本,replica副本存在不同的kafka broker节点上,第一个partition是leader,其他的是follower,message先写到partition leader上,再由partition leader push到parition follower上。所以说kafka可以水平扩展,也就是扩展partition。
- 离线数据装载:Kafka由于对可拓展的数据持久化的支持,它也非常适合向Hadoop或者数据仓库中进行数据装载。
- 实时数据与离线数据:kafka既支持离线数据也支持实时数据,因为kafka的message持久化到文件,并可以设置有效期,因此可以把kafka作为一个高效的存储来使用,可以作为离线数据供后面的分析。当然作为分布式实时消息系统,大多数情况下还是用于实时的数据处理的,但是当cosumer消费能力下降的时候可以通过message的持久化在淤积数据在kafka。
- 插件支持:现在不少活跃的社区已经开发出不少插件来拓展Kafka的功能,如用来配合Storm、Hadoop、flume相关的插件。
- 解耦: 相当于一个MQ,使得Producer和Consumer之间异步的操作,系统之间解耦
- 冗余: replica有多个副本,保证一个broker node宕机后不会影响整个服务
- 扩展性: broker节点可以水平扩展,partition也可以水平增加,partition replica也可以水平增加
- 峰值: 在访问量剧增的情况下,kafka水平扩展, 应用仍然需要继续发挥作用
- 可恢复性: 系统的一部分组件失效时,由于有partition的replica副本,不会影响到整个系统。
- 顺序保证性:由于kafka的producer的写message与consumer去读message都是顺序的读写,保证了高效的性能。
- 缓冲:由于producer那面可能业务很简单,而后端consumer业务会很复杂并有数据库的操作,因此肯定是producer会比consumer处理速度快,如果没有kafka,producer直接调用consumer,那么就会造成整个系统的处理速度慢,加一层kafka作为MQ,可以起到缓冲的作用。
- 异步通信:作为MQ,Producer与Consumer异步通信
2.Kafka文件存储机制
2.1 Kafka部分名词解释如下:

Kafka中发布订阅的对象是topic。我们可以为每类数据创建一个topic,把向topic发布消息的客户端称作producer,从topic订阅消息的客户端称作consumer。Producers和consumers可以同时从多个topic读写数据。一个kafka集群由一个或多个broker服务器组成,它负责持久化和备份具体的kafka消息。 Broker:Kafka节点,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群。 Topic:一类消息,消息存放的目录即主题,例如page view日志、click日志等都可以以topic的形式存在,Kafka集群能够同时负责多个topic的分发。 Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列 Segment:partition物理上由多个segment组成,每个Segment存着message信息 Producer : 生产message发送到topic Consumer : 订阅topic消费message, consumer作为一个线程来消费 Consumer Group:一个Consumer Group包含多个consumer, 这个是预先在配置文件中配置好的。各个consumer(consumer 线程)可以组成一个组(Consumer group ),partition中的每个message只能被组(Consumer group ) 中的一个consumer(consumer 线程 )消费,如果一个message可以被多个consumer(consumer 线程 ) 消费的话,那么这些consumer必须在不同的组。Kafka不支持一个partition中的message由两个或两个以上的consumer thread来处理,即便是来自不同的consumer group的也不行。它不能像AMQ那样可以多个BET作为consumer去处理message,这是因为多个BET去消费一个Queue中的数据的时候,由于要保证不能多个线程拿同一条message,所以就需要行级别悲观所(for update),这就导致了consume的性能下降,吞吐量不够。而kafka为了保证吞吐量,只允许一个consumer线程去访问一个partition。如果觉得效率不高的时候,可以加partition的数量来横向扩展,那么再加新的consumer thread去消费。这样没有锁竞争,充分发挥了横向的扩展性,吞吐量极高。这也就形成了分布式消费的概念。
2.2 kafka一些原理概念
1.持久化
kafka使用文件存储消息(append only log),这就直接决定kafka在性能上严重依赖文件系统的本身特性.且无论任何OS下,对文件系统本身的优化是非常艰难的.文件缓存/直接内存映射等是常用的手段.因为kafka是对日志文件进行append操作,因此磁盘检索的开支是较小的;同时为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数.对于kafka而言,较高性能的磁盘,将会带来更加直接的性能提升.

2.性能
除磁盘IO之外,我们还需要考虑网络IO,这直接关系到kafka的吞吐量问题.kafka并没有提供太多高超的技巧;对于producer端,可以将消息buffer起来,当消息的条数达到一定阀值时,批量发送给broker;对于consumer端也是一样,批量fetch多条消息.不过消息量的大小可以通过配置文件来指定.对于kafka broker端,似乎有个sendfile系统调用可以潜在的提升网络IO的性能:将文件的数据映射到系统内存中,socket直接读取相应的内存区域即可,而无需进程再次copy和交换(这里涉及到"磁盘IO数据"/"内核内存"/"进程内存"/"网络缓冲区",多者之间的数据copy).
其实对于producer/consumer/broker三者而言,CPU的开支应该都不大,因此启用消息压缩机制是一个良好的策略;压缩需要消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑.可以将任何在网络上传输的消息都经过压缩.kafka支持gzip/snappy等多种压缩方式.

3.负载均衡
kafka集群中的任何一个broker,都可以向producer提供metadata信息,这些metadata中包含"集群中存活的servers列表"/"partitions leader列表"等信息(请参看zookeeper中的节点信息). 当producer获取到metadata信息之后, producer将会和Topic下所有partition leader保持socket连接;消息由producer直接通过socket发送到broker,中间不会经过任何"路由层".
异步发送,将多条消息暂且在客户端buffer起来,并将他们批量发送到broker;小数据IO太多,会拖慢整体的网络延迟,批量延迟发送事实上提升了网络效率;不过这也有一定的隐患,比如当producer失效时,那些尚未发送的消息将会丢失。

4.Topic模型
其他JMS实现,消息消费的位置是有prodiver保留,以便避免重复发送消息或者将没有消费成功的消息重发等,同时还要控制消息的状态.这就要求JMS broker需要太多额外的工作.在kafka中,partition中的消息只有一个consumer在消费,且不存在消息状态的控制,也没有复杂的消息确认机制,可见kafka broker端是相当轻量级的.当消息被consumer接收之后,consumer可以在本地保存最后消息的offset,并间歇性的向zookeeper注册offset.由此可见,consumer客户端也很轻量级。
kafka中consumer负责维护消息的消费记录,而broker则不关心这些,这种设计不仅提高了consumer端的灵活性,也适度的减轻了broker端设计的复杂度;这是和众多JMS prodiver的区别.此外,kafka中消息ACK的设计也和JMS有很大不同,kafka中的消息是批量(通常以消息的条数或者chunk的尺寸为单位)发送给consumer,当消息消费成功后,向zookeeper提交消息的offset,而不会向broker交付ACK.或许你已经意识到,这种"宽松"的设计,将会有"丢失"消息/"消息重发"的危险.

5.消息传输一致
Kafka提供3种消息传输一致性语义:最多1次,最少1次,恰好1次。
最少1次:可能会重传数据,有可能出现数据被重复处理的情况;
最多1次:可能会出现数据丢失情况;
恰好1次:并不是指真正只传输1次,只不过有一个机制。确保不会出现“数据被重复处理”和“数据丢失”的情况。

at most once: 消费者fetch消息,然后保存offset,然后处理消息;当client保存offset之后,但是在消息处理过程中consumer进程失效(crash),导致部分消息未能继续处理.那么此后可能其他consumer会接管,但是因为offset已经提前保存,那么新的consumer将不能fetch到offset之前的消息(尽管它们尚没有被处理),这就是"at most once".
at least once: 消费者fetch消息,然后处理消息,然后保存offset.如果消息处理成功之后,但是在保存offset阶段zookeeper异常或者consumer失效,导致保存offset操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息,这就是"at least once".
"Kafka Cluster"到消费者的场景中可以采取以下方案来得到“恰好1次”的一致性语义:
最少1次+消费者的输出中额外增加已处理消息最大编号:由于已处理消息最大编号的存在,不会出现重复处理消息的情况。

6.副本
kafka中,replication策略是基于partition,而不是topic;kafka将每个partition数据复制到多个server上,任何一个partition有一个leader和多个follower(可以没有);备份的个数可以通过broker配置文件来设定。leader处理所有的read-write请求,follower需要和leader保持同步.Follower就像一个"consumer",消费消息并保存在本地日志中;leader负责跟踪所有的follower状态,如果follower"落后"太多或者失效,leader将会把它从replicas同步列表中删除.当所有的follower都将一条消息保存成功,此消息才被认为是"committed",那么此时consumer才能消费它,这种同步策略,就要求follower和leader之间必须具有良好的网络环境.即使只有一个replicas实例存活,仍然可以保证消息的正常发送和接收,只要zookeeper集群存活即可.
选择follower时需要兼顾一个问题,就是新leader server上所已经承载的partition leader的个数,如果一个server上有过多的partition leader,意味着此server将承受着更多的IO压力.在选举新leader,需要考虑到"负载均衡",partition leader较少的broker将会更有可能成为新的leader.

7.log
每个log entry格式为"4个字节的数字N表示消息的长度" + "N个字节的消息内容";每个日志都有一个offset来唯一的标记一条消息,offset的值为8个字节的数字,表示此消息在此partition中所处的起始位置..每个partition在物理存储层面,有多个log file组成(称为segment).segment file的命名为"最小offset".kafka.例如"00000000000.kafka";其中"最小offset"表示此segment中起始消息的offset.
获取消息时,需要指定offset和最大chunk尺寸,offset用来表示消息的起始位置,chunk size用来表示最大获取消息的总长度(间接的表示消息的条数).根据offset,可以找到此消息所在segment文件,然后根据segment的最小offset取差值,得到它在file中的相对位置,直接读取输出即可.

8.分布式
kafka使用zookeeper来存储一些meta信息,并使用了zookeeper watch机制来发现meta信息的变更并作出相应的动作(比如consumer失效,触发负载均衡等)
Broker node registry: 当一个kafka broker启动后,首先会向zookeeper注册自己的节点信息(临时znode),同时当broker和zookeeper断开连接时,此znode也会被删除.
Broker Topic Registry: 当一个broker启动时,会向zookeeper注册自己持有的topic和partitions信息,仍然是一个临时znode.
Consumer and Consumer group: 每个consumer客户端被创建时,会向zookeeper注册自己的信息;此作用主要是为了"负载均衡".一个group中的多个consumer可以交错的消费一个topic的所有partitions;简而言之,保证此topic的所有partitions都能被此group所消费,且消费时为了性能考虑,让partition相对均衡的分散到每个consumer上.
Consumer id Registry: 每个consumer都有一个唯一的ID(host:uuid,可以通过配置文件指定,也可以由系统生成),此id用来标记消费者信息.
Consumer offset Tracking: 用来跟踪每个consumer目前所消费的partition中最大的offset.此znode为持久节点,可以看出offset跟group_id有关,以表明当group中一个消费者失效,其他consumer可以继续消费.
Partition Owner registry: 用来标记partition正在被哪个consumer消费.临时znode。此节点表达了"一个partition"只能被group下一个consumer消费,同时当group下某个consumer失效,那么将会触发负载均衡(即:让partitions在多个consumer间均衡消费,接管那些"游离"的partitions)
当consumer启动时,所触发的操作:
A) 首先进行"Consumer id Registry";
B) 然后在"Consumer id Registry"节点下注册一个watch用来监听当前group中其他consumer的"leave"和"join";只要此znode path下节点列表变更,都会触发此group下consumer的负载均衡.(比如一个consumer失效,那么其他consumer接管partitions).
C) 在"Broker id registry"节点下,注册一个watch用来监听broker的存活情况;如果broker列表变更,将会触发所有的groups下的consumer重新balance.

总结:
1) Producer端使用zookeeper用来"发现"broker列表,以及和Topic下每个partition leader建立socket连接并发送消息.
2) Broker端使用zookeeper用来注册broker信息,已经监测partition leader存活性.
3) Consumer端使用zookeeper用来注册consumer信息,其中包括consumer消费的partition列表等,同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息。

9.Leader的选择
Kafka的核心是日志文件,日志文件在集群中的同步是分布式数据系统最基础的要素。
如果leaders永远不会down的话我们就不需要followers了!一旦leader down掉了,需要在followers中选择一个新的leader.但是followers本身有可能延时太久或者crash,所以必须选择高质量的follower作为leader.必须保证,一旦一个消息被提交了,但是leader down掉了,新选出的leader必须可以提供这条消息。大部分的分布式系统采用了多数投票法则选择新的leader,对于多数投票法则,就是根据所有副本节点的状况动态的选择最适合的作为leader.Kafka并不是使用这种方法。
Kafka动态维护了一个同步状态的副本的集合(a set of in-sync replicas),简称ISR,在这个集合中的节点都是和leader保持高度一致的,任何一条消息必须被这个集合中的每个节点读取并追加到日志中了,才回通知外部这个消息已经被提交了。因此这个集合中的任何一个节点随时都可以被选为leader.ISR在ZooKeeper中维护。ISR中有f+1个节点,就可以允许在f个节点down掉的情况下不会丢失消息并正常提供服。ISR的成员是动态的,如果一个节点被淘汰了,当它重新达到“同步中”的状态时,他可以重新加入ISR.这种leader的选择方式是非常快速的,适合kafka的应用场景。
一个邪恶的想法:如果所有节点都down掉了怎么办?Kafka对于数据不会丢失的保证,是基于至少一个节点是存活的,一旦所有节点都down了,这个就不能保证了。
实际应用中,当所有的副本都down掉时,必须及时作出反应。可以有以下两种选择:
1. 等待ISR中的任何一个节点恢复并担任leader。
2. 选择所有节点中(不只是ISR)第一个恢复的节点作为leader.
这是一个在可用性和连续性之间的权衡。如果等待ISR中的节点恢复,一旦ISR中的节点起不起来或者数据都是了,那集群就永远恢复不了了。如果等待ISR意外的节点恢复,这个节点的数据就会被作为线上数据,有可能和真实的数据有所出入,因为有些数据它可能还没同步到。Kafka目前选择了第二种策略,在未来的版本中将使这个策略的选择可配置,可以根据场景灵活的选择。
这种窘境不只Kafka会遇到,几乎所有的分布式数据系统都会遇到。

10.副本管理
以上仅仅以一个topic一个分区为例子进行了讨论,但实际上一个Kafka将会管理成千上万的topic分区.Kafka尽量的使所有分区均匀的分布到集群所有的节点上而不是集中在某些节点上,另外主从关系也尽量均衡这样每个几点都会担任一定比例的分区的leader.
优化leader的选择过程也是很重要的,它决定了系统发生故障时的空窗期有多久。Kafka选择一个节点作为“controller”,当发现有节点down掉的时候它负责在游泳分区的所有节点中选择新的leader,这使得Kafka可以批量的高效的管理所有分区节点的主从关系。如果controller down掉了,活着的节点中的一个会备切换为新的controller.

11.Leader与副本同步
对于某个分区来说,保存正分区的"broker"为该分区的"leader",保存备份分区的"broker"为该分区的"follower"。备份分区会完全复制正分区的消息,包括消息的编号等附加属性值。为了保持正分区和备份分区的内容一致,Kafka采取的方案是在保存备份分区的"broker"上开启一个消费者进程进行消费,从而使得正分区的内容与备份分区的内容保持一致。一般情况下,一个分区有一个“正分区”和零到多个“备份分区”。可以配置“正分区+备份分区”的总数量,关于这个配置,不同主题可以有不同的配置值。注意,生产者,消费者只与保存正分区的"leader"进行通信。

Kafka允许topic的分区拥有若干副本,这个数量是可以配置的,你可以为每个topic配置副本的数量。Kafka会自动在每个副本上备份数据,所以当一个节点down掉时数据依然是可用的。
Kafka的副本功能不是必须的,你可以配置只有一个副本,这样其实就相当于只有一份数据。
创建副本的单位是topic的分区,每个分区都有一个leader和零或多个followers.所有的读写操作都由leader处理,一般分区的数量都比broker的数量多的多,各分区的leader均匀的分布在brokers中。所有的followers都复制leader的日志,日志中的消息和顺序都和leader中的一致。followers向普通的consumer那样从leader那里拉取消息并保存在自己的日志文件中。
许多分布式的消息系统自动的处理失败的请求,它们对一个节点是否着(alive)”有着清晰的定义。Kafka判断一个节点是否活着有两个条件:
1. 节点必须可以维护和ZooKeeper的连接,Zookeeper通过心跳机制检查每个节点的连接。
2. 如果节点是个follower,他必须能及时的同步leader的写操作,延时不能太久。
符合以上条件的节点准确的说应该是“同步中的(in sync)”,而不是模糊的说是“活着的”或是“失败的”。Leader会追踪所有“同步中”的节点,一旦一个down掉了,或是卡住了,或是延时太久,leader就会把它移除。至于延时多久算是“太久”,是由参数replica.lag.max.messages决定的,怎样算是卡住了,怎是由参数replica.lag.time.max.ms决定的。
只有当消息被所有的副本加入到日志中时,才算是“committed”,只有committed的消息才会发送给consumer,这样就不用担心一旦leader down掉了消息会丢失。Producer也可以选择是否等待消息被提交的通知,这个是由参数acks决定的。
Kafka保证只要有一个“同步中”的节点,“committed”的消息就不会丢失。

2.3 kafka拓扑结构

一个典型的Kafka集群中包含若干Producer(可以是web前端FET,或者是服务器日志等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干ConsumerGroup,以及一个Zookeeper集群。Kafka通过Zookeeper管理Kafka集群配置:选举Kafka broker的leader,以及在Consumer Group发生变化时进行rebalance,因为consumer消费kafka topic的partition的offsite信息是存在Zookeeper的。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。

分析过程分为以下4个步骤: topic中partition存储分布 partiton中文件存储方式 (partition在linux服务器上就是一个目录(文件夹)) partiton中segment文件存储结构 在partition中如何通过offset查找message
通过上述4过程详细分析,我们就可以清楚认识到kafka文件存储机制的奥秘。

2.3 topic中partition存储分布
假设实验环境中Kafka集群只有一个broker,xxx/message-folder为数据文件存储根目录,在Kafka broker中server.properties文件配置(参数log.dirs=xxx/message-folder),例如创建2个topic名 称分别为report_push、launch_info, partitions数量都为partitions=4
存储路径和目录规则为:
xxx/message-folder
|--report_push-0
|--report_push-1
|--report_push-2
|--report_push-3
|--launch_info-0
|--launch_info-1
|--launch_info-2
|--launch_info-3

在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。
消息发送时都被发送到一个topic,其本质就是一个目录,而topic由是由一些Partition组成,其组织结构如下图所示:

我们可以看到,Partition是一个Queue的结构,每个Partition中的消息都是有序的,生产的消息被不断追加到Partition上,其中的每一个消息都被赋予了一个唯一的offset值。

Kafka集群会保存所有的消息,不管消息有没有被消费;我们可以设定消息的过期时间,只有过期的数据才会被自动清除以释放磁盘空间。比如我们设置消息过期时间为2天,那么这2天内的所有消息都会被保存到集群中,数据只有超过了两天才会被清除。

Kafka只维护在Partition中的offset值,因为这个offsite标识着这个partition的message消费到哪条了。Consumer每消费一个消息,offset就会加1。其实消息的状态完全是由Consumer控制的,Consumer可以跟踪和重设这个offset值,这样的话Consumer就可以读取任意位置的消息。

把消息日志以Partition的形式存放有多重考虑,第一,方便在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了;第二就是可以提高并发,因为可以以Partition为单位读写了。

通过上面介绍的我们可以知道,kafka中的数据是持久化的并且能够容错的。Kafka允许用户为每个topic设置副本数量,副本数量决定了有几个broker来存放写入的数据。如果你的副本数量设置为3,那么一份数据就会被存放在3台不同的机器上,那么就允许有2个机器失败。一般推荐副本数量至少为2,这样就可以保证增减、重启机器时不会影响到数据消费。如果对数据持久化有更高的要求,可以把副本数量设置为3或者更多。

Kafka中的topic是以partition的形式存放的,每一个topic都可以设置它的partition数量,Partition的数量决定了组成topic的message的数量。Producer在生产数据时,会按照一定规则(这个规则是可以自定义的)把消息发布到topic的各个partition中。上面将的副本都是以partition为单位的,不过只有一个partition的副本会被选举成leader作为读写用。

关于如何设置partition值需要考虑的因素。一个partition只能被一个消费者消费(一个消费者可以同时消费多个partition),因此,如果设置的partition的数量小于consumer的数量,就会有消费者消费不到数据。所以,推荐partition的数量一定要大于同时运行的consumer的数量。另外一方面,建议partition的数量大于集群broker的数量,这样leader partition就可以均匀的分布在各个broker中,最终使得集群负载均衡。在Cloudera,每个topic都有上百个partition。需要注意的是,kafka需要为每个partition分配一些内存来缓存消息数据,如果partition数量越大,就要为kafka分配更大的heap space。
2.4 partiton中文件存储方式 每个partion(目录)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除。 每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。
这样做的好处就是能快速删除无用文件,有效提高磁盘利用率。
2.5 partiton中segment文件存储结构
producer发message到某个topic,message会被均匀的分布到多个partition上(随机或根据用户指定的回调函数进行分布),kafka broker收到message往对应partition的最后一个segment上添加该消息,当某个segment上的消息条数达到配置值或消息发布时间超过阈值时,segment上的消息会被flush到磁盘,只有flush到磁盘上的消息consumer才能消费,segment达到一定的大小后将不会再往该segment写数据,broker会创建新的segment。

每个part在内存中对应一个index,记录每个segment中的第一条消息偏移。 segment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件. segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个全局partion的最大offset(偏移message数)。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。

每个segment中存储很多条消息,消息id由其逻辑位置决定,即从消息id可直接定位到消息的存储位置,避免id到位置的额外映射。
下面文件列表是笔者在Kafka broker上做的一个实验,创建一个topicXXX包含1 partition,设置每个segment大小为500MB,并启动producer向Kafka broker写入大量数据,如下图2所示segment文件列表形象说明了上述2个规则:
以上述图2中一对segment file文件为例,说明segment中index<—->data file对应关系物理结构如下:
上述图3中索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。其中以索引文件中 元数据3,497为例,依次在数据文件中表示第3个message(在全局partiton表示第368772个message)、以及该消息的物理偏移 地址为497。
从上述图3了解到segment data file由许多message组成,下面详细说明message物理结构如下:
参数说明:
关键字 解释说明
8 byte offset 在parition(分区)内的每条消息都有一个有序的id号,这个id号被称为偏移(offset),它可以唯一确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message
4 byte message size message大小
4 byte CRC32 用crc32校验message
1 byte “magic" 表示本次发布Kafka服务程序协议版本号
1 byte “attributes" 表示为独立版本、或标识压缩类型、或编码类型。
4 byte key length 表示key的长度,当key为-1时,K byte key字段不填
K byte key
value bytes payload
可选
表示实际消息数据。

2.6 在partition中如何通过offset查找message
例如读取offset=368776的message,需要通过下面2个步骤查找。 第一步查找segment file
上述图2为例,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件 00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset **二分查找**文件列表,就可以快速定位到具体文件。
当offset=368776时定位到00000000000000368769.index|log 第二步通过segment file查找message通过第一步定位到segment file,当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和 00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到 offset=368776为止。
segment index file采取稀疏索引存储方式,它减少索引文件大小,通过mmap可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它 比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。

kafka会记录offset到zk中。但是,zk client api对zk的频繁写入是一个低效的操作。0.8.2 kafka引入了native offset storage,将offset管理从zk移出,并且可以做到水平扩展。其原理就是利用了kafka的compacted topic,offset以consumer group,topic与partion的组合作为key直接提交到compacted topic中。同时Kafka又在内存中维护了的三元组来维护最新的offset信息,consumer来取最新offset信息的时候直接内存里拿即可。当然,kafka允许你快速的checkpoint最新的offset信息到磁盘上。

3.Partition Replication原则
Kafka高效文件存储设计特点 Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。 通过索引信息可以快速定位message和确定response的最大大小。 通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。


1. Kafka集群partition replication默认自动分配分析
下面以一个Kafka集群中4个Broker举例,创建1个topic包含4个Partition,2 Replication;数据Producer流动如图所示:
(1)


(2)当集群中新增2节点,Partition增加到6个时分布情况如下:

副本分配逻辑规则如下: 在Kafka集群中,每个Broker都有均等分配Partition的Leader机会。 上述图Broker Partition中,箭头指向为副本,以Partition-0为例:broker1中parition-0为Leader,Broker2中Partition-0为副本。 上述图种每个Broker(按照BrokerId有序)依次分配主Partition,下一个Broker为副本,如此循环迭代分配,多副本都遵循此规则。

副本分配算法如下: 将所有N Broker和待分配的i个Partition排序. 将第i个Partition分配到第(i mod n)个Broker上. 将第i个Partition的第j个副本分配到第((i + j) mod n)个Broker上.

4.Kafka Broker一些特性
4.1 无状态的Kafka Broker :
1. Broker没有副本机制,一旦broker宕机,该broker的消息将都不可用。
2. Broker不保存订阅者的状态,由订阅者自己保存。
3. 无状态导致消息的删除成为难题(可能删除的消息正在被订阅),kafka采用基于时间的SLA(服务水平保证),消息保存一定时间(通常为7天)后会被删除。
4. 消息订阅者可以rewind back到任意位置重新进行消费,当订阅者故障时,可以选择最小的offset进行重新读取消费消息。

4.2 message的交付与生命周期 :
1. 不是严格的JMS, 因此kafka对消息的重复、丢失、错误以及顺序型没有严格的要求。(这是与AMQ最大的区别)
2. kafka提供at-least-once delivery,即当consumer宕机后,有些消息可能会被重复delivery。
3. 因每个partition只会被consumer group内的一个consumer消费,故kafka保证每个partition内的消息会被顺序的订阅。
4. Kafka为每条消息为每条消息计算CRC校验,用于错误检测,crc校验不通过的消息会直接被丢弃掉。

4.3 压缩

Kafka支持以集合(batch)为单位发送消息,在此基础上,Kafka还支持对消息集合进行压缩,Producer端可以通过GZIP或Snappy格式对消息集合进行压缩。Producer端进行压缩之后,在Consumer端需进行解压。压缩的好处就是减少传输的数据量,减轻对网络传输的压力,在对大数据处理上,瓶颈往往体现在网络上而不是CPU。

那么如何区分消息是压缩的还是未压缩的呢,Kafka在消息头部添加了一个描述压缩属性字节,这个字节的后两位表示消息的压缩采用的编码,如果后两位为0,则表示消息未被压缩。

4.4 消息可靠性

在消息系统中,保证消息在生产和消费过程中的可靠性是十分重要的,在实际消息传递过程中,可能会出现如下三中情况:

- 一个消息发送失败

- 一个消息被发送多次

- 最理想的情况:exactly-once ,一个消息发送成功且仅发送了一次

有许多系统声称它们实现了exactly-once,但是它们其实忽略了生产者或消费者在生产和消费过程中有可能失败的情况。比如虽然一个Producer成功发送一个消息,但是消息在发送途中丢失,或者成功发送到broker,也被consumer成功取走,但是这个consumer在处理取过来的消息时失败了。

从Producer端看:Kafka是这么处理的,当一个消息被发送后,Producer会等待broker成功接收到消息的反馈(可通过参数控制等待时间),如果消息在途中丢失或是其中一个broker挂掉,Producer会重新发送(我们知道Kafka有备份机制,可以通过参数控制是否等待所有备份节点都收到消息)。

从Consumer端看:前面讲到过partition,broker端记录了partition中的一个offset值,这个值指向Consumer下一个即将消费message。当Consumer收到了消息,但却在处理过程中挂掉,此时Consumer可以通过这个offset值重新找到上一个消息再进行处理。Consumer还有权限控制这个offset值,对持久化到broker端的消息做任意处理。

4.5 备份机制

备份机制是Kafka0.8版本的新特性,备份机制的出现大大提高了Kafka集群的可靠性、稳定性。有了备份机制后,Kafka允许集群中的节点挂掉后而不影响整个集群工作。一个备份数量为n的集群允许n-1个节点失败。在所有备份节点中,有一个节点作为lead节点,这个节点保存了其它备份节点列表,并维持各个备份间的状体同步。下面这幅图解释了Kafka的备份机制:

4.6 Kafka高效性相关设计

4.6.1 消息的持久化
Kafka高度依赖文件系统来存储和缓存消息(AMQ的nessage是持久化到mysql数据库中的),因为一般的人认为磁盘是缓慢的,这导致人们对持久化结构具有竞争性持怀疑态度。其实,磁盘的快或者慢,这决定于我们如何使用磁盘。因为磁盘线性写的速度远远大于随机写。线性读写在大多数应用场景下是可以预测的。
4.6.2 常数时间性能保证
每个Topic的Partition的是一个大文件夹,里面有无数个小文件夹segment,但partition是一个队列,队列中的元素是segment,消费的时候先从第0个segment开始消费,新来message存在最后一个消息队列中。对于segment也是对队列,队列元素是message,有对应的offsite标识是哪个message。消费的时候先从这个segment的第一个message开始消费,新来的message存在segment的最后。

消息系统的持久化队列可以构建在对一个文件的读和追加上,就像一般情况下的日志解决方案。它有一个优点,所有的操作都是常数时间,并且读写之间不会相互阻塞。这种设计具有极大的性能优势:最终系统性能和数据大小完全无关,服务器可以充分利用廉价的硬盘来提供高效的消息服务。

事实上还有一点,磁盘空间的无限增大而不影响性能这点,意味着我们可以提供一般消息系统无法提供的特性。比如说,消息被消费后不是立马被删除,我们可以将这些消息保留一段相对比较长的时间(比如一个星期)。

5.Kafka 生产者-消费者
消息系统通常都会由生产者,消费者,Broker三大部分组成,生产者会将消息写入到Broker,消费者会从Broker中读取出消息,不同的MQ实现的Broker实现会有所不同,不过Broker的本质都是要负责将消息落地到服务端的存储系统中。具体步骤如下: 生产者客户端应用程序产生消息: 客户端连接对象将消息包装到请求中发送到服务端 服务端的入口也有一个连接对象负责接收请求,并将消息以文件的形式存储起来 服务端返回响应结果给生产者客户端 消费者客户端应用程序消费消息: 客户端连接对象将消费信息也包装到请求中发送给服务端 服务端从文件存储系统中取出消息 服务端返回响应结果给消费者客户端 客户端将响应结果还原成消息并开始处理消息
图4-1 客户端和服务端交互

5.1 Producers

Producers直接发送消息到broker上的leader partition,不需要经过任何中介或其他路由转发。为了实现这个特性,kafka集群中的每个broker都可以响应producer的请求,并返回topic的一些元信息,这些元信息包括哪些机器是存活的,topic的leader partition都在哪,现阶段哪些leader partition是可以直接被访问的。

Producer客户端自己控制着消息被推送到哪些partition。实现的方式可以是随机分配、实现一类随机负载均衡算法,或者指定一些分区算法。Kafka提供了接口供用户实现自定义的partition,用户可以为每个消息指定一个partitionKey,通过这个key来实现一些hash分区算法。比如,把userid作为partitionkey的话,相同userid的消息将会被推送到同一个partition。

以Batch的方式推送数据可以极大的提高处理效率,kafka Producer 可以将消息在内存中累计到一定数量后作为一个batch发送请求。Batch的数量大小可以通过Producer的参数控制,参数值可以设置为累计的消息的数量(如500条)、累计的时间间隔(如100ms)或者累计的数据大小(64KB)。通过增加batch的大小,可以减少网络请求和磁盘IO的次数,当然具体参数设置需要在效率和时效性方面做一个权衡。

Producers可以异步的并行的向kafka发送消息,但是通常producer在发送完消息之后会得到一个future响应,返回的是offset值或者发送过程中遇到的错误。这其中有个非常重要的参数“acks”,这个参数决定了producer要求leader partition 收到确认的副本个数,如果acks设置数量为0,表示producer不会等待broker的响应,所以,producer无法知道消息是否发送成功,这样有可能会导致数据丢失,但同时,acks值为0会得到最大的系统吞吐量。

若acks设置为1,表示producer会在leader partition收到消息时得到broker的一个确认,这样会有更好的可靠性,因为客户端会等待直到broker确认收到消息。若设置为-1,producer会在所有备份的partition收到消息时得到broker的确认,这个设置可以得到最高的可靠性保证。

Kafka 消息有一个定长的header和变长的字节数组组成。因为kafka消息支持字节数组,也就使得kafka可以支持任何用户自定义的序列号格式或者其它已有的格式如Apache Avro、protobuf等。Kafka没有限定单个消息的大小,但我们推荐消息大小不要超过1MB,通常一般消息大小都在1~10kB之前。

发布消息时,kafka client先构造一条消息,将消息加入到消息集set中(kafka支持批量发布,可以往消息集合中添加多条消息,一次行发布),send消息时,producer client需指定消息所属的topic。

5.2 Consumers
Kafka提供了两套consumer api,分为high-level api和sample-api。Sample-api 是一个底层的API,它维持了一个和单一broker的连接,并且这个API是完全无状态的,每次请求都需要指定offset值,因此,这套API也是最灵活的。

在kafka中,当前读到哪条消息的offset值是由consumer来维护的,因此,consumer可以自己决定如何读取kafka中的数据。比如,consumer可以通过重设offset值来重新消费已消费过的数据。不管有没有被消费,kafka会保存数据一段时间,这个时间周期是可配置的,只有到了过期时间,kafka才会删除这些数据。(这一点与AMQ不一样,AMQ的message一般来说都是持久化到mysql中的,消费完的message会被delete掉)

High-level API封装了对集群中一系列broker的访问,可以透明的消费一个topic。它自己维持了已消费消息的状态,即每次消费的都是下一个消息。

High-level API还支持以组的形式消费topic,如果consumers有同一个组名,那么kafka就相当于一个队列消息服务,而各个consumer均衡的消费相应partition中的数据。若consumers有不同的组名,那么此时kafka就相当与一个广播服务,会把topic中的所有消息广播到每个consumer。

High level api和Low level api是针对consumer而言的,和producer无关。

High level api是consumer读的partition的offsite是存在zookeeper上。High level api 会启动另外一个线程去每隔一段时间,offsite自动同步到zookeeper上。换句话说,如果使用了High level api, 每个message只能被读一次,一旦读了这条message之后,无论我consumer的处理是否ok。High level api的另外一个线程会自动的把offiste+1同步到zookeeper上。如果consumer读取数据出了问题,offsite也会在zookeeper上同步。因此,如果consumer处理失败了,会继续执行下一条。这往往是不对的行为。因此,Best Practice是一旦consumer处理失败,直接让整个conusmer group抛Exception终止,但是最后读的这一条数据是丢失了,因为在zookeeper里面的offsite已经+1了。等再次启动conusmer group的时候,已经从下一条开始读取处理了。

Low level api是consumer读的partition的offsite在consumer自己的程序中维护。不会同步到zookeeper上。但是为了kafka manager能够方便的监控,一般也会手动的同步到zookeeper上。这样的好处是一旦读取某个message的consumer失败了,这条message的offsite我们自己维护,我们不会+1。下次再启动的时候,还会从这个offsite开始读。这样可以做到exactly once对于数据的准确性有保证。


对于Consumer group:
1. 允许consumer group(包含多个consumer,如一个集群同时消费)对一个topic进行消费,不同的consumer group之间独立消费。
2. 为了对减小一个consumer group中不同consumer之间的分布式协调开销,指定partition为最小的并行消费单位,即一个group内的consumer只能消费不同的partition。

Consumer与Partition的关系:
- 如果consumer比partition多,是浪费,因为kafka的设计是在一个partition上是不允许并发的,所以consumer数不要大于partition数
- 如果consumer比partition少,一个consumer会对应于多个partitions,这里主要合理分配consumer数和partition数,否则会导致partition里面的数据被取的不均匀
- 如果consumer从多个partition读到数据,不保证数据间的顺序性,kafka只保证在一个partition上数据是有序的,但多个partition,根据你读的顺序会有不同
- 增减consumer,broker,partition会导致rebalance,所以rebalance后consumer对应的partition会发生变化
- High-level接口中获取不到数据的时候是会block的

负载低的情况下可以每个线程消费多个partition。但负载高的情况下,Consumer 线程数最好和Partition数量保持一致。如果还是消费不过来,应该再开 Consumer 进程,进程内线程数同样和分区数一致。

消费消息时,kafka client需指定topic以及partition number(每个partition对应一个逻辑日志流,如topic代表某个产品线,partition代表产品线的日志按天切分的结果),consumer client订阅后,就可迭代读取消息,如果没有消息,consumer client会阻塞直到有新的消息发布。consumer可以累积确认接收到的消息,当其确认了某个offset的消息,意味着之前的消息也都已成功接收到,此时broker会更新zookeeper上地offset registry。

5.3 高效的数据传输
1. 发布者每次可发布多条消息(将消息加到一个消息集合中发布), consumer每次迭代消费一条消息。
2. 不创建单独的cache,使用系统的page cache。发布者顺序发布,订阅者通常比发布者滞后一点点,直接使用 Linux 的page cache效果也比较后,同时减少了cache管理及垃圾收集的开销。
3. 使用sendfile优化网络传输,减少一次内存拷贝。

6.Kafka 与 Zookeeper

6.1 Zookeeper 协调控制
1. 管理broker与consumer的动态加入与离开。(Producer不需要管理,随便一台计算机都可以作为Producer向Kakfa Broker发消息)
2. 触发负载均衡,当broker或consumer加入或离开时会触发负载均衡算法,使得一
个consumer group内的多个consumer的消费负载平衡。(因为一个comsumer消费一个或多个partition,一个partition只能被一个consumer消费)
3. 维护消费关系及每个partition的消费信息。

6.2 Zookeeper上的细节:
1. 每个broker启动后会在zookeeper上注册一个临时的broker registry,包含broker的ip地址和端口号,所存储的topics和partitions信息。
2. 每个consumer启动后会在zookeeper上注册一个临时的consumer registry:包含consumer所属的consumer group以及订阅的topics。
3. 每个consumer group关联一个临时的owner registry和一个持久的offset registry。对于被订阅的每个partition包含一个owner registry,内容为订阅这个partition的consumer id;同时包含一个offset registry,内容为上一次订阅的offset。

转自:https://blog.csdn.net/YChenFeng/article/details/74980531
问题导读:
1.zookeeper在kafka的作用是什么?
2.kafka中几乎不允许对消息进行“随机读写”的原因是什么?
3.kafka集群consumer和producer状态信息是如何保存的?
4.partitions设计的目的的根本原因是什么?



一、入门
1、简介
Kafka is a distributed,partitioned,replicated commit logservice。它提供了类似于JMS的特性,但是在 设计 实现上完全不同,此外它并不是JMS规范的实现。kafka对消息保存时根据Topic进行归类,发送消息者成为Producer,消息接受者成为Consumer,此外kafka集群有多个kafka实例组成,每个实例( server )成为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。


2、Topics/logs
一个Topic可以认为是一类消息,每个topic将被分成多个partition(区),每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型数字,它是唯一标记一条消息。它唯一的标记一条消息。kafka并没有提供其他额外的索引机制来存储offset,因为在kafka中几乎不允许对消息进行“随机读写”。



kafka和JMS(Java Message Service)实现(activeMQ)不同的是:即使消息被消费,消息仍然不会被立即删除.日志文件将会根据broker中的配置要求,保留一定的时间之后删除;比如log文件保留2天,那么两天后,文件会被清除,无论其中的消息是否被消费.kafka通过这种简单的手段,来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘IO开支.

对于consumer而言,它需要保存消费消息的offset,对于offset的保存和使用,有consumer来控制;当consumer正常消费消息时,offset将会"线性"的向前驱动,即消息将依次顺序被消费.事实上consumer可以使用任意顺序消费消息,它只需要将offset重置为任意值..(offset将会保存在zookeeper中,参见下文)

kafka集群几乎不需要维护任何consumer和producer状态信息,这些信息有zookeeper保存;因此producer和consumer的 客户端 实现非常轻量级,它们可以随意离开,而不会对集群造成额外的影响.

partitions的 设计 目的有多个.最根本原因是kafka基于文件存储.通过分区,可以将日志内容分散到多个 server 上,来避免文件尺寸达到单机磁盘的上限,每个partiton都会被当前server(kafka实例)保存;可以将一个topic切分多任意多个partitions,来消息保存/消费的效率.此外越多的partitions意味着可以容纳更多的consumer,有效提升并发消费的能力.(具体原理参见下文).

3、Distribution
一个Topic的多个partitions,被分布在kafka集群中的多个server上;每个server(kafka实例)负责partitions中消息的读写操作;此外kafka还可以配置partitions需要备份的个数(replicas),每个partition将会被备份到多台机器上,以提高可用性.

基于replicated方案,那么就意味着需要对多个备份进行调度;每个partition都有一个 server 为"leader";leader负责所有的读写操作,如果leader失效,那么将会有其他follower来接管(成为新的leader);follower只是单调的和leader跟进,同步消息即可..由此可见作为leader的server承载了全部的请求压力,因此从集群的整体考虑,有多少个partitions就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定.

Producers
Producer将消息发布到指定的Topic中,同时Producer也能决定将此消息归属于哪个partition;比如基于"round-robin"方式或者通过其他的一些算法等.

Consumers
本质上kafka只支持Topic.每个consumer属于一个consumer group;反过来说,每个group中可以有多个consumer.发送到Topic的消息,只会被订阅此Topic的每个group中的一个consumer消费.

如果所有的consumer都具有相同的group,这种情况和queue模式很像;消息将会在consumers之间负载均衡.
如果所有的consumer都具有不同的group,那这就是"发布-订阅";消息将会广播给所有的消费者.
在kafka中,一个partition中的消息只会被group中的一个consumer消费;每个group中consumer消息消费互相独立;我们可以认为一个group是一个"订阅"者,一个Topic中的每个partions,只会被一个"订阅者"中的一个consumer消费,不过一个consumer可以消费多个partitions中的消息.kafka只能保证一个partition中的消息被某个consumer消费时,消息是顺序的.事实上,从Topic角度来说,消息仍不是有序的.

kafka的 设计 原理决定,对于一个topic,同一个group中不能有多于partitions个数的consumer同时消费,否则将意味着某些consumer将无法得到消息.

Guarantees
1) 发送到partitions中的消息将会按照它接收的顺序追加到日志中
2) 对于消费者而言,它们消费消息的顺序和日志中消息顺序一致.
3) 如果Topic的"replicationfactor"为N,那么允许N-1个kafka实例失效.

二、使用场景

1、Messaging
对于一些常规的消息系统,kafka是个不错的选择;partitons/replication和容错,可以使kafka具有良好的扩展性和性能优势.不过到目前为止,我们应该很清楚认识到,kafka并没有提供JMS中的"事务性""消息传输担保(消息确认机制)""消息分组"等企业级特性;kafka只能使用作为"常规"的消息系统,在一定程度上,尚未确保消息的发送与接收绝对可靠(比如,消息重发,消息发送丢失等)

2、Websit activity tracking
kafka可以作为"网站活性跟踪"的最佳工具;可以将网页/用户操作等信息发送到kafka中.并实时监控,或者离线统计分析等

3、Log Aggregation
kafka的特性决定它非常适合作为"日志收集中心";application可以将操作日志"批量""异步"的发送到kafka集群中,而不是保存在本地或者DB中;kafka可以批量提交消息/压缩消息等,这对producer端而言,几乎感觉不到性能的开支.此时consumer端可以使hadoop等其他系统化的存储和分析系统.

三、设计原理

kafka的 设计 初衷是希望作为一个统一的信息收集平台,能够实时的收集反馈信息,并需要能够支撑较大的数据量,且具备良好的容错能力.

1、持久性
kafka使用文件存储消息,这就直接决定kafka在性能上严重依赖文件系统的本身特性.且无论任何OS下,对文件系统本身的优化几乎没有可能.文件缓存/直接内存映射等是常用的手段.因为kafka是对日志文件进行append操作,因此磁盘检索的开支是较小的;同时为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数.
2、性能
需要考虑的影响性能点很多,除磁盘IO之外,我们还需要考虑网络IO,这直接关系到kafka的吞吐量问题.kafka并没有提供太多高超的技巧;对于producer端,可以将消息buffer起来,当消息的条数达到一定阀值时,批量发送给broker;对于consumer端也是一样,批量fetch多条消息.不过消息量的大小可以通过配置文件来指定.对于kafka broker端,似乎有个sendfile系统调用可以潜在的提升网络IO的性能:将文件的数据映射到系统内存中,socket直接读取相应的内存区域即可,而无需进程再次copy和交换. 其实对于producer/consumer/broker三者而言,CPU的开支应该都不大,因此启用消息压缩机制是一个良好的策略;压缩需要消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑.可以将任何在网络上传输的消息都经过压缩.kafka支持gzip/snappy等多种压缩方式.

3、生产者
负载均衡: producer将会和Topic下所有partition leader保持socket连接;消息由producer直接通过socket发送到broker,中间不会经过任何"路由层".事实上,消息被路由到哪个partition上,有producer 客户端 决定.比如可以采用"random""key-hash""轮询"等,如果一个topic中有多个partitions,那么在producer端实现"消息均衡分发"是必要的.

其中partition leader的位置(host:port)注册在zookeeper中,producer作为zookeeper client,已经注册了watch用来监听partition leader的变更事件.
异步发送:将多条消息暂且在客户端buffer起来,并将他们批量的发送到broker,小数据IO太多,会拖慢整体的网络延迟,批量延迟发送事实上提升了网络效率。不过这也有一定的隐患,比如说当producer失效时,那些尚未发送的消息将会丢失。

4、消费者
consumer端向broker发送"fetch"请求,并告知其获取消息的offset;此后consumer将会获得一定条数的消息;consumer端也可以重置offset来重新消费消息.

在JMS实现中,Topic模型基于push方式,即broker将消息推送给consumer端.不过在kafka中,采用了pull方式,即consumer在和broker建立连接之后,主动去pull(或者说fetch)消息;这中模式有些优点,首先consumer端可以根据自己的消费能力适时的去fetch消息并处理,且可以控制消息消费的进度(offset);此外,消费者可以良好的控制消息消费的数量,batch fetch.

其他JMS实现,消息消费的位置是有prodiver保留,以便避免重复发送消息或者将没有消费成功的消息重发等,同时还要控制消息的状态.这就要求JMS broker需要太多额外的工作.在kafka中,partition中的消息只有一个consumer在消费,且不存在消息状态的控制,也没有复杂的消息确认机制,可见kafka broker端是相当轻量级的.当消息被consumer接收之后,consumer可以在本地保存最后消息的offset,并间歇性的向zookeeper注册offset.由此可见,consumer 客户端 也很轻量级.



5、消息传送机制
对于JMS实现,消息传输担保非常直接:有且只有一次(exactly once).在kafka中稍有不同:
1) at most once: 最多一次,这个和JMS中"非持久化"消息类似.发送一次,无论成败,将不会重发.
2) at least once: 消息至少发送一次,如果消息未能接受成功,可能会重发,直到接收成功.
3) exactly once: 消息只会发送一次.
at most once: 消费者fetch消息,然后保存offset,然后处理消息;当client保存offset之后,但是在消息处理过程中出现了异常,导致部分消息未能继续处理.那么此后"未处理"的消息将不能被fetch到,这就是"at most once".
at least once: 消费者fetch消息,然后处理消息,然后保存offset.如果消息处理成功之后,但是在保存offset阶段zookeeper异常导致保存操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息,这就是"at least once",原因offset没有及时的提交给zookeeper,zookeeper恢复正常还是之前offset状态.
exactly once: kafka中并没有严格的去实现(基于2阶段提交,事务),我们认为这种策略在kafka中是没有必要的.
通常情况下"at-least-once"是我们搜选.(相比at most once而言,重复接收数据总比丢失数据要好).

6、复制备份
kafka将每个partition数据复制到多个server上,任何一个partition有一个leader和多个follower(可以没有);备份的个数可以通过broker配置文件来设定.leader处理所有的read-write请求,follower需要和leader保持同步.Follower和consumer一样,消费消息并保存在本地日志中;leader负责跟踪所有的follower状态,如果follower"落后"太多或者失效,leader将会把它从replicas同步列表中删除.当所有的follower都将一条消息保存成功,此消息才被认为是"committed",那么此时consumer才能消费它.即使只有一个replicas实例存活,仍然可以保证消息的正常发送和接收,只要zookeeper集群存活即可.(不同于其他分布式存储,比如hbase需要"多数派"存活才行)
当leader失效时,需在followers中选取出新的leader,可能此时follower落后于leader,因此需要选择一个"up-to-date"的follower.选择follower时需要兼顾一个问题,就是新leader server 上所已经承载的partition leader的个数,如果一个server上有过多的partition leader,意味着此server将承受着更多的IO压力.在选举新leader,需要考虑到"负载均衡".

7.日志
如果一个topic的名称为"my_topic",它有2个partitions,那么日志将会保存在my_topic_0和my_topic_1两个目录中;日志文件中保存了一序列"log entries"(日志条目),每个log entry格式为"4个字节的数字N表示消息的长度" + "N个字节的消息内容";每个日志都有一个offset来唯一的标记一条消息,offset的值为8个字节的数字,表示此消息在此partition中所处的起始位置..每个partition在物理存储层面,有多个log file组成(称为segment).segmentfile的命名为"最小offset".kafka.例如"00000000000.kafka";其中"最小offset"表示此segment中起始消息的offset.

其中每个partiton中所持有的segments列表信息会存储在zookeeper中.
当segment文件尺寸达到一定阀值时(可以通过配置文件设定,默认1G),将会创建一个新的文件;当buffer中消息的条数达到阀值时将会触发日志信息flush到日志文件中,同时如果"距离最近一次flush的时间差"达到阀值时,也会触发flush到日志文件.如果broker失效,极有可能会丢失那些尚未flush到文件的消息.因为 server 意外实现,仍然会导致log文件格式的破坏(文件尾部),那么就要求当server启东是需要检测最后一个segment的文件结构是否合法并进行必要的修复.
获取消息时,需要指定offset和最大chunk尺寸,offset用来表示消息的起始位置,chunk size用来表示最大获取消息的总长度(间接的表示消息的条数).根据offset,可以找到此消息所在segment文件,然后根据segment的最小offset取差值,得到它在file中的相对位置,直接读取输出即可.
日志文件的删除策略非常简单:启动一个后台线程定期扫描log file列表,把保存时间超过阀值的文件直接删除(根据文件的创建时间).为了避免删除文件时仍然有read操作(consumer消费),采取copy-on-write方式.

8、分配
kafka使用zookeeper来存储一些meta信息,并使用了zookeeper watch机制来发现meta信息的变更并作出相应的动作(比如consumer失效,触发负载均衡等)
1) Broker node registry: 当一个kafkabroker启动后,首先会向zookeeper注册自己的节点信息(临时znode),同时当broker和zookeeper断开连接时,此znode也会被删除.
格式: /broker/ids/[0...N] -->host:port;其中[0..N]表示broker id,每个broker的配置文件中都需要指定一个数字类型的id(全局不可重复),znode的值为此broker的host:port信息.
2) Broker Topic Registry: 当一个broker启动时,会向zookeeper注册自己持有的topic和partitions信息,仍然是一个临时znode.
格式: /broker/topics/[topic]/[0...N] 其中[0..N]表示partition索引号.
3) Consumer and Consumer group: 每个consumer 客户端 被创建时,会向zookeeper注册自己的信息;此作用主要是为了"负载均衡".
一个group中的多个consumer可以交错的消费一个topic的所有partitions;简而言之,保证此topic的所有partitions都能被此group所消费,且消费时为了性能考虑,让partition相对均衡的分散到每个consumer上.
4) Consumer id Registry: 每个consumer都有一个唯一的ID(host:uuid,可以通过配置文件指定,也可以由系统生成),此id用来标记消费者信息.
格式:/consumers/[group_id]/ids/[consumer_id]
仍然是一个临时的znode,此节点的值为{"topic_name":#streams...},即表示此consumer目前所消费的topic + partitions列表.
5) Consumer offset Tracking: 用来跟踪每个consumer目前所消费的partition中最大的offset.
格式:/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]-->offset_value
此znode为持久节点,可以看出offset跟group_id有关,以表明当group中一个消费者失效,其他consumer可以继续消费.
6) Partition Owner registry: 用来标记partition被哪个consumer消费.临时znode
格式:/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]-->consumer_node_id当consumer启动时,所触发的操作:
A) 首先进行"Consumer id Registry";
B) 然后在"Consumer id Registry"节点下注册一个watch用来监听当前group中其他consumer的"leave"和"join";只要此znode path下节点列表变更,都会触发此group下consumer的负载均衡.(比如一个consumer失效,那么其他consumer接管partitions).
C) 在"Broker id registry"节点下,注册一个watch用来监听broker的存活情况;如果broker列表变更,将会触发所有的groups下的consumer重新balance.

1) Producer端使用zookeeper用来"发现"broker列表,以及和Topic下每个partition leader建立socket连接并发送消息.
2) Broker端使用zookeeper用来注册broker信息,已经监测partitionleader存活性.
3) Consumer端使用zookeeper用来注册consumer信息,其中包括consumer消费的partition列表等,同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息.

四、主要配置

1、Broker配置



2.Consumer主要配置



3.Producer主要配置




以上是关于kafka一些基础说明,在其中我们知道如果要kafka正常运行,必须配置zookeeper,否则无论是kafka集群还是 客户端 的生存者和消费者都无法正常的工作的,以下是对zookeeper进行一些简单的介绍:

五、zookeeper集群
zookeeper是一个为分布式应用提供一致性服务的软件,它是开源的Hadoop项目的一个子项目,并根据google发表的一篇论文来实现的。zookeeper为分布式系统提供了高笑且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等。zookeeper接口简单,我们不必过多地纠结在分布式系统 编程 难于处理的同步和一致性问题上,你可以使用zookeeper提供的现成(off-the-shelf)服务来实现来实现分布式系统额配置管理,组管理,Leader选举等功能。
zookeeper集群的安装,准备三台服务器server1:192.168.0.1,server2:192.168.0.2,
server3:192.168.0.3.
1)下载zookeeper
到 http://zookeeper.apache.org/releases.html 去下载最新版本Zookeeper-3.4.5的安装包zookeeper-3.4.5.tar.gz.将文件保存server1的~目录下
2)安装zookeeper
先在服务器 server 分别执行a-c步骤
a)解压
tar -zxvf zookeeper-3.4.5.tar.gz
解压完成后在目录~下会发现多出一个目录zookeeper-3.4.5,重新命令为zookeeper
b)配置
将conf/zoo_sample.cfg拷贝一份命名为zoo.cfg,也放在conf目录下。然后按照如下值修改其中的配置:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/home/wwb/zookeeper /data
dataLogDir=/home/wwb/zookeeper/logs
# the port at which the clients will connect
clientPort=2181
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
# http://zookeeper.apache.org/doc/ ... html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.0.1:3888:4888
server.2=192.168.0.2:3888:4888
server .3=192.168.0.3:3888:4888
tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
initLimit:这个配置项是用来配置 Zookeeper 接受 客户端 (这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 5个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
syncLimit:这个配置项标识 Leader 与Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是2*2000=4 秒
server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号
注意:dataDir,dataLogDir中的wwb是当前登录用户名,data,logs目录开始是不存在,需要使用mkdir命令创建相应的目录。并且在该目录下创建文件myid,serve1,server2,server3该文件内容分别为1,2,3。
针对服务器server2,server3可以将server1复制到相应的目录,不过需要注意dataDir,dataLogDir目录,并且文件myid内容分别为2,3
3)依次启动 server 1,server2,server3的zookeeper.
/home/wwb/zookeeper/bin/zkServer.sh start,出现类似以下内容
JMX enabled by default
Using config: /home/wwb/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
4) 测试zookeeper是否正常工作,在server1上执行以下命令
/home/wwb/zookeeper/bin/zkCli.sh -server192.168.0.2:2181,出现类似以下内容
JLine support is enabled
2013-11-27 19:59:40,560 - INFO [main-SendThread(localhost.localdomain:2181):ClientCnxn$SendThread@736]- Session establishmentcomplete on server localhost.localdomain/127.0.0.1:2181, sessionid = 0x1429cdb49220000, negotiatedtimeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: 127.0.0.1:2181(CONNECTED) 0] [root@localhostzookeeper2]#
即代表集群构建成功了,如果出现错误那应该是第三部时没有启动好集群,
运行,先利用
ps aux | grep zookeeper查看是否有相应的进程的,没有话,说明集群启动出现问题,可以在每个服务器上使用
./home/wwb/zookeeper/bin/zkServer.sh stop。再依次使用./home/wwb/zookeeper/binzkServer.sh start,这时在执行4一般是没有问题,如果还是有问题,那么先stop再到bin的上级目录执行./bin/zkServer.shstart试试。

注意:zookeeper集群时,zookeeper要求半数以上的机器可用,zookeeper才能提供服务。

六、kafka集群
(利用上面server1,server2,server3,下面以server1为实例)
1)下载kafka0.8( http://kafka.apache.org/downloads.html ),保存到服务器/home/wwb目录下kafka-0.8.0-beta1-src.tgz(kafka_2.8.0-0.8.0-beta1.tgz)
2)解压 tar -zxvf kafka-0.8.0-beta1-src.tgz,产生文件夹kafka-0.8.0-beta1-src更改为kafka01
3)配置
修改kafka01/config/ server .properties,其中broker.id,log.dirs,zookeeper.connect必须根据实际情况进行修改,其他项根据需要自行斟酌。大致如下:
broker.id=1
port=9091
num.network.threads=2
num.io.threads=2
socket.send.buffer.bytes=1048576
socket.receive.buffer.bytes=1048576
socket.request.max.bytes=104857600
log.dir=./logs
num.partitions=2
log.flush.interval.messages=10000
log.flush.interval.ms=1000
log.retention.hours=168
#log.retention.bytes=1073741824
log.segment.bytes=536870912
num.replica.fetchers=2
log.cleanup.interval.mins=10
zookeeper.connect=192.168.0.1:2181,192.168.0.2:2182,192.168.0.3:2183
zookeeper.connection.timeout.ms=1000000
kafka.metrics.polling.interval.secs=5
kafka.metrics.reporters=kafka.metrics.KafkaCSVMetricsReporter
kafka.csv.metrics.dir=/tmp/kafka_metrics
kafka.csv.metrics.reporter.enabled=false

4)初始化因为kafka用scala语言编写,因此运行kafka需要首先准备scala相关环境。
> cd kafka01
> ./sbt update
> ./sbt package
> ./sbt assembly-package-dependency
在第二个命令时可能需要一定时间,由于要下载更新一些依赖包。所以请大家 耐心点。
5) 启动kafka01
>JMX_PORT=9997 bin/kafka- server -start.sh config/server.properties &
a)kafka02操作步骤与kafka01雷同,不同的地方如下
修改kafka02/config/server.properties
broker.id=2
port=9092
##其他配置和kafka-0保持一致
启动kafka02
JMX_PORT=9998 bin/kafka-server-start.shconfig/server.properties &
b)kafka03操作步骤与kafka01雷同,不同的地方如下
修改kafka03/config/server.properties
broker.id=3
port=9093
##其他配置和kafka-0保持一致
启动kafka02
JMX_PORT=9999 bin/kafka- server -start.shconfig/server.properties &
6)创建Topic(包含一个分区,三个副本)
>bin/kafka-create-topic.sh--zookeeper 192.168.0.1:2181 --replica 3 --partition 1 --topicmy-replicated-topic
7)查看topic情况
>bin/kafka-list-top.sh --zookeeper 192.168.0.1:2181
topic: my-replicated-topic partition: 0 leader: 1 replicas: 1,2,0 isr: 1,2,0
8)创建发送者
>bin/kafka-console-producer.sh--broker-list 192.168.0.1:9091 --topic my-replicated-topic
my test message1
my test message2
^C
9)创建消费者
>bin/kafka-console-consumer.sh --zookeeper127.0.0.1:2181 --from-beginning --topic my-replicated-topic
...
my test message1
my test message2
^C
10)杀掉server1上的broker
>pkill -9 -f config/ server .properties
11)查看topic
>bin/kafka-list-top.sh --zookeeper192.168.0.1:2181
topic: my-replicated-topic partition: 0 leader: 1 replicas: 1,2,0 isr: 1,2,0
发现topic还正常的存在
11)创建消费者,看是否能查询到消息
>bin/kafka-console-consumer.sh --zookeeper192.168.0.1:2181 --from-beginning --topic my-replicated-topic
...
my test message 1
my test message 2
^C
说明一切都是正常的。

OK,以上就是对Kafka个人的理解,不对之处请大家及时指出。


补充说明:
1、public Map>> createMessageStreams(Map topicCountMap),其中该方法的参数Map的key为topic名称,value为topic对应的分区数,譬如说如果在kafka中不存在相应的topic时,则会创建一个topic,分区数为value,如果存在的话,该处的value则不起什么作用

2、关于生产者向指定的分区发送数据,通过设置partitioner.class的属性来指定向那个分区发送数据,如果自己指定必须编写相应的程序,默认是kafka.producer.DefaultPartitioner,分区程序是基于散列的键。

3、在多个消费者读取同一个topic的数据,为了保证每个消费者读取数据的唯一性,必须将这些消费者group_id定义为同一个值,这样就构建了一个类似队列的数据结构,如果定义不同,则类似一种广播结构的。

4、在consumerapi中,参数 设计 到数字部分,类似Map,
numStream,指的都是在topic不存在的时,会创建一个topic,并且分区个数为Integer,numStream,注意如果数字大于broker的配置中num.partitions属性,会以num.partitions为依据创建分区个数的。

5、producerapi,调用send时,如果不存在topic,也会创建topic,在该方法中没有提供分区个数的参数,在这里分区个数是由服务端broker的配置中num.partitions属性决定的

关于kafka说明可以参考: http://kafka.apache.org/documentation.html

文章转自:http://www.aboutyun.com/thread-9341-1-1.html
简介
kafka是一个分布式消息队列。具有高性能、持久化、多副本备份、横向扩展能力。生产者往队列里写消息,消费者从队列里取消息进行业务逻辑。一般在架构设计中起到解耦、削峰、异步处理的作用。
kafka对外使用topic的概念,生产者往topic里写消息,消费者从读消息。为了做到水平扩展,一个topic实际是由多个partition组成的,遇到瓶颈时,可以通过增加partition的数量来进行横向扩容。单个parition内是保证消息有序。
每新写一条消息,kafka就是在对应的文件append写,所以性能非常高。
kafka的总体数据流是这样的:

kafka data flow

大概用法就是,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉去指定Topic的消息,然后进行业务处理。
图中有两个topic,topic 0有两个partition,topic 1有一个partition,三副本备份。可以看到consumer gourp 1中的consumer 2没有分到partition处理,这是有可能出现的,下面会讲到。
关于broker、topics、partitions的一些元信息用zk来存,监控和路由啥的也都会用到zk。
生产
基本流程是这样的:

kafka sdk product flow.png

创建一条记录,记录中一个要指定对应的topic和value,key和partition可选。 先序列化,然后按照topic和partition,放进对应的发送队列中。kafka produce都是批量请求,会积攒一批,然后一起发送,不是调send()就进行立刻进行网络发包。
如果partition没填,那么情况会是这样的: key有填
按照key进行哈希,相同key去一个partition。(如果扩展了partition的数量那么就不能保证了) key没填
round-robin来选partition
这些要发往同一个partition的请求按照配置,攒一波,然后由一个单独的线程一次性发过去。
API
有high level api,替我们把很多事情都干了,offset,路由啥都替我们干了,用以来很简单。
还有simple api,offset啥的都是要我们自己记录。
partition
当存在多副本的情况下,会尽量把多个副本,分配到不同的broker上。 kafka会为partition选出一个leader,之后所有该partition的请求,实际操作的都是leader,然后再同步到其他的follower。 当一个broker歇菜后,所有leader在该broker上的partition都会重新选举,选出一个leader。(这里不像分布式文件存储系统那样会自动进行复制保持副本数)
然后这里就涉及两个细节:怎么分配partition,怎么选leader。
关于partition的分配,还有leader的选举,总得有个执行者。在kafka中,这个执行者就叫controller。 kafka使用zk在broker中选出一个controller,用于partition分配和leader选举。
partition的分配 将所有Broker(假设共n个Broker)和待分配的Partition排序 将第i个Partition分配到第(i mod n)个Broker上 (这个就是leader) 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上
leader容灾
controller会在Zookeeper的/brokers/ids节点上注册Watch,一旦有broker宕机,它就能知道。当broker宕机后,controller就会给受到影响的partition选出新leader。controller从zk的/brokers/topics/[topic]/partitions/[partition]/state中,读取对应partition的ISR(in-sync replica已同步的副本)列表,选一个出来做leader。
选出leader后,更新zk,然后发送LeaderAndISRRequest给受影响的broker,让它们改变知道这事。为什么这里不是使用zk通知,而是直接给broker发送rpc请求,我的理解可能是这样做zk有性能问题吧。
如果ISR列表是空,那么会根据配置,随便选一个replica做leader,或者干脆这个partition就是歇菜。如果ISR列表的有机器,但是也歇菜了,那么还可以等ISR的机器活过来。
多副本同步
这里的策略,服务端这边的处理是follower从leader批量拉取数据来同步。但是具体的可靠性,是由生产者来决定的。
生产者生产消息的时候,通过request.required.acks参数来设置数据的可靠性。
acks what happen
0 which means that the producer never waits for an acknowledgement from the broker.发过去就完事了,不关心broker是否处理成功,可能丢数据。
1
-1
which means that the producer gets an acknowledgement after the leader replica has received the data. 当写Leader成功后就返回,其他的replica都是通过fetcher去同步的,所以kafka是异步写,主备切换可能丢数据。
which means that the producer gets an acknowledgement after all in-sync replicas have received the data. 要等到isr里所有机器同步成功,才能返回成功,延时取决于最慢的机器。强一致,不会丢数据。
在acks=-1的时候,如果ISR少于min.insync.replicas指定的数目,那么就会返回不可用。
这里ISR列表中的机器是会变化的,根据配置replica.lag.time.max.ms,多久没同步,就会从ISR列表中剔除。以前还有根据落后多少条消息就踢出ISR,在1.0版本后就去掉了,因为这个值很难取,在高峰的时候很容易出现节点不断的进出ISR列表。
从ISA中选出leader后,follower会从把自己日志中上一个高水位后面的记录去掉,然后去和leader拿新的数据。因为新的leader选出来后,follower上面的数据,可能比新leader多,所以要截取。这里高水位的意思,对于partition和leader,就是所有ISR中都有的最新一条记录。消费者最多只能读到高水位;
从leader的角度来说高水位的更新会延迟一轮,例如写入了一条新消息,ISR中的broker都fetch到了,但是ISR中的broker只有在下一轮的fetch中才能告诉leader。
也正是由于这个高水位延迟一轮,在一些情况下,kafka会出现丢数据和主备数据不一致的情况,0.11开始,使用leader epoch来代替高水位。( https://cwiki.apache.org/confluence/display/KAFKA/KIP-101+-+Alter+Replication+Protocol+to+use+Leader+Epoch+rather+than+High+Watermark+for+Truncation#KIP-101-AlterReplicationProtocoltouseLeaderEpochratherthanHighWatermarkforTruncation-Scenario1:HighWatermarkTruncationfollowedbyImmediateLeaderElection )
思考:
当acks=-1时 是follwers都来fetch就返回成功,还是等follwers第二轮fetch? leader已经写入本地,但是ISR中有些机器失败,那么怎么处理呢?
消费
订阅topic是以一个消费组来订阅的,一个消费组里面可以有多个消费者。同一个消费组中的两个消费者,不会同时消费一个partition。换句话来说, 就是一个partition,只能被消费组里的一个消费者消费 ,但是可以同时被多个消费组消费。因此,如果消费组内的消费者如果比partition多的话,那么就会有个别消费者一直空闲。
untitled_page.png

API
订阅topic时,可以用正则表达式,如果有新topic匹配上,那能自动订阅上。
offset的保存
一个消费组消费partition,需要保存offset记录消费到哪,以前保存在zk中,由于zk的写性能不好,以前的解决方法都是consumer每隔一分钟上报一次。这里zk的性能严重影响了消费的速度,而且很容易出现重复消费。
在0.10版本后,kafka把这个offset的保存,从zk总剥离,保存在一个名叫__consumeroffsets topic的topic中。写进消息的key由groupid、topic、partition组成,value是偏移量offset。topic配置的清理策略是compact。总是保留最新的key,其余删掉。一般情况下,每个key的offset都是缓存在内存中,查询的时候不用遍历partition,如果没有缓存,第一次就会遍历partition建立缓存,然后查询返回。
确定consumer group位移信息写入__consumers_offsets的哪个partition,具体计算公式: __consumers_offsets partition = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount) //groupMetadataTopicPartitionCount由offsets.topic.num.partitions指定,默认是50个分区。
思考:
如果正在跑的服务,修改了offsets.topic.num.partitions,那么offset的保存是不是就乱套了?
分配partition--reblance
生产过程中broker要分配partition, 消费过程这里,也要分配partition给消费者。类似broker中选了一个controller出来,消费也要从broker中选一个coordinator,用于分配partition。
下面从顶向下,分别阐述一下 怎么选coordinator。 交互流程。 reblance的流程。
选coordinator 看offset保存在那个partition 该partition leader所在的broker就是被选定的coordinator
这里我们可以看到,consumer group的coordinator,和保存consumer group offset的partition leader是同一台机器。
交互流程
把coordinator选出来之后,就是要分配了
整个流程是这样的: consumer启动、或者coordinator宕机了,consumer会任意请求一个broker,发送ConsumerMetadataRequest请求,broker会按照上面说的方法,选出这个consumer对应coordinator的地址。 consumer 发送heartbeat请求给coordinator,返回IllegalGeneration的话,就说明consumer的信息是旧的了,需要重新加入进来,进行reblance。返回成功,那么consumer就从上次分配的partition中继续执行。
reblance流程 consumer给coordinator发送JoinGroupRequest请求。 这时其他consumer发heartbeat请求过来时,coordinator会告诉他们,要reblance了。 其他consumer发送JoinGroupRequest请求。 所有记录在册的consumer都发了JoinGroupRequest请求之后,coordinator就会在这里consumer中随便选一个leader。然后回JoinGroupRespone,这会告诉consumer你是follower还是leader,对于leader,还会把follower的信息带给它,让它根据这些信息去分配partition
5、consumer向coordinator发送SyncGroupRequest,其中leader的SyncGroupRequest会包含分配的情况。
6、coordinator回包,把分配的情况告诉consumer,包括leader。
当partition或者消费者的数量发生变化时,都得进行reblance。
列举一下会reblance的情况: 增加partition 增加消费者 消费者主动关闭 消费者宕机了 coordinator自己也宕机了
消息投递语义
kafka支持3种消息投递语义
At most once:最多一次,消息可能会丢失,但不会重复
At least once:最少一次,消息不会丢失,可能会重复
Exactly once:只且一次,消息不丢失不重复,只且消费一次(0.11中实现,仅限于下游也是kafka)
在业务中,常常都是使用At least once的模型,如果需要可重入的话,往往是业务自己实现。
At least once
先获取数据,再进行业务处理,业务处理成功后commit offset。
1、生产者生产消息异常,消息是否成功写入不确定,重做,可能写入重复的消息
2、消费者处理消息,业务处理成功后,更新offset失败,消费者重启的话,会重复消费
At most once
先获取数据,再commit offset,最后进行业务处理。
1、生产者生产消息异常,不管,生产下一个消息,消息就丢了
2、消费者处理消息,先更新offset,再做业务处理,做业务处理失败,消费者重启,消息就丢了
Exactly once
思路是这样的,首先要保证消息不丢,再去保证不重复。所以盯着At least once的原因来搞。 首先想出来的: 生产者重做导致重复写入消息----生产保证幂等性 消费者重复消费---消灭重复消费,或者业务接口保证幂等性重复消费也没问题
由于业务接口是否幂等,不是kafka能保证的,所以kafka这里提供的exactly once是有限制的,消费者的下游也必须是kafka。 所以一下讨论的,没特殊说明,消费者的下游系统都是kafka(注:使用kafka conector,它对部分系统做了适配,实现了exactly once)。
生产者幂等性好做,没啥问题。
解决重复消费有两个方法: 下游系统保证幂等性,重复消费也不会导致多条记录。 把commit offset和业务处理绑定成一个事务。
本来exactly once实现第1点就ok了。
但是在一些使用场景下,我们的数据源可能是多个topic,处理后输出到多个topic,这时我们会希望输出时要么全部成功,要么全部失败。 这就需要实现事务性。 既然要做事务,那么干脆 把重复消费的问题从根源上解决,把commit offset和输出到其他topic绑定成一个事务。
生产幂等性
思路是这样的,为每个producer分配一个pid,作为该producer的唯一标识。producer会为每一个维护一个单调递增的seq。类似的,broker也会为每个记录下最新的seq。当req_seq == broker_seq+1时,broker才会接受该消息。因为: 消息的seq比broker的seq大超过时,说明中间有数据还没写入,即乱序了。 消息的seq不比broker的seq小,那么说明该消息已被保存。
解决重复生产
事务性/原子性广播
场景是这样的: 先从多个源topic中获取数据。 做业务处理,写到下游的多个目的topic。 更新多个源topic的offset。
其中第2、3点作为一个事务,要么全成功,要么全失败。这里得益与offset实际上是用特殊的topic去保存,这两点都归一为写多个topic的事务性处理。

基本思路是这样的:
引入tid(transaction id),和pid不同,这个id是应用程序提供的,用于标识事务,和producer是谁并没关系。就是任何producer都可以使用这个tid去做事务,这样进行到一半就死掉的事务,可以由另一个producer去恢复。
同时为了记录事务的状态,类似对offset的处理,引入transaction coordinator用于记录transaction log。在集群中会有多个transaction coordinator,每个tid对应唯一一个transaction coordinator。
注:transaction log删除策略是compact,已完成的事务会标记成null,compact后不保留。
做事务时,先标记开启事务,写入数据,全部成功就在transaction log中记录为prepare commit状态,否则写入prepare abort的状态。之后再去给每个相关的partition写入一条marker(commit或者abort)消息,标记这个事务的message可以被读取或已经废弃。成功后在transaction log记录下commit/abort状态,至此事务结束。
数据流:

Kafka Transactions Data Flow.png 首先使用tid请求任意一个broker(代码中写的是负载最小的broker),找到对应的transaction coordinator。 请求transaction coordinator获取到对应的pid,和pid对应的epoch,这个epoch用于防止僵死进程复活导致消息错乱,当消息的epoch比当前维护的epoch小时,拒绝掉。tid和pid有一一对应的关系,这样对于同一个tid会返回相同的pid。 client先请求transaction coordinator记录的事务状态,初始状态是BEGIN,如果是该事务中第一个到达的,同时会对事务进行计时;client输出数据到相关的partition中;client再请求transaction coordinator记录offset的事务状态;client发送offset commit到对应offset partition。 client发送commit请求,transaction coordinator记录prepare commit/abort,然后发送marker给相关的partition。全部成功后,记录commit/abort的状态,最后这个记录不需要等待其他replica的ack,因为prepare不丢就能保证最终的正确性了。
这里prepare的状态主要是用于事务恢复,例如给相关的partition发送控制消息,没发完就宕机了,备机起来后,producer发送请求获取pid时,会把未完成的事务接着完成。
当partition中写入commit的marker后,相关的消息就可被读取。所以kafka事务在prepare commit到commit这个时间段内,消息是逐渐可见的,而不是同一时刻可见。
详细细节可看: https://cwiki.apache.org/confluence/display/KAFKA/KIP-98+-+Exactly+Once+Delivery+and+Transactional+Messaging#KIP-98-ExactlyOnceDeliveryandTransactionalMessaging-TransactionalGuarantees
消费事务
前面都是从生产的角度看待事务。还需要从消费的角度去考虑一些问题。
消费时,partition中会存在一些消息处于未commit状态,即业务方应该看不到的消息,需要过滤这些消息不让业务看到,kafka选择在消费者进程中进行过来,而不是在broker中过滤,主要考虑的还是性能。kafka高性能的一个关键点是zero copy,如果需要在broker中过滤,那么势必需要读取消息内容到内存,就会失去zero copy的特性。
文件组织
kafka的数据,实际上是以文件的形式存储在文件系统的。topic下有partition,partition下有segment,segment是实际的一个个文件,topic和partition都是抽象概念。
在目录/${topicName}-{$partitionid}/下,存储着实际的log文件(即segment),还有对应的索引文件。
每个segment文件大小相等,文件名以这个segment中最小的offset命名,文件扩展名是.log;segment对应的索引的文件名字一样,扩展名是.index。有两个index文件,一个是offset index用于按offset去查message,一个是time index用于按照时间去查,其实这里可以优化合到一起,下面只说offset index。总体的组织是这样的:
kafka 文件组织.png
为了减少索引文件的大小,降低空间使用,方便直接加载进内存中,这里的索引使用稀疏矩阵,不会每一个message都记录下具体位置,而是每隔一定的字节数,再建立一条索引。 索引包含两部分,分别是baseOffset,还有position。
baseOffset:意思是这条索引对应segment文件中的第几条message。这样做方便使用数值压缩算法来节省空间。例如kafka使用的是varint。
position:在segment中的绝对位置。
查找offset对应的记录时,会先用二分法,找出对应的offset在哪个segment中,然后使用索引,在定位出offset在segment中的大概位置,再遍历查找message。
常用配置项
broker配置
配置项 作用
broker.id broker的唯一标识
auto.create.topics.auto
log.dirs
设置成true,就是遇到没有的topic自动创建topic。
log的目录数,目录里面放partition,当生成新的partition时,会挑目录里partition数最少的目录放。
topic配置
配置项 作用
num.partitions 新建一个topic,会有几个partition。
log.retention.ms 对应的还有minutes,hours的单位。日志保留时间,因为删除是文件维度而不是消息维度,看的是日志文件的mtime。
log.retention.bytes partion最大的容量,超过就清理老的。注意这个是partion维度,就是说如果你的topic有8个partition,配置1G,那么平均分配下,topic理论最大值8G。
log.segment.bytes 一个segment的大小。超过了就滚动。
log.segment.ms
message.max.bytes
一个segment的打开时间,超过了就滚动。
message最大多大
关于日志清理,默认当前正在写的日志,是怎么也不会清理掉的。
还有0.10之前的版本,时间看的是日志文件的mtime,但这个指是不准确的,有可能文件被touch一下,mtime就变了。因此在0.10版本开始,改为使用该文件最新一条消息的时间来判断。
按大小清理这里也要注意,Kafka在定时任务中尝试比较当前日志量总大小是否超过阈值至少一个日志段的大小。如果超过但是没超过一个日志段,那么就不会删除。
大数据
2018-12-25 13:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一、准备环境
采用三台CentOS6.5部署Elasticsearch集群,部署Elasticsearch集群就不得不提索引分片,以下是索引分片的简单介绍。
系统 节点名称 IP地址
centos 6.5 els-node1 192.168.1.101
centos 6.5
centos 6.5
els-node2
els-node3
192.168.1.102
192.168.1.103
ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本。通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题。不能运行的原因可能是内存也可能是存储。由于每个分片可以有多个副本,通过将副本分配到多个服务器,可以提高查询的负载能力。
由于 Elasticsearch 6.5.4要求linux 内核版本要高于3.5+,所以我们先要将系统内核升级至3.5+,详细请移步
CentOS6.5升级内核至4.4
二、Elasticsearch集群搭建
1.安装JDK
Elasticsearch是基于Java开发是一个Java程序,运行在Jvm中,所以第一步要安装JDK yum install -y java-1.8.0-openjdk-devel
2.下载elasticsearch
https://www.elastic.co/cn/downloads/elasticsearch ,是ELasticsearch的官方站点,如果需要下载最新的版本,进入官网下载即可。可以下载到本地电脑然后再导入CentOS中,也可以直接在CentOS中下载。 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.rpm
3.安装elasticsearch rpm -ivh elasticsearch-6.5.4.rpm
4.配置目录
安装完毕后会生成很多文件,包括配置文件日志文件等等,下面几个是最主要的配置文件路径 /etc/elasticsearch/elasticsearch.yml # els的配置文件 /etc/elasticsearch/jvm.options # JVM相关的配置,内存大小等等 /etc/elasticsearch/log4j2.properties # 日志系统定义 /usr/share/elasticsearch # elasticsearch 默认安装目录 /var/lib/elasticsearch # 数据的默认存放位置
5.创建用于存放数据与日志的目录
数据文件会随着系统的运行飞速增长,所以默认的日志文件与数据文件的路径不能满足我们的需求,那么手动创建日志与数据文件路径,可以使用NFS、可以使用Raid等等方便以后的管理与扩展 mkdir -p /opt/elasticsearch/data mkdir -p /opt/elasticsearch/log chown -R elasticsearch.elasticsearch /opt/elasticsearch/*
6.集群配置
集群配置中最重要的两项是 node.name 与 network.host ,每个节点都必须不同。其中 node.name 是节点名称主要是在Elasticsearch自己的日志加以区分每一个节点信息。
discovery.zen.ping.unicast.hosts 是集群中的节点信息,可以使用IP地址、可以使用主机名(必须可以解析)。 vim /etc/elasticsearch cluster.name: my-els # 集群名称 node.name: els-node1 # 节点名称,仅仅是描述名称,用于在日志中区分 path.data: /opt/elasticsearch/data # 数据的默认存放路径 path.logs: /opt/elasticsearch/log # 日志的默认存放路径 network.host: 192.168.1.101 # 当前节点的IP地址 http.port: 9200 # 对外提供服务的端口,9300为集群服务的端口 #添加如下内容 #culster transport port transport.tcp.port: 9300 transport.tcp.compress: true discovery.zen.ping.unicast.hosts: ["192.168.1.101", "192.168.1.102","192.168.1.103"] # 集群个节点IP地址,也可以使用els、els.shuaiguoxia.com等名称,需要各节点能够解析 discovery.zen.minimum_master_nodes: 2 # 为了避免脑裂,集群节点数最少为 半数+1
7.JVM配置
由于Elasticsearch是Java开发的,所以可以通过 /etc/elasticsearch/jvm.options 配置文件来设定JVM的相关设定。如果没有特殊需求按默认即可。
不过其中还是有两项最重要的 -Xmx1g 与 -Xms1g JVM的最大最小内存。如果太小会导致Elasticsearch刚刚启动就立刻停止。太大会拖慢系统本身。 vim /etc/elasticsearch/jvm.options -Xms1g # JVM最大、最小使用内存 -Xmx1g
8.配置为系统服务 sudo chkconfig --add elasticsearch
9.启动Elasticsearch sudo service elasticsearch start Starting elasticsearch: [ OK ]
10.测试
Elasticsearch直接听过了http接口,所以直接使用curl命令就可以查看到一些集群相关的信息。
可以使用curl命令来获取集群的相关的信息, _cat代表查看信息 nodes为查看节点信息,默认会显示为一行,所以就用刀了?preety让信息更有好的显示 ?preety让输出信息更友好的显示 可以使用curl命令来获取集群的相关的信息, #单台测试 curl http://192.168.1.101:9200 { "name" : "els-node1", "cluster_name" : "my-els", "cluster_uuid" : "Kmi9jmG7SdWAYu9k8_-yaw", "version" : { "number" : "6.5.4", "build_flavor" : "default", "build_type" : "rpm", "build_hash" : "d2ef93d", "build_date" : "2018-12-17T21:17:40.758843Z", "build_snapshot" : false, "lucene_version" : "7.5.0", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" } #集群测试 curl http://192.168.1.101:9200/_cluster/health?pretty { "cluster_name" : "my-els", "status" : "green", #green即为健康 "timed_out" : false, "number_of_nodes" : 3, #可以看到number_of_nodes为3 "number_of_data_nodes" : 3, "active_primary_shards" : 0, "active_shards" : 0, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 } curl http://192.168.1.101:9200/_cat/nodes?pretty 192.168.1.103 33 16 0 0.08 0.10 0.12 mdi - els-node3 192.168.1.101 31 37 0 0.02 0.12 0.13 mdi - els-node1 192.168.1.102 24 26 0 0.02 0.01 0.01 mdi * els-node2 # *号表示为当前节点为主节点的意思
如果你要想查看更多有关于集群信息、当前节点统计信息等等,可以使用一下命令来获取到所有可以查看的信息。 curl http://192.168.1.101:9200/_cat?pretty =^.^= /_cat/allocation /_cat/shards /_cat/shards/{index} /_cat/master /_cat/nodes /_cat/tasks /_cat/indices /_cat/indices/{index} /_cat/segments /_cat/segments/{index} /_cat/count /_cat/count/{index} /_cat/recovery /_cat/recovery/{index} /_cat/health /_cat/pending_tasks /_cat/aliases /_cat/aliases/{alias} /_cat/thread_pool /_cat/thread_pool/{thread_pools} /_cat/plugins /_cat/fielddata /_cat/fielddata/{fields} /_cat/nodeattrs /_cat/repositories /_cat/snapshots/{repository} /_cat/templates
***如果无法访问,说明启动失败了,在配置 /opt/elasticsearch/log 中 my-els.log 日志文件, 可能遇到如下的错误:
1、第一种错误 max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
解决办法:切换到root用户,编辑limits.conf 添加类似如下内容 vi /etc/security/limits.conf # # 添加如下内容 * soft nofile 65536 * hard nofile 131072 * soft nproc 2048 * hard nproc 4096
2、第二种错误 max number of threads [1024] for user [elasticsearch] is too low, increase to at least [4096]
解决办法:切换到root用户,进入limits.d目录下修改配置文件 vi /etc/security/limits.d/90-nproc.conf 修改如下内容: * soft nproc 1024 #修改为 * soft nproc 4096
3、第三种错误 max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]
解决办法:切换到root用户,修改配置sysctl.conf vi /etc/sysctl.conf 添加下面配置: vm.max_map_count=655360 并执行命令: sysctl -p
然后,重新启动elasticsearch,即可启动成功。
大数据
2018-12-25 11:51:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Kafka 消息监控 - Kafka Eagle
1.概述
  在开发工作当中,消费 Kafka 集群中的消息时,数据的变动是我们所关心的,当业务并不复杂的前提下,我们可以使用 Kafka 提供的命令工具,配合 Zookeeper 客户端工具,可以很方便的完成我们的工作。随着业务的复杂化,Group 和 Topic 的增加,此时我们使用 Kafka 提供的命令工具,已预感到力不从心,这时候 Kafka 的监控系统此刻便尤为显得重要,我们需要观察消费应用的详情。 监控系统业界有很多杰出的开源监控系统。我们在早期,有使用 KafkaMonitor 和 Kafka Manager 等,不过随着业务的快速发展,以及互联网公司特有的一些需求,现有的开源的监控系统在性能、扩展性、和 DEVS 的使用效率方面,已经无法满足了。 因此,我们在过去的时间里,从互联网公司的一些需求出发,从各位 DEVS 的使用经验和反馈出发,结合业界的一些开源的 Kafka 消息监控,用监控的一些思考出发,设计开发了现在 Kafka 集群消息监控系统:Kafka Eagle。
  Kafka Eagle 用于监控 Kafka 集群中 Topic 被消费的情况。包含 Lag 的产生,Offset 的变动,Partition 的分布,Owner ,Topic 被创建的时间和修改的时间等信息。下载地址如下所示: [ Kafka Eagle 下载地址 ] [ Kafka Eagle Github ]
2.内容
  Kafka Eagle 涉及以下内容模块: Dashboard Topic(Create & List) Consumers Cluster Info
2.1 Dashboard
  我们通过在浏览器中输入 http://host:port/ke,访问 Kafka Eagle 的 Dashboard 页面。该页面包含以下内容: Brokers Topics Zookeepers Consumers Kafka Brokers Graph
  展示 Kafka 集群的 Topic 数量,消费者数量,Kafka 的 Brokers 数,以及所属的 Zookeeper 集群信息。Dashboard 信息展示截图如下:
2.2 Topic
  在 Topic 模块下,包含创建 Topic 和展示 Topic 信息详情。
2.2.1 Create
  通过创建模块可以创建一个自定义分区和备份数的 Topic。如下图所示:
2.2.2 List
  该模块下列出 Kafka 集群中所有的 Topic,包含 Topic 的分区数,创建时间以及修改时间,如下图所示:
  上图中,每一个 Topic 名称对应一个详情的超链接,通过该链接可以查看该 Topic 的详情,如:分区索引号,Leader,Replicas 和 Isr,如下图所示所示:
2.3 Consumers
  该模块显示有消费记录的 Topic 信息,其中包含如下内容: Running Pending Active Topic Graph Offsets Rate Graph
2.4 Cluster Info
  该模块显示 Kafka 集群信息和 Zookeeper 集群信息,包含如下内容: Kafka Broker Host & IP Kafka Broker Created & Modify Date Zookeeper Host & IP
3.数据采集
  Kafka Eagel 监控的消息数据源,来自于 Zookeeper。由于创建,修改或是消费 Kafka 的消息,都会在 Zookeeper 中进行注册,我们可以从中获取数据的变动,例如:Topic,Brokers,Partitions 以及 Group 等,Kafka 在 Zookeeper 的结构存储,如下图所示:
4.总结
  Kafka Eagle 的安装使用很简单,下载安装,配置好 Kafka 集群所属的 Zookeeper 集群地址即可,安装部署文档地址如下: Kafka Eagle 使用文档
5.结束语
  这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!
前面有文章说到了一个叫kafka manager的kafka管理工具,这个工具管理kafka确实很强大,但是没有安全认证,随便都可以创建,删除,修改topic,而且告警系统,流量波动做的不好。所以,在这里浪尖,再给大家推荐一款kafka 的告警监控管理工具,kafka-eagle。
kafka-eagle主要是有几个我们关注 但kafkamanager不存在的点,值得一提:
流量,最长可以查看最近七天的流量波动图
lag size邮件告警
可以用kafkasql分析
1.下载解压
直接下载压缩包
http://download.smartloli.org/
源码地址
https://github.com/smartloli/kafka-eagle
建议直接下载压缩包,而不是使用源码编译,编译过程中容易出错。有兴趣的可以下载源码阅读一下。
解压
tar -zxf ~/Downloads/kafka-eagle-bin-1.2.4.tar.gz -C ./
给文件加充命名为kafka-eagle。
2. 配置
配置环境变量。
vi /etc/profile
export KE_HOME=/opt/hadoop/kafka-eagle
export PATH=$PATH:$KE_HOME/bin
3. 配置system-config.properties
浪尖这里运行了多个kafka版本,以此测试多版本多集群,具体配置方式可以参考下面配置。
######################################
# multi zookeeper&kafka cluster list
######################################
kafka.eagle.zk.cluster.alias=cluster1,cluster2
cluster1.zk.list=localhost:2181/kafka010
cluster2.zk.list=localhost:2181/kafka082
######################################
# zk client thread limit
######################################
kafka.zk.limit.size=25
######################################
# kafka eagle webui port
######################################
kafka.eagle.webui.port=8048
######################################
# kafka offset storage
######################################
cluster1.kafka.eagle.offset.storage=kafka
cluster2.kafka.eagle.offset.storage=zookeeper
######################################
# enable kafka metrics
######################################
kafka.eagle.metrics.charts=false
######################################
# alarm email configure
######################################
kafka.eagle.mail.enable=false
kafka.eagle.mail.sa=alert_sa
kafka.eagle.mail.username=alert_sa@163.com
kafka.eagle.mail.password=mqslimczkdqabbbh
kafka.eagle.mail.server.host=smtp.163.com
kafka.eagle.mail.server.port=25
######################################
# delete kafka topic token
######################################
kafka.eagle.topic.token=keadmin
######################################
# kafka sasl authenticate
######################################
kafka.eagle.sasl.enable=false
kafka.eagle.sasl.protocol=SASL_PLAINTEXT
kafka.eagle.sasl.mechanism=PLAIN
######################################
# kafka jdbc driver address
######################################
#kafka.eagle.driver=com.mysql.jdbc.Driver
#kafka.eagle.url=jdbc:mysql://127.0.0.1:3306/ke?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
#kafka.eagle.username=root
#kafka.eagle.password=mt2018 @#
kafka.eagle.driver=org.sqlite.JDBC
kafka.eagle.url=jdbc:sqlite:/opt/hadoop/kafka-eagle/db/ke.db
kafka.eagle.username=root
kafka.eagle.password=smartloli
启动运行
在启动运行之前要做下面一步,
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home
否则,会报错误如下:
正式启动kafka-eagle
bin/ke.sh start
启动成功之后,会有如下日志,注意红框处。
打开浏览器,输入上面截图红框处提示的url
点击登陆之后进入
具体使用,可以参考手册
https://ke.smartloli.org/1.Overview/1.Overview.html
Kafka三款监控工具比较 博客分类: MQ

在之前的博客中,介绍了 Kafka Web Console 这 个监控工具,在生产环境中使用,运行一段时间后,发现该工具会和Kafka生产者、消费者、ZooKeeper建立大量连接,从而导致网络阻塞。并且这个 Bug也在其他使用者中出现过,看来使用开源工具要慎重!该Bug暂未得到修复,不得已,只能研究下其他同类的Kafka监控软件。
通过研究,发现主流的三种kafka监控程序分别为: Kafka Web Conslole Kafka Manager KafkaOffsetMonitor
现在依次介绍以上三种工具:
Kafka Web Conslole
使用Kafka Web Console,可以监控: Brokers列表 Kafka 集群中 Topic列表,及对应的Partition、LogSiz e等信息 点击Topic,可以浏览对应的Consumer Groups、Offset、Lag等信息 生产和消费流量图、消息预览…
程序运行后,会定时去读取kafka集群分区的日志长度,读取完毕后,连接没有正常释放,一段时间后产生大量的socket连接,导致网络堵塞。
Kafka Manager
雅虎开源的Kafka集群管理工具: 管理几个不同的集群 监控集群的状态(topics, brokers, 副本分布, 分区分布) 产生分区分配(Generate partition assignments)基于集群的当前状态 重新分配分区
KafkaOffsetMonitor KafkaOffsetMonitor可以实时监控: Kafka集群状态 Topic、Consumer Group列表 图形化展示topic和consumer之间的关系 图形化展示consumer的Offset、Lag等信息
总结
通过使用,个人总结以上三种监控程序的优缺点:
Kafka Web Console:监控功能较为全面,可以预览消息,监控Offset、Lag等信息,但存在bug,不建议在生产环境中使用。
Kafka Manager:偏向Kafka集群管理,若操作不当,容易导致集群出现故障。对Kafka实时生产和消费消息是通过JMX实现的。没有记录Offset、Lag等信息。
KafkaOffsetMonitor:程序一个jar包的形式运行,部署较为方便。只有监控功能,使用起来也较为安全。
若只需要监控功能,推荐使用KafkaOffsetMonito,若偏重Kafka集群管理,推荐使用Kafka Manager。
因为都是开源程序,稳定性欠缺。故需先了解清楚目前已存在哪些Bug,多测试一下,避免出现类似于Kafka Web Console的问题。
kafka监控
kafka-web-console
https://github.com/claudemamo/kafka-web-console
部署sbt:
http://www.scala-sbt.org/0.13/tutorial/Manual-Installation.html
http://www.scala-sbt.org/release/tutorial/zh-cn/Installing-sbt-on-Linux.html
KafkaOffsetMonitor
https://github.com/quantifind/KafkaOffsetMonitor/releases/tag/v0.2.0

java -cp KafkaOffsetMonitor-assembly-0.2.0.jar com.quantifind.kafka.offsetapp.OffsetGetterWeb --zk localhost:12181 --port 8080 --refresh 5.minutes --retain 1.day
kafka 监控之Mx4jLoader
源码未做任何修改,在lib下添加jar包mx4j-tools-3.0.1
启动server之后访问 ip:8082
/**
* If mx4j-tools is in the classpath call maybeLoad to load the HTTP interface of mx4j.
*
* The default port is 8082. To override that provide e.g. -Dmx4jport=8083
* The default listen address is 0.0.0.0. To override that provide -Dmx4jaddress=127.0.0.1
* This feature must be enabled with -Dmx4jenable=true
*
* This is a Scala port of org.apache.cassandra.utils.Mx4jTool written by Ran Tavory for CASSANDRA-1068
* */
JMX监控指标参数列表如下:

参数 Mbean名称 说明
Message in rate "kafka.server":name="AllTopicsMessagesInPerSec",type="BrokerTopicMetrics" 所有topic消息(进出)流量
Byte in rate "kafka.server":name="AllTopicsBytesInPerSec",type="BrokerTopicMetrics"
Request rate "kafka.network":name="{Produce|Fetch-consumer|Fetch-follower}-RequestsPerSec",type="RequestMetrics"
Byte out rate "kafka.server":name="AllTopicsBytesOutPerSec",type="BrokerTopicMetrics"
Log flush rate and time "kafka.log":name="LogFlushRateAndTimeMs",type="LogFlushStats"
# of under replicated partitions (|ISR| < |all replicas|) "kafka.server":name="UnderReplicatedPartitions",type="ReplicaManager" 0
Is controller active on broker "kafka.controller":name="ActiveControllerCount",type="KafkaController" only one broker in the cluster should have 1
Leader election rate "kafka.controller":name="LeaderElectionRateAndTimeMs",type="ControllerStats" non-zero when there are broker failures
Unclean leader election rate "kafka.controller":name="UncleanLeaderElectionsPerSec",type="ControllerStats" 0
Partition counts "kafka.server":name="PartitionCount",type="ReplicaManager" mostly even across brokers
Leader replica counts "kafka.server":name="LeaderCount",type="ReplicaManager" mostly even across brokers
ISR shrink rate "kafka.server":name="ISRShrinksPerSec",type="ReplicaManager" If a broker goes down, ISR for some of the partitions will shrink. When that broker is up again, ISR will be expanded once the replicas are fully caught up. Other than that, the expected value for both ISR shrink rate and expansion rate is 0.
ISR expansion rate "kafka.server":name="ISRExpandsPerSec",type="ReplicaManager" See above
Max lag in messages btw follower and leader replicas "kafka.server":name="([-.\w]+)-MaxLag",type="ReplicaFetcherManager" 副本消息滞后数量
Lag in messages per follower replica "kafka.server":name="([-.\w]+)-ConsumerLag",type="FetcherLagMetrics" 副本消息滞后数量
Requests waiting in the producer purgatory "kafka.server":name="PurgatorySize",type="ProducerRequestPurgatory"
Requests waiting in the fetch purgatory "kafka.server":name="PurgatorySize",type="FetchRequestPurgatory"
Request total time "kafka.network":name="{Produce|Fetch-Consumer|Fetch-Follower}-TotalTimeMs",type="RequestMetrics"
Time the request waiting in the request queue "kafka.network":name="{Produce|Fetch-Consumer|Fetch-Follower}-QueueTimeMs",type="RequestMetrics"
Time the request being processed at the leader "kafka.network":name="{Produce|Fetch-Consumer|Fetch-Follower}-LocalTimeMs",type="RequestMetrics"
Time the request waits for the follower "kafka.network":name="{Produce|Fetch-Consumer|Fetch-Follower}-RemoteTimeMs",type="RequestMetrics"
Time to send the response "kafka.network":name="{Produce|Fetch-Consumer|Fetch-Follower}-ResponseSendTimeMs",type="RequestMetrics"
Number of messages the consumer lags behind the producer by
"kafka.consumer":name="([-.\w]+)-MaxLag",type="ConsumerFetcherManager"
大数据
2018-12-25 11:29:00