Learning Spark——Spark连接Mysql、mapPartitions高效连接HBase

2024-04-22 05:32

本文主要是介绍Learning Spark——Spark连接Mysql、mapPartitions高效连接HBase,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

执行Spark任务免不了从多个数据源拿数据,除了从HDFS获取数据以外,我们还经常从Mysql和HBase中拿数据,今天讲一下如何使用Spark查询Mysql和HBase

1. Spark查询Mysql

首先,Spark连接Mysql当然需要有Mysql的驱动包,你可以在启动时加上如下命令:

bin/spark-shell --driver-class-path /home/hadoop/jars/mysql-connector-java-5.1.34.jar --jars /home/hadoop/jars/mysql-connector-java-5.1.34.jar

还有一种更方便的方法就是直接将这个jar包放到Spark放jar包的目录下面,我的目录是/data/install/spark-2.0.0-bin-hadoop2.7/jars,这样Spark就可以直接找到Mysql驱动包了

然后给出Spark读Mysql时的标准代码:

    val imeis = spark.read.format("jdbc").options(Map("url" -> DbUtil.IMEI_DB_URL,
//        "dbtable" -> "(SELECT id,imei,imeiid FROM t_imei_all) a","dbtable" -> DbUtil.IMEI_ALL_TABLE,"user" -> DbUtil.IMEI_DB_USERNAME,"password" -> DbUtil.IMEI_DB_PASSWORD,"driver" -> "com.mysql.jdbc.Driver",
//        "fetchSize" -> "1000","partitionColumn" -> "id","lowerBound" -> "1","upperBound" -> "15509195","numPartitions" -> "20")).load()

解释一下这段代码:

1、其中spark就是SparkSession

2、如果是读操作,就是spark.read,如果是写操作,就是spark.write

3、options里面就是我们需要查询的一些具体信息,有关配置如下:

配置项含义
url数据库连接地址,如:jdbc:mysql://localhost:3306/IMEI?useUnicode=true&characterEncoding=UTF8
dbtable要查询的表,这里有两种书写方式,一种可以直接写一个表名,如:t_test;另一种是写一条查询语句,但是注意要给一个别名,如:(SELECT id,imei,imeiid FROM t_imei_all) a。建议使用第一种,第二种查询会很慢
driver数据库驱动,如Mysql的是:com.mysql.jdbc.Driver
user数据库用户名
password数据库密码
partitionColumn, lowerBound, upperBound, numPartitions这几个参数用来指定用哪个列来分区,当我们查询的量很大时,例如超过千万的数据量,如果Spark不分区查询的话很快就会报OOM异常了。而且这几个参数只要指定其中一个,其他的就也要指定,partitionColumn是要分区的列,必须是整数类型;lowerBound和upperBound是分区的上下限;numPartitions是分区数
fetchsize用于读操作,每次读取多少条记录
batchsize用于写操作,每次写入多少条记录
isolationLevel用于写操作,数据库的隔离级别
truncate用于写操作,当Spark要执行覆盖表操作时,即启用了SaveMode.Overwrite,使用truncate比使用drop或者recreate操作更高效,默认是false
createTableOptions用于写操作,可以指定建表的语句,如: CREATE TABLE t (name string) ENGINE=InnoDB

4、最后的结果是一个DataFrame,可以很方便地使用SparkSql继续其他操作

2. 如何在RDD中高效连接HBase

连接HBase直接用HBase的API就好了,我们这里重点讲的是在RDD中连接HBase,大家都知道Spark处理的都是很大的数据量,而RDD连接HBase的时候势必会产生很多与HBase的连接,这样很快就会用光连接数,这里我们使用一个算子mapPartitions来解决这个问题

mapPartitions函数和map函数类似,只不过映射函数的参数由RDD中的每一个元素变成了RDD中每一个分区的迭代器,返回的结果也是每一个分区的迭代器。如果在映射的过程中需要频繁创建额外的对象,使用mapPartitions要比map高效的多

比如,将RDD中的所有数据通过JDBC连接写入数据库,如果使用map函数,可能要为每一个元素都创建一个connection,这样开销很大,如果使用mapPartitions,那么只需要针对每一个分区建立一个connection

下面上栗子:RDD中的内容是字符串,这段字符串是HBase主键rowKey的一部分,现在要根据这条字符串查出HBase中的一条信息。例如HBase的主键是aaabbb111222,RDD中存的内容是aaa,我们要查出HBase主键前缀是aaa的所有记录的主键、包名列表和时间

代码如下:

package com.trigl.spark.mainimport com.trigl.spark.util.HbaseUtil
import org.apache.hadoop.hbase.TableName
import org.apache.hadoop.hbase.client.{Result, Scan}
import org.apache.hadoop.hbase.util.Bytes
import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkConf, SparkContext}/*** 高效连接HBase示例* created by Trigl at 2017-05-20 15:34*/
object HBaseDemo {def main(args: Array[String]) {Logger.getLogger("org.apache.spark").setLevel(Level.WARN)System.setProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")val sparkConf = new SparkConf().setAppName("HBaseDemo")val sc = new SparkContext(sparkConf)val data = sc.textFile("/test/imei.txt").mapPartitions(getHBaseInfo)// 结果以 主键|包列表|时间 的格式存入HDFSdata.map(l => {val rowKey = l._2._1val pkgList = l._2._2val time = l._1rowKey + "|" + pkgList + "|" + time // 用"|"分隔}).repartition(1).saveAsTextFile("/test/fenxi/cpz")sc.stop()}/*** 从HBase查询** @param iter mapPartion算子的参数是Iterator* @return 返回的也是Iterator*/def getHBaseInfo(iter: Iterator[String]): Iterator[(String, (String, String))] = {var pkgList = List[(String, (String, String))]() // 结果格式为(日期,(主键,包名集合))// 建立连接查询表val conn = HbaseUtil.getConnection(HbaseUtil.TABLE_NAME_CPZ_APP)val table = conn.getTable(TableName.valueOf(HbaseUtil.TABLE_NAME_CPZ_APP))// 新建Scan用于指定查询内容val scan = new Scan()scan.setCaching(10000)scan.setCacheBlocks(false)// 要查询的列scan.addColumn(HbaseUtil.COLUMN_FAMILY.getBytes, "packagelist".getBytes)scan.addColumn(HbaseUtil.COLUMN_FAMILY.getBytes, "cdate".getBytes)while (iter.hasNext) {// 要查询的前缀val imei = iter.next()// HBase前缀查询scan.setRowPrefixFilter(imei.getBytes)// 查询结果val resultScanner = table.getScanner(scan)val it = resultScanner.iterator()if (it.hasNext) {val result: Result = it.next()// 主键val key = Bytes.toString(result.getRow)// 日期val cdate = Bytes.toString(result.getValue(HbaseUtil.COLUMN_FAMILY.getBytes, "cdate".getBytes))// 包列表val packagelist = Bytes.toString(result.getValue(HbaseUtil.COLUMN_FAMILY.getBytes, "packagelist".getBytes))// 添加到集合中pkgList.::=(cdate, (key, packagelist))}}// 关闭HBase连接table.close()conn.close()// 结果返回iteratorpkgList.iterator}
}

最后存的结果是:

8643960350683910864396035068383020170421134920XHt3IGTdqIKtV5Y|2627,com.sogou.activity.src,fc8dbdce14c111859fd0111b03e80cd7,0;2856,com.qihoo.appstore,aa90bca1ab548eadd44a0c1d8c34cbda,0|2017-04-21 13:49:18

上面的完整代码见我的github:

https://github.com/Trigl/SparkLearning/blob/master/src/main/scala/com/trigl/spark/main/JDBC2Mysql.scala

https://github.com/Trigl/SparkLearning/blob/master/src/main/scala/com/trigl/spark/main/HBaseDemo.scala

Refer:

http://spark.apache.org/docs/latest/sql-programming-guide.html#jdbc-to-other-databases

http://lxw1234.com/archives/2015/07/348.htm

http://blog.csdn.net/lsshlsw/article/details/48627737

这篇关于Learning Spark——Spark连接Mysql、mapPartitions高效连接HBase的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/924927

相关文章

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

MySQL 衍生表(Derived Tables)的使用

《MySQL衍生表(DerivedTables)的使用》本文主要介绍了MySQL衍生表(DerivedTables)的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学... 目录一、衍生表简介1.1 衍生表基本用法1.2 自定义列名1.3 衍生表的局限在SQL的查询语句select

MySQL 横向衍生表(Lateral Derived Tables)的实现

《MySQL横向衍生表(LateralDerivedTables)的实现》横向衍生表适用于在需要通过子查询获取中间结果集的场景,相对于普通衍生表,横向衍生表可以引用在其之前出现过的表名,本文就来... 目录一、横向衍生表用法示例1.1 用法示例1.2 使用建议前面我们介绍过mysql中的衍生表(From子句

六个案例搞懂mysql间隙锁

《六个案例搞懂mysql间隙锁》MySQL中的间隙是指索引中两个索引键之间的空间,间隙锁用于防止范围查询期间的幻读,本文主要介绍了六个案例搞懂mysql间隙锁,具有一定的参考价值,感兴趣的可以了解一下... 目录概念解释间隙锁详解间隙锁触发条件间隙锁加锁规则案例演示案例一:唯一索引等值锁定存在的数据案例二:

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA