MongoDB之Hadoop驅動介紹
------------------------
1. 一些概念
Hadoop是一套Apache開源的分散式運算架構,其中包括了Distributed File System與分散式運算模型MapReduce,而MongoDB是一個面向文檔的分散式資料庫,它是NoSql的一種,而這裡所要介紹的就是一個MongoDB的Hadoop驅動,這裡就是把MongoDB作為MapReduce的輸入源,充分利用MapReduce的優勢來對MongoDB的資料進行處理與計算。
2. MongoDB的Hadoop驅動
目前這個版本的Hadoop驅動還是測試版本,還不能應用到實際的生產環境中去。
你可以從下面網址https://github.com/mongodb/mongo-hadoop下載到最新的驅動包,下面是它的一些依賴說明:
- 目前推薦用最新的Hadoop 0.20.203版本,或者是用Cloudera CHD3還做
- MongoDB的版本最好是用1.8+
- 還有是MongoDB的java驅動必須是2.5.3+
它的一些特點:
- 提供了一個Hadoop的Input和Output適配層,讀於對資料的讀入與寫出
- 提供了大部分參數的可配置化,這些參數都可有XML設定檔來進行配置,你可以在設定檔中定義要查詢的欄位,查詢條件,排序策略等
目前還不支援的功能:
- 目前還不支援多Sharding的來源資料讀取
- 還不支援資料的split操作
3. 程式碼分析
運行其examples中的WordCount.java代碼
// 事先在MongoDB的test資料庫的in表中加入的測試樣本,使用如下方法 /** * test.in db.in.insert( { x : "eliot was here" } ) db.in.insert( { x : * "eliot is here" } ) db.in.insert( { x : "who is here" } ) = */public class WordCount { private static final Log log = LogFactory.getLog( WordCount.class );// 這是一個Map操作 public static class TokenizerMapper extends Mapper<Object, BSONObject, Text, IntWritable> { private final static IntWritable one = new IntWritable( 1 ); private final Text word = new Text(); public void map( Object key , BSONObject value , Context context ) throws IOException, InterruptedException{ System.out.println( "key: " + key ); System.out.println( "value: " + value );// 對詞進行按空格切分 final StringTokenizer itr = new StringTokenizer( value.get( "x" ).toString() ); while ( itr.hasMoreTokens() ) { word.set( itr.nextToken() ); context.write( word, one ); // 這裡的key為詞,而value為1 } } }// 這是Reduce操作,用於計算詞出現的頻率 public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> { private final IntWritable result = new IntWritable(); public void reduce( Text key , Iterable<IntWritable> values , Context context ) throws IOException, InterruptedException{// 計算詞出現的頻率,把相同詞的value相加 int sum = 0; for ( final IntWritable val : values ) { sum += val.get(); } result.set( sum ); context.write( key, result ); // key為單個詞,value為這個詞所對應的詞頻率 } } public static void main( String[] args ) throws Exception{ final Configuration conf = new Configuration();// 定義MongoDB資料庫的輸入與輸出表名,這裡是調用本地的MongoDB,預設連接埠號碼為27017 MongoConfigUtil.setInputURI( conf, "mongodb://localhost/test.in" ); MongoConfigUtil.setOutputURI( conf, "mongodb://localhost/test.out" ); System.out.println( "Conf: " + conf ); final Job job = new Job( conf , "word count" ); job.setJarByClass( WordCount.class );// 定義Mapper,Reduce與Combiner類 job.setMapperClass( TokenizerMapper.class ); job.setCombinerClass( IntSumReducer.class ); job.setReducerClass( IntSumReducer.class );// 定義Mapper與Reduce的輸出key/value的類型 job.setOutputKeyClass( Text.class ); job.setOutputValueClass( IntWritable.class );// 定義InputFormat與OutputFormat的類型 job.setInputFormatClass( MongoInputFormat.class ); job.setOutputFormatClass( MongoOutputFormat.class ); System.exit( job.waitForCompletion( true ) ? 0 : 1 ); }}
4. 分塊機制的簡單介紹
這裡沒有實現對不同shard的split操作,也就是說,對於分布在不同shard上的資料,只會產生一個Map操作。
這裡本人提供了一個分區的思路,有興趣的可以討論一下。
我們知道,對於Collection分塊後,會產生一個Config資料庫,在這個資料庫下有一個叫做chunks的表,其中每個chunk記錄了start_row與end_row,而這些chunk可以分布在不同的shard上,我們可以通過分析這個Collection來得到每個shard上的chunk資訊,從而把每個shard上的chunk資訊組合成一個InputSplit,這就是這裡的MongoInputSplit,這樣的話,只要去修改MongoInputFormat這個類的getSplits這個方法,加入對chunks表的分析,得到shard的資訊,這樣就可以實現多split的Map操作,對於不同的Shard,每個Map都會調用本地的Mongos代理服務,這樣就實現了移動計算而不是移動資料的目的。
這隻是本人的一些想法,有興趣的朋友可以一起來討論一下。
下來我會發一個具體的實現。
5. 參考
* https://github.com/mongodb/mongo-hadoop
* http://www.mongodb.org/display/DOCS/Java+Language+Center