MongoDB作为一个适用于敏捷开发的数据库,MongoDB的数据模式可以随着应用程序的发展而灵活地更新。与此同时,它也为开发人员 提供了传统数据库的功能:二级索引,完整的查询系统以及严格一致性等等。 MongoDB能够使企业更加具有敏捷性和可扩展性,各种规模的企业都可以通过使用MongoDB来创建新的应用,提高与客户之间的工作效率,加快产品上市时间,以及降低企业成本。
MongoDB是专为可扩展性,高性能和高可用性而设计的数据库。它可以从单服务器部署扩展到大型、复杂的多数据中心架构。利用内存计算的优势,MongoDB能够提供高性能的数据读写操作。 MongoDB的本地复制和自动故障转移功能使您的应用程序具有企业级的可靠性和操作灵活性。
Hbase是什么
HBase是一种构建在HDFS之上的分布式、面向列的存储系统。在需要实时读写、随机访问超大规模数据集时,可以使用HBase。
尽管已经有许多数据存储和访问的策略和实现方法,但事实上大多数解决方案,特别是一些关系类型的,在构建时并没有考虑超大规模和分布式的特点。许多商家通过复制和分区的方法来扩充数据库使其突破单个节点的界限,但这些功能通常都是事后增加的,安装和维护都和复杂。同时,也会影响RDBMS的特定功能,例如联接、复杂的查询、触发器、视图和外键约束这些操作在大型的RDBMS上的代价相当高,甚至根本无法实现。
HBase从另一个角度处理伸缩性问题。它通过线性方式从下到上增加节点来进行扩展。HBase不是关系型数据库,也不支持SQL,但是它有自己的特长,这是RDBMS不能处理的,HBase巧妙地将大而稀疏的表放在商用的服务器集群上。
HBase 是Google Bigtable 的开源实现,与Google Bigtable 利用GFS作为其文件存储系统类似, HBase 利用Hadoop HDFS 作为其文件存储系统;Google 运行MapReduce 来处理Bigtable中的海量数据, HBase 同样利用Hadoop MapReduce来处理HBase中的海量数据;Google Bigtable 利用Chubby作为协同服务, HBase 利用Zookeeper作为对应。
HBase的特点
大:一个表可以有上亿行,上百万列。
面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。
稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可以有截然不同的列。
数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时的时间戳。
数据类型单一:HBase中的数据都是字符串,没有类型。
以上介绍mongodb和hbase,下面将介绍如何将mongodb的数据同步到Hbase。
需求分析:mongodb的数据同步到Hbase,即分为三步:
1.将数据从mongdb读出来
2.将读出来的数据进行操作或按照特定格式转化
3.存入hbase
下面用两种方式来实现
普通的纯JAVA代码
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.sql.*;
import java.util.*;
import java.util.Date;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import org.bson.types.ObjectId;
/**
* Created by grant on 2018/4/17.
* hbase地址:
zookeeper_address=bd1,bd2,bd3
clientPort=2181
zookeeper_parent=hbase
*/
public class Syn {
private static Configuration conf = null;
static {
conf = HBaseConfiguration.create();
conf.set("zookeeper.znode.parent", "/hbase");
conf.set("hbase.zookeeper.quorum","192.168.163.140,192.168.163.141,192.168.163.142");
conf.set("hbase.zookeeper.property.clientPort", "2181");
}
/**
* 获取Mongodb
* @param host
* @param port
* @param username
* @param dbname
* @param passwd
* @return
*/
public static MongoClient getMongodb(String host,int port,String username,String dbname,String passwd){
//通过认证获取mongoDB的连接
ServerAddress serverAddress = new ServerAddress(host,port);
List address = new ArrayList();
address.add(serverAddress);
//下面是使用权限验证来连接mongo的方式,本例中没有使用
//MongoCredential credential = MongoCredential.createScramSha1Credential(username,
// dbname, passwd.toCharArray());
//List credentials = new ArrayList();
//credentials.add(credential);
MongoClient mongoClient = new MongoClient(serverAddress);
return mongoClient;
}
//创建表
public static void createTable(String tableName,String columnFamilys) {
System.out.println("start create table");
try {
HBaseAdmin hBaseAdmin = new HBaseAdmin(conf);
// 如果存在要创建的表,那么先删除,再创建
if (hBaseAdmin.tableExists(tableName)) {
hBaseAdmin.disableTable(tableName);
hBaseAdmin.deleteTable(tableName);
System.out.println(tableName + " is exist,delete it ");
}
HTableDescriptor tableDescriptor = new HTableDescriptor(tableName);
tableDescriptor.addFamily(new HColumnDescriptor(columnFamilys.getBytes()));
hBaseAdmin.createTable(tableDescriptor);
} catch (MasterNotRunningException e) {
e.printStackTrace();
} catch (ZooKeeperConnectionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("end create table ......");
}
/**
*插入hbase
* @param tableName
* @param row
* @param columnFamily
* @param column
* @param value
* @throws Exception
*/
public static void insertData(String tableName, String row,
String columnFamily, String column, Object value) throws Exception {
System.out.println(tableName+"---"+row+"---"+columnFamily+"---"+column+"---"+value);
HTable hTable = new HTable(conf, tableName);
Put put = new Put(Bytes.toBytes(row));
byte[] b = null;
if (value instanceof Boolean){
b = Bytes.toBytes((Boolean) value);
}else if (value instanceof Integer){
b = Bytes.toBytes((Integer) value);
}else if (value instanceof Double){
b = Bytes.toBytes((Double) value);
}else if (value instanceof Float){
b = Bytes.toBytes((Float) value);
}else if (value instanceof Long){
b = Bytes.toBytes((Long) value);
}else if (value instanceof Short){
b = Bytes.toBytes((Short) value);
}else if (value instanceof ByteBuffer) {
b = Bytes.toBytes((ByteBuffer) value);
}else if (value instanceof BigDecimal){
b = Bytes.toBytes((BigDecimal) value);
}else if(value instanceof Date){
//b=Bytes.toBytes(value);
}else{
b=Bytes.toBytes((String) value);
}
put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), b);
//put.add(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));
try {
hTable.put(put);
}catch (IOException e){
System.err.println(e.getClass().getName()+ ":" +e.getMessage());
}
}
public static void main(String[] args) throws Exception {
String MongoDatabaseName="admin";
String mongoTableName = "test0";
String hbaseTableName = "test000";
String columnFamilys = "info";
try {
MongoClient mongoClient=Syn.getMongodb("192.168.1.2",20000,"admin","admin","123");
MongoDatabase mongoDatabase = mongoClient.getDatabase(MongoDatabaseName);
System.out.println("MongoDB Connection Successfully");
MongoCollection collection = mongoDatabase.getCollection(mongoTableName);
FindIterable findIterable = collection.find();
MongoCursor mongoCursor = findIterable.iterator();
System.out.println("共"+collection.count());
//循环添加row
int count = 0;
Syn.createTable(hbaseTableName,columnFamilys);
while(mongoCursor.hasNext()){
count+=1;
Document document = (Document)mongoCursor.next();
Set set = document.keySet();
Iterator it = set.iterator();
while(it.hasNext()){
Object o=it.next();
// System.out.println(it.next().getClass().getTypeName());
if(((String)o).equals("_id")){
continue;
}
Syn.insertData(hbaseTableName, document.getObjectId("_id").toString(), columnFamilys,o.toString(), document.get(o));
}
System.out.println("Insert success the "+count+" data");
}
System.out.println("Finished");
} catch (Exception e) {
throw e;
System.err.println(e.getClass().getName()+ ":" +e.getMessage());
}
}
}
这种方式是使用两个循环来读取mongo的字段名称,并按照格式插入Hbase当中,纯JAVA方式,性能不佳。
下面是使用Spark读取并存入Hbase,使用spark-mongo-connecter包
import com.mongodb.spark.MongoSpark
import org.apache.commons.lang3.StringUtils
import org.apache.hadoop.hbase.{HBaseConfiguration, HColumnDescriptor, HTableDescriptor}
import org.apache.hadoop.hbase.client.{HBaseAdmin, Put}
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapred.TableOutputFormat
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ColumnFamilySchema
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.mapred.JobConf
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* Created by grant on 2018/4/19.
*/
object MongoSaveHbase {
def main(args: Array[String]): Unit = {
val spark=SparkSession.builder().appName("MongoSaveHbase").master("local[2]").getOrCreate()
val columnFamily="property"
val hbase_zq="192.168.1.1,192.168.1.2,192.168.1.3"
val hbase_zqPort="2181"
val hBase_zParent="/hbase"
val conf = HBaseConfiguration.create
// val inputUri="mongodb://xxx:xxx@localhost:27017/test.test"
// val mongoDb="test"
val inputUri="mongodb://192.168.1.1:27017/bigdata"
val mongoDb="bigdata"
//val collection="ddd978437b7548b5bcd74e1b63b56c3d"
val collection="dbc6b49ff03946cdab8eef8d6d164b1c"
def getMongoDf(inputUri:String,mongoDb:String,collection:String):DataFrame={
val mongoDf=spark.read.format("com.mongodb.spark.sql").options(
Map("spark.mongodb.input.uri"->inputUri,
"spark.mongodb.input.database"->mongoDb,
"spark.mongodb.input.collection"->collection
)
).load()
mongoDf
}
val mongoDf=getMongoDf(inputUri,mongoDb,collection).limit(10000)
println(mongoDf.count())
mongoDf.show(false)
/**
*获得hbaseConf
* @param hbaseTableName
* @param columnFamily
* @param hbase_zq
* @param hbase_zqPort
* @param hbase_zParent
* @return
*/
def getHbaseConf(hbaseTableName:String,columnFamily:String,hbase_zq:String,hbase_zqPort:String,hbase_zParent:String)={
conf.set("hbase.zookeeper.quorum", hbase_zq)
conf.set("hbase.zookeeper.property.clientPort", hbase_zqPort)
conf.set("zookeeper.znode.parent",hBase_zParent)
conf.set(TableOutputFormat.OUTPUT_TABLE, hbaseTableName)
//初始化jobconf,TableOutputFormat必须是org.apache.hadoop.hbase.mapred包下的!
val jobConf = new JobConf(conf)
jobConf.setOutputFormat(classOf[TableOutputFormat])
jobConf
}
val hbaseConf=getHbaseConf(collection,columnFamily,hbase_zq,hbase_zqPort,hBase_zParent)
/**
* * 判断表是否存在创建新表
*
* @param columnFamily
* @param hbaseTableName
*/
def createTable(hbaseTableName:String,columnFamily:String)={
val admin = new HBaseAdmin(conf)
if (!admin.isTableAvailable(hbaseTableName)) {
// 创建Hbase表模式
val tableDescriptor = new HTableDescriptor(hbaseTableName)
tableDescriptor.addFamily(new HColumnDescriptor(columnFamily.getBytes()))
admin.createTable(tableDescriptor)
}
}
createTable(collection,columnFamily)
/**
* 根据格式创建表,写入hbase
*/
def writeHbase(mongoDf:DataFrame,columnFamily:String)={
val fields=mongoDf.columns.toList //写入dataframe
mongoDf.rdd.map( x => {
val _id=StringUtils.strip(x(0).toString,"[]")
println(_id)
val put = new Put(Bytes.toBytes(_id))
var id=1
for(field <- fields) {
println(field)
if(!field.equals("_id")){
put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(field), Bytes.toBytes(x.get(id).toString))
id=id+1
}
}
(new ImmutableBytesWritable, put)
}).saveAsHadoopDataset(hbaseConf)
}
writeHbase(mongoDf,columnFamily)
}
}
这种方式使用spark框架,代码量较少,读取数据量大的时候表现的性能较佳,需求可以自己添加,是不错的选择。