1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.mapreduce;
20
21 import java.io.IOException;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.apache.hadoop.hbase.HConstants;
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27 import org.apache.hadoop.hbase.classification.InterfaceStability;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.fs.Path;
30 import org.apache.hadoop.hbase.Cell;
31 import org.apache.hadoop.hbase.CellUtil;
32 import org.apache.hadoop.hbase.HBaseConfiguration;
33 import org.apache.hadoop.hbase.client.Result;
34 import org.apache.hadoop.hbase.client.Scan;
35 import org.apache.hadoop.hbase.filter.CompareFilter;
36 import org.apache.hadoop.hbase.filter.Filter;
37 import org.apache.hadoop.hbase.filter.PrefixFilter;
38 import org.apache.hadoop.hbase.filter.RegexStringComparator;
39 import org.apache.hadoop.hbase.filter.RowFilter;
40 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
41 import org.apache.hadoop.hbase.util.Bytes;
42 import org.apache.hadoop.io.IntWritable;
43 import org.apache.hadoop.io.Text;
44 import org.apache.hadoop.mapreduce.Job;
45 import org.apache.hadoop.mapreduce.Reducer;
46 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
47 import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
48 import org.apache.hadoop.util.GenericOptionsParser;
49
50 import com.google.common.base.Preconditions;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 @InterfaceAudience.Public
71 @InterfaceStability.Stable
72 public class CellCounter {
73 private static final Log LOG =
74 LogFactory.getLog(CellCounter.class.getName());
75
76
77
78
79
80 static final String NAME = "CellCounter";
81
82
83
84
85 static class CellCounterMapper
86 extends TableMapper<Text, IntWritable> {
87
88
89
90 public static enum Counters {
91 ROWS,
92 CELLS
93 }
94
95 private Configuration conf;
96 private String separator;
97
98
99
100
101 private byte[] lastRow;
102 private String currentRowKey;
103 byte[] currentFamily = null;
104 String currentFamilyName = null;
105 byte[] currentQualifier = null;
106
107 String currentQualifierName = null;
108
109 String currentRowQualifierName = null;
110
111 @Override
112 protected void setup(Context context) throws IOException, InterruptedException {
113 conf = context.getConfiguration();
114 separator = conf.get("ReportSeparator",":");
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128 @Override
129 public void map(ImmutableBytesWritable row, Result values,
130 Context context)
131 throws IOException {
132 Preconditions.checkState(values != null,
133 "values passed to the map is null");
134
135 try {
136 byte[] currentRow = values.getRow();
137 if (lastRow == null || !Bytes.equals(lastRow, currentRow)) {
138 lastRow = currentRow;
139 currentRowKey = Bytes.toStringBinary(currentRow);
140 currentFamily = null;
141 currentQualifier = null;
142 context.getCounter(Counters.ROWS).increment(1);
143 context.write(new Text("Total ROWS"), new IntWritable(1));
144 }
145 if (!values.isEmpty()) {
146 int cellCount = 0;
147 for (Cell value : values.listCells()) {
148 cellCount++;
149 if (currentFamily == null || !CellUtil.matchingFamily(value, currentFamily)) {
150 currentFamily = CellUtil.cloneFamily(value);
151 currentFamilyName = Bytes.toStringBinary(currentFamily);
152 currentQualifier = null;
153 context.getCounter("CF", currentFamilyName).increment(1);
154 if (1 == context.getCounter("CF", currentFamilyName).getValue()) {
155 context.write(new Text("Total Families Across all Rows"), new IntWritable(1));
156 context.write(new Text(currentFamily), new IntWritable(1));
157 }
158 }
159 if (currentQualifier == null || !CellUtil.matchingQualifier(value, currentQualifier)) {
160 currentQualifier = CellUtil.cloneQualifier(value);
161 currentQualifierName = currentFamilyName + separator +
162 Bytes.toStringBinary(currentQualifier);
163 currentRowQualifierName = currentRowKey + separator + currentQualifierName;
164
165 context.write(new Text("Total Qualifiers across all Rows"),
166 new IntWritable(1));
167 context.write(new Text(currentQualifierName), new IntWritable(1));
168 }
169
170 context.write(new Text(currentRowQualifierName + "_Versions"), new IntWritable(1));
171 }
172 context.getCounter(Counters.CELLS).increment(cellCount);
173 }
174 } catch (InterruptedException e) {
175 e.printStackTrace();
176 }
177 }
178 }
179
180 static class IntSumReducer<Key> extends Reducer<Key, IntWritable,
181 Key, IntWritable> {
182
183 private IntWritable result = new IntWritable();
184 public void reduce(Key key, Iterable<IntWritable> values,
185 Context context)
186 throws IOException, InterruptedException {
187 int sum = 0;
188 for (IntWritable val : values) {
189 sum += val.get();
190 }
191 result.set(sum);
192 context.write(key, result);
193 }
194 }
195
196
197
198
199
200
201
202
203
204 public static Job createSubmittableJob(Configuration conf, String[] args)
205 throws IOException {
206 String tableName = args[0];
207 Path outputDir = new Path(args[1]);
208 String reportSeparatorString = (args.length > 2) ? args[2]: ":";
209 conf.set("ReportSeparator", reportSeparatorString);
210 Job job = new Job(conf, NAME + "_" + tableName);
211 job.setJarByClass(CellCounter.class);
212 Scan scan = getConfiguredScanForJob(conf, args);
213 TableMapReduceUtil.initTableMapperJob(tableName, scan,
214 CellCounterMapper.class, ImmutableBytesWritable.class, Result.class, job);
215 job.setNumReduceTasks(1);
216 job.setMapOutputKeyClass(Text.class);
217 job.setMapOutputValueClass(IntWritable.class);
218 job.setOutputFormatClass(TextOutputFormat.class);
219 job.setOutputKeyClass(Text.class);
220 job.setOutputValueClass(IntWritable.class);
221 FileOutputFormat.setOutputPath(job, outputDir);
222 job.setReducerClass(IntSumReducer.class);
223 return job;
224 }
225
226 private static Scan getConfiguredScanForJob(Configuration conf, String[] args)
227 throws IOException {
228
229 Scan s = TableInputFormat.createScanFromConfiguration(conf);
230
231 if (conf.get(TableInputFormat.SCAN_MAXVERSIONS) == null) {
232
233 s.setMaxVersions(Integer.MAX_VALUE);
234 }
235 s.setCacheBlocks(false);
236
237 Filter rowFilter = getRowFilter(args);
238 if (rowFilter!= null) {
239 LOG.info("Setting Row Filter for counter.");
240 s.setFilter(rowFilter);
241 }
242
243 long timeRange[] = getTimeRange(args);
244 if (timeRange != null) {
245 LOG.info("Setting TimeRange for counter.");
246 s.setTimeRange(timeRange[0], timeRange[1]);
247 }
248 return s;
249 }
250
251
252 private static Filter getRowFilter(String[] args) {
253 Filter rowFilter = null;
254 String filterCriteria = (args.length > 3) ? args[3]: null;
255 if (filterCriteria == null) return null;
256 if (filterCriteria.startsWith("^")) {
257 String regexPattern = filterCriteria.substring(1, filterCriteria.length());
258 rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator(regexPattern));
259 } else {
260 rowFilter = new PrefixFilter(Bytes.toBytesBinary(filterCriteria));
261 }
262 return rowFilter;
263 }
264
265 private static long[] getTimeRange(String[] args) throws IOException {
266 final String startTimeArgKey = "--starttime=";
267 final String endTimeArgKey = "--endtime=";
268 long startTime = 0L;
269 long endTime = 0L;
270
271 for (int i = 1; i < args.length; i++) {
272 System.out.println("i:" + i + "arg[i]" + args[i]);
273 if (args[i].startsWith(startTimeArgKey)) {
274 startTime = Long.parseLong(args[i].substring(startTimeArgKey.length()));
275 }
276 if (args[i].startsWith(endTimeArgKey)) {
277 endTime = Long.parseLong(args[i].substring(endTimeArgKey.length()));
278 }
279 }
280
281 if (startTime == 0 && endTime == 0)
282 return null;
283
284 endTime = endTime == 0 ? HConstants.LATEST_TIMESTAMP : endTime;
285 return new long [] {startTime, endTime};
286 }
287
288
289
290
291
292
293 public static void main(String[] args) throws Exception {
294 Configuration conf = HBaseConfiguration.create();
295 String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
296 if (otherArgs.length < 2) {
297 System.err.println("ERROR: Wrong number of parameters: " + args.length);
298 System.err.println("Usage: CellCounter ");
299 System.err.println(" <tablename> <outputDir> <reportSeparator> [^[regex pattern] or " +
300 "[Prefix] for row filter]] --starttime=[starttime] --endtime=[endtime]");
301 System.err.println(" Note: -D properties will be applied to the conf used. ");
302 System.err.println(" Additionally, all of the SCAN properties from TableInputFormat");
303 System.err.println(" can be specified to get fine grained control on what is counted..");
304 System.err.println(" -D " + TableInputFormat.SCAN_ROW_START + "=<rowkey>");
305 System.err.println(" -D " + TableInputFormat.SCAN_ROW_STOP + "=<rowkey>");
306 System.err.println(" -D " + TableInputFormat.SCAN_COLUMNS + "=\"<col1> <col2>...\"");
307 System.err.println(" -D " + TableInputFormat.SCAN_COLUMN_FAMILY + "=<familyName>");
308 System.err.println(" -D " + TableInputFormat.SCAN_TIMESTAMP + "=<timestamp>");
309 System.err.println(" -D " + TableInputFormat.SCAN_TIMERANGE_START + "=<timestamp>");
310 System.err.println(" -D " + TableInputFormat.SCAN_TIMERANGE_END + "=<timestamp>");
311 System.err.println(" -D " + TableInputFormat.SCAN_MAXVERSIONS + "=<count>");
312 System.err.println(" -D " + TableInputFormat.SCAN_CACHEDROWS + "=<count>");
313 System.err.println(" -D " + TableInputFormat.SCAN_BATCHSIZE + "=<count>");
314 System.err.println(" <reportSeparator> parameter can be used to override the default report separator " +
315 "string : used to separate the rowId/column family name and qualifier name.");
316 System.err.println(" [^[regex pattern] or [Prefix] parameter can be used to limit the cell counter count " +
317 "operation to a limited subset of rows from the table based on regex or prefix pattern.");
318 System.exit(-1);
319 }
320 Job job = createSubmittableJob(conf, otherArgs);
321 System.exit(job.waitForCompletion(true) ? 0 : 1);
322 }
323 }