1 概述 (General Notes)
HBase 的主要客户端入口点是 org.apache.hadoop.hbase.client 包的 Table interface. 该接口为用户提供了向 HBase 存储和获取数据全部必须的功能,
以及删除无效数据之类的操作。它通过 Connection 实例建立与 HBase 服务器连接。不过,在讨论这些类提供的各种方法之前,让我们了解一些这些用法的
总体方面。
所有修改数据的操作都保证了行级别的原子性(guaranteed to be atomic on a per-row basis), 这会影响到这一行数据所有的并发读写操作。换句话说,
其它客户端或线程对同一行的读写操作都不会影响改行的原子性:要么读到最新的一致性数据,要么必须等待系统允许写入该行。
在常规操作和正常负载情况下,客户端的读操作不会受到其它修改该行数据的客户端的影响,因为它们之间的竞争可以忽略不计。但是,当很多客户端需要
同时修改同一行数据时就会产生问题。因此,用户应当尽量使用批处理 (batch) 更新来减少单独操作同一行数据的次数。
也不用担心对于一个特定的行可以写入多少的列,所有的列都由这个行原子性保证。
最后,创建一个到 HBase 的初始连接不是没有代价的。每个实例化包括扫描 hbase:meta 表以检查该表是否存在,和是否启用,以及其它操作,使得该调用
非常繁重。因此建议仅创建一次 Connection 实例,然后在客户端应用中重用该实例。
一旦有了一个连接实例就可以获取实际的 table. 理想情况下,每个线程获取一个 table, 因为 Table 底层实现不保证线程安全。确保关闭所有得到的资源。
2 数据类型和层次结构 (Data Types and Hierarchy)
-----------------------------------------------------------------------------------------------------------------------------------------
在深入研究实际的操作和 API 类之前,首先看一下这些类如何关联。有一些低级别的类提供非常基本的功能,表现为大多数以数据为中心的类 (the
data-centric classes), 如 Put, Get, 或 Scan, 下表列出本章介绍的所有基本的数据相关的类型:
List of basic data-centric types
+---------------+-----------+-----------------------------------------------------------------------------
| Type | Kind | Description
+---------------+-----------+-----------------------------------------------------------------------------
| Get | Query | Retrieve previously stored data from a single row.
+---------------+-----------+-----------------------------------------------------------------------------
| Scan | Query | Iterate over all or specific rows and return their data.
+---------------+-----------+-----------------------------------------------------------------------------
| Put | Mutation | Create or update one or more columns in a single row.
+---------------+-----------+-----------------------------------------------------------------------------
| Delete | Mutation | Remove a specific cell, column, row, etc.
+---------------+-----------+-----------------------------------------------------------------------------
| Increment | Mutation | Treat a column as a counter and increment its value.
+---------------+-----------+-----------------------------------------------------------------------------
| Append | Mutation | Attach the given data to one or more columns in a single row.
+---------------+-----------+-----------------------------------------------------------------------------
2.1 通用属性 (Generic Attributes)
-----------------------------------------------------------------------------------------------------------------------------------------
Attributes 是基础接口之一,提供如下方法:
Attributes setAttribute(String name, byte[] value)
byte[] getAttribute(String name)
Map<String, byte[]> getAttributesMap()
All Known Implementing Classes:
Append, Delete, Get, Increment, Mutation, OperationWithAttributes, Put, Query, Scan
它提供了一个通用的机制以属性(attribute)的形式向 data-centric 类添加任何类型的信息。默认情况下,没有任何属性集,开发人员可以在需要的时候通过
setAttribute() 方法添加自定义属性。因为多数时候一个数据类型的构造,例如 Put, 会立即跟一个 API 调用,把它发送到服务器上,一个问题是:能在什么
地方使用属性呢(attribute)?
要注意的一件事是 attribute 是被序列化的,并被发送到服务器端,意味着可以使用它们获取其中的值,例如,在一个协处理器上(a coprocessor). 另一个
使用 attribute 的场景是 Append 类,在 call 到服务器之后,利用 attribute 向用户返回信息。
2.2 操作 (Operations: Fingerprint and ID)
-----------------------------------------------------------------------------------------------------------------------------------------
另一个基础类型是抽象类 Operation, 它向所有数据类型添加如下方法:
abstract Map<String, Object> getFingerprint()
abstract Map<String, Object> toMap(int maxCols)
Map<String, Object> toMap()
String toJSON(int maxCols) throws IOException
String toJSON() throws IOException
String toString(int maxCols)
String toString()
Direct Known Subclasses:
OperationWithAttributes
Superclass for any type that maps to a potentially application-level query. (e.g. Put, Get, Delete, Scan, Next, etc.) Contains methods for
exposure to logging and debugging tools.
这些方法由 HBase 0.92 引入,并帮助生成日志和通用调试信息的有用的信息集合。所有后面的方法依赖于 toMap(int maxCols) 方法的实现,Operation 中的
抽象方法。Mutation 类为所有继承的数据类型实现了该方法。如果没有明确指定,默认包含在输出中的列的数量为 5.
另外,中间类 OperationWithAttributes 扩展了上述的 Operation 类,实现了 Attributes 接口,并添加了如下方法:
OperationWithAttributes setId(String id)
String getId()
ID 是一个客户端提供的值,用于记录日志时识别 operation.
The various methods to retrieve instance information
+-----------------------+---------------------------------------------------------------------------------------+
| Method | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| getId() | Returns what was set by the setId() method. |
+-----------------------+---------------------------------------------------------------------------------------+
| getFingerprint() | Returns the list of column families included in the instance. |
+-----------------------+---------------------------------------------------------------------------------------+
| toMap(int maxCols) | Compiles a list including fingerprint, column families with all columns and their |
| | data, total column count, row key, and—if set—the ID and cell-level TTL. |
+-----------------------+---------------------------------------------------------------------------------------+
| toMap() | Same as above, but only for 5 columns. |
+-----------------------+---------------------------------------------------------------------------------------+
| toJSON(int maxCols) | Same as toMap(maxCols) but converted to JSON. Might fail due to encoding issues. |
+-----------------------+---------------------------------------------------------------------------------------+
| toJSON() | Same as above, but only for 5 columns. |
+-----------------------+---------------------------------------------------------------------------------------+
| toString(int maxCols) | Attempts to call toJSON(maxCols), but when it fails, falls back to toMap(maxCols). |
+-----------------------+---------------------------------------------------------------------------------------+
| toString() | Same as above, but only for 5 columns |
+-----------------------+---------------------------------------------------------------------------------------+
2.3 查询和数据修改操作 (Query versus Mutation)
-----------------------------------------------------------------------------------------------------------------------------------------
Row 接口,提供如下方法:
byte[] getRow()
该方法简单地返回一个给定行的 row key 实例。Get 类实现了该方法,因其正好处理一个行。Mutation 超类也实现了该方法,作为修改数据所需类型的基类
另外,Mutation 也实现了 CellScannable interface, 它提供如下方法:
CellScanner cellScanner()
通过该接口,客户端可以迭代返回的单元格(cell). Mutation 还有些其它方法由所有继承类共享。下面是它的一个常用方法列表:
Methods provided by the Mutation superclass
+-----------------------+---------------------------------------------------------------------------------------+
| Method | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| getACL()/setACL() | The Access Control List (ACL) for this operation |
+-----------------------+---------------------------------------------------------------------------------------+
| getCellVisibility() | The cell level visibility for all included cells. |
| setCellVisibility() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getClusterIds() | The cluster ID as needed for replication purposes. |
| setClusterIds() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getDurability() | The durability settings for the mutation. |
| setDurability() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getFamilyCellMap() | The list of all cells per column family available in this instance. |
| setFamilyCellMap() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getTimeStamp() | Retrieves the associated timestamp of the Put instance. Can be optionally set using |
| | the constructor’s ts parameter. If not set, may return Long.MAX_VALUE (also defined as|
| | HConstants.LATEST_TIMESTAMP). |
+-----------------------+---------------------------------------------------------------------------------------+
| getTTL() | Sets the cell level TTL value, which is being applied to all included Cell instances |
| setTTL() | before being persisted. |
+-----------------------+---------------------------------------------------------------------------------------+
| heapSize() | Computes the heap space required for the current Put instance. This includes all |
| | contained data and space needed for internal structures. |
+-----------------------+---------------------------------------------------------------------------------------+
| isEmpty() | Checks if the family map contains any Cell instances. |
+-----------------------+---------------------------------------------------------------------------------------+
| numFamilies() | Convenience method to retrieve the size of the family map, containing all Cell instances.
+-----------------------+---------------------------------------------------------------------------------------+
| size() | Returns the number of Cell instances that will be added with this Put. |
+-----------------------+---------------------------------------------------------------------------------------+
另一个在获取方面较大的超级类是 Query, 为从 HBase 表读取数据相关的所有数据类型提供一个通用层。下表展示了其提供的方法:
Methods provided by the Query superclass
+-----------------------+---------------------------------------------------------------------------------------+
| Method | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| getAuthorizations() | Visibility labels for the operation. |
| setAuthorizations() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getACL()/setACL() | The Access Control List (ACL) for this operation. |
+-----------------------+---------------------------------------------------------------------------------------+
| getFilter()/setFilter() The filters that apply to the retrieval operation. |
+-----------------------+---------------------------------------------------------------------------------------+
| getConsistency() | The consistency level that applies to the current query instance. |
| setConsistency() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getIsolationLevel() | Specifies the read isolation level for the operation. |
| setIsolationLevel() | |
+-----------------------+---------------------------------------------------------------------------------------+
| getReplicaId() | Gives access to the replica ID that served the data. |
| setReplicaId() | |
+-----------------------+---------------------------------------------------------------------------------------+
2.4 Durability, Consistency, and Isolation
-----------------------------------------------------------------------------------------------------------------------------------------
Durability, 如 Mutation 类的 setDurability() method 涉及的概念,写操作过程的一部分,durabilty 涉及到服务器如何处理客户端发过来的更新。下表
列出 Durability enumeration 提供的选项:
Durability levels
+-----------------------+---------------------------------------------------------------------------------------+
| Method | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| USE_DEFAULT | For tables use the global default setting, which is SYNC_WAL. For a mutation use the |
| | table’s default value. |
+-----------------------+---------------------------------------------------------------------------------------+
| SKIP_WAL | Do not write the mutation to the WAL. |
+-----------------------+---------------------------------------------------------------------------------------+
| ASYNC_WAL | Write the mutation asynchronously to the WAL. |
+-----------------------+---------------------------------------------------------------------------------------+
| SYNC_WAL | Write the mutation synchronously to the WAL. |
+-----------------------+---------------------------------------------------------------------------------------+
| FSYNC_WAL | Write the Mutation to the WAL synchronously and force the entries to disk. |
+---------------------------------------------------------------------------------------------------------------+
注:WAL 表示预写日志(write-ahead log, WAL), 是保护数据安全的核心机制。
读方面,由两个设置控制,首先是一致性级别(consistency level), 由 Query 基类的 setConsistency() 和 getConsistency() 方法使用。由 Consistency
enumeration 提供:
Consistency Levels
+-----------------------+---------------------------------------------------------------------------------------+
| Method | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| STRONG | Strong consistency as per the default of HBase. Data is always current. |
+-----------------------+---------------------------------------------------------------------------------------+
| TIMELINE | Replicas may not be consistent with each other, but updates are guaranteed to be |
| | applied in the same order at all replicas. Data might be stale! |
+---------------------------------------------------------------------------------------------------------------+
处置读方面的最后的级别是隔离级别(isolation level), 用于 Query 超类的 setIsolationLevel() 和 getIsolationLevel() 方法:
Isolation Levels
+-----------------------+---------------------------------------------------------------------------------------+
| Method | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| READ_COMMITTED | Read only data that has been committed by the authoritative server |
+-----------------------+---------------------------------------------------------------------------------------+
| READ_UNCOMMITTED | Allow reads of data that is in flight, i.e. not committed yet. |
+-----------------------+---------------------------------------------------------------------------------------+
通常,客户端读取数据期望看到只提交的数据,但服务也有选项可以读取任何服务器上已存在的数据。应用 READ_UNCOMMITTED 设置时要小心谨慎,其结果
完全依赖于写入的模式。
2.5 单元格 (The Cell)
-----------------------------------------------------------------------------------------------------------------------------------------
客户端代码可能必须直接与 Cell 实例一起工作。Cell 实例包含数据,及特定 Cell 的坐标(coordinates). 坐标是 row key, 列族名称,列修饰符(
qualifier) 以及时间戳(timestamp). 此接口提供了访问低级别的细节:
getRowArray(), getRowOffset(), getRowLength()
getFamilyArray(), getFamilyOffset(), getFamilyLength()
getQualifierArray(), getQualifierOffset(), getQualifierLength()
getValueArray(), getValueOffset(), getValueLength()
getTagsArray(), getTagsOffset(), getTagsLength()
getTimestamp()
getTypeByte()
getSequenceId()
因为 Cell 仅仅是一个接口,不能简单创建其实例。其实现类为 KeyValue, 是一个私有的类,也不能实例化。CellUtil 类,为我们提供了必要的方法创建
Cell 实例:
static Cell createCell(final byte[] row, final byte[] family,
final byte[] qualifier, final long timestamp, final byte type,
final byte[] value)
static Cell createCell(final byte[] rowArray, final int rowOffset,
final int rowLength, final byte[] familyArray, final int family‐
Offset,final int familyLength, final byte[] qualifierArray,
final int qualifierOffset, final int qualifierLength)
static Cell createCell(final byte[] row, final byte[] family,
final byte[] qualifier, final long timestamp, final byte type,
final byte[] value, final long memstoreTS)
static Cell createCell(final byte[] row, final byte[] family,
final byte[] qualifier, final long timestamp, final byte type,
final byte[] value, byte[] tags, final long memstoreTS)
static Cell createCell(final byte[] row, final byte[] family,
final byte[] qualifier, final long timestamp, Type type,
final byte[] value, byte[] tags)
static Cell createCell(final byte[] row)
static Cell createCell(final byte[] row, final byte[] value)
static Cell createCell(final byte[] row, final byte[] family,
final byte[] qualifier)
通常,根本不需要显示创建 Cell, 当添加 column 时系统会为我们创建,如 Put 或 Delete 实例。可以之后再次获取它们,如利用下面的 Query 和 Mutation
方法:
CellScanner cellScanner()
NavigableMap<byte[], List<Cell>> getFamilyCellMap()
数据及其坐标信息存储在 Java 的 byte[] 中,一个字节数组。如此设计的背后是,低级别存储的类型允许任意数据,能有效存储必要的字节,保持内部数据结构
的开销最小。这也是每个数组参数有一个 Offset 和 Length 参数的原因。它们允许在进行非常快速的字节级别操作时传递现有的字节数组。并且,对于每个成员
坐标, Cell 接口都有一个 getter 方法来获取字节数组,及它们给出的 offset 和 length.
CellUtil 类有很多有用的方法,帮助 HBase 客户端开发者更轻松地处理 Cell. 例如可以 clone cell 的各个部分,如,行或值。
The possible type values for a given Cell instance
+-----------------------+---------------------------------------------------------------------------------------+
| Type | Description |
+-----------------------+---------------------------------------------------------------------------------------+
| Put | The Cell instance represents a normal Put operation. |
+-----------------------+---------------------------------------------------------------------------------------+
| Delete | This instance of Cell represents a Delete operation, also known as a tombstone marker.|
+-----------------------+---------------------------------------------------------------------------------------+
| DeleteFamilyVersion | This is the same as Delete, but more broadly deletes all columns of a column family |
| | matching a specific timestamp. |
+-----------------------+---------------------------------------------------------------------------------------+
| DeleteColumn | This is the same as Delete, but more broadly deletes an entire column. |
+-----------------------+---------------------------------------------------------------------------------------+
| DeleteFamily | This is the same as Delete, but more broadly deletes an entire column family, |
| | including all contained columns. |
+---------------------------------------------------------------------------------------------------------------+
可以查看 Cell 实例的类型,如利用 getTypeByte() 方法,或者使用 CellUtil.isDeleteFamily(cell) 以及其它类似的方法。
可以联合使用 cellScanner() 和 Cell.toString() 方法以人类可读的方式查看 cell 类型,如下示例:
//Shows how to use the cell scanner
Put put = new Put(Bytes.toBytes("testrow"));
put.addColumn(Bytes.toBytes("fam-1"), Bytes.toBytes("qual-1"),
Bytes.toBytes("val-1"));
put.addColumn(Bytes.toBytes("fam-1"), Bytes.toBytes("qual-2"),
Bytes.toBytes("val-2"));
put.addColumn(Bytes.toBytes("fam-2"), Bytes.toBytes("qual-3"),
Bytes.toBytes("val-3"));
CellScanner scanner = put.cellScanner();
while (scanner.advance()) {
Cell cell = scanner.current();
System.out.println("Cell: " + cell);
}
输出:
Cell: testrow/fam-1:qual-1/LATEST_TIMESTAMP/Put/vlen=5/seqid=0
Cell: testrow/fam-1:qual-2/LATEST_TIMESTAMP/Put/vlen=5/seqid=0
Cell: testrow/fam-2:qual-3/LATEST_TIMESTAMP/Put/vlen=5/seqid=0
示例打印出当前 Cell 实例的元数据信息,以如下格式:
<row-key>/<family>:<qualifier>/<version>/<type>/<value-length>/<sequence-id>
■ 数据的版本化 (Versioning of Data)
-------------------------------------------------------------------------------------------------------------------------------------
HBase 的一个特殊的特性是具有为每个 cell 存储多个版本的能力。这是通过每个版本的时间戳实现的,并将它们按降序排序。每个时间戳是一个长整型
值代表毫秒数。它记录了从 UTC 1970 年 1 月 1 日午夜 0 点到现在经历的时间 —— 也称为 Unix time, 或 Unix 纪元(epoch). 大多数操作系统提供了
计时器,从编程语言可以读取。 Java 中,可以使用 System.currentTimeMillis() 读取计时器的值。
当向 HBase 中放置 (put) 一个值时,有机会显式地向其提供一个时间戳,或者忽略该值,在之后的 put 操作执行时由 Region server 填充。
正如之前 “Requirements” 章节提示的那样,必须确保集群中的服务器有正确的时间并且彼此同步。客户端有可能失去用户的控制,因此会有不同的时间
可能使几个小时,也有可能会是几年。
只要在客户端 API 调用中没有指定时间,服务器的时间就会应用。
大多数应用从不担心版本化问题,并且依赖于 HBase 内置的时间戳处理,如果使用自己显式的方式处理,需要了解一些问题。
下面是一个示例:
hbase(main):001:0> create 'test', { NAME => 'cf1', VERSIONS => 3 }
0 row(s) in 0.1540 seconds
=> Hbase::Table - test
hbase(main):002:0> put 'test', 'row1', 'cf1', 'val1'
0 row(s) in 0.0230 seconds
hbase(main):003:0> put 'test', 'row1', 'cf1', 'val2'
0 row(s) in 0.0170 seconds
hbase(main):004:0> scan 'test'
ROW COLUMN+CELL
row1 column=cf1:, timestamp=1426248821749, value=
val2
1 row(s) in 0.0200 seconds
hbase(main):005:0> scan 'test', { VERSIONS => 3 }
ROW COLUMN+CELL
row1 column=cf1:, timestamp=1426248821749, value=val2
row1 column=cf1:, timestamp=1426248816949, value=val1
1 row(s) in 0.0230 seconds
示例中创建了一个表 test, 带有一个列族 cf1, 并指示 HBase 每个 cell 保留三个版本(默认为 1 )。然后向同一行、同一列发出两个 put command,
但是两个不同的值:分别为 val1 和 val2。 然后执行 scan 操作来查看表的整个内容,没有像想象的那样只显示 val2, 可能只是想通过第二个 put
调用替换掉 val1 值。结果显示了两个版本的值 val2, val1.
最后,有一个 CellComparator 类,形成利用 Java Comparator 模式比较给定 cell 实例的类的基础。CellComparator 类有一个内部类 RowComparator
公开可用。可用利用这个类仅仅通过它们的 row 组件比较 cell, 换句话说,给定的 row key.
2.6 API 构建 (API Building Blocks)
-----------------------------------------------------------------------------------------------------------------------------------------
介绍完底层 API 类和它们的功能之后,再来看下基本的客户端 API. 通常需要通过以下的代码过程连接到 HBase 实例,可以是单机模式的本地实例,伪分
布式的实例,或者远程集群上的完全分布式实例。客户端连接和 API 调用的基本流程类似如下代码:
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
TableName tableName = TableName.valueOf("testtable");
Table table = connection.getTable(tableName);
...
Result result = table.get(get);
...
table.close();
connection.close();
有几个类需要简单介绍下:
● Configuration
-------------------------------------------------------------------------------------------------------------------------------------
这个来来自 Hadoop, 由 HBase 共享过来,为客户端应用载入并提供配置信息。它从配置文件 hbase-site.xml 和 hbase-default.xml 载入详细信息。
● ConnectionFactory
-------------------------------------------------------------------------------------------------------------------------------------
提供了工厂方法来获取 Connection 实例,由给定的 Configuration 提供配置信息。
● Connection
-------------------------------------------------------------------------------------------------------------------------------------
实际的连接对象。每个应用程序只需创建一个 Connection 实例,并在运行期间共享该实例。不再需要时应关闭 Connection 实例以释放资源。
● TableName
-------------------------------------------------------------------------------------------------------------------------------------
表示为一个带有名称空间的表名称。
Immutable POJO class for representing a table name. Which is of the form: <table namespace>:<table qualifier> Two special namespaces:
1. hbase - system namespace, used to contain hbase internal tables
2. default - tables with no explicit specified namespace will automatically fall into this namespace.
示例:
a) foo:bar, means namespace=foo and qualifier=bar
b) bar, means namespace=default and qualifier=bar
c) default:bar, means namespace=default and qualifier=bar
● Table
-------------------------------------------------------------------------------------------------------------------------------------
客户端 API 中轻型的、非线程安全的数据表的表现形式。按每个线程来创建一个实例,并且在不再需要时,关闭它以释放资源。
在实践中,应小心仔细地以一种可靠的形式分配和释放 HBase 客户端资源。示例代码 GetTryWithResourcesExample.java 演示了以 Java 7 的新特性
try-with-resources 获取并释放资源。
■ Accessing Configuration Files from Client Code
-------------------------------------------------------------------------------------------------------------------------------------
"Client Configuration" 一节介绍了 HBase 客户端应用使用的配置文件。需要访问 hbase-site.xml 以获取集群所在的位置信息 —— 或者,需要在
代码中指定该位置信息。
不管采用哪种方法,都需要在代码中通过 HBaseConfiguration 类来处理配置属性。这由该类提供的两个静态方法来完成:
static Configuration create()
static Configuration create(Configuration that)
调用这两个方法时,其底层的代码会在当前 Java 类路径内载入两个配置文件:hbase-default.xml 和 hbase-site.xml
如果指定一个现有的配置(an existing configuration), 使用第二个方法 create(Configuration that), that 参数指定的配置信息会拥有最高的优先级
HBaseConfiguration 实际上扩展自 Hadoop Configuration 类,但仍与之兼容:可以在 Hadoop Configuration 实例中配置信息,然后会很好地合并到
HBaseConfiguration 实例中。
获取到一个 HBaseConfiguration 实例之后,就有了一个合并的配置信息,由默认值(hbase-default.xml 中的配置值),被 hbase-site.xml 覆盖的配置,
和可选的已存在的配置信息组成。可以随意修改此配置信息,然后利用该配置创建 Connection 实例。例如,可以覆盖掉 ZooKeeper quorum 的地址信息,
以指向另一个集群:
Configuration config = HBaseConfiguration.create();
config.set("hbase.zookeeper.quorum","zk1.foo.com,zk2.foo.com");
换句话说,可以简单地在代码中设置属性来忽略掉外部的客户端配置文件中指定的配置信息。这种方法,不需要额外的配置就可以创建一个客户端程序。
■ 资源共享 (Resource Sharing)
-------------------------------------------------------------------------------------------------------------------------------------
每个 Table 实例要求一个与远程服务器的连接。这是通过 ConnectionFactory 获取的 Connection 的实现实例处理的。但为什么在应用中不给每个
Table 实例创建一个连接呢?为什么在应用中只创建一个连接实例并在整个应用中共享该实例?因为每个连接要做很多的内部资源处理:
● 共享 ZooKeeper 连接 (Share ZooKeeper Connections)
---------------------------------------------------------------------------------------------------------------------------------
由于每个客户端实际上需要一个到 ZooKeeper 集合体(ZooKeeper ensemble) 的连接来初始化查找用户表分区的位置,建立这个连接然后与之后的
客户端实例共享该实例是明智的。
● 缓存通用资源 (Cache Common Resources)
---------------------------------------------------------------------------------------------------------------------------------
每次用户表分区位置信息的查找都通过 ZooKeeper, 或者目录表(catalog tables)执行,这需要多次的网络往返操作。将位置信息缓存到客户端
有利于降低网络流量,并提升查找过程的速度。这与每个本地客户端连接到一个远程集群实际上是相同的,因此,在同一进程中的多个客户端间共
享连接有同等的好处。这是通过共享 Connection 实例实现的。
另外,当一个查找失败时,例如,当一个分区拆分时,连接有其内置的重试机制可以刷新过时的缓存信息。之后新的信息可以立刻被同一连接内的
其它共享线程使用,从而继续降低由客户端发起的网络流量。
共享连接资源的缺点是清理:如果没有显式关闭一个连接,该连接会一直打开直到客户端进程退出。这可能会导致多个连接保留打开 ZooKeeper, 特别是
是繁重的分布式应用,如与 HBase 对话的 MapReduce 作业。最坏的情况下,会耗尽可用的连接,并收到 IOException 异常。
避免这种问题出现的办法是在使用完共享连接之后显式地关闭该连接。可用通过 Connection 提供的 Close() method 完成。该调用会降低其内部的引用
计数器并最终关闭所有的共享资源,如到 ZooKeeper 集合体的连接,并从其内部列表中移除该连接的引用。
系列目录:
参考:
《HBase - The Definitive Guide - 2nd Edition》Early release —— 2015.7 Lars George