简化数据处理,掌握Excel去除空格的高效技巧
557
2022-05-29
本文档提到的一些规范条例,主要来源于对定位问题过程中所积累的经验、客户端代码实践、对HBase Client源码的分析以及已总结的ReleaseNotes中的一些注意点等。这些条款,主要分为规则、建议、示例三种类型。规则类,是在写HBase客户端代码时必须遵循的一些条款。建议类,需要依据实际的应用需求来决定是否遵循。示例类,给出了一些功能代码的实现示例,供参考,本文中使用的HBase版本仍为1.0+。
1【规则】 Configuration实例的创建
该类应该通过调用HBaseConfiguration的Create()方法来实例化。否则,将无法正确加载HBase中的相关配置项。
正确示例:
//该部分,应该是在类成员变量的声明区域声明
private Configuration hbaseConfig = null;
//最好在类的构造函数中,或者初始化方法中实例化该类
hbaseConfig = HBaseConfiguration.create();
错误示例:
hbaseConfig = new Configuration();
2【规则】 共享Configuration实例
HBase客户端代码通过创建一个与Zookeeper之间的HConnection,来获取与一个HBase集群进行交互的权限。一个Zookeeper的HConnection连接,对应着一个Configuration实例,已经创建的HConnection实例,会被缓存起来。也就是说,如果客户端需要与HBase集群进行交互的时候,会传递一个Configuration实例过去,HBase Client部分通过已缓存的HConnection实例,来判断属于这个Configuration实例的HConnection实例是否存在,如果不存在,就会创建一个新的,如果存在,就会直接返回相应的实例。
因此,如果频频的创建Configuration实例,会导致创建很多不必要的HConnection实例,很容易达到Zookeeper的连接数上限。
建议在整个客户端代码范围内,都共用同一个Configuration对象实例。
3【规则】 HTable实例的创建
HTable类有多种构造函数,如:
1.public HTable(final String tableName)
2.public HTable(final byte [] tableName)
3.public HTable(Configuration conf, final byte [] tableName)
4.public HTable(Configuration conf, final String tableName)
5.public HTable(final byte[] tableName, final HConnection connection, final ExecutorService pool)
建议采用第5种构造函数。之所以不建议使用前面的4种,是因为:前两种方法实例化一个HTable时,没有指定Configuration实例,那么,在实例化的时候,就会自动创建一个Configuration实例。如果需要实例化过多的HTable实例,那么,就可能会出现很多不必要的HConnection(关于这一点,前面部分已经有讲述)。因此,而对于第3、4种构造方法,每个实例都可能会创建一个新的线程池,也可能会创建新的连接,导致性能偏低。
正确示例:
private HTable table = null;
public initTable(Configuration config, byte[] tableName)
{
// sharedConn和pool都已经是事先实例化好的。建议进程级别内共享同一个。
// 初始化HConnection的方法:
// HConnection sharedConn =
// HConnectionManager.createConnection(this.config);
table = new HTable(config, tableName, sharedConn, pool);
}
错误示例:
private HTable table = null;
public initTable(String tableName)
{
table = new HTable(tableName);
}
public initTable(byte[] tableName)
{
table = new HTable(tableName);
}
4 【规则】 不允许多个线程在同一时间共用同一个HTable实例
HTable是一个非线程安全类,因此,同一个HTable实例,不应该被多个线程同时使用,否则可能会带来并发问题。
5【规则】 HTable实例缓存
如果一个HTable实例可能会被长时间且被同一个线程固定且频繁的用到,例如,通过一个线程不断的往一个表内写入数据,那么这个HTable在实例化后,就需要缓存下来,而不是每一次插入操作,都要实例化一个HTable对象(尽管提倡实例缓存,但也不是在一个线程中一直沿用一个实例,个别场景下依然需要重构,可参见下一条规则)。
正确示例:
//注意该实例中提供的以Map形式缓存HTable实例的方法,未必通用。这与多线程多HTable实例的设计方案有关。如果确定一个HTable实例仅仅可能会被用于一个线程,而且该线程也仅有一个HTable实例的话,就无须使用Map。这里提供的思路仅供参考
//该Map中以TableName为Key值,缓存所有已经实例化的HTable
private Map
//所有的HTable实例,都将共享这个Configuration实例
private Configuration demoConf = null;
/**
* <初始化一个HTable类>
* <功能详细描述>
* @param tableName
* @return
* @throws IOException
* @see [类、类#方法、类#成员]
*/
private HTable initNewTable(String tableName) throws IOException
{
return new HTable(demoConf, tableName);
}
/**
* <获取HTable实例>
* <功能详细描述>
* @see [类、类#方法、类#成员]
*/
private HTable getTable(String tableName)
{
if (demoTables.containsKey(tableName))
{
return demoTables.get(tableName);
} else {
HTable table = null;
try
{
table = initNewTable(tableName);
demoTables.put(tableName, table);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return table;
}
}
/**
* <写数据>
* <这里未涉及到多线程多HTable实例在设计模式上的优化.这里只所以采用同步方法,
* 是考虑到同一个HTable是非线程安全的.通常,我们建议一个HTable实例,在同一
* 时间只能被用在一个写数据的线程中>
* @param dataList
* @param tableName
* @see [类、类#方法、类#成员]
*/
public void putData(List
{
HTable table = getTable(tableName);
//关于这里的同步:如果在采用的设计方案中,不存在多线程共用同一个HTable实例
//的可能的话,就无须同步了。这里需要注意的一点,就是HTable实例是非线程安全的
synchronized (table)
{
try
{
table.put(dataList);
table.notifyAll();
}
catch (IOException e)
{
// 在捕获到IOE时,需要将缓存的实例重构。
try {
// 关闭之前的Connection.
table.close();
// 重新创建这个实例.
table = new HTable(this.config, "jeason");
} catch (IOException e1) {
// TODO
}
}
}
}
错误示例:
public void putDataIncorrect(List
{
HTable table = null;
try
{
//每次写数据,都创建一个HTable实例
table = new HTable(demoConf, tableName);
table.put(dataList);
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
finally
{
table.close();
}
}
6【规则】 HTable实例写数据的异常处理
尽管在前一条规则中提到了提倡HTable实例的重构,但是,并非提倡一个线程自始至终要沿用同一个HTable实例,当捕获到IOException时,依然需要重构HTable实例。示例代码可参考上一个规则的示例。
另外,勿轻易调用如下两个方法:
Ø Configuration#clear:
这个方法,会清理掉所有的已经加载的属性,那么,对于已经在使用这个Configuration的类或线程而言,可能会带来潜在的问题(例如,假如HTable还在使用这个Configuration,那么,调用这个方法后,HTable中的这个Configuration的所有的参数,都被清理掉了),也就是说:只要还有对象或者线程在使用这个Configuration,我们就不应该调用这个clear方法,除非,所有的类或线程,都已经确定不用这个Configuration了。那么,这个操作,可以在所有的线程要退出的时候来做,而不是每一次。
因此,这个方法,应该要放在进程要退出的地方去做。而不是每一次HBaseAdmin要重构的时候做。
Ø HConnectionManager#deleteAllConnections:
这个可能会导致现有的正在使用的连接被从连接集合中清理掉,同时,因为在HTable中保存了原有连接的引用,可能会导致这个连接无法关闭,进而可能会造成泄漏。因此,这个方法不建议使用。
7【规则】 写入失败的数据要做相应的处理
在写数据的过程中,如果进程异常或一些其它的短暂的异常,可能会导致一些写入操作失败。因此,对于操作的数据,需要将其记录下来。在集群恢复正常后,重新将其写入到HBase数据表中。
另外,有一点需要注意:HBase Client返回写入失败的数据,是不会自动重试的,仅仅会告诉接口调用者哪些数据写入失败了。对于写入失败的数据,一定要做一些安全的处理,例如可以考虑将这些失败的数据,暂时写在文件中,或者,直接缓存在内存中。
正确示例:
private List
/**
* <采用PutList的模式插入数据>
* <如果不是多线程调用该方法,可不采用同步>
* @param put 一条数据记录
* @throws IOException
* @see [类、类#方法、类#成员]
*/
public synchronized void putData(Put put)
{
// 暂时将数据缓存在该List中
dataList.add(put);
// 当dataList的大小达到PUT_LIST_SIZE之后,就执行一次Put操作
if (dataList.size() >= PUT_LIST_SIZE)
{
try
{
demoTable.put(dataList);
}
catch (IOException e)
{
// 如果是RetriesExhaustedWithDetailsException类型的异常,
// 说明这些数据中有部分是写入失败的这通常都是因为
// HBase集群的进程异常引起,当然有时也会因为有大量
// 的Region正在被转移,导致尝试一定的次数后失败
if (e instanceof RetriesExhaustedWithDetailsException)
{
RetriesExhaustedWithDetailsException ree =
(RetriesExhaustedWithDetailsException)e;
int failures = ree.getNumExceptions();
for (int i = 0; i < failures; i++)
{
errorList.add(ree.getRow(i));
}
}
}
dataList.clear();
}
}
8【规则】 资源释放
关于ResultScanner和HTable实例,在用完之后,需要调用它们的Close方法,将资源释放掉。Close方法,要放在finally块中,来确保一定会被调用到。
正确示例:
ResultScanner scanner = null;
try
{
scanner = demoTable.getScanner(s);
//Do Something here.
}
finally
{
scanner.close();
}
错误示例:
1. 在代码中未调用scanner.close()方法释放相关资源。
2. scanner.close()方法未放置在finally块中:
ResultScanner scanner = null;
scanner = demoTable.getScanner(s);
//Do Something here.
scanner.close();
9【规则】 Scan时的容错处理
Scan时不排除会遇到异常,例如,租约过期。在遇到异常时,建议Scan应该有重试的操作。
事实上,重试在各类异常的容错处理中,都是一种优秀的实践,这一点,可以应用在各类与HBase操作相关的接口方法的容错处理过程中。
10【规则】 不用HBaseAdmin时,要及时关闭,HBaseAdmin实例不应常驻内存
HBaseAdmin的示例应尽量遵循 “用时创建,用完关闭”的原则。不应该长时间缓存同一个HBaseAdmin实例。
11【规则】 暂时不建议使用HTablePool获取HTable实例,因为当前的HTablePool实现中可能会带来泄露。创建HTable实例的方法,参考上面的规则4。
12【建议】 不要调用HBaseAdmin的closeRegion方法关闭一个Region
HBaseAdmin中,提供了关闭一个Region的接口:
//hostAndPort可以指定,也可以不指定。
public void closeRegion(final String regionname, final String hostAndPort)
通过该方法关闭一个Region, HBase Client端会直接发RPC请求到Region所在的RegionServer上,整个流程对Master而言,是不感知的。也就是说,尽管RegionServer关闭了这个Region,但是,在Master侧,还以为该Region是在该RegionServer上面打开的。假如,在执行Balance的时候,Master计算出恰好要转移这个Region,那么,这个Region将无法被关闭,本次转移操作将无法完成(关于这个问题,在当前的HBase版本中的处理的确还欠缺妥当)。
因此,暂时不建议使用该方法关闭一个Region。
13【建议】 采用PutList模式写数据
HTable类中提供了两种写数据的接口:
1. public void put(final Put put) throws IOException
2. public void put(final List
第1种方法较之第2种方法,在性能上有明显的弱势。因此,写数据时应该采用第2种方法。
14【建议】 Scan时指定StartKey和EndKey
一个有确切范围的Scan,在性能上会带来较大的好处。
代码示例:
Scan scan = new Scan();
scan.addColumn(Bytes.toBytes("familyname"),Bytes.toBytes("columnname"));
scan.setStartRow( Bytes.toBytes("rowA")); // 假设起始Key为rowA
scan.setStopRow( Bytes.toBytes("rowB")); // 假设EndKey为rowB
for(Result result : demoTable.getScanner(scan)) {
// process Result instance
}
15【建议】 不要关闭WAL
WAL是Write-Ahead-Log的简称,是指数据在入库之前,首先会写入到日志文件中,借此来确保数据的安全性。
WAL功能默认是开启的,但是,在Put类中提供了关闭WAL功能的接口:
public void setWriteToWAL(boolean write)
因此,不建议调用该方法将WAL关闭(即将writeToWAL设置为False),因为可能会造成最近1S(该值由RegionServer端的配置参数hbase.regionserver.optionallogflushinterval决定,默认为1S)内的数据丢失。但如果在实际应用中,对写入的速率要求很高,并且可以容忍丢失最近1S内的数据的话,可以将该功能关闭。
16【建议】 创建一张表或Scan时设定blockcache为 true
HBase客户端建表和scan时,设置blockcache=true。需要根据具体的应用需求来设定它的值,这取决于有些数据是否会被反复的查询到,如果存在较多的重复记录,将这个值设置为true可以提升效率,否则,建议关闭。
建议按默认配置,默认就是true,只要不强制设置成false就可以,例如:
HColumnDescriptor fieldADesc = new HColumnDescriptor("value".getBytes());
fieldADesc.setBlockCacheEnabled(false);
17【示例】 Configuration可以设置的参数
为了能够建立一个HBase Client端到HBase Server端的连接,需要设置如下几个参数:
hbase.zookeeper.quorum: Zookeeper的IP。多个Zookeeper节点的话,中间用”,”隔开。
hbase.zookeeper.property.clientPort: Zookeeper的端口。
说明:
通过HBaseConfiguration.create()创建的Configuration实例,会自动加载如下配置文件中的配置项:
1. core-default.xml
2. core-site.xml
3. hbase-default.xml
4. hbase-site.xml
因此,这四个配置文件,应该要放置在“Source Folder”下面(将一个文件夹设置为Source Folder的方法:如果在工程下面建立了一个resource的文件夹,那么,可以在该文件夹上右键鼠标,依次选择”Build Path”->”Use as Source Folder”即可,可参考下图)
下面是客户端可配置的一些参数集合(在通常情况下,这些值都不建议修改):
参数名
参数解释
hbase.client.pause
每次异常或者其它情况下重试等待相关的时间参数(实际等待时间将根据该值与已重试次数计算得出)
hbase.client.retries.number
异常或者其它情况下的重试次数
hbase.client.retries.longer.multiplier
与重试次数有关
hbase.client.rpc.maxattempts
RPC请求不可达时的重试次数
hbase.regionserver.lease.period
与Scanner超时时间有关(单位ms)
hbase.client.write.buffer
在启用AutoFlush的情况下,该值不起作用。如果未启用AotoFlush的话,HBase Client端会首先缓存写入的数据,达到设定的大小后才向HBase集群下发一次写入操作
hbase.client.scanner.caching
Scan时一次next请求获取的行数
hbase.client.keyvalue.maxsize
一条keyvalue数据的最大值
hbase.htable.threads.max
HTable实例中与数据操作有关的最大线程数
hbase.client.prefetch.limit
客户端在写数据或者读取数据时,需要首先获取对应的Region所在的地址。客户端可以预缓存一些Region地址,这个参数就是与缓存的数目有关的配置
正确设置参数的方法:
hbaseConfig = HBaseConfiguration.create();
//如下参数,如果在配置文件中已经存在,则无须再配置
hbaseConfig.set("hbase.zookeeper.quorum", "157.5.100.1,157.5.100.2,157.5.100.3");
hbaseConfig.set("hbase.zookeeper.property.clientPort", "2181");
18【示例】 HTablePool在多线程写入操作中的应用
有多个写数据线程时,可以采用HTablePool。现在先简单介绍下该类的使用方法和注意点:
(1) 多个写数据的线程之间,应共享同一个HTablePool实例。
(2) 实例化HTablePool的时候,应要指定最大的HTableInterface实例个数maxSize,即需要通过如下构造函数实例化该类:
public HTablePool(final Configuration config, final int maxSize)
关于maxSize的值,可以根据写数据的线程数Threads以及涉及到的用户表个数Tables来定,理论上,不应该超过(Threads*Tables)。
(3) 客户端线程通过HTablePool#getTable(tableName)的方法,获取一个表名为tableName的HTableInterface实例。
(4) 同一个HTableInterface实例,在同一个时刻只能给一个线程使用。
(5) 如果HTableInterface使用用完了,需要调用HTablePool#putTable(HTableInterface table)方法将它放回去。
示例代码:
/**
* 写数据失败后需要一定的重试次数,每一次重试的等待时间,需要根据已经重试的次数而定.
*/
private static final int[] RETRIES_WAITTIME = {1, 1, 1, 2, 2, 4, 4, 8, 16, 32};
/**
* 限定的重试次数
*/
private static final int RETRIES = 10;
/**
* 失败后等待的基本时间单位
*/
private static final int PAUSE_UNIT = 1000;
private static Configuration hadoopConfig;
private static HTablePool tablePool;
private static String[] tables;
/**
* <初始化HTablePool>
* <功能详细描述>
* @param config
* @see [类、类#方法、类#成员]
*/
public static void initTablePool()
{
DemoConfig config = DemoConfig.getInstance();
if (hadoopConfig == null)
{
hadoopConfig = HBaseConfiguration.create();
hadoopConfig.set("hbase.zookeeper.quorum", config.getZookeepers());
hadoopConfig.set("hbase.zookeeper.property.clientPort", config.getZookeeperPort());
}
if (tablePool == null)
{
tablePool = new HTablePool(hadoopConfig, config.getTablePoolMaxSize());
tables = config.getTables().split(",");
}
}
public void run()
{
// 初始化HTablePool.因为这是多线程间共享的一个实例, 仅被实例化一次.
initTablePool();
for (;;)
{
Map
String tableName = tables[(Integer)data.get("table")];
List
// 以Row为Key,保存List中所有的Put.该集合仅仅使用于写入失败时查找失败的数据记录.
// 因为从Server端仅仅返回了失败的数据记录的Row值.
Map
// 如果失败了(哪怕是部分数据失败),需要重试.每一次重试,都仅仅提交失败的数据条目
INNER_LOOP :
for (int retry = 0; retry < RETRIES; retry++)
{
// 从HTablePool中获取一个HTableInterface实例.用完后需要放回去.
HTableInterface table = tablePool.getTable(tableName);
try
{
table.put(list);
// 如果执行到这里,说明成功了 .
break INNER_LOOP;
}
catch (IOException e)
{
// 如果是RetriesExhaustedWithDetailsException类型的异常,
// 说明这些数据中有部分是写入失败的这通常都是因为HBase集群
// 的进程异常引起,当然有时也会因为有大量的Region正在被转移,
// 导致尝试一定的次数后失败.
// 如果非RetriesExhaustedWithDetailsException异常,则需要将
// list中的所有数据都要重新插入.
if (e instanceof RetriesExhaustedWithDetailsException)
{
RetriesExhaustedWithDetailsException ree =
(RetriesExhaustedWithDetailsException)e;
int failures = ree.getNumExceptions();
System.out.println("本次插入失败了[" + failures + "]条数据.");
// 第一次失败且重试时,实例化该Map.
if (rowPutMap == null)
{
rowPutMap = new HashMap
for (int m = 0; m < list.size(); m++)
{
Put put = list.get(m);
rowPutMap.put(put.getRow(), put);
}
}
//先Clear掉原数据,然后将失败的数据添加进来
list.clear();
for (int m = 0; m < failures; m++)
{
list.add(rowPutMap.get(ree.getRow(m)));
}
}
}
finally
{
// 用完之后,再将该实例放回去
tablePool.putTable(table);
}
// 如果异常了,就暂时等待一段时间.该等待应该在将HTableInterface实例放回去之后
try
{
sleep(getWaitTime(retry));
}
catch (InterruptedException e1)
{
System.out.println("Interruped");
}
}
}
}
19【示例】 Put实例的创建
HBase是一个面向列的数据库,一行数据,可能对应多个列族,而一个列族又可以对应多个列。通常,写入数据的时候,我们需要指定要写入的列(含列族名称和列名称):
如果要往HBase表中写入一行数据,需要首先构建一个Put实例。Put中包含了数据的Key值和相应的Value值,Value值可以有多个(即可以有多列值)。
有一点需要注意:在往Put实例中add一条KeyValue数据时,传入的family,qualifier,value都是字节数组。在将一个字符串转换为字节数组时,需要使用Bytes.toBytes方法,不要使用String.toBytes方法,因为后者无法保证编码,尤其是在Key或Value中出现中文字符的时候,就会出现问题。
代码示例:
//列族的名称为privateInfo
private final static byte[] FAMILY_PRIVATE = Bytes.toBytes("privateInfo");
//列族privateInfo中总共有两个列"name"&"address"
private final static byte[] COLUMN_NAME = Bytes.toBytes("name");
private final static byte[] COLUMN_ADDR = Bytes.toBytes("address");
/**
* <创建一个Put实例>
* <在该方法中,将会创建一个具有1个列族,2列数据的Put>
* @param rowKey Key值
* @param name 人名
* @param address 地址
* @return
* @see [类、类#方法、类#成员]
*/
public Put createPut(String rowKey, String name, String address)
{
Put put = new Put(Bytes.toBytes(rowKey));
put.add(FAMILY_PRIVATE, COLUMN_NAME, Bytes.toBytes(name));
put.add(FAMILY_PRIVATE, COLUMN_ADDR, Bytes.toBytes(address));
return put;
}
20【示例】 HBaseAdmin实例的创建以及常用方法
代码示例:
private Configuration demoConf = null;
private HBaseAdmin hbaseAdmin = null;
/**
* <构造函数>
* 需要将已经实例化好的Configuration实例传递进来
*/
public HBaseAdminDemo(Configuration conf)
{
this.demoConf = conf;
try
{
// 实例化HBaseAdmin
hbaseAdmin = new HBaseAdmin(this.demoConf);
}
catch (MasterNotRunningException e)
{
e.printStackTrace();
}
catch (ZooKeeperConnectionException e)
{
e.printStackTrace();
}
}
/**
* <一些方法使用示例>
* <更多的方法,请参考HBase接口文档>
* @throws IOException
* @throws ZooKeeperConnectionException
* @throws MasterNotRunningException
* @see [类、类#方法、类#成员]
*/
public void demo() throws MasterNotRunningException, ZooKeeperConnectionException, IOException
{
byte[] regionName = Bytes.toBytes("mrtest,jjj,1315449869513.fc41d70b84e9f6e91f9f01affdb06703.");
byte[] encodeName = Bytes.toBytes("fc41d70b84e9f6e91f9f01affdb06703");
// 重新分配一个Reigon.
hbaseAdmin.unassign(regionName, false);
// 主动触发Balance.
hbaseAdmin.balancer();
// 移动一个Region,第2个参数,是RegionServer的HostName+StartCode,例如:
// host187.example.com,60020,1289493121758.如果将该参数设置为null,则会随机移动该Region
hbaseAdmin.move(encodeName, null);
// 判断一个表是否存在
hbaseAdmin.tableExists("tableName");
// 判断一个表是否被激活
hbaseAdmin.isTableEnabled("tableName");
}
/**
* <快速创建一个表的方法>
* <首先创建一个HTableDescriptor实例,它里面包含了即将要创建的HTable的描述信息,同时,需要创建相应的列族。列族关联的实例是HColumnDescriptor。在本示例中,创建的列族名称为“columnName”>
* @param tableName 表名
* @return
* @see [类、类#方法、类#成员]
*/
public boolean createTable(String tableName)
{
try {
if (hbaseAdmin.tableExists(tableName)) {
return false;
}
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
HColumnDescriptor fieldADesc = new HColumnDescriptor("columnName".getBytes());
fieldADesc.setBlocksize(640 * 1024);
tableDesc.addFamily(fieldADesc);
hbaseAdmin.createTable(tableDesc);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
附 1 Scan时的两个关键参数—Batch和Caching
Batch:使用scan调用next接口每次最大返回的记录数,与一次读取的列数有关。
Caching:一个RPC查询请求最大的返回的next数目,与一次RPC获取的行数有关。
首先举几个例子,来介绍这两个参数在Scan时所起到的作用:
假设表A的一个Region中存在2行(rowkey)数据,每行有1000column,且每列当前只有一个version,即每行就会有1000个key value。
ColuA1
ColuA2
ColuA3
ColuA4
………
ColuN1
ColuN2
ColuN3
ColuN4
Row1
………
Row2
………
例1: 查询参数: 不设batch,设定caching=2
那么,一次RPC请求,就会返回2000个KeyValue.
例2: 查询参数: 设定batch=500,设定caching=2
那么,一次RPC请求,只能返回1000个KeyValue.
例3: 查询参数: 设定batch=300,设定caching=4
那么,一次RPC请求,也只能返回1000个KeyValue.
关于Batch和Caching的进一步解释:
Ø 一次Caching,是一次请求数据的机会。
Ø 同一行数据是否可以通过一次Caching读完,取决于Batch的设置,如果Batch的值小于一行的总列数,那么,这一行至少需要2次Caching才可以读完(后面的一次Caching的机会,会继续前面读取到的位置继续读取)。
Ø 一次Caching读取,不能跨行。如果某一行已经读完,并且Batch的值还没有达到设定的大小,也不会继续读下一行了。
那么,关于例1与例2的结果,就很好解释了:
例1的解释:
不设定Batch的时候,默认会读完改行所有的列。那么,在caching为2的时候,一次RPC请求就会返回2000个KeyValue。
例2的解释:
设定Batch为500,caching为2的情况下,也就是说,每一次Caching,最多读取500列数据。那么,第一次Caching,读取到500列,剩余的500列,会在第2次Caching中读取到。因此,两次Caching会返回1000个KeyValue。
例3的解释:
设定Batch为300,caching为4的情况下,读取完1000条数据,正好需要4次caching。因此,只能返回1000条数据。
代码示例:
Scan s = new Scan();
//设置查询的起始key和结束key
s.setStartRow(Bytes.toBytes("01001686138100001"));
s.setStopRow(Bytes.toBytes("01001686138100002"));
s.setBatch(1000);
s.setCaching(100);
ResultScanner scanner = null;
try {
scanner = tb.getScanner(s);
for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
for (KeyValue kv : rr.raw()) {
//显示查询的结果
System.out.println("key:" + Bytes.toString(kv.getRow())
+ "getQualifier:" + Bytes.toString(kv.getQualifier())
+ "value" + Bytes.toString(kv.getValue()));
}
}
} catch (IOException e) {
System.out.println("error!" + e.toString());
} finally {
scanner.close();
}
EI企业智能
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。