当前 Assignment Manager V2 与区域副本不兼容,因此可能会破坏此功能。请谨慎使用。
从结构上看,HBase 从一开始就始终具有强大的一致性保证。所有读取和写入都通过单个区域服务器进行路由,这可确保所有写入按顺序发生,并且所有读取都会查看最新提交的数据。
但是,由于将读取单一归位到单个位置,如果服务器变得不可用,则区域服务器中托管的表的区域将在一段时间内不可用。区域恢复过程分为三个阶段 - 检测,分配和恢复。其中,检测通常是最长的,目前大约 20-30 秒,具体取决于 ZooKeeper 会话超时。在此期间和恢复完成之前,客户端将无法读取区域数据。
但是,对于某些用例,要么数据可能是只读的,要么对某些陈旧数据进行读取是可以接受的。通过时间线一致的高可用读取,HBase 可用于这类延迟敏感的用例,其中应用程序可能期望在读取完成时具有时间限制。
为了实现读取的高可用性,HBase 提供了一种称为 _ 区域复制 _ 的功能。在此模型中,对于表的每个区域,将有多个副本在不同的 RegionServers 中打开。默认情况下,区域复制设置为 1,因此只部署了一个区域副本,并且不会对原始模型进行任何更改。如果区域复制设置为 2 或更多,则主服务器将分配表的区域的副本。负载均衡器可确保区域副本不在同一区域服务器中共存,也可在同一机架中共存(如果可能)。
单个区域的所有副本将具有从 0 开始的唯一 replica_id。具有 replica_id == 0 的区域副本被称为主要区域,而其他区域 _ 次要区域 _ 或次要区域。只有主服务器可以接受来自客户端的写入,主服务器将始终包含最新的更改。由于所有写入仍然必须通过主区域,因此写入不是高度可用的(这意味着如果区域变得不可用,它们可能会阻塞一段时间)。
通过此功能,HBase 引入了一致性定义,可以根据读取操作(获取或扫描)提供该定义。
public enum Consistency {
STRONG,
TIMELINE
}
Consistency.STRONG
是 HBase 提供的默认一致性模型。如果表具有 region replication = 1,或者在具有区域副本的表中但读取是使用此一致性完成的,则读取始终由主要区域执行,因此不会对先前的行为进行任何更改,并且客户端始终观察最新数据。
如果使用Consistency.TIMELINE
执行读取,则首先将读取的 RPC 发送到主区域服务器。在短暂的间隔(hbase.client.primaryCallTimeout.get
,默认为 10 毫秒)之后,如果主服务器没有响应,也将发送辅助区域副本的并行 RPC。在此之后,从最先完成的 RPC 返回结果。如果响应从主要区域副本返回,我们始终可以知道数据是最新的。为此,添加了 Result.isStale()API 来检查陈旧性。如果结果来自辅助区域,则 Result.isStale()将设置为 true。然后,用户可以检查该字段以可能推断数据。
在语义方面,HBase 实现的 TIMELINE 一致性与这些方面的纯粹最终一致性不同:
单个归属和有序更新:区域复制与否,在写入端,仍然只有 1 个可以接受写入的已定义副本(主要)。此副本负责对编辑进行排序并防止冲突。这可以保证不同副本不会同时提交两个不同的写入,并且数据会发散。有了这个,就没有必要进行读取修复或最后时间戳获胜的冲突解决方案。
辅助节点也按主节点提交的顺序应用编辑。这样,辅助节点将包含任何时间点的原色数据的快照。这类似于 RDBMS 复制,甚至是 HBase 自己的多数据中心复制,但是在单个集群中。
在读取端,客户端可以检测读取是来自最新数据还是过时数据。此外,客户端可以在每个操作的基础上发出具有不同一致性要求的读取,以确保其自身的语义保证。
客户端仍然可以观察无序编辑,如果它首先观察来自一个辅助副本的读取,然后是另一个辅助副本,则可以及时返回。区域副本或基于事务 ID 的保证没有粘性。如果需要,可以稍后实施。
为了更好地理解 TIMELINE 语义,让我们看一下上图。假设有两个客户端,第一个客户端首先写入 x = 1,然后 x = 2,x = 3。如上所述,所有写入都由主要区域副本处理。写入保存在写入日志(WAL)中,并异步复制到其他副本。在上图中,请注意 replica_id = 1 接收到 2 次更新,其数据显示 x = 2,而 replica_id = 2 仅接收单次更新,其数据显示 x = 1。
如果 client1 以 STRONG 一致性读取,它将仅与 replica_id = 0 对话,因此保证观察到 x = 3 的最新值。如果客户端发出 TIMELINE 一致性读取,则 RPC 将转到所有副本(在主超时之后),并且将返回第一个响应的结果。因此,客户端可以看到 1,2 或 3 作为 x 的值。假设主要区域已失败,并且日志复制无法继续一段时间。如果客户端使用 TIMELINE 一致性进行多次读取,则可以先观察 x = 2,然后 x = 1,依此类推。
为读取可用性托管次要区域需要进行一些权衡,应根据用例仔细评估。以下是优点和缺点。
好处
只读表的高可用性
过时读取的高可用性
能够进行非常低延迟的读取,具有非常高的百分位数(99.9%+)延迟读取的延迟
缺点
具有区域复制的表的双/三 MemStore 使用(取决于区域复制计数)> 1
增加块缓存使用率
日志复制的额外网络流量
副本的额外备份 RPC
为了从多个副本服务区域数据,HBase 在区域服务器中以辅助模式打开区域。以辅助模式打开的区域将与主要区域副本共享相同的数据文件,但是每个辅助区域副本将具有其自己的 MemStore 以保留未刷新的数据(仅主要区域可以执行刷新)。同样为了提供来自次要区域的读取,数据文件块也可以高速缓存在用于次要区域的块高速缓存中。
此功能分两个阶段提供,第 1 阶段和第 2 阶段。第一阶段及时完成 HBase-1.0.0 版本。这意味着使用 HBase-1.0.x,您可以使用为阶段 1 标记的所有功能。阶段 2 在 HBase-1.1.0 中提交,这意味着 1.1.0 之后的所有 HBase 版本都应包含阶段 2 项。
如上所述,写入仅转到主要区域副本。为了将写入从主要区域副本传播到辅助副本,有两种不同的机制。对于只读表,您不需要使用以下任何方法。禁用和启用表应使所有区域副本中的数据可用。对于可变表,您必须仅使用以下机制之一:storefile refresher 或 async wal replication。推荐后者。
第一种机制是存储文件刷新,它在 HBase-1.0 +中引入。存储文件刷新是每个区域服务器的一个线程,它定期运行,并对辅助区域副本的主区域的存储文件执行刷新操作。如果启用,则刷新器将确保辅助区域副本及时查看来自主区域的新刷新,压缩或批量加载文件。但是,这意味着只能从辅助区域副本中读回刷新的数据,并且在刷新运行之后,使辅助数据库滞后于主数据库更长时间。
要打开此功能,应将hbase.regionserver.storefile.refresh.period
配置为非零值。请参阅下面的配置部分
第二种写入传播的机制是通过“异步 WAL 复制”功能完成的,仅在 HBase-1.1 +中可用。这类似于 HBase 的多数据中心复制,但是来自区域的数据被复制到辅助区域。每个辅助副本始终以与主区域提交它们相同的顺序接收和观察写入。从某种意义上说,这种设计可以被认为是“群集内复制”,而不是复制到不同的数据中心,数据会转到次要区域,以保持次要区域的内存状态为最新状态。数据文件在主区域和其他副本之间共享,因此不会产生额外的存储开销。但是,辅助区域将在其存储库中具有最近的非刷新数据,这会增加内存开销。主要区域也将 flush,compaction 和 bulk load 事件写入其 WAL,这些事件也通过 wal 复制到辅助节点进行复制。当他们观察到 flush / compaction 或 bulk load 事件时,辅助区域会重放事件以获取新文件并删除旧文件。
以与主数据库相同的顺序提交写入可确保辅助数据不会偏离主要区域数据,但由于日志复制是异步的,因此数据在辅助区域中可能仍然是陈旧的。由于此功能可用作复制端点,因此预计性能和延迟特性与群集间复制类似。
默认情况下,异步 WAL 复制禁用。您可以通过将hbase.region.replica.replication.enabled
设置为true
来启用此功能。当您使用区域复制>创建表时,Asyn WAL 复制功能将添加名为region_replica_replication
的新复制对等体作为复制对等体。 1 是第一次。启用后,如果要禁用此功能,则需要执行两项操作:_ 在hbase-site.xml
中将配置属性hbase.region.replica.replication.enabled
设置为 false(请参阅下面的配置部分)_ 禁用名为region_replica_replication
的复制对等体在使用 hbase shell 或Admin
类的集群中:
hbase> disable_peer 'region_replica_replication'
在上面提到的两种写传播方法中,主存储文件将在辅助节点中独立于主区域打开。因此,对于主要压缩的文件,辅助节点可能仍然会引用这些文件进行读取。这两个功能都使用 HFileLinks 来引用文件,但是没有保护(尚未)保证文件不会过早删除。因此,作为保护,您应该将配置属性hbase.master.hfilecleaner.ttl
设置为更大的值,例如 1 小时,以保证您不会收到转发到副本的请求的 IOExceptions。
目前,没有为 META 表的 WAL 执行异步 WAL 复制。元表的辅助副本仍然会从持久存储文件中刷新自己。因此,需要将hbase.regionserver.meta.storefile.refresh.period
设置为某个非零值以刷新元存储文件。请注意,此配置的配置与hbase.regionserver.storefile.refresh.period
不同。
辅助区域副本引用主要区域副本的数据文件,但它们有自己的存储库(在 HBase-1.1 +中)并且也使用块缓存。但是,一个区别是次要区域副本在其存储器存在压力时无法刷新数据。它们只能在主区域执行刷新时释放 memstore 内存,并且此刷新将复制到辅助节点。由于在某些区域中托管主副本的区域服务器和其他区域的辅助副本,因此辅助副本可能会导致额外刷新到同一主机中的主区域。在极端情况下,没有内存可用于添加来自主要来自 wal 复制的新写入。为了解除阻塞这种情况(并且由于辅助服务器不能自行刷新),允许辅助服务器通过执行文件系统列表操作来执行“存储文件刷新”,以从主服务器获取新文件,并可能删除其 memstore。仅当最大辅助区域副本的 memstore 大小至少比主副本的最大 memstore 大hbase.region.replica.storefile.refresh.memstore.multiplier
(默认值为 4)时,才会执行此刷新。需要注意的是,如果执行此操作,则辅助节点可以观察跨列族的部分行更新(因为列族是独立刷新的)。默认情况下应该不经常执行此操作。如果需要,可以将此值设置为较大的数值以禁用此功能,但请注意,它可能会导致复制永久阻止。
当辅助区域副本首次联机或故障转移时,它可能已从其 memstore 中进行了一些编辑。由于辅助副本的恢复处理方式不同,因此辅助副本必须确保它在分配后开始提供请求之前不会及时返回。为此,辅助节点等待直到它观察到从主节点复制的完全刷新周期(开始刷新,提交刷新)或“区域打开事件”。在此情况发生之前,辅助区域副本将通过抛出 IOException 来拒绝所有读取请求,并显示消息“区域的读取被禁用”。但是,其他副本可能仍然可以读取,因此不会对具有 TIMELINE 一致性的 rpc 造成任何影响。为了促进更快的恢复,辅助区域将在打开时触发主要的刷新请求。如果需要,配置属性hbase.region.replica.wait.for.primary.flush
(默认启用)可用于禁用此功能。
要使用高可用性读取,应在hbase-site.xml
文件中设置以下属性。没有用于启用或禁用区域副本的特定配置。相反,您可以在创建表时或使用 alter table 更改每个表的区域副本数量以增加或减少。以下配置用于使用异步 wal 复制和使用 3 的元副本。
<property>
<name>hbase.regionserver.storefile.refresh.period</name>
<value>0</value>
<description>
The period (in milliseconds) for refreshing the store files for the secondary regions. 0 means this feature is disabled. Secondary regions sees new files (from flushes and compactions) from primary once the secondary region refreshes the list of files in the region (there is no notification mechanism). But too frequent refreshes might cause extra Namenode pressure. If the files cannot be refreshed for longer than HFile TTL (hbase.master.hfilecleaner.ttl) the requests are rejected. Configuring HFile TTL to a larger value is also recommended with this setting.
</description>
</property>
<property>
<name>hbase.regionserver.meta.storefile.refresh.period</name>
<value>300000</value>
<description>
The period (in milliseconds) for refreshing the store files for the hbase:meta tables secondary regions. 0 means this feature is disabled. Secondary regions sees new files (from flushes and compactions) from primary once the secondary region refreshes the list of files in the region (there is no notification mechanism). But too frequent refreshes might cause extra Namenode pressure. If the files cannot be refreshed for longer than HFile TTL (hbase.master.hfilecleaner.ttl) the requests are rejected. Configuring HFile TTL to a larger value is also recommended with this setting. This should be a non-zero number if meta replicas are enabled (via hbase.meta.replica.count set to greater than 1).
</description>
</property>
<property>
<name>hbase.region.replica.replication.enabled</name>
<value>true</value>
<description>
Whether asynchronous WAL replication to the secondary region replicas is enabled or not. If this is enabled, a replication peer named "region_replica_replication" will be created which will tail the logs and replicate the mutations to region replicas for tables that have region replication > 1\. If this is enabled once, disabling this replication also requires disabling the replication peer using shell or Admin java class. Replication to secondary region replicas works over standard inter-cluster replication.
</description>
</property>
<property>
<name>hbase.region.replica.replication.memstore.enabled</name>
<value>true</value>
<description>
If you set this to `false`, replicas do not receive memstore updates from
the primary RegionServer. If you set this to `true`, you can still disable
memstore replication on a per-table basis, by setting the table's
`REGION_MEMSTORE_REPLICATION` configuration property to `false`. If
memstore replication is disabled, the secondaries will only receive
updates for events like flushes and bulkloads, and will not have access to
data which the primary has not yet flushed. This preserves the guarantee
of row-level consistency, even when the read requests `Consistency.TIMELINE`.
</description>
</property>
<property>
<name>hbase.master.hfilecleaner.ttl</name>
<value>3600000</value>
<description>
The period (in milliseconds) to keep store files in the archive folder before deleting them from the file system.</description>
</property>
<property>
<name>hbase.meta.replica.count</name>
<value>3</value>
<description>
Region replication count for the meta regions. Defaults to 1.
</description>
</property>
<property>
<name>hbase.region.replica.storefile.refresh.memstore.multiplier</name>
<value>4</value>
<description>
The multiplier for a “store file refresh” operation for the secondary region replica. If a region server has memory pressure, the secondary region will refresh it’s store files if the memstore size of the biggest secondary replica is bigger this many times than the memstore size of the biggest primary replica. Set this to a very big value to disable this feature (not recommended).
</description>
</property>
<property>
<name>hbase.region.replica.wait.for.primary.flush</name>
<value>true</value>
<description>
Whether to wait for observing a full flush cycle from the primary before start serving data in a secondary. Disabling this might cause the secondary region replicas to go back in time for reads between region movements.
</description>
</property>
还要记住的一点是,区域副本放置策略仅由作为默认平衡器的StochasticLoadBalancer
强制执行。如果在 hbase-site.xml(hbase.master.loadbalancer.class
中)使用自定义负载均衡器属性,则区域的副本最终可能会托管在同一服务器中。
确保为将使用区域副本的所有客户端(和服务器)设置以下内容。
<property>
<name>hbase.ipc.client.specificThreadForWriting</name>
<value>true</value>
<description>
Whether to enable interruption of RPC threads at the client side. This is required for region replicas with fallback RPC’s to secondary regions.
</description>
</property>
<property>
<name>hbase.client.primaryCallTimeout.get</name>
<value>10000</value>
<description>
The timeout (in microseconds), before secondary fallback RPC’s are submitted for get requests with Consistency.TIMELINE to the secondary replicas of the regions. Defaults to 10ms. Setting this lower will increase the number of RPC’s, but will lower the p99 latencies.
</description>
</property>
<property>
<name>hbase.client.primaryCallTimeout.multiget</name>
<value>10000</value>
<description>
The timeout (in microseconds), before secondary fallback RPC’s are submitted for multi-get requests (Table.get(List<Get>)) with Consistency.TIMELINE to the secondary replicas of the regions. Defaults to 10ms. Setting this lower will increase the number of RPC’s, but will lower the p99 latencies.
</description>
</property>
<property>
<name>hbase.client.replicaCallTimeout.scan</name>
<value>1000000</value>
<description>
The timeout (in microseconds), before secondary fallback RPC’s are submitted for scan requests with Consistency.TIMELINE to the secondary replicas of the regions. Defaults to 1 sec. Setting this lower will increase the number of RPC’s, but will lower the p99 latencies.
</description>
</property>
<property>
<name>hbase.meta.replicas.use</name>
<value>true</value>
<description>
Whether to use meta table replicas or not. Default is false.
</description>
</property>
注意 HBase-1.0.x 用户应该使用hbase.ipc.client.allowsInterrupt
而不是hbase.ipc.client.specificThreadForWriting
。
在主用户界面中,表的区域副本也与主要区域一起显示。您可以注意到区域的副本将共享相同的开始和结束键以及相同的区域名称前缀。唯一的区别是附加的 replica_id(编码为十六进制),区域编码的名称将不同。您还可以在 UI 中看到显式显示的副本 ID。
区域复制是每个表的属性。默认情况下,所有表都有REGION_REPLICATION = 1
,这意味着每个区域只有一个副本。您可以通过在表描述符中提供REGION_REPLICATION
属性来设置和更改表的每个区域的副本数。
create 't1', 'f1', {REGION_REPLICATION => 2}
describe 't1'
for i in 1..100
put 't1', "r#{i}", 'f1:c1', i
end
flush 't1'
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(“test_table”));
htd.setRegionReplication(2);
...
admin.createTable(htd);
您还可以使用setRegionReplication()
和 alter table 来增加,减少表的区域复制。
您可以使用 Consistency.TIMELINE 语义在 shell 中进行读取,如下所示
hbase(main):001:0> get 't1','r6', {CONSISTENCY => "TIMELINE"}
您可以模拟暂停或变为不可用的区域服务器,并从辅助副本执行读取操作:
$ kill -STOP <pid or primary region server>
hbase(main):001:0> get 't1','r6', {CONSISTENCY => "TIMELINE"}
使用扫描也是类似的
hbase> scan 't1', {CONSISTENCY => 'TIMELINE'}
您可以按如下方式设置获取和扫描的一致性并执行请求。
Get get = new Get(row);
get.setConsistency(Consistency.TIMELINE);
...
Result result = table.get(get);
您还可以传递多个获取:
Get get1 = new Get(row);
get1.setConsistency(Consistency.TIMELINE);
...
ArrayList<Get> gets = new ArrayList<Get>();
gets.add(get1);
...
Result[] results = table.get(gets);
和扫描:
Scan scan = new Scan();
scan.setConsistency(Consistency.TIMELINE);
...
ResultScanner scanner = table.getScanner(scan);
您可以通过调用Result.isStale()
方法检查结果是否来自主区域:
Result result = table.get(get);
if (result.isStale()) {
...
}
有关设计和实施的更多信息,请参阅 jira 问题: HBASE-10070
HBaseCon 2014 讲话: HBase 使用时间轴一致区域副本读取高可用性还包含一些细节和幻灯片。