前言
既然做了Hive的整理,那就把spark的也整理下吧,当做入门指南和自己的笔记吧~与君共勉
Spark基础
Spark原理过程
使用spark-submit提交一个Spark作业之后,这个作业就会启动一个对应的Driver进程。
Driver:运行Application的main()函数并且创建SparkContext
Driver根据我们设置的参数(比如说设定任务队列,设定最大内存等)Cluster Manager 申请运行Spark作业需要使用的资源,这里的资源指的就是Executor进程,YARN集群管理器会根据我们为Spark作业设置的资源参数,在各个工作节点上,启动一定数量的Executor进程,每个Executor进程都占有一定数量的内存和CPU core。
Executor:是为某Application运行在Worker Node上的一个进程,该进程负责运行Task,并且负责将数据存在内存或者磁盘上,每个Application都有各自独立的Executors
Cluster Manager:集群管理器,在集群上获取资源的外部服务(例如:Local、Standalone、Mesos或Yarn等集群管理系统)
申请到了作业执行所需的资源之后,river进程会将我们编写的Spark作业代码分拆为多个stage,每个stage执行一部分代码片段,并为每个stage创建一批task,然后将这些task分配到各个Executor进程中执行,一个stage的所有task都执行完毕之后,会在各个节点本地的磁盘文件中写入计算中间结果,然后Driver就会调度运行下一个stage。下一个stage的task的输入数据就是上一个stage输出的中间结果。如此循环往复,直到将我们自己编写的代码逻辑全部执行完
task:最小的计算单元,负责执行一模一样的计算逻辑(也就是我们自己编写的某个代码片段)
什么是RDD?
全称为弹性分布式数据集;本质上,RDD是种编程抽象,代表可以跨机器进行分割的只读对象集合。RDD可以从一个继承结构(lineage)重建(因此可以容错),通过并行操作访问,可以读写HDFS或S3这样的分布式存储,更重要的是,可以缓存到worker节点的内存中进行立即重用。由于RDD可以被缓存在内存中,Spark对迭代应用特别有效,因为这些应用中,数据是在整个算法运算过程中都可以被重用。大多数机器学习和最优化算法都是迭代的,使得Spark对数据科学来说是个非常有效的工具。另外,由于Spark非常快,可以通过类似Python REPL的命令行提示符交互式访问。
RDD特点
RDD在抽象上来说是一种元素集合,包含了数据。它是被分区的,分为多个分区,每个分区分布在集群中的不同节点上,从而让RDD中的数据可以被并行操作。(分布式数据集)
RDD的数据默认情况下存放在内存中的,但是在内存资源不足时,Spark会自动将RDD数据写入磁盘。比如每个节点最多放5万数据,结果你每个partition是10万数据。那么就会把partition中的部分数据写入磁盘上,进行保存。(弹性)
RDD将操作分为两类:transformation与action。无论执行了多少次transformation操作,RDD都不会真正执行运算,只有当action操作被执行时,运算才会触发。而在RDD的内部实现机制中,底层接口则是基于迭代器的,从而使得数据访问变得更高效,也避免了大量中间结果对内存的消耗。
RDD最重要的特性就是,提供了容错性,可以自动从节点失败中恢复过来。即如果某个节点上的RDD partition,因为节点故障,导致数据丢了,那么RDD会自动通过自己的数据来源重新计算该partition。这一切对使用者是透明的。
RDD在Spark中的地位及作用
这需要从四个方面阐述
为什么会有Spark?
因为传统的并行计算模型无法有效的解决迭代计算(iterative)和交互式计算(interactive);而Spark的使命便是解决这两个问题,这也是他存在的价值和理由。
Spark如何解决迭代计算?
其主要实现思想就是RDD,把所有计算的数据保存在分布式的内存中。迭代计算通常情况下都是对同一个数据集做反复的迭代计算,数据在内存中将大大提升IO操作。这也是Spark涉及的核心:内存计算。
Spark如何实现交互式计算?
因为Spark是用scala语言实现的,Spark和scala能够紧密的集成,所以Spark可以完美的运用scala的解释器,使得其中的scala可以向操作本地集合对象一样轻松操作分布式数据集。当然你也可以使用python,java,R等接口,spark也提供了相应的操作方式
Spark和RDD的关系?
可以理解为:RDD是一种具有容错性基于内存的集群计算抽象方法,Spark则是这个抽象方法的实现。
如何操作RDD?
Step1-获取RDD
自己创建个RDD,如以下语句:
rdd = sc.parallelize(['1,2,3,4','5,6,6','9,10,11'])
从共享的文件系统获取,(如:HDFS)
通过已存在的RDD转换
将已存在scala集合(只要是Seq对象)并行化,通过调用SparkContext的parallelize方法实现
改变现有RDD的之久性;RDD是懒散,短暂的.(RDD的固化:cache缓存至内错;save保存到分布式文件系统)
Step2-操作RDD
Transformation:根据数据集创建一个新的数据集,计算后返回一个新RDD;例如:Map将数据的每个元素经过某个函数计算后,返回一个新的分布式数据集即RDD。
值得注意的是,RDD的转化操作都是惰性求值得,也就意味着在被调用行动操作之前Spark不会开始计算,相反,Spark会在内部记录下所要求执行的操作的相关信息,因此在调用sc.textFile()时候,数据并没有读取进来,而是在必要的时候才会进行读取。所以也就导致了导入文件的时候感觉很快的错觉
Transformation的一些例子
image
def func(a): line_split = a.split(",") return sum(map(int,line_split)) data = sc.parallelize(['1,2,3,4','5,6,6','9,10,11']) # 生成rddt_rdd= data.map(func) # rdd的Transformation过程a_rdd = t_rdd.collect() # action过程 [10, 17, 30]
Actions:对数据集计算后返回一个数值value给驱动程序;例如:Reduce将数据集的所有元素用某个函数聚合后,将最终结果返回给程序。返回的是一个新的数据类型,这里注意的是,返回的并不是新的RDD,只有Transformation之后是新的RDD
Actions具体内容
image
spark执行步骤
定义一个或多个RDD,可以通过获取存储在磁盘上的数据(HDFS,Cassandra,HBase,Local Disk),并行化内存中的某些集合,转换(transform)一个已存在的RDD,或者,缓存或保存。
通过传递一个闭包(函数)给RDD上的每个元素来调用RDD上的操作。Spark提供了除了Map和Reduce的80多种高级操作。
使用结果RDD的动作(action)(如count、collect、save等)。动作将会启动集群上的worker机器进行计算。
当Spark在一个worker上运行闭包时,闭包中用到的所有变量都会被拷贝到节点上,但是由闭包的局部作用域来维护。Spark提供了两种类型的共享变量,这些变量可以按照限定的方式被所有worker访问。广播变量会被分发给所有worker,但是是只读的。累加器这种变量,worker可以使用关联操作来“加”,通常用作计数器。
Spark实际操作
那么, sc的是什么鬼?
你可以把他理解成由
SparkContext
构造出来的实例,通过这个实例我们可以构造自己的RDD
# -*- coding:utf-8 -*-from pyspark import SparkContext, SparkConffrom pyspark.streaming import StreamingContextimport math appName ="hellospark" #你的应用程序名称master= "local"#设置单机conf = SparkConf().setAppName(appName).setMaster(master)#配置SparkContextsc = SparkContext(conf=conf)# 一个简单的wordcount测试str_ = '''this is a word count test only test show twice'''data = sc.parallelize(str_.split(" ")) data.map(lambda x:(x,1)).reduceByKey(lambda x,y:x+y).collect()# spark.akka.frameSize: 控制Spark中通信消息的最大容量 (如 task 的输出结果),默认为10M。当处理大数据时,task 的输出可能会大于这个值,需要根据实际数据设置一个更高的值。# SparkConf为Spark配置类,配置已键值对形式存储,封装了一个ConcurrentHashMap类实例settings用于存储Spark的配置信息;配置项包括:master、appName、Jars、ExecutorEnv等等# SparkContext用于连接Spark集群、创建RDD、累加器(accumlator)、广播变量(broadcast variables),所以说SparkContext为Spark程序的根
注意sc的构造是怎么来的
方法一:在jupyter中操作(推荐)
当然,你得把pyspark的kernel配到jupyter中,可参考解决:win远程连接ubuntu服务器安装jupyter,启动pyspark
这里写图片描述
方法二:使用spark-submit pythonfile.py
来实现提交python脚本操作
# 前提是在一个文件夹中,不然要定位文件位置$ spark-submit --driver-memory 6G --queue 如果有队列填上队列名字 testpy.py 可带参数# pyspark test# 中文测试
方法三:使用ipython在pyspark的shell中操作
# 启动local spark:pyspark --master local[2]# local[2]是开双核的意思,[4]即是开4核xiaoju@map-traffic-spd131.gz01:~$ pyspark --master local[2] In [8]: line = sc.textFile("file:/home/xiaoju/user/xukai/test.tx #创建RDD载入的路径这里是机器路径 ...: t") In [9]: pythonlines = line.filter(lambda line:"test" in line) # 转化操作 In [10]: pythonlines.first() # 行动操作 Out[10]: u'test;'# 当一个文本读取为RDD时,输入的每一行都会成为RDD的一个元素In [21]: line.first() Out[21]: u'this is a test txtfile!' In [24]: print line.first().split(" ")[0] # 这样就可以流畅使用python进行操作了,只是导入的时候用的是RDD存储 this In [26]: stringlist = line.first().split(" ") In [27]: nums = sc.parallelize(stringlist) # 用sparkContext的parallelize制作RDD的,是ParallelCollectionRDD,创建一个并行集合。 In [28]: squared = nums.map(lambda x:x=="this").collect() In [29]: for num in squared: ...: print num ...: True False False False False In [34]: words = lines.flatMap(lambda line:line.split(" ")).collec ...: t() # 使用collece()才能进行for输出,flatmap文件中的所有行数据仅返回了一个数组对象 In [35]: for i in words: ...: print i ...: this is a test# 产生新的键值对pair类型RDDIn [56]: rdd = sc.parallelize([1,2,3,3])# 操作过程中,转化并不会被执行,需要有个动作操作才被执行,比如collect()In [57]: rdd.collect() Out[57]: [1, 2, 3, 3] In [59]: rdd1 = rdd.map(lambda x:(x,x+1)) In [60]: rdd1.collect() Out[60]: [(1, 2), (2, 3), (3, 4), (3, 4)] In [64]: rdd2 = rdd1.filter(lambda x: x[0]>2) In [65]: rdd2.collect() Out[65]: [(3, 4), (3, 4)] In [67]: rdd1.sortByKey().collect() Out[67]: [(1, 2), (2, 3), (3, 4), (3, 4)]
一些Demo
# -*- coding:utf-8 -*-# 如果使用jupyter的话,sc已经构造好了,不需要再倒入包from pyspark import SparkContext, SparkConffrom pyspark.streaming import StreamingContextimport math appName ="hellospark" #你的应用程序名称master= "local"#设置单机conf = SparkConf().setAppName(appName).setMaster(master)#配置SparkContextsc = SparkContext(conf=conf) # parallelize:并行化数据,转化为RDD# 并行集合的一个重要参数是slices,表示数据集切分的份数。Spark将会在集群上为每一份数据起一个任务。# 典型地,你可以在集群的每个CPU上分布2-4个slices. 一般来说,Spark会尝试根据集群的状况,# 来自动设定slices的数目。data = [1, 2, 3, 4, 5] distData = sc.parallelize(data, numSlices=10) # numSlices为分块数目,根据集群数进行分块 #--------------------------------------------------# textFile读取外部数据rdd = sc.textFile("file:/data/map_da/xukai/sparkstreaming/test/test.txt") # 以行为单位读取外部文件,并转化为RDDprint rdd.collect()# 打印出的结果是 [u'lslsllslsiiiiiiiiiii']#--------------------------------------------------# map:迭代,对数据集中数据进行单独操作def my_add(l): return (l,l) data = [1, 2, 3, 4, 5] distData = sc.parallelize(data) # 并行化数据集result = distData.map(my_add)print (result.collect()) # 返回一个分布数据集# 打印出的结果 [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]#--------------------------------------------------# filter:过滤数据def my_add(l): result = False if l > 2: result = True return result data = [1, 2, 3, 4, 5] distData = sc.parallelize(data)#并行化数据集,分片result = distData.filter(my_add)print (result.collect())#返回一个分布数据集 # [3, 4, 5]# zip:将两个RDD对应元素组合为元组#--------------------------------------------------x = sc.parallelize(range(0,5)) y = sc.parallelize(range(1000, 1005))print x.zip(y).collect() # [(0, 1000), (1, 1001), (2, 1002), (3, 1003), (4, 1004)] #union 组合两个RDDprint x.union(x).collect()# [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]# Aciton操作#-------------------------------------------------- # collect:返回RDD中的数据rdd = sc.parallelize(range(1, 10))print rddprint rdd.collect()# ParallelCollectionRDD[11] at parallelize at PythonRDD.scala:423# [1, 2, 3, 4, 5, 6, 7, 8, 9]#--------------------------------------------------# collectAsMap:以rdd元素为元组,以元组中一个元素作为索引返回RDD中的数据m = sc.parallelize([('a', 2), (3, 4)]).collectAsMap()print m['a']print m[3]#2#4#--------------------------------------------------# groupby函数:根据提供的方法为RDD分组:rdd = sc.parallelize([1, 1, 2, 3, 5, 8])def fun(i): return i % 2result = rdd.groupBy(fun).collect()print [(x, sorted(y)) for (x, y) in result] # [(0, [2, 8]), (1, [1, 1, 3, 5])]#--------------------------------------------------# reduce:对数据集进行运算rdd = sc.parallelize(range(1, 10)) result = rdd.reduce(lambda a, b: a + b)print result# 45#--------------------------------------------------a = sc.parallelize([i for i in range(9)], 3)print a.collect()#[0, 1, 2, 3, 4, 5, 6, 7, 8]y = a.map(lambda a:(a,a*2)) # 需要的表现形式为(a,a*2)的形式,而a是传递的参数print y.collect()#[(0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14), (8, 16)]z = a.map(lambda a:a*2)print print z.collect()#[0, 2, 4, 6, 8, 10, 12, 14, 16]y = a.flatMap(lambda a:(a*2,a*3))print y.collect();#[0, 0, 2, 3, 4, 6, 6, 9, 8, 12, 10, 15, 12, 18, 14, 21, 16, 24]#--------------------------------------------------# union有点像appendx = sc.parallelize(['A','A','B']) y = sc.parallelize(['D','C','A']) z = x.union(y) z2 = x.intersection(y) print(x.collect())#['A', 'A', 'B']print(y.collect())#['D', 'C', 'A']print(z.collect())#['A', 'A', 'B', 'D', 'C', 'A']print(z2.collect())# ['A']#--------------------------------------------------x = sc.parallelize([1,2,3]) y = x.groupBy(lambda x: 'A' if (x%2 == 1) else 'B' ) print(x.collect())#[1, 2, 3]print([(j[0],[i for i in j[1]]) for j in y.collect()])#[('A', [1, 3]), ('B', [2])]#--------------------------------------------------x=sc.parallelize([1,3,1,2,3]) y=x.countByValue()print y[1]#2#--------------------------------------------------# 按升序排,取前n个x = sc.parallelize([1,3,1,2,3,4,1,6]) y=x.takeOrdered(5)# [1, 1, 1, 2, 3]
小结
总的来说,RDD之所以被描述为"弹性",是因为在任何时候都能进行重算,因为保存RDD数据的一台机器失败时,Spark可以使用这种特性来重算出丢弃的部分分区。
转化RDD的时候,是返回新的RDD而不是对现有的RDD进行操作,只有在执行动作的时候返回的是其他数据类型 。
Spark进阶
这里会总结下我以前实习时候用到过的一些处理方法
使用MySqldb+Pyspark操作Mysql
首先得知道,这个数据库在哪,也就是数据库所在服务器的ip地址,才能进行连接
# 使用ping命令进行所需要连接的数据库的ip地址获取$ ping 服务器PING xxxxxx bytes of data. 64 bytes from xxxxxx: icmp_seq=1 ttl=64 time=0.020 ms
使用Mysqldb包进行数据库的连接操作
In [22]: import MySQLdb In [23]: conn = MySQLdb.connect(host=ip地址,user=用户名 ...: ,passwd=密码,db='test',charset='utf8')# 这边连接的时候最好制定数据库,即添加 db="test",charset="utf8",如不制定,则在sql语句中选择上指定的数据库名字In [24]: cursor = conn.cursor() In [25]: count = cursor.execute("select count(*) from test_uk ") In [26]: print cursor.fetchall() ((8L,),)# 打开服务器上的Mysql查看一下,ok,没问题,获取行数正确mysql> select * from test_uk; +----+----+-------+| id | tp | value |+----+----+-------+| 3 | 1 | 0.75 || 4 | 5 | 0 || 5 | 6 | 2 || 6 | 4 | 0 || 9 | 7 | 3 || 11 | 9 | 2 || 12 | 10 | 11 || 14 | 13 | 13 |+----+----+-------+8 rows in set (0.00 sec)# 写入test数据库中的tbl_realtime_statis表,记得需要提交mysql> desc tbl_realtime_statis +-------+---------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | || value | double | YES | | NULL | | +-------+---------+------+-----+---------+-------+ 2 rows in set (0.00 sec) # 开始执行insert动作 In [38]: cursor.execute('INSERT INTO tbl_realtime_statis (id,value) VALUE ...: S (1,11)') Out[38]: 1L In [39]: cursor.connection.commit() mysql> select * from tbl_realtime_statis; +------+-------+ | id | value |+------+-------+| 1 | 11 | +------+-------+ 1 row in set (0.00 sec)
更多语句参考:python使用mysqldb连接数据库操作方法示例详解
使用spark-submit pythonfile来执行本地txt写入指定数据库的操作
在文件同目录下创建名为txttosql.py的python脚本,填写如下
conn = MySQLdb.connect(host='',user='',passwd='',db='test',charset='utf8') cursor = conn.cursor() datapath = "test.txt"with open(datapath) as f: for line in f.readlines(): linesplit = line.strip().split(",") key = int(linesplit[0]) value = int(linesplit[1]) sqlstring = 'INSERT INTO tbl_realtime_statis (id,value) VALUES (%d,%d)'%(key,value) cursor.execute(sqlstring) cursor.connection.commit() cursor.close() conn.close()
之后执行spark-submit txttosql.py即可,注意数据库如果已有数据,将不会被覆盖,而是之后插入操作
使用JDBC+Pypark进行MySql操作
建一个parallelize的RDD
In [12]: rdd1 = sc.parallelize([(1,'id1',100000,12,1.2),(2,'id2',2000000,13,1.22)])
转化成为DataFrame的RDD
In [13]: rdd2 = rdd1.toDF()# 会耗费比较长的时间In [14]: rdd2.collect() Out[14]: [Row(_1=1, _2=u'id1', _3=100000, _4=12, _5=1.2), Row(_1=2, _2=u'id2', _3=2000000, _4=13, _5=1.22)] In [15]: rdd2.show() +---+---+-------+---+----+| _1| _2| _3| _4| _5|+---+---+-------+---+----+| 1|id1| 100000| 12| 1.2|| 2|id2|2000000| 13|1.22|+---+---+-------+---+----+ In [18]: rdd2.filter("_3 > 100000").show() +---+---+-------+---+----+| _1| _2| _3| _4| _5|+---+---+-------+---+----+| 2|id2|2000000| 13|1.22|+---+---+-------+---+----+# 可以修改别名,貌似只有一次改的?In [34]: rdd2.withColumnRenamed("_2","name_string").withColumnRenamed("_3","money_bigint") Out[34]: DataFrame[_1: bigint, name_string: string, money_bigint: bigint, _4: bigint, _5: double]# 尝试在toDF的时候就写好名字In [41]: rdd3 = rdd1.toDF(["id_int","name_string","money_bigint","age_double","tall_float"]) In [42]: rdd3.write.jdbc("jdbc:mysql://xxxx/test", "testalltype", "overwrite", {" ...: user":"", "password":""}) # 查看mysql> select * from testalltype; +--------+-------------+--------------+------------+------------+| id_int | name_string | money_bigint | age_double | tall_float |+--------+-------------+--------------+------------+------------+| 2 | id2 | 2000000 | 13 | 1.22 || 1 | id1 | 100000 | 12 | 1.2 |+--------+-------------+--------------+------------+------------+2 rows in set (0.00 sec)# 插入语句可以用append,使用另一种方法创建dataframeIn [48]: newline = [(932,'Alice', 1929291,2,22.92)] In [51]: rdd4 = sqlContext.createDataFrame(newline,['id_int','name ...: _string','money_bigint','age_double','tall_float']) In [52]: rdd4.show() +------+-----------+------------+----------+-----------+|id_int|name_string|money_bigint|age_double|tall_float|+------+-----------+------------+----------+-----------+| 932| Alice| 1929291| 2| 22.92|+------+-----------+------------+----------+-----------+ In [55]: rdd4.write.jdbc("jdbc:mysql://xxxx/test","t ...: estalltype","append",{"user":"","password":" ...: "})# 查看mysql> select * from testalltype; +--------+-------------+--------------+------------+------------+| id_int | name_string | money_bigint | age_double | tall_float |+--------+-------------+--------------+------------+------------+| 2 | id2 | 2000000 | 13 | 1.22 || 1 | id1 | 100000 | 12 | 1.2 || 932 | Alice | 1929291 | 2 | 22.92 |+--------+-------------+--------------+------------+-------------+##############使用pyspark+jdbc将本地csv存储到mysql###########In [1]: datapath = "dataform.csv"In [2]: with open(datapath) as f: ...: k = 1 ...: parallelizelist = [] ...: for line in f.readlines(): ...: linesplit = line.strip().split("|") ...: tuple_data = tuple(linesplit) ...: if k == 1: ...: tuple_title = linesplit ...: else: ...: parallelizelist.append(tuple_data) ...: ...: k +=1# 方法1:sqlContext.createDataFrameIn [3]: rdd3 = sqlContext.createDataFrame(parallelizelist,tuple_title) In [4]: rdd3.write.jdbc("jdbc:mysql://xxxx/test","testallty ...: pe","overwrite",{"user":"","password":" ...: "})# 方法2:toDFIn [8]: rdd4 = sc.parallelize(parallelizelist) In [9]: rdd5 = rdd4.toDF(tuple_title) In [10]: rdd5.write.jdbc("jdbc:mysql://xxxx/test","testallt ...: ype","append",{"user":"","password":"" ...: })
Spark对Hive表操作
首先理解下什么是SparkContext, SQLContext 和HiveContext,原文可参考@pig2--让你真正理解什么是SparkContext, SQLContext 和HiveContext这位版主很厉害!这里简单总结下
SparkContext:用于连接Spark集群、创建RDD、累加器(accumlator)、广播变量(broadcast variables),所以说SparkContext为Spark程序的根,你只要知道它能让一个普通的列表编程rdd就行了,非常牛逼,就是传说中的sc!
SparkSQL:是spark的一个模块,是spark的一个模块,SparkSQL 用来处理结构化数据,所以SparkSQL你的data必须定义schema.在spark1.3.1,sparksql继承dataframes 和SQL 查询引擎
SQLContext:spark处理结构化数据的入口。允许创建DataFrame以及sql查询
HiveContext:spark sql执行引擎,集成hive数据
In [68]: from pyspark.sql import HiveContext,Row In [69]: hiveCtx = HiveContext(sc) In [70]: rows = hiveCtx.sql("SELECT * FROM test.table1 limit ...: 5") In [71]: firstRow = rows.first() [Stage 32:=====> [Stage 32:========> [Stage 32:==========> [Stage 32:=============> [Stage 32:================> [Stage 32:==================> [Stage 32:=====================> [Stage 32:======================> In [72]: print firstRow.business_id 257 In [73]: print firstRow.order_id 3057564118 In [74]: hiveowntest = HiveContext(sc) In [75]: rows2 = hiveowntest.sql("SELECT * FROM test.owntest") In [76]: rows2.show() +--------+---+ | name|age| +--------+---+ |shangsan| 20| | lisi| 22| | zhouwu| 21| +--------+---+# 保存入表,其实就是讲hive表读入RDD,然后再写入新的hive表中In [79]: rows2.saveAsTable("hive_test_spark")# 然后进入hive中进行操作,虽然有点错误,单还是可以执行查询动作hive> select * from hive_test_spark;OK hive_test_spark.name hive_test_spark.age SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. shangsan 20 lisi 22 zhouwu 21hive> select * from hive_test_spark where name="shangsan";OK hive_test_spark.name hive_test_spark.age shangsan 20 Time taken: 0.645 seconds, Fetched: 1 row(s)
作者:mrlevo520
链接:https://www.jianshu.com/p/a3bb01cc4622
转载请注明出处:http://blog.csdn.net/MrLevo520/article/details/76087612