Hadoop學習之路(一)理論基礎和邏輯思維
三個題目
第一題
問題描述
統計出當前這個一行一個IP的文件中,到底哪個IP出現的次數最多
解決思路
//必須要能讀取這個內容 BufferedReader br = new BuffedReader(new FileInputStream(new File("c:/big.txt"))); // 每次讀取一行 String line = null; while( (line=br.readLine()) != null){ // 處理這讀取到的一行內容的代碼 }//最簡單的一種思路: 初始化一個hashmap //hashmap中存儲的鍵值對的 key : IP value : 次數 int count = 0; // 就是用來進行存儲當前出現次數最多的那個IP的次數 String maxip = null; Set<String> ips = hashmap.keySet(); for(String ip : ips){ int ipcount = hashmap.get(ip) if(ipcount > count){ count = ipcount maxip = ip; } } System.out.println(maxip + " : " +count);
問題難點
1、當讀取的文件的大小超過內存的大小時,以上的解決方案是不可行的。
2、假如說你的內存足夠大,能裝下這個文件中的所有ip,整個任務的執行效率會非常低,消耗的時間會非常的長。
1GB -- 5分
1TB --- 1024 * 5 分
3、最終整個任務就使用一臺機器,那麽最終整個任務執行完成所消耗的時間是和數據的大小成正比。提升服務器的執行性能來提高數據的處理速度。
當前這一臺機器的執行性能: 5分鐘/GB
提升服務器的執行性能: CPU :i3 ---> i7 1分鐘/GB
在最開始的服務器領域:提升服務器對外提供服務的效率手段就是縱向提升服務器性能。理想是豐滿的,現實是骨感的,但是服務器性能提升有瓶頸。
摩爾定律:每隔18-24個月,服務器的性能提升一倍。
如果說數據的增長是每隔18-24個月就增長一倍,工作量增加了一倍。工作效率也增加了一倍,那麽最終完成同一個任務所花費的時間是一樣的。
但是數據的增長速度是遠遠超過服務器性能的提升。在數據不斷增長的情況下,單位時間內,服務器所需要處理的數據量是越來越大。
假如:
服務器的性能提升 速率 和 數據的增長速率一樣: 在18-24個月
10GB --- 性能: 1分鐘/GB --- 10分鐘
20GB --- 性能: 1分鐘/2GB --- 10分鐘
假如:
服務器的性能提升 速率 和 數據的增長速率不一樣: 在18-24個月
10GB --- 性能: 1分鐘/GB --- 10分鐘
100GB --- 性能: 1分鐘/2GB --- 50分鐘
最終的結論: 靠 縱向提升服務器性能的手段 在理論上有 瓶頸的。
最終解決方案:縱向不可取,所以采取橫向擴展。
所謂的橫向擴展:就是增加服務器的數量。
一個龐大的復雜任務就應該 平均分配給所有的服務器做處理
10GB 一臺服務器 10分鐘
100GB 一臺服務器 100分鐘
100GB 10臺服務器 10分鐘
10000GB 1000臺 10分鐘
在理論上 有上限麽??沒有
兩種情況下:
1、在數據量比較小的情況下,單臺服務器就可以再用戶可接受的時限範圍內完成任務。
2、當數據量變大時,如果用戶也想在可接受的時限範圍內完成任務,那麽可行的方案就是進行服務器的橫向擴展。
核心思想: 大事化小 分而治之
終極解決方案:
1、先把文件切碎成很多的小文件。
2、每一個服務器節點去處理一個小文件。
3、再把所有服務器的處理結果匯總到一起。
4、再把所有的數據合並到一起求出出現次數最多的那個ip。
只要是通過網絡傳輸數據,就一定存在丟失數據的可能。
第二題
問題描述
在兩個龐大文件中,文件也都是存儲的URL地址(每行一個),比如文件名叫做file1和file2, 找出這兩個文件中的交集(相同的URL)?
以上問題等同於SQL:select url from file1 a join file2 b on a.url = b.url
問題分析
概念:出現在在file1中的元素也出現在file2中。這些元素的集合就是交集
需求:求2個文件的交集
文件中的元素:URL
解決方案
1.當2個文件都比較小的時候
步驟:
1. 編寫一個程序可以去讀文件的內容,把文件中的所有元素都放置在一個set1中
編寫一個流處理取讀取文件內容,逐行讀取,每次讀取到的一行放入set1中
2. 運行相同的程序處理另外一個文件的內容,把文件中的所有元素都放置在一個set2中
3. 先遍歷一個集合,每次遍歷出來的元素都去另外一個集合中判斷存在不存在。如果存在,就是共同元素,這個共同元素就存儲在某個集合中resultSet;如果不存在,就不是共同元素。
4. 結果集:集合resultSet
2.當2個文件都比較大的時候
第一種思路:采取跟第一個題目一樣的大事化小的策略
第二種思路:改良第一種思路。避免第一種思路中的很多無效匹配 a1 * a2
必須做到合理的數據分區,數據分區的兩種最基本的思路:
1.先排序,然後分段==分區
2.hash散列 -- 求hash值,然後利用hash值求和分區個數的余數,如果余數相同,就證明這些元素在同一個分區中
改良了實現思路之後,可以讓原來應該執行16個小任務的大任務。只需要執行4個小任務即可。
終極解決方案:
1.先指定一個分區策略:hash散列
2.預估預估一下數據要被切分成多少個塊,一定要保證兩個文件切分出來的小文件個數成倍數
3.根據hash散列的策略,對兩份文件分別進行操作
4.根據原來指定的策略,尋找對應的兩個大文件中的對應小文件進行求交集操作
5.所有的結果,根本就不用再進行去重了。直接進行拼接即可。
學到的東西:
整個大數據生態系統中的很多技術軟件的底層處理數據的分區時,默認的策略都是hash散列。
第三題
問題描述
現在有一個非常龐大的URL庫(10000E),然後現在還有一個URL,(迅速)判斷這個URL是否在這個URL庫中?
問題分析
需求:判斷一個元素在不在某個集合中
解決方案
1、計數排序
1、初始化一個數組 數組的長度 就是 集合中元素的區間長度
2、遍歷集合,把每個元素放入數組中 尋找對應的下標位置,找出值,然後重新設置成+1的值
3、按序遍歷即可
array[0] = 1, array[1] = 2, array[2] = 0, array[3] = 3,
0 1 1 3 3 3
2、改進需求:
求出某個元素在不在這個數組(數據結構改良之前的集合)中
array[5] = count if count > 0
3、既然是判斷存在不存在。
所以結果其實就是一個狀態 : 要麽存在 要麽不存在
存在 : 1
不存在: 0
把int數組進化成 為位數組
優勢:把數組所要消耗的內存降低到原來的 1/32
改良了之後這種數據結構: BitMap
真正的結構: 一個位數組 + 一個hash函數
4、BitMap再次進行進化
解決的問題: BitMap 中非常容易出現 hash碰撞的問題
咱們可以結合使用多個hash函數來搞定
缺點: 存在一定的誤判率
1、如果這種數據結構(BloomFilter) 告訴你說你要驗證的那個元素不在這個 BloomFilter 中
那就表示 這個元素一定不在這個 BloomFilter 中
2、如果它告訴你這個元素存在, 它告訴你的這個存在的結果 有可能是假的
真正的組成:
一個位數組 + 一組hash函數
如果要做工程實現:不管是什麽變種的BloomFilter, 都一定要實現兩個方法:
1、第一個方法是往BloomFilter中存入一個元素
2、第二個方法是驗證一個元素是否在這個BloomFilter 中
誤判率的估計:
1、位數組的長度 m
2、總元素的個數 n
3、hash函數的個數 k
hash函數的個數 並不是越多越好
在誤判率最低的情況下。 這三個參數應該滿足的一個公式: k = 0.7 * m / n
Hadoop學習之路(一)理論基礎和邏輯思維