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 org.apache.hadoop.hbase.classification.InterfaceAudience;
22 import org.apache.hadoop.hbase.classification.InterfaceStability;
23
24 import java.io.IOException;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Random;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.conf.Configured;
33 import org.apache.hadoop.fs.FileSystem;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.HBaseConfiguration;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.TableName;
38 import org.apache.hadoop.hbase.client.Connection;
39 import org.apache.hadoop.hbase.client.ConnectionFactory;
40 import org.apache.hadoop.hbase.client.Scan;
41 import org.apache.hadoop.hbase.client.Table;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.mapreduce.Job;
44 import org.apache.hadoop.util.GenericOptionsParser;
45 import org.apache.hadoop.util.Tool;
46 import org.apache.hadoop.util.ToolRunner;
47
48
49
50
51
52
53 @InterfaceAudience.Public
54 @InterfaceStability.Stable
55 public class CopyTable extends Configured implements Tool {
56 private static final Log LOG = LogFactory.getLog(CopyTable.class);
57
58 final static String NAME = "copytable";
59 long startTime = 0;
60 long endTime = HConstants.LATEST_TIMESTAMP;
61 int batch = Integer.MAX_VALUE;
62 int cacheRow = -1;
63 int versions = -1;
64 String tableName = null;
65 String startRow = null;
66 String stopRow = null;
67 String dstTableName = null;
68 String peerAddress = null;
69 String families = null;
70 boolean allCells = false;
71 static boolean shuffle = false;
72
73 boolean bulkload = false;
74 Path bulkloadDir = null;
75
76 private final static String JOB_NAME_CONF_KEY = "mapreduce.job.name";
77
78 public CopyTable(Configuration conf) {
79 super(conf);
80 }
81
82
83
84
85
86
87
88 public Job createSubmittableJob(String[] args)
89 throws IOException {
90 if (!doCommandLine(args)) {
91 return null;
92 }
93
94 Job job = Job.getInstance(getConf(), getConf().get(JOB_NAME_CONF_KEY, NAME + "_" + tableName));
95
96 job.setJarByClass(CopyTable.class);
97 Scan scan = new Scan();
98 scan.setBatch(batch);
99 scan.setCacheBlocks(false);
100
101 if (cacheRow > 0) {
102 scan.setCaching(cacheRow);
103 } else {
104 scan.setCaching(getConf().getInt(HConstants.HBASE_CLIENT_SCANNER_CACHING, 100));
105 }
106
107 scan.setTimeRange(startTime, endTime);
108 if (allCells) {
109 scan.setRaw(true);
110 }
111 if (shuffle) {
112 job.getConfiguration().set(TableInputFormat.SHUFFLE_MAPS, "true");
113 }
114 if (versions >= 0) {
115 scan.setMaxVersions(versions);
116 }
117
118 if (startRow != null) {
119 scan.setStartRow(Bytes.toBytesBinary(startRow));
120 }
121
122 if (stopRow != null) {
123 scan.setStopRow(Bytes.toBytesBinary(stopRow));
124 }
125
126 if(families != null) {
127 String[] fams = families.split(",");
128 Map<String,String> cfRenameMap = new HashMap<String,String>();
129 for(String fam : fams) {
130 String sourceCf;
131 if(fam.contains(":")) {
132
133 String[] srcAndDest = fam.split(":", 2);
134 sourceCf = srcAndDest[0];
135 String destCf = srcAndDest[1];
136 cfRenameMap.put(sourceCf, destCf);
137 } else {
138
139 sourceCf = fam;
140 }
141 scan.addFamily(Bytes.toBytes(sourceCf));
142 }
143 Import.configureCfRenaming(job.getConfiguration(), cfRenameMap);
144 }
145 job.setNumReduceTasks(0);
146
147 if (bulkload) {
148 TableMapReduceUtil.initTableMapperJob(tableName, scan, Import.KeyValueImporter.class, null,
149 null, job);
150
151
152 TableInputFormat.configureSplitTable(job, TableName.valueOf(dstTableName));
153
154 FileSystem fs = FileSystem.get(getConf());
155 Random rand = new Random();
156 Path root = new Path(fs.getWorkingDirectory(), "copytable");
157 fs.mkdirs(root);
158 while (true) {
159 bulkloadDir = new Path(root, "" + rand.nextLong());
160 if (!fs.exists(bulkloadDir)) {
161 break;
162 }
163 }
164
165 System.out.println("HFiles will be stored at " + this.bulkloadDir);
166 HFileOutputFormat2.setOutputPath(job, bulkloadDir);
167 try (Connection conn = ConnectionFactory.createConnection(getConf());
168 Table htable = conn.getTable(TableName.valueOf(dstTableName))) {
169 HFileOutputFormat2.configureIncrementalLoadMap(job, htable);
170 }
171 } else {
172 TableMapReduceUtil.initTableMapperJob(tableName, scan,
173 Import.Importer.class, null, null, job);
174
175 TableMapReduceUtil.initTableReducerJob(dstTableName, null, job, null, peerAddress, null,
176 null);
177 }
178
179 return job;
180 }
181
182
183
184
185 private static void printUsage(final String errorMsg) {
186 if (errorMsg != null && errorMsg.length() > 0) {
187 System.err.println("ERROR: " + errorMsg);
188 }
189 System.err.println("Usage: CopyTable [general options] [--starttime=X] [--endtime=Y] " +
190 "[--new.name=NEW] [--peer.adr=ADR] <tablename>");
191 System.err.println();
192 System.err.println("Options:");
193 System.err.println(" rs.class hbase.regionserver.class of the peer cluster");
194 System.err.println(" specify if different from current cluster");
195 System.err.println(" rs.impl hbase.regionserver.impl of the peer cluster");
196 System.err.println(" startrow the start row");
197 System.err.println(" stoprow the stop row");
198 System.err.println(" starttime beginning of the time range (unixtime in millis)");
199 System.err.println(" without endtime means from starttime to forever");
200 System.err.println(" endtime end of the time range. Ignored if no starttime specified.");
201 System.err.println(" versions number of cell versions to copy");
202 System.err.println(" new.name new table's name");
203 System.err.println(" peer.adr Address of the peer cluster given in the format");
204 System.err.println(" hbase.zookeeer.quorum:hbase.zookeeper.client.port:zookeeper.znode.parent");
205 System.err.println(" families comma-separated list of families to copy");
206 System.err.println(" To copy from cf1 to cf2, give sourceCfName:destCfName. ");
207 System.err.println(" To keep the same name, just give \"cfName\"");
208 System.err.println(" all.cells also copy delete markers and deleted cells");
209 System.err.println(" bulkload Write input into HFiles and bulk load to the destination "
210 + "table");
211 System.err.println();
212 System.err.println("Args:");
213 System.err.println(" tablename Name of the table to copy");
214 System.err.println();
215 System.err.println("Examples:");
216 System.err.println(" To copy 'TestTable' to a cluster that uses replication for a 1 hour window:");
217 System.err.println(" $ bin/hbase " +
218 "org.apache.hadoop.hbase.mapreduce.CopyTable --starttime=1265875194289 --endtime=1265878794289 " +
219 "--peer.adr=server1,server2,server3:2181:/hbase --families=myOldCf:myNewCf,cf2,cf3 TestTable ");
220 System.err.println("For performance consider the following general option:\n"
221 + " It is recommended that you set the following to >=100. A higher value uses more memory but\n"
222 + " decreases the round trip time to the server and may increase performance.\n"
223 + " -Dhbase.client.scanner.caching=100\n"
224 + " The following should always be set to false, to prevent writing data twice, which may produce \n"
225 + " inaccurate results.\n"
226 + " -Dmapreduce.map.speculative=false");
227 }
228
229 private boolean doCommandLine(final String[] args) {
230
231
232 if (args.length < 1) {
233 printUsage(null);
234 return false;
235 }
236 try {
237 for (int i = 0; i < args.length; i++) {
238 String cmd = args[i];
239 if (cmd.equals("-h") || cmd.startsWith("--h")) {
240 printUsage(null);
241 return false;
242 }
243
244 final String startRowArgKey = "--startrow=";
245 if (cmd.startsWith(startRowArgKey)) {
246 startRow = cmd.substring(startRowArgKey.length());
247 continue;
248 }
249
250 final String stopRowArgKey = "--stoprow=";
251 if (cmd.startsWith(stopRowArgKey)) {
252 stopRow = cmd.substring(stopRowArgKey.length());
253 continue;
254 }
255
256 final String startTimeArgKey = "--starttime=";
257 if (cmd.startsWith(startTimeArgKey)) {
258 startTime = Long.parseLong(cmd.substring(startTimeArgKey.length()));
259 continue;
260 }
261
262 final String endTimeArgKey = "--endtime=";
263 if (cmd.startsWith(endTimeArgKey)) {
264 endTime = Long.parseLong(cmd.substring(endTimeArgKey.length()));
265 continue;
266 }
267
268 final String batchArgKey = "--batch=";
269 if (cmd.startsWith(batchArgKey)) {
270 batch = Integer.parseInt(cmd.substring(batchArgKey.length()));
271 continue;
272 }
273
274 final String cacheRowArgKey = "--cacheRow=";
275 if (cmd.startsWith(cacheRowArgKey)) {
276 cacheRow = Integer.parseInt(cmd.substring(cacheRowArgKey.length()));
277 continue;
278 }
279
280 final String versionsArgKey = "--versions=";
281 if (cmd.startsWith(versionsArgKey)) {
282 versions = Integer.parseInt(cmd.substring(versionsArgKey.length()));
283 continue;
284 }
285
286 final String newNameArgKey = "--new.name=";
287 if (cmd.startsWith(newNameArgKey)) {
288 dstTableName = cmd.substring(newNameArgKey.length());
289 continue;
290 }
291
292 final String peerAdrArgKey = "--peer.adr=";
293 if (cmd.startsWith(peerAdrArgKey)) {
294 peerAddress = cmd.substring(peerAdrArgKey.length());
295 continue;
296 }
297
298 final String familiesArgKey = "--families=";
299 if (cmd.startsWith(familiesArgKey)) {
300 families = cmd.substring(familiesArgKey.length());
301 continue;
302 }
303
304 if (cmd.startsWith("--all.cells")) {
305 allCells = true;
306 continue;
307 }
308
309 if (cmd.startsWith("--bulkload")) {
310 bulkload = true;
311 continue;
312 }
313
314 if (cmd.startsWith("--shuffle")) {
315 shuffle = true;
316 continue;
317 }
318
319 if (i == args.length-1) {
320 tableName = cmd;
321 } else {
322 printUsage("Invalid argument '" + cmd + "'" );
323 return false;
324 }
325 }
326 if (dstTableName == null && peerAddress == null) {
327 printUsage("At least a new table name or a " +
328 "peer address must be specified");
329 return false;
330 }
331 if ((endTime != 0) && (startTime > endTime)) {
332 printUsage("Invalid time range filter: starttime=" + startTime + " > endtime=" + endTime);
333 return false;
334 }
335
336 if (bulkload && peerAddress != null) {
337 printUsage("Remote bulkload is not supported!");
338 return false;
339 }
340
341
342 if (dstTableName == null) {
343 dstTableName = tableName;
344 }
345 } catch (Exception e) {
346 e.printStackTrace();
347 printUsage("Can't start because " + e.getMessage());
348 return false;
349 }
350 return true;
351 }
352
353
354
355
356
357
358
359 public static void main(String[] args) throws Exception {
360 int ret = ToolRunner.run(new CopyTable(HBaseConfiguration.create()), args);
361 System.exit(ret);
362 }
363
364 @Override
365 public int run(String[] args) throws Exception {
366 String[] otherArgs = new GenericOptionsParser(getConf(), args).getRemainingArgs();
367 Job job = createSubmittableJob(otherArgs);
368 if (job == null) return 1;
369 if (!job.waitForCompletion(true)) {
370 LOG.info("Map-reduce job failed!");
371 if (bulkload) {
372 LOG.info("Files are not bulkloaded!");
373 }
374 return 1;
375 }
376 int code = 0;
377 if (bulkload) {
378 code = new LoadIncrementalHFiles(this.getConf()).run(new String[]{this.bulkloadDir.toString(),
379 this.dstTableName});
380 if (code == 0) {
381
382
383 FileSystem fs = FileSystem.get(this.getConf());
384 if (!fs.delete(this.bulkloadDir, true)) {
385 LOG.error("Deleting folder " + bulkloadDir + " failed!");
386 code = 1;
387 }
388 }
389 }
390 return code;
391 }
392 }