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