Phát triển Java 2.0: Phân tích dữ liệu lớn bằng MapReduce của Hadoop

Làm sao các núi dữ liệu lại trở thành mỏ vàng thông tin

Apache Hadoop là công cụ hàng đầu thường dùng để phân tích dữ liệu phân tán và giống như hầu hết các công nghệ 2.0 của Java™, nó được xây dựng để mở rộng quy mô. Hãy bắt đầu với mô hình lập trình MapReduce của Hadoop và tìm hiểu cách sử dụng nó để phân tích dữ liệu cho các nhu cầu thông tin của cả doanh nghiệp lớn và nhỏ.

Andrew Glover, Tác giả và Nhà phát triển, Beacon50

 Andrew GloverAndrew Glover là một nhà phát triển, tác giả, diễn giả và là người điều hành doanh nghiệp với niềm đam mê phát triển các hệ thống điều khiển hành vi, tích hợp liên tục và phát triển phần mềm dựa trên quy trình Agile. Ông đã sáng lập ra nền tảng phát triển điều khiển hành vi easyb và là đồng tác giả của 3 cuốn sách: Continuous Integration (Tích hợp liên tục), Groovy in Action (Thực hành ngôn ngữ Groovy), và Java Testing Patterns (Các mẫu kiểm thử Java). Bạn có thể theo dõi và tìm hiểu về ông qua trang blogTwitter.



14 01 2013

Khi Google tung ra tính năng tìm kiếm hình ảnh vào năm 2001, Google đã có 250 triệu hình ảnh được lập chỉ mục. Gần một thập kỷ sau đó, gã khổng lồ tìm kiếm này đã lập chỉ mục hơn 10 tỷ hình ảnh. Ba mươi lăm giờ nội dung được tải lên YouTube mỗi phút. Tính trung bình, Twitter được cho là xử lý 55 triệu mẫu tin ngắn mỗi ngày. Đầu năm nay, 600 triệu truy cập hàng ngày đã đăng nhập vào tính năng tìm kiếm của Twitter. Đó là những gì chúng tôi muốn đưa ra khi nói về dữ liệu lớn.

Về loạt bài này

Viễn cảnh phát triển Java đã thay đổi hoàn toàn kể từ khi công nghệ Java xuất hiện lần đầu tiên. Nhờ hoàn thiện các framework mở và các cơ sở hạ tầng triển khai cho thuê đáng tin cậy, bây giờ chúng ta có thể tích hợp, thử nghiệm, chạy và bảo trì các ứng dụng Java một cách nhanh chóng và không tốn kém. Trong loạt bài này, Andrew Glover khám phá một loạt các công nghệ và các công cụ làm cơ sở phát triển Java kiểu mới này thành hiện thực.

Đã có lúc dữ liệu trên một quy mô lớn như vậy chỉ được sử dụng trong các tập đoàn lớn, các trường đại học và chính phủ — những nơi có khả năng mua siêu máy tính đắt tiền và thuê nhân viên để duy trì. Ngày nay, với việc hạ thấp chi phí lưu trữ và sức mạnh xử lý trở nên thông dụng, các công ty nhỏ hơn và một số cá nhân, đã bắt đầu lưu trữ và khai phá dữ liệu tương tự như vậy, thúc đẩy một làn sóng về đổi mới ứng dụng.

Một trong những công nghệ tạo khả năng của cuộc cách mạng dữ liệu lớn là MapReduce, một mô hình lập trình và công cụ được Google phát triển để xử lý các tập hợp dữ liệu phân tán quy mô lớn. Trong bài này, tôi giới thiệu công cụ MapReduce nguồn mở của Apache, đó là Hadoop, mà một số người đã gọi là ứng dụng sát thủ của điện toán đám mây.

Giới thiệu Hadoop

Về cơ bản, Hadoop framework (khung công tác Hadoop) của Apache là một cơ chế dùng để phân tích các tập dữ liệu rất lớn mà không cần đặt trong một kho dữ liệu. Hadoop trừu tượng hóa công cụ phân tích dữ liệu to lớn của MapReduce, làm nó trở nên dễ tiếp cận hơn với các nhà phát triển. Hadoop có khả năng mở rộng vô số các node và có thể xử lý tất cả hoạt động và phối hợp liên quan đến việc phân loại dữ liệu.

Hadoop với các tính năng và tùy chỉnh phong phú đã trở thành một framework có ích và mạnh mẽ đến không ngờ. Yahoo! và vô số các tổ chức khác đã tìm thấy ở nó một cơ chế hiệu quả để phân tích các núi dữ liệu bit, byte. Hadoop cũng khá dễ thực hiện trên một node đơn; tất cả những gì bạn cần là dữ liệu nào đó để phân tích và biết rõ về mã Java, bao gồm một phương tiện lập trình tổng quát (generics). Hadoop cũng làm việc với Ruby, Python và C++.

Thông tin thêm về MapReduce

Nếu bạn là độc giả của loạt bài này, thì có thể bạn đã tìm hiểu cách hoạt động của MapReduce. Trong bài "Tìm hiểu dịch vụ REST với CouchDB và RESTClient của Groovy" (REST up with CouchDB and Groovy's RESTClient), tôi đã trình bày cách CouchDB sử dụng MapReduce cho các khung nhìn, sau đó tôi lại sử dụng nó trong bài "MongoDB: Một kho dữ liệu NoSQL với các di chuyển RDBMS (tất cả đều đúng)) (MongoDB: A NoSQL datastore with (all the right) RDBMS moves)," như là cơ chế dùng để xử lý các tài liệu MongoDB.

Là một framework dựa trên các khái niệm dùng để xử lý các tập hợp dữ liệu khổng lồ, MapReduce được tối ưu hóa rất cao để giải quyết vấn đề phân tán bằng cách sử dụng một số lượng lớn các máy tính. Framework này bao gồm hai hàm, như tên gọi của nó cho thấy. Hàm map (ánh xạ) được thiết kế để nhận một đầu vào dữ liệu lớn và phân chia nó thành từng mảnh nhỏ hơn, rồi nó chuyển chúng đến các quá trình khác có thể làm điều gì đó với nó. Hàm reduce (giảm) phân loại các câu trả lời riêng lẻ do hàm map thu thập và đưa chúng tới một đầu ra cuối cùng.

Trong Hadoop, bạn định nghĩa các việc thực hiện mapreduce bằng cách mở rộng các lớp cơ sở riêng của Hadoop. Các việc thực hiện này được gắn chặt với nhau bằng một cấu hình quy định chúng, cùng với các định dạng đầu vào và đầu ra. Hadoop rất thích hợp cho việc xử lý các tệp rất lớn chứa dữ liệu có cấu trúc. Một khía cạnh rất tiện dụng của Hadoop là ở chỗ nó xử lý phân tích cú pháp thô của một tệp đầu vào, do đó bạn có thể xử lý một dòng tại một thời điểm. Vì thế việc định nghĩa một hàm map thực ra chỉ là vấn đề xác định những gì bạn cần nắm lấy từ một dòng văn bản gửi đến.


Dữ liệu khắp mọi nơi!

Phát triển kỹ năng về chủ đề này

Nội dung này nằm trong "đường dẫn đến kiến thức (knowledge path)" để bổ sung các kỹ năng của bạn. Xem Using NoSQL and analyzing big data

Chính phủ Hoa Kỳ tạo ra rất nhiều dữ liệu, phần lớn trong số đó quan trọng với các công dân trung lưu. Các cơ quan chính phủ khác tự do phân phối dữ liệu liên quan đến sức khỏe nền kinh tế Mỹ và việc thay đổi kết quả thống kê dân số xã hội. Cơ quan Khảo sát Địa chất Hoa Kỳ (USGS - The U.S. Geological Survey) công bố các dữ liệu động đất quốc tế.

Nhiều trận động đất nhỏ xảy ra hàng ngày tại nhiều khu vực trên toàn thế giới. Phần lớn trong số đó xảy ra ở sâu bên trong lớp vỏ trái đất, do đó không ai cảm thấy chúng, nhưng các trạm thu vẫn ghi lại chúng. USGS công bố hàng tuần dữ liệu động đất của mình dưới dạng của một tệp CSV (hoặc các giá trị được phân cách bằng dấu phẩy).

Một tệp trung bình hàng tuần không phải là quá lớn — chỉ khoảng 100KB hay tương đương như vậy. Mặc dù vậy, nó sẽ dùng làm cơ sở cho việc tìm hiểu về Hadoop. Tuy nhiên, hãy nhớ rằng Hadoop có khả năng xử lý các tập hợp dữ liệu lớn hơn nhiều.

Theo dõi các trận động đất

Tệp CSV mà mới đây tôi đã tải về từ trang web của USGS có khoảng 920 dòng, như trong Liệt kê 1:

Liệt kê 1. Tổng số dòng của một tệp dữ liệu động đất của USGS
$> wc -l eqs7day-M1.txt 
  920 eqs7day-M1.txt

Các nội dung của tệp CVS trông đại thể giống như những gì bạn thấy trong Liệt kê 2 (đây chính là hai dòng đầu tiên):

Liệt kê 2. Hai dòng đầu tiên của tệp CVS
$> head -n 2 eqs7day-M1.txt 
Src,Eqid,Version,Datetime,Lat,Lon,Magnitude,Depth,NST,Region
ci,14896484,2,"Sunday, December 12, 2010 23:23:20 UTC",33.3040,-116.4130,1.0,11.70,22,
  "Southern California"

Đó là những gì tôi sẽ gọi là một tệp giàu thông tin (information rich), đặc biệt là khi bạn biết rằng tệp này có tổng số 920 dòng. Tuy nhiên, tôi chỉ muốn biết có bao nhiêu trận động đất xảy ra mỗi ngày trong tuần được tệp này báo cáo. Sau đó, tôi muốn biết khu vực bình thường nào đã có nhiều trận động đất nhất trong vòng bảy ngày đó.

Suy nghĩ đầu tiên của tôi là tôi có thể sử dụng các lệnh grep đơn giản để tìm kiếm số lượng các trận động đất mỗi ngày. Hãy xem tệp này, tôi thấy rằng dữ liệu của nó bắt đầu từ ngày12 tháng 12 (December 12). Vì vậy, tôi thực hiện một lệnh grep -c của chuỗi đó, với kết quả được hiển thị trong Liệt kê 3:

Liệt kê 3. Có bao nhiêu trận động đất vào ngày 12 tháng 12?
$> grep -c 'December 12' eqs7day-M1.txt 
98

Cài đặt Hadoop

Nếu bạn vẫn chưa cài đặt Hadoop thì hãy làm điều đó ngay. Trước tiên, tải về tệp mã nhị phân mới nhất, giải nén nó rồi thiết lập đường dẫn thư mục bin của Hadoop vào biến môi trường path trên máy tính của bạn. Việc này cho phép bạn thực hiện lệnh hadoop trực tiếp. Việc sử dụng Hadoop đòi hỏi bạn phải thực hiện lệnh hadoop của nó chứ không phải bằng cách gọi lệnh java. Bạn có thể chuyển các tùy chọn vào lệnh hadoop, chẳng hạn như có thể tìm thấy các tệp nhị phân Java của bạn ở đâu (ví dụ, các tệp này đại diện cho các việc thực hiện mapreduce của bạn). Trong trường hợp của tôi, tôi tạo ra một tệp jar và nói cho Hadoop biết những công việc nào mà tôi muốn chạy bên trong tệp jar của mình. Tôi cũng thêm vào bất kỳ tệp nhị phân bổ sung nào cần thiết để chạy ứng dụng của mình với đường dẫn lớp (classpath) của Hadoop.

Bây giờ tôi biết rằng vào ngày 12 tháng 12 đã có 98 mục nhập hay 98 trận động đất được ghi nhận. Tôi chỉ có thể xuống dòng và thực hiện một lệnh grep với ngày 10, 11 tháng 12 và v.v. Nhưng điều đó thật là chán. Tệ hơn nữa là để làm được, tôi cần phải biết trong tệp đó có những ngày nào. Tôi thì lại không muốn quan tâm về điều đó và trong một số trường hợp, có thể tôi không có quyền truy cập vào thông tin. Thực sự tôi chỉ muốn biết những con số cho từng ngày cụ thể trong bất kỳ khoảng thời gian bảy ngày nào và tôi có thể nhận được thông tin đó một cách dễ dàng bằng Hadoop.

Hadoop chỉ cần một vài mẩu thông tin để trả lời câu hỏi thứ nhất và thứ hai của tôi: đó là, cần xử lý những đầu vào nào và làm thế nào để xử lý mapreduce. Ngoài ra tôi cũng sẽ phải cung cấp một công việc gắn chặt mọi thứ lại với nhau. Nhưng trước khi tôi bắt đầu làm về mã đó, tôi sẽ mất một vài phút để chắc chắn rằng mọi thứ đều hợp lệ với dữ liệu CSV của tôi.


Phân tích cú pháp dữ liệu bằng opencsv

Ngoài dòng đầu tiên chính là tiêu đề của tệp dữ liệu CSV về các trận động đất, thì mỗi dòng là một loạt các giá trị dữ liệu được phân cách bằng dấu phẩy. Đầu tiên tôi quan tâm đến ba mẩu thông tin: ngày, tọa độ và độ lớn của mỗi trận động đất. Để có được những mẩu tin này, tôi sẽ sử dụng một thư viện mã nguồn mở tiện lợi tên là opencsv, giúp phân tích cú pháp các tệp CSV.

Trong lần kiểm tra đầu tiên, tôi sẽ bắt đầu bằng cách viết một bài kiểm tra nhanh JUnit để xác minh rằng tôi có thể nhận được thông tin mà tôi cần từ một dòng ví dụ mẫu thu được của tệp CSV, được hiển thị trong Liệt kê 4:

Liệt kê 4. Phân tích cú pháp một dòng CSV
public class CSVProcessingTest {

 private final String LINE = "ci,14897012,2,\"Monday, December 13, 2010 " +
            "14:10:32 UTC\",33.0290,-115." +
            "5388,1.9,15.70,41,\"Southern California\"";

 @Test
 public void testReadingOneLine() throws Exception {
  String[] lines = new CSVParser().parseLine(LINE);

  assertEquals("should be Monday, December 13, 2010 14:10:32 UTC",
    "Monday, December 13, 2010 14:10:32 UTC", lines[3]);

  assertEquals("should be Southern California",
    "Southern California", lines[9]);

  assertEquals("should be 1.9", "1.9", lines[6]);
 }
}

Như bạn có thể thấy trong Liệt kê 4, opencsv thực hiện công việc với các giá trị được phân cách bằng dấu phẩy khá dễ dàng. Trình phân tích cú pháp chỉ trả về một mảng của các String (Chuỗi ký tự), do đó, có thể nhận được các giá trị vị trí (chỉ cần nhớ lại rằng việc truy cập vào mảng và collection của Java dựa vào số 0 (zero-based)).

Chuyển đổi định dạng ngày

Khi làm việc với MapReduce, công việc của hàm map là chọn một vài giá trị để trả lời, cùng với khóa nào đó. Có nghĩa là, trước tiên hàm map làm việc và trả về hai phần tử: một khóa và một giá trị. Hãy quay lại các yêu cầu trước đây của tôi, trước hết tôi muốn tìm ra có bao nhiêu trận động đất xảy ra mỗi ngày. Theo đó, khi tôi phân tích tệp động đất, tôi sẽ phát ra hai giá trị: khóa của tôi sẽ là ngày và giá trị sẽ là một bộ đếm. Sau đó hàm reduce của tôi sẽ tính tổng các bộ đếm (là các số nguyên), vì thế cung cấp cho tôi số lần động đất trong một ngày.

Vì tôi đang quan tâm đến khoảng thời gian là 24-giờ, nên tôi sẽ phải loại bỏ khía cạnh thời gian của ngày trong mỗi tệp. Trong Liệt kê 5, tôi viết một bài kiểm tra nhanh để xác nhận hợp lệ rằng tôi sẽ chuyển đổi định dạng ngày tháng cụ thể trong một tệp gửi đến thành một ngày có khoảng thời gian 24-giờ tổng quát hơn như thế nào:

Liệt kê 5. Các chuyển đổi định dạng ngày
@Test
public void testParsingDate() throws Exception {
 String datest = "Monday, December 13, 2010 14:10:32 UTC";
 SimpleDateFormat formatter = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy HH:mm:ss Z");
 Date dt = formatter.parse(datest);

 formatter.applyPattern("dd-MM-yyyy");
 String dtstr = formatter.format(dt);
 assertEquals("should be 13-12-2010", "13-12-2010", dtstr);
}

Trong Liệt kê 5, tôi đã sử dụng đối tượng Java SimpleDateFormat để định dạng một String ngày theo định dạng tệp CSV là thứ Hai, ngày 13 tháng 12, 2010 14:10:32 UTC thành 13-12-2010 tổng quát hơn.


Các hàm map và reduce của Hadoop

Bây giờ tôi đã trả lời tôi sẽ xử lý tệp CSV và định dạng ngày của nó như thế nào, tôi đã sẵn sàng để bắt đầu thực hiện các hàm mapreduce của mình bằng Hadoop. Quá trình này đòi hỏi có sự hiểu biết về một phương tiện lập trình tổng quát của Java, bởi vì Hadoop thích an toàn kiểu rõ ràng.

Khi định nghĩa một việc thực thi map với Hadoop, tôi chỉ đơn giản mở rộng lớp Mapper của Hadoop. Sau đó tôi có thể sử dụng generic để xác định kiểu rõ ràng cho cả hai khóa và giá trị gửi đi. Mệnh đề kiểu cũng phác họa khóa và giá trị gửi đến, mà trong trường hợp đọc một tệp tương ứng là tổng số byte và dòng văn bản.

Lớp EarthQuakesPerDateMapper mở rộng đối tượng Mapper của Hadoop. Nó phác họa rõ ràng khóa đầu ra của nó như là một đối tượng Text và giá trị của nó như là một IntWritable, là một lớp đặc trưng của Hadoop mà về bản chất là một số nguyên. Cũng lưu ý rằng hai kiểu đầu tiên trong mệnh đề lớp là LongWritableText, tương ứng là tổng số byte và dòng văn bản.

Do mệnh đề kiểu trong định nghĩa lớp, nên các kiểu tham số của tôi nhập vào phương thức map được đặt cùng với các kết quả đầu ra của phương thức này bên trong mệnh đề context.write. Nếu tôi cố gắng quy định một cái gì đó khác, hoặc là tôi sẽ nhận được một vấn đề về trình biên dịch hoặc Hadoop sẽ báo lỗi với một thông báo mô tả sự không trùng khớp về kiểu.

Liệt kê 6. Thực hiện ánh xạ (mapping)
public class EarthQuakesPerDateMapper extends Mapper<LongWritable, 
  Text, Text, IntWritable> {
 @Override
 protected void map(LongWritable key, Text value, Context context) throws IOException,
   InterruptedException {

  if (key.get() > 0) {
   try {
     CSVParser parser = new CSVParser();
     String[] lines = parser.parseLine(value.toString());

     SimpleDateFormat formatter = 
       new SimpleDateFormat("EEEEE, MMMMM dd, yyyy HH:mm:ss Z");
     Date dt = formatter.parse(lines[3]);
     formatter.applyPattern("dd-MM-yyyy");

     String dtstr = formatter.format(dt);
     context.write(new Text(dtstr), new IntWritable(1));
   } catch (ParseException e) {}
  }
 }
}

Việc thực hiện map của tôi trong Liệt kê 6 rất đơn giản: về cơ bản Hadoop gọi lớp này cho mỗi dòng văn bản mà nó tìm thấy trong một tệp đầu vào. Để tránh cố gắng xử lý tiêu đề của CSV, trước tiên tôi kiểm tra để xem liệu tổng số byte (đối tượng key ) có khác 0 hay không. Sau đó, tôi làm những gì bạn đã thấy trong Liệt kê 4 và 5: tôi lấy ngày gửi đến, chuyển đổi nó rồi đặt nó làm khóa gửi đi. Tôi cũng cung cấp một số đếm: 1. Có nghĩa là, tôi đã mã hoá một bộ đếm cho mỗi ngày và khi gọi việc thực hiện reduce nó sẽ nhận được một khóa và một tập hợp các giá trị. Trong trường hợp này, các khóa sẽ là ngày và giá trị của chúng, như thể hiện trong Liệt kê 7:

Liệt kê 7. Cái nhìn logic của một đầu ra map và các đầu vào reduce
"13-12-2010":[1,1,1,1,1,1,1,1]
"14-12-2010":[1,1,1,1,1,1]
"15-12-2010":[1,1,1,1,1,1,1,1,1]

Lưu ý rằng dòng context.write(new Text(dtstr), new IntWritable(1)) (trong Liệt kê 6) đã tạo ra một tập hợp logic hiển thị trong Liệt kê 7. Như bạn đã biết, context là một cấu trúc dữ liệu của Hadoop chứa các mẩu thông tin khác nhau. context này được chuyển đi cùng với việc thực hiện reduce mà nó sẽ nhận các giá trị 1 đó và tính tổng chúng. Do đó, việc thực hiện reduce tạo ra một cách hợp lý các cấu trúc dữ liệu giống như một cấu trúc trong Liệt kê 8:

Liệt kê 8. Kết quả đầu ra của reduce
"13-12-2010":8
"14-12-2010":6
"15-12-2010":9

Việc thực hiện reduce của tôi được hiển thị trong Liệt kê 9. Như với Mapper của Hadoop, Reducer được tham số hóa: 2 tham số đầu tiên là kiểu khóa gửi đến (Text) và kiểu giá trị (IntWritable) và 2 tham số sau cùng là các kiểu kết quả đầu ra: khóa và giá trị, mà trong trường hợp này là như nhau.

Liệt kê 9. Việc thực hiện reduce
public class EarthQuakesPerDateReducer extends Reducer<Text, IntWritable, Text, 
  IntWritable> {
 @Override
 protected void reduce(Text key, Iterable<IntWritable> values, Context context)
  throws IOException, InterruptedException {
  int count = 0;
  for (IntWritable value : values) {
   count++;
  }
  context.write(key, new IntWritable(count));
 }
}

Việc thực hiện reduce của tôi rất đơn giản. Như tôi đã chỉ ra trong Liệt kê 7, giá trị gửi đến thực sự là một bộ sưu tập các giá trị, mà trong trường hợp này có nghĩa là một tập hợp các giá trị là 1. Tất cả những gì tôi làm là tính tổng chúng, rồi viết ra một cặp khóa-giá trị mới đại diện cho ngày và tổng số đếm được. Sau đó về cơ bản mã reduce của tôi đưa ra các dòng mà bạn đã thấy trong Liệt kê 8. Luồng hợp lý trông đại thể như sau:

"13-12-2010":[1,1,1,1,1,1,1,1] -> "13-12-2010":8

Tất nhiên, dạng trừu tượng của liệt kê này là, map -> reduce.


Định nghĩa một Hadoop Job

Bây giờ tôi đã viết xong các đoạn mã thực thi mapreduce, việc còn lại để làm là liên kết tất cả mọi thứ vào một Job (công việc) của Hadoop. Việc định nghĩa một Job rất đơn giản: bạn cung cấp các đầu vào và các đầu ra, các việc thực hiện của mapreduce (như trong Liệt kê 6Liệt kê 9) và các kiểu đầu ra. Các kiểu đầu ra của tôi trong trường hợp này là các kiểu tương tự đã dùng để thực hiện mã reduce của mình.

Liệt kê 10. Một Job buộc map và reduce lại với nhau
public class EarthQuakesPerDayJob {

 public static void main(String[] args) throws Throwable {

  Job job = new Job();
  job.setJarByClass(EarthQuakesPerDayJob.class);
  FileInputFormat.addInputPath(job, new Path(args[0]));
  FileOutputFormat.setOutputPath(job, new Path(args[1]));

  job.setMapperClass(EarthQuakesPerDateMapper.class);
  job.setReducerClass(EarthQuakesPerDateReducer.class);
  job.setOutputKeyClass(Text.class);
  job.setOutputValueClass(IntWritable.class);

  System.exit(job.waitForCompletion(true) ? 0 : 1);
 }
}

Trong Liệt kê 10, tôi đã buộc mọi thứ lại với nhau bằng một phương thức main có nhận hai tham số: thư mục, nơi đặt tệp CSV của các trận động đất và một thư mục khác là nơi sẽ đặt báo cáo kết quả vào (Hadoop thích tạo thư mục này).

Để thực hiện khung công tác ngắn này, tôi sẽ cần thực hiện các lớp này. Tôi cũng sẽ cần nói cho Hadoop biết nơi nó có thể tìm thấy tệp nhị phân opencsv. Sau đó tôi có thể thực hiện Hadoop thông qua dòng lệnh, như thể hiện trong Liệt kê 11:

Liệt kê 11. Thực thi Hadoop
$> export HADOOP_CLASSPATH=lib/opencsv-2.2.jar
$> hadoop jar target/quake.jar com.b50.hadoop.quake.EarthQuakesPerDayJob
   ~/temp/mreduce/in/ ~/temp/mreduce/out

Hãy chạy mã này và bạn sẽ thấy một loạt dòng chữ chạy trên màn hình khi Hadoop bắt đầu thực hiện công việc của mình. Hãy nhớ rằng, tệp CSV mà tôi đang sử dụng chỉ là một điển hình tập hợp dữ liệu nhỏ so với dữ liệu lớn Hadoop đã được xây dựng để xử lý. Tùy thuộc vào sức mạnh xử lý trong máy tính của bạn, Hadoop cần hoàn thành trong vòng vài giây.

Khi thực thi xong, bạn có thể xem các nội dung của tệp đầu ra bằng bất kỳ trình soạn thảo nào. Một lựa chọn khác là sử dụng lệnh hadoop trực tiếp, như tôi đã thực hiện trong Liệt kê 12:

Liệt kê 12. Đọc kết quả đầu ra của Hadoop
$> hadoop dfs -cat part-r-00000 
05-12-2010      43
06-12-2010      143
07-12-2010      112
08-12-2010      136
09-12-2010      178
10-12-2010      114
11-12-2010      114
12-12-2010      79

Nếu bạn cũng ra kết quả giống như tôi, điều đầu tiên bạn sẽ nhận thấy trong Liệt kê 12 là toàn bộ số lượng các trận động đất cho mỗi ngày — chỉ riêng vào ngày 09 tháng 12 là 178 trận! Hy vọng rằng bạn cũng sẽ lưu ý rằng Hadoop đã làm chính xác những gì tôi đã muốn nó làm: đã lập bảng cẩn thận số lần xảy ra các trận động đất cho mỗi ngày trong phạm vi của tôi.


Viết một Mapper khác

Tiếp theo, tôi muốn tìm ra các trận động đất đang xảy ra ở đâu và bằng cách nào đó nhanh chóng xác định xem vị trí nào ghi trong nhật ký có nhiều trận động đất nhất trong phạm vi ngày của mình. Hadoop cũng sẽ dễ dàng thực hiện việc này. Khóa trong trường hợp này không phải là ngày, mà là vị trí. Vì vậy, tôi viết một lớp Mapper mới.

Liệt kê 13. Thực hiện map mới
public class EarthQuakeLocationMapper extends Mapper<LongWritable, Text, Text,
  IntWritable> {
 @Override
 protected void map(LongWritable key, Text value, Context context) throws IOException,
  InterruptedException {
  if (key.get() > 0) {
   String[] lines = new CSVParser().parseLine(value.toString());
   context.write(new Text(lines[9]), new IntWritable(1));
  }
 }
}

Thay vì lấy ngày và chuyển đổi nó thì lần này việc tôi cần làm trong Liệt kê 13 là lấy ra vị trí, đó là mục cuối cùng trong mảng CSV.

Thay vì lấy ra một danh sách khổng lồ về các vị trí và các con số động đất của chúng, tôi muốn hạn chế các kết quả của mình vào bất kỳ vị trí nào có 10 hoặc nhiều trận động đất hơn trong bất kỳ khoảng thời gian bảy ngày nào.

Liệt kê 14. Ở đâu xảy ra nhiều trận động đất hơn?
public class EarthQuakeLocationReducer extends Reducer<Text, IntWritable, Text,
  IntWritable> {
 @Override
 protected void reduce(Text key, Iterable<IntWritable> values, Context context)
  throws IOException, InterruptedException {
  int count = 0;
  for (IntWritable value : values) {
   count++;
  }
  if (count >= 10) {
   context.write(key, new IntWritable(count));
  }
 }
}

Mã trong Liệt kê 14 hoàn toàn giống mã của Liệt kê 9; tuy nhiên, trong trường hợp này, tôi đã hạn chế kết quả đầu ra với các tổng số là 10 hoặc cao hơn. Tiếp theo, tôi có thể gắn việc thực hiện mapreduce của tôi lại với nhau bằng cách thực hiện một Job khác, thực hiện mọi thứ và thực thi Hadoop như bình thường để nhận được kết quả mong muốn.

Gọi lệnh hadoop dfs sẽ hiển thị các giá trị mới mà tôi đã yêu cầu:

Liệt kê 15. Các trận động đất theo vị trí
$> hadoop dfs -cat part-r-00000 
Andreanof Islands, Aleutian Islands, Alaska     24
Arkansas        40
Baja California, Mexico 101
Central Alaska  74
Central California      68
Greater Los Angeles area, California    16
Island of Hawaii, Hawaii        16
Kenai Peninsula, Alaska 11
Nevada  15
Northern California     114
San Francisco Bay area, California      21
Southern Alaska 97
Southern California     115
Utah    19
western Montana 11

Từ kết quả ở Liệt kê 15, bạn sẽ nhận được thông tin gì? Đầu tiên, bờ biển phía tây của Bắc Mỹ từ Mexico đến Alaska là một nơi luôn biến động. Thứ hai, Arkansas dường như nằm gần một rãnh nứt, mà trước đây tôi đã không nhận ra. Cuối cùng, nếu bạn sống ở miền Bắc hay miền Nam California (nơi mà nhiều nhà phát triển phần mềm sống) thì mặt đất xung quanh bạn rung lắc khoảng 13 phút/lần.


Kết luận

Việc phân tích dữ liệu bằng Hadoop là dễ dàng và hiệu quả và thậm chí tôi vẫn chưa xem xét sơ bộ những gì nó phải cung cấp để phân tích dữ liệu. Hadoop thực sự được thiết kế để chạy theo cách phân tán, nơi nó xử lý sự phối hợp của các node khác nhau đang chạy mapreduce. Vì là ví dụ nên trong bài này tôi chạy Hadoop trong một JVM với một tệp nhỏ, duy nhất.

Hadoop là một công cụ tuyệt vời hoạt động độc lập và cũng có một hệ sinh thái đang phát triển, đầy đủ xung quanh nó, từ các dự án con đến các dịch vụ Hadoop dựa trên đám mây. Hệ sinh thái Hadoop cho thấy cộng đồng rộng lớn đằng sau dự án này. Nhiều công cụ đã được tung ra từ cộng đồng đó chứng tỏ tính khả thi của việc phân tích dữ liệu lớn như là một hoạt động kinh doanh toàn cầu. Với Hadoop, việc phân tích và khai phá dữ liệu phân tán có sẵn cho tất cả những nhà cải cách và các nhà doanh nghiệp phần mềm, bao gồm nhưng không hạn chế các ông lớn như Google và Yahoo!.

Tài nguyên

Học tập

  • Phát triển Java 2.0: Loạt bài trên dW này khám phá các công nghệ định nghĩa lại bối cảnh phát triển Java; các chủ đề mới đây gồm MongoDB (09.2010); CouchDB (11.2009); và Objectify AppEngine (11.2010).
  • "Xử lý dữ liệu phân tán với Hadoop, Phần 1: Bắt đầu" (M. Tim Jones, developerWorks, 05.2010): Bài này — bài đầu tiên trong một loạt bài — khám phá khung công tác Hadoop, gồm có hệ thống tệp Hadoop (HDFS) và các kiểu nút thường dùng phổ biến. Tìm hiểu xem làm thế nào để cài đặt và cấu hình một cụm Hadoop nút đơn và đi sâu vào các ứng dụng MapReduce. Cuối cùng, khám phá các cách để theo dõi và quản lý Hadoop bằng cách sử dụng các giao diện Web cốt lõi của nó. Xem thêm Phần 2Phần 3.
  • "Sử dụng MapReduce và cân bằng tải trên đám mây (Kirpal A. Venkatesh, et. al., developerWorks, 07.2010.): Tìm hiểu xem sự ảo hóa và MapReduce của Hadoop cải thiện hiệu năng nút ra sao.
  • "Một lược tả về hiệu quả tính toán của MapReduce của Apache Hadoop, Phần 1" (Paul Burkhardt, Cloudera Development Center, 12.2010): Giải trình gồm hai phần về các ứng dụng MapReduce sử dụng tài nguyên máy tính hiệu quả thế nào. Nửa phần đầu là một cái nhìn tổng quan về hiệu quả của máy tính vì nó liên quan để việc đánh giá các ứng dụng MapReduce của Hadoop.
  • "Các công ty Hadoop ở khắp mọi nơi" (Alex Handy, SD Times, 07.2009): Các công ty đang tạo ra nhiều dữ liệu hơn mỗi ngày, nhưng nhiều công ty trong số đó không thu được kinh doanh thông minh từ nó. Điều đó báo hiệu cơ hội, Handy nói.
  • Duyệt hiệu sách công nghệ Java để biết các cuốn sách về các chủ đề kỹ thuật này và khác.
  • Vùng công nghệ Java trên developerWorks: Tìm hàng trăm bài viết về mọi khía cạnh của lập trình Java.

Lấy sản phẩm và công nghệ

Thảo luận

  • Hãy tham gia vào cộng đồng developerWorks. Kết nối với những người sử dụng developerWorks khác trong khi khám phá các blog, các diễn đàn, các nhóm và các wiki theo hướng nhà phát triển.

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, Nguồn mở
ArticleID=854873
ArticleTitle=Phát triển Java 2.0: Phân tích dữ liệu lớn bằng MapReduce của Hadoop
publish-date=01142013