RAM,RAM,RAM。不要饿死HBase。
使用64位平台(和64位JVM)。
注意交换。将swappiness设置为0。
确保已将Hadoop设置为使用本机硬件校验和。见链接:[hadoop.native.lib]。
避免网络问题降低Hadoop和HBase性能的最重要因素可能是使用的交换硬件,在项目范围的早期做出的决策可能会导致群集大小增加一倍或三倍(或更多)时出现重大问题。
需要考虑的重要事项:
-
设备的切换容量
-
连接的系统数量
-
上行容量
此配置中最重要的一个因素是硬件的交换容量能够处理连接到交换机的所有系统可能产生的流量。一些较低价格的商用硬件可以具有比完整交换机可以使用的更慢的交换容量。
多个交换机是架构中的潜在缺陷。低价硬件的最常见配置是从一个交换机到另一个交换机的简单1Gbps上行链路。这种经常被忽视的夹点很容易成为集群通信的瓶颈。特别是对于同时读取和写入大量数据的MapReduce作业,此上行链路上的通信可能已经饱和。
缓解此问题非常简单,可以通过多种方式完成:
-
使用适当的硬件来处理您尝试构建的群集的规模。
-
使用较大的单开关配置,即单个48端口而不是2个24端口
-
配置上行链路的端口中继,以利用多个接口来增加交叉交换机带宽。
多个机架配置带来与多个交换机相同的潜在问题,并且可能会从两个主要方面降低性能:
-
开关容量性能不佳
-
上行链路到另一个机架的能力不足
如果机架中的交换机具有适当的交换容量以全速处理所有主机,则下一个最可能的问题将是通过在机架中引导更多群集引起的。跨越多个机架时避免问题的最简单方法是使用端口中继来创建到其他机架的绑定上行链路。然而,该方法的缺点在于可能使用的端口的开销。这样做的一个例子是,从机架A到机架B创建一个8Gbps端口通道,使用24个端口中的8个在机架之间进行通信会降低投资回报率,使用太少但是可能意味着您无法充分利用簇。
在机架之间使用10Gbe链路将大大提高性能,并且假设您的交换机支持10Gbe上行链路或允许扩展卡,则允许您为机器而不是上行链路保存端口。
所有网络接口都正常运行吗?你确定吗?请参阅案例研究#1(单节点上的性能问题)中的故障排除案例研究。
CAP定理指出分布式系统可以保持以下三个特征中的两个: - C onsistency - 所有节点都看到相同的数据。 - A 可用性 - 每个请求都会收到有关它是成功还是失败的响应。 - P 的动作容忍度 - 即使其他组件无法使用,系统也会继续运行。
HBase支持一致性和分区容忍度,必须做出决定。 Coda Hale在 http://codahale.com/you-cant-sacrifice-partition-tolerance/ 中解释了为什么分区容差如此重要。
罗伯特Yokota使用一个名为 Jepson 的自动化测试框架来测试HBase在网络分区面前的分区容忍度,使用Aphyr的 Call Me Maybe 系列之后建模的技术。结果,作为博客文章和附录,显示HBase正确执行。
在他的演讲中,避免使用MemStore-本地分配缓冲区,Todd Lipcon描述了HBase中常见的两种世界性垃圾收集案例,特别是在加载过程中; CMS故障模式和老一代堆碎片带来了。
要解决第一个问题,请通过添加-XX:CMSInitiatingOccupancyFraction并将其设置为默认值来启动早于默认值的CMS。从60%或70%开始(降低阈值越低,GCing越多,使用的CPU越多)。为了解决第二个碎片问题,Todd添加了一个实验工具(MSLAB),必须在Apache HBase 0.90.x中明确启用(它在Apache 0.92.x HBase中默认为上的_)。在Configuration中将hbase.hregion.memstore.mslab.enabled设置为true。有关背景和细节,请参阅引用的幻灯片。最新的JVM更好地考虑碎片,因此请确保您运行的是最新版本。在消息中读入,识别由碎片引起的并发模式故障。请注意,启用后,每个MemStore实例将至少占用一个MSLAB内存实例。如果您有数千个区域或许多区域,每个区域都有许多列族,那么MSLAB的这种分配可能会导致很大一部分堆分配,并且在极端情况下会导致您进入OOME。在这种情况下禁用MSLAB,或者降低它使用的内存量或每个服务器浮动更少的区域。_
如果您的工作量很大,请查看 HBASE-8163 MemStoreChunkPool:使用MSLAB 时对JAVA GC的改进。它描述了在写入负载期间降低年轻GC数量的配置。如果您没有安装HBASE-8163,并且您正在尝试改善年轻的GC时间,那么我们需要考虑的一个诀窍 - 我们的梁燮 - 是在 hbase-env.sh中设置GC配置-XX:PretenureSizeThreshold 只是小于hbase.hregion.memstore.mslab.chunksize的大小所以MSLAB分配直接发生在终身空间而不是年轻时代。你这样做是因为这些MSLAB分配很可能无论如何都要用于旧版本,而不是在eden空间中支付s0和s1之间的拷贝价格,然后在MSLABs之后从年轻到旧代复制。取得了足够的任期,节省了一点YGC流失并直接分配到旧版。
长GC的其他来源可以是JVM本身的日志记录。请参阅消除由后台IO流量引起的大型JVM GC暂停
有关GC日志的更多信息,请参阅 JVM垃圾收集日志。
还要考虑启用堆外块缓存。这已被证明可以缓解GC暂停时间。见 Block Cache
参见推荐配置。
尝试链接:[hedged_reads]。
对于较大的系统,管理链接:[compactions和splits]可能是您想要考虑的事情。
见 [hbase.regionserver.handler.count] 。
见 [hfile.block.cache.size] 。 RegionServer进程的内存设置。
HBASE-9857 添加了一个新选项,用于在打开BlockCache时预取HFile内容,如果设置了Column系列或RegionServer属性。此选项适用于HBase 0.98.3及更高版本。目的是在打开缓存后,使用内存中的表数据尽可能快地加热BlockCache,而不将预取计为缓存未命中。这对于快速读取非常有用,但如果要预加载的数据不适合BlockCache,则不是一个好主意。它可用于调整预取的IO影响与所有数据块在缓存中的时间之间的关系。
要在给定列族上启用预取,可以使用HBase Shell或使用API。
使用HBase Shell启用预取
hbase> create 'MyTable', { NAME => 'myCF', PREFETCH_BLOCKS_ON_OPEN => 'true' }
示例38.使用API启用预取
// ...
HTableDescriptor tableDesc = new HTableDescriptor("myTable");
HColumnDescriptor cfDesc = new HColumnDescriptor("myCF");
cfDesc.setPrefetchBlocksOnOpen(true);
tableDesc.addFamily(cfDesc);
// ...
请参阅 CacheConfig 的API文档。
要查看运行中的预取,请在hbase-2.0 +中的org.apache.hadoop.hbase.io.hfile.HFileReaderImpl或HBase的早期版本hbase-1.x中的org.apache.hadoop.hbase.io.hfile.HFileReaderV2上启用TRACE级别日志记录。
见 [hbase.regionserver.global.memstore.size] 。通常根据需要为RegionServer进程调整此内存设置。
见 [hbase.regionserver.global.memstore.size.lower.limit] 。通常根据需要为RegionServer进程调整此内存设置。
参见 [hbase.hstore.blockingStoreFiles] 。如果RegionServer日志中存在阻塞,则增加此功能会有所帮助。
参见 [hbase.hregion.memstore.block.multiplier] 。如果有足够的RAM,增加这个可以帮助。
让HBase将校验和写入数据块,并保存在您阅读时必须进行校验和搜索。
见 [hbase.regionserver.checksum.verify] , [hbase.hstore.bytes.per.checksum] 和 [hbase.hstore.checksum.algorithm] 。有关更多信息,请参阅HBase块缓存中 HBASE-5074支持校验和的发行说明。
HBASE-11355 引入了几种可以提高性能的callQueue调整机制。有关基准信息,请参阅JIRA。
要增加呼叫队列的数量,请将hbase.ipc.server.num.callqueue设置为大于1的值。要将callqueue拆分为单独的读写队列,请将hbase.ipc.server.callqueue.read.ratio设置为0和1之间的值。此因子将队列加权写入(如果低于.5)或读取(如果高于.5)。另一种说法是,该因子决定了拆分队列中有多少百分比用于读取。以下示例说明了一些可能性。请注意,无论您使用何种设置,始终至少有一个写入队列。
-
0的默认值不会拆分队列。 -
值
.3使用30%的队列进行读取,60%用于写入。给定hbase.ipc.server.num.callqueue的10值,3个队列将用于读取,7个用于写入。 -
值
.5使用相同数量的读取队列和写入队列。给定hbase.ipc.server.num.callqueue的10值,5个队列将用于读取,5个用于写入。 -
值
.6使用60%的队列进行阅读,30%进行阅读。给定hbase.ipc.server.num.callqueue的10值,7个队列将用于读取,3个用于写入。 -
值
1.0使用一个队列来处理写请求,所有其他队列处理读请求。高于1.0的值与1.0的值具有相同的效果。给定hbase.ipc.server.num.callqueue的10值,9个队列将用于读取,1个用于写入。
您还可以拆分读取队列,以便通过设置hbase.ipc.server.callqueue.scan.ratio选项将单独的队列用于短读取(来自Get操作)和长读取(来自扫描操作)。此选项是介于0和1之间的因子,它决定了用于获取和扫描的读取队列的比率。如果值低于.5,则将更多队列用于获取;如果值高于.5,则将更多队列用于扫描。无论您使用何种设置,至少有一个读取队列用于Get操作。
-
值
0不会拆分读取队列。 -
值
.3使用60%的读取队列获取,30%用于扫描。给定hbase.ipc.server.num.callqueue的20值和hbase.ipc.server.callqueue.read.ratio的.5值,将有10个队列用于读取,其中10个,7个用于获取,3个用于扫描。 -
值
.5使用获取的一半读取队列和扫描的一半。给定hbase.ipc.server.num.callqueue的20值和hbase.ipc.server.callqueue.read.ratio的.5值,将有10个队列用于读取,其中10个用于获取,5个用于扫描。 -
值
.6使用30%的读取队列获取,60%用于扫描。给定hbase.ipc.server.num.callqueue的20值和hbase.ipc.server.callqueue.read.ratio的.5值,将有10个队列用于读取,其中10个用于获取,3个用于扫描。 -
值
1.0使用除扫描队列之外的所有读取队列。给定hbase.ipc.server.num.callqueue的20值和hbase.ipc.server.callqueue.read.ratio的.5值,将有10个队列用于读取,其中10个用于获取,1个用于扫描。
您可以使用新选项hbase.ipc.server.callqueue.handler.factor以编程方式调整队列数:
-
值
0在所有处理程序之间使用单个共享队列。 -
值
1为每个处理程序使用单独的队列。 -
0和1之间的值会根据处理程序的数量调整队列数。例如,.5的值在每两个处理程序之间共享一个队列。拥有更多队列(例如,在每个处理程序中有一个队列的情况下)可以在将任务添加到队列或从队列中选择任务时减少争用。权衡的是,如果你有一些长时间运行任务的队列,处理程序可能最终等待从该队列执行而不是处理另一个具有等待任务的队列。
要使这些值在给定的RegionServer上生效,必须重新启动RegionServer。这些参数仅用于测试目的,应谨慎使用。
有关配置ZooKeeper的信息,请参阅 ZooKeeper ,并参阅有关具有专用磁盘的部分。
参见关于列族数。
请参阅尝试最小化行和列大小。另见但是...... 用于压缩警告。
在某些表需要与配置的默认区域大小不同的区域大小的情况下,可以通过 HTableDescriptor 上的setFileSize在每个表的基础上设置区域大小。
有关详细信息,请参阅确定区域计数和大小。
以其创建者Burton Howard Bloom命名的Bloom过滤器是一种数据结构,旨在预测给定元素是否是一组数据的成员。 Bloom过滤器的阳性结果并不总是准确的,但保证阴性结果是准确的。布隆过滤器被设计成对于数据集“足够准确”,这些数据集非常大以至于传统的散列机制是不切实际的。有关Bloom过滤器的更多信息,请参阅 http://en.wikipedia.org/wiki/Bloom_filter 。
就HBase而言,Bloom过滤器提供了一个轻量级的内存结构,以便将给定Get操作(Bloom过滤器不能与Scans一起使用)的磁盘读取次数减少到仅可能包含所需Row的StoreFiles。潜在的性能增益随着并行读取的数量而增加。
Bloom过滤器本身存储在每个HFile的元数据中,永远不需要更新。当因为区域部署到RegionServer而打开HFile时,Bloom过滤器将加载到内存中。
HBase包括一些调整机制,用于折叠Bloom过滤器以减小大小并将误报率保持在所需范围内。
在 HBASE-1200 中引入了Bloom过滤器。自HBase 0.96起,默认情况下启用基于行的Bloom过滤器。 ( HBASE-8450 )
有关与HBase相关的布隆过滤器的更多信息,请参阅布隆过滤器以获取更多信息,或参考以下Quora讨论:如何在HBase中使用布隆过滤器? 。
自HBase 0.96起,默认情况下启用基于行的Bloom过滤器。您可以选择禁用它们或更改某些表以使用行+列Bloom过滤器,具体取决于数据的特征以及如何将其加载到HBase中。
要确定Bloom过滤器是否会产生积极影响,请检查RegionServer度量标准中的blockCacheHitRatio值。如果启用了布隆过滤器,则blockCacheHitRatio的值应该增加,因为布隆过滤器正在过滤掉绝对不需要的块。
您可以选择为行或行+列组合启用Bloom过滤器。如果您通常扫描整行,则行+列组合不会提供任何好处。基于行的Bloom过滤器可以在行+列Get上运行,但不能相反。但是,如果您有大量的列级Puts,这样每个StoreFile中可能存在一行,则基于行的过滤器将始终返回正结果并且不会带来任何好处。除非每行有一列,否则行+列布隆过滤器需要更多空间,以便存储更多键。当每个数据条目的大小至少为几千字节时,Bloom过滤器最有效。
当您的数据存储在几个较大的StoreFiles中时,开销将减少,以避免在低级扫描期间额外的磁盘IO以查找特定行。
Bloom过滤器需要在删除时重建,因此可能不适合具有大量删除的环境。
在列族上启用布隆过滤器。您可以使用HColumnDescriptor的setBloomFilterType方法或使用HBase API来完成此操作。有效值为NONE,ROW(默认值)或ROWCOL。有关ROW与ROWCOL的更多信息,请参见何时使用布隆过滤器。另请参阅 HColumnDescriptor 的API文档。
以下示例创建一个表,并在colfam1列族上启用ROWCOL Bloom过滤器。
hbase> create 'mytable',{NAME => 'colfam1', BLOOMFILTER => 'ROWCOL'}
您可以在 hbase-site.xml 中配置以下设置。
| 参数 | 默认 | 描述 |
|---|---|---|
| io.storefile.bloom.enabled | 是 | 如果出现问题,设置为no以杀死服务器范围内的bloom过滤器 |
| io.storefile.bloom.error.rate | 0.01 | 布隆过滤器的平均误报率。折叠用于维持误报率。表示为百分比的十进制表示。 |
| io.storefile.bloom.max.fold | 7 | 保证最大折叠率。不需要更改此设置,不建议这样做。 |
| io.storefile.bloom.max.keys | 1.28 | 对于默认(单块)布隆过滤器,它指定最大键数。 |
| io.storefile.delete.family.bloom.enabled | 真正 | 主开关启用Delete Family Bloom过滤器并将其存储在StoreFile中。 |
| io.storefile.bloom.block.size | 131072 | 目标Bloom块大小。大约这个大小的布隆过滤器块与数据块交织。 |
| hfile.block.bloom.cacheonwrite | 假 | 为复合布隆过滤器的内联块启用写入高速缓存。 |
可以为表中的每个ColumnFamily配置blocksize,默认为64k。较大的单元值需要较大的块大小。 blocksize与生成的StoreFile索引之间存在反比关系(即,如果块大小加倍,则结果索引应大致减半)。
有关详细信息,请参阅 HColumnDescriptor 和 Store 。
ColumnFamilies可以选择定义为内存中。数据仍然保留在磁盘上,就像任何其他ColumnFamily一样。内存块在块缓存中具有最高优先级,但不保证整个表将在内存中。
有关详细信息,请参阅 HColumnDescriptor 。
生产系统应使用其ColumnFamily定义进行压缩。有关详细信息,请参阅HBase 中的压缩和数据块编码。
压缩会压缩磁盘上的数据_。当它在内存中(例如,在MemStore中)或在线上(例如,在RegionServer和Client之间传输)时,它会膨胀。因此,虽然使用ColumnFamily压缩是最佳做法,但它不会完全消除过大的Keys,超大的ColumnFamily名称或超大的列名称的影响。_
请参阅尝试最小化模式设计技巧的行和列大小,以及 KeyValue 以获取有关HBase内部存储数据的更多信息。
当人们开始使用HBase时,他们倾向于编写如下代码:
Get get = new Get(rowkey);
Result r = table.get(get);
byte[] b = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes("attr")); // returns current version of value
但特别是当内部循环(和MapReduce作业)时,将columnFamily和column-names重复转换为字节数组的成本非常高。最好对字节数组使用常量,如下所示:
public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Get get = new Get(rowkey);
Result r = table.get(get);
byte[] b = r.getValue(CF, ATTR); // returns current version of value
如果可以,请使用批量加载工具。参见批量加载。否则,请注意以下内容。
默认情况下,HBase中的表最初是使用一个区域创建的。对于批量导入,这意味着所有客户端都将写入同一区域,直到它足够大,可以拆分并在集群中分布。加速批量导入过程的有用模式是预先创建空白区域。在这方面要保守一些,因为太多的地区实际上会降低性能。
使用HBase API预创建拆分有两种不同的方法。第一种方法是依赖默认的Admin策略(在Bytes.split中实现)...
byte[] startKey = ...; // your lowest key
byte[] endKey = ...; // your highest key
int numberOfRegions = ...; // # of regions to create
admin.createTable(table, startKey, endKey, numberOfRegions);
另一种使用HBase API的方法是自己定义分割...
byte[][] splits = ...; // create your own splits
admin.createTable(table, splits);
使用HBase Shell可以通过指定拆分选项来创建表,从而实现类似的效果。
# create table with specific split points
hbase>create 't1','f1',SPLITS => ['\x10\x00', '\x20\x00', '\x30\x00', '\x40\x00']
# create table with four regions based on random bytes keys
hbase>create 't2','f1', { NUMREGIONS => 4 , SPLITALGO => 'UniformSplit' }
# create table with five regions based on hex keys
create 't3','f1', { NUMREGIONS => 5, SPLITALGO => 'HexStringSplit' }
有关理解键空间和预创建区域的相关问题,请参阅 RowKeys和Region Splits 之间的关系。有关手动预分割区域的讨论,请参见手动区域分割决策。有关使用HBase Shell预分割表的更多详细信息,请参见使用HBase Shell 预分割表。
使用预写日志(WAL)的Puts的默认行为是将立即写入WAL编辑。如果使用延迟日志刷新,则WAL编辑将保留在内存中直到刷新周期。好处是聚合和异步WAL - 写入,但潜在的缺点是如果RegionServer发生故障,则尚未刷新的编辑将丢失。然而,这比使用Puts完全不使用WAL更安全。
可以通过 HTableDescriptor 在表上配置延迟日志刷新。 hbase.regionserver.optionallogflushinterval的默认值为1000ms。
经常请求禁用WAL以提高Puts的性能。这仅适用于批量加载,因为它会在区域服务器崩溃时通过删除WAL的保护来使您的数据处于危险之中。在发生崩溃时可以重新运行批量加载,几乎没有数据丢失的风险。
如果禁用WAL而不是批量加载,则数据存在风险。
通常,最好使用WAL for Puts,而使用批量加载技术时,加载吞吐量是一个问题。对于正常的Puts,您不太可能看到性能改善超过风险。要禁用WAL,请参阅禁用WAL 。
除了使用writeBuffer之外,通过RegionServer对Put进行分组可以减少每个writeBuffer flush的客户端RPC调用次数。当前在MASTER上有一个实用程序HTableUtil可以执行此操作,但您可以复制该实用程序,也可以为仍在0.90.x或更早版本的版本实现自己的版本。
当从MR作业向HBase表写入大量数据时(例如,使用 TableOutputFormat ),特别是从Mapper发出Puts的地方,跳过Reducer步骤。当使用Reducer步骤时,来自Mapper的所有输出(Puts)将被假脱机到磁盘,然后排序/改组到其他很可能是非节点的Reducers。直接写入HBase效率更高。
对于将HBase用作源和接收器的摘要作业,写入将来自Reducer步骤(例如,汇总值然后写出结果)。这是与上述情况不同的处理问题。
如果所有数据一次写入一个区域,则重新阅读有关处理时间序列数据的部分。
此外,如果您正在预分割区域并且所有数据仍然是_仍然_在单个区域中清理,即使您的密钥没有单调增加,请确认您的密钥空间实际上与分离策略一起使用。区域可能出现“分裂”的原因有多种,但不适用于您的数据。由于HBase客户端直接与RegionServers通信,因此可以通过 RegionLocator.getRegionLocation 获得。
如果您遇到性能问题,邮件列表可以提供帮助。例如,这里是关于解决读取时间问题的一个很好的通用线程: HBase随机读取延迟> 100毫秒
例如,如果将HBase用作MapReduce作业的输入源,请确保MapReduce作业的输入 Scan 实例将setCaching设置为大于默认值(即1)的值。使用默认值意味着map-task将为处理的每个记录回调region-server。例如,将此值设置为500将一次传输500行到要处理的客户端。缓存值很大是有成本/收益的,因为客户端和RegionServer的内存成本更高,因此更大并不总是更好。
MapReduce作业中的扫描设置值得特别注意。如果在客户端返回到RegionServer以获取下一组数据之前处理一批记录需要更长时间,则可能会在Map任务中导致超时(例如,UnknownScannerException)。发生此问题的原因是每行发生非平凡处理。如果快速处理行,请将缓存设置得更高。如果您更慢地处理行(例如,每行进行大量转换,写入),则将缓存设置得更低。
超时也可能发生在非MapReduce用例(即执行扫描的单线程HBase客户端)中,但通常在MapReduce作业中执行的处理往往会加剧此问题。
每当使用扫描处理大量行(特别是用作MapReduce源)时,请注意选择了哪些属性。如果调用scan.addFamily,则_指定ColumnFamily中属性的所有_将返回给客户端。如果只处理少量可用属性,则只应在输入扫描中指定那些属性,因为属性过度选择对大型数据集而言是一个非常重要的性能损失。
当使用scan.addColumn显式选择列时,HBase将调度搜索操作以在所选列之间进行搜索。当行包含很少的列而每列只有几个版本时,这可能效率很低。如果不寻求至少过去5-10列/版本或512-1024字节,则查找操作通常较慢。
为了机会性地向前看几个列/版本以查看在调度搜索操作之前是否可以找到下一列/版本,可以在Scan对象上设置新属性Scan.HINT_LOOKAHEAD。以下代码指示RegionServer在调度搜索之前尝试下一次迭代:
Scan scan = new Scan();
scan.addColumn(...);
scan.setAttribute(Scan.HINT_LOOKAHEAD, Bytes.toBytes(2));
table.getScanner(scan);
对于使用HBase表作为源的MapReduce作业,如果存在“慢”映射任务似乎具有相同输入拆分的模式(即,为服务数据提供服务的RegionServer),请参阅案例研究中的故障排除案例研究#1(单节点上的性能问题)。
这不是提高性能,而是_避免_性能问题。如果您忘记关闭 ResultScanners ,可能会导致RegionServers出现问题。始终将ResultScanner处理包含在try / catch块中。
Scan scan = new Scan();
// set attrs...
ResultScanner rs = table.getScanner(scan);
try {
for (Result r = rs.next(); r != null; r = rs.next()) {
// process result...
} finally {
rs.close(); // always close the ResultScanner!
}
table.close();
Scan 实例可以通过setCacheBlocks方法设置为使用RegionServer中的块缓存。对于输入扫描到MapReduce作业,这应该是false。对于频繁访问的行,建议使用块缓存。
通过在堆外移动块缓存来缓存更多数据。请参阅堆外块缓存
执行仅需要行键的表扫描时(无族,限定符,值或时间戳),使用setFilter将带有MUST_PASS_ALL运算符的FilterList添加到扫描仪。过滤器列表应包括 FirstKeyOnlyFilter 和 KeyOnlyFilter 。使用此过滤器组合将导致RegionServer读取磁盘中的单个值以及单个行的客户端最小网络流量的最坏情况。
执行大量并发读取时,监视目标表的数据传播。如果目标表具有太少的区域,则可能从太少的节点提供读取。
See Table Creation: Pre-Creating Regions, as well as HBase Configurations
启用Bloom过滤器可以节省您必须转到磁盘并有助于改善读取延迟。
Bloom过滤器是在 HBase-1200 Add bloomfilters 中开发的。有关开发过程的描述 - 为什么是静态绽放而不是动态 - 以及有关HBase中的绽放的独特属性的概述,以及可能的未来方向,请参阅文档的_开发过程_部分 HBase 中的BloomFilters附着于 HBASE-1200 。这里描述的布隆过滤器实际上是HBase中的第二版布隆。在高达0.19.x的版本中,HBase根据欧盟委员会一个实验室项目034819 所做的工作有一个动态绽放选项。 HBase bloom工作的核心后来被引入Hadoop以实现org.apache.hadoop.io.BloomMapFile。 HBase绽放的第1版从未如此顺利。版本2是从头开始重写,但它再次从单实验室工作开始。
另请参见布隆过滤器。
布隆过滤器向StoreFile通用FileInfo数据结构添加条目,然后向StoreFile元数据部分添加两个额外条目。
FileInfo有一个BLOOM_FILTER_TYPE条目,设置为NONE,ROW或ROWCOL.
BLOOM_FILTER_META保存Bloom Size,使用的哈希函数等。它的大小很小,并且在StoreFile.Reader加载时缓存
BLOOM_FILTER_DATA是实际的bloomfilter数据。按需获得。存储在LRU缓存中,如果已启用(默认情况下已启用)。
如果出现问题,Configuration中的io.storefile.bloom.enabled将作为终止开关。默认= true。
io.storefile.bloom.error.rate =平均误报率。默认值= 1%。每个布鲁德条目减少½(例如降至0.5%)== +1比特。
io.storefile.bloom.max.fold =保证最低折叠率。大多数人应该单独留下这个。默认值= 7,或者可以折叠到原始大小的至少1/128。有关此选项的含义的更多信息,请参阅HBase 文档 BloomFilters的_开发过程_部分。
对冲读取是HDFS的一个特性,在Hadoop 2.4.0中引入了 HDFS-5776 。通常,为每个读取请求生成一个线程。但是,如果启用了对冲读取,则客户端会等待一些可配置的时间,如果读取未返回,则客户端会针对相同数据的不同块副本生成第二个读取请求。无论先使用哪个读取返回,都会丢弃另一个读取请求。
对冲读取是“......非常擅长消除异常数据节点,这反过来使它们成为延迟敏感设置的非常好的选择。但是,如果您正在寻找最大化吞吐量,对冲读取往往会产生负载放大,因为一般情况下变慢。简而言之,需要注意的是当您运行接近某个吞吐量阈值时,非优雅的性能下降。“ (引自Ashu Pachauri的HBASE-17083)。
在启用对冲读取的情况下运行时要记住的其他问题包括:
-
它们可能导致网络拥塞。见 HBASE-17083
-
确保将线程池设置得足够大,以便池上的阻塞不会成为瓶颈(再次参见 HBASE-17083 )
(来自HBASE-17083的Yu Li)
由于HBase RegionServer是HDFS客户端,因此您可以在HBase中启用对冲读取,方法是将以下属性添加到RegionServer的hbase-site.xml并调整值以适合您的环境。
对冲读数的配置
-
dfs.client.hedged.read.threadpool.size- 专用于服务对冲读取的线程数。如果将其设置为0(默认值),则禁用对冲读取。 -
dfs.client.hedged.read.threshold.millis- 产生第二个读取线程之前等待的毫秒数。
例39.对冲读取配置示例
<property>
<name>dfs.client.hedged.read.threadpool.size</name>
<value>20</value> <!-- 20 threads -->
</property>
<property>
<name>dfs.client.hedged.read.threshold.millis</name>
<value>10</value> <!-- 10 milliseconds -->
</property>
使用以下指标调整群集上对冲读取的设置。有关详细信息,请参阅 HBase指标。
对冲读数的指标
-
hedgedReadOps - 已触发对冲读取线程的次数。这可能表明读取请求通常很慢,或者对冲读取的触发过快。
-
hedgeReadOpsWin - 对冲读取线程比原始线程快的次数。这可能表示给定的RegionServer在处理请求时遇到问题。
HBase表有时用作队列。在这种情况下,必须特别注意定期对以这种方式使用的表格进行主要压缩。如数据模型中所述,将行标记为已删除会创建额外的StoreFiles,然后需要在读取时对其进行处理。墓碑只能通过主要压缩来清理。
另见压缩和 Admin.majorCompact 。
请注意Table.delete(Delete)不使用writeBuffer。它将在每次调用时执行RegionServer RPC。对于大量删除,请考虑Table.delete(List)。
因为HBase在 HDFS 上运行,所以了解它是如何工作以及它如何影响HBase是很重要的。
HDFS的原始用例是批处理。因此,低延迟读取在历史上不是优先事项。随着Apache HBase的采用日益增多,这种情况正在发生变化,并且已经在开发中进行了一些改进。有关HBase 的HDFS改进,请参阅 Umbrella Jira Ticket。
由于Hadoop 1.0.0(也是0.22.1,0.23.1,CDH3u3和HDP 1.0)通过 HDFS-2246 ,DFSClient可以进行“短路”并直接从磁盘读取当数据是本地数据时,而不是通过DataNode。对于HBase来说,这意味着RegionServers可以直接读取其机器的磁盘,而不必打开套接字与DataNode通信,前者通常要快得多。参见JD的 Performance Talk 。另请参阅 HBase,邮件#dev - 读取短路线程,以获得有关短路读取的更多讨论。
要启用“短路”读取,它将取决于您的Hadoop版本。在 HDFS-347 中,Hadoop 2中的原始短路读取补丁得到了很大改善。参见 http://blog.cloudera.com/blog/2013/08/how-improved-short-circuit-local-reads-bring-better-performance-and-security-to-hadoop/ for有关旧实现和新实现之间差异的详细信息。参见 Hadoop短路读取配置页面,了解如何启用后者,更好的短路版本。例如,这是一个最小配置。启用短路读取添加到 hbase-site.xml :
<property>
<name>dfs.client.read.shortcircuit</name>
<value>true</value>
<description>
This configuration parameter turns on short-circuit local reads.
</description>
</property>
<property>
<name>dfs.domain.socket.path</name>
<value>/home/stack/sockets/short_circuit_read_socket_PORT</value>
<description>
Optional. This is a path to a UNIX domain socket that will be used for
communication between the DataNode and local HDFS clients.
If the string "_PORT" is present in this path, it will be replaced by the
TCP port of the DataNode.
</description>
</property>
注意托管共享域套接字的目录的权限;如果对hbase用户以外的其他人开放,dfsclient会抱怨。
如果您运行的是旧的Hadoop,没有 HDFS-347 ,但有 HDFS-2246 ,则必须设置两个配置。首先,需要修改hdfs-site.xml。将属性dfs.block.local-path-access.user设置为可以使用快捷方式的_仅_用户。这必须是启动HBase的用户。然后在hbase-site.xml中,将dfs.client.read.shortcircuit设置为true
服务 - 至少是HBase RegionServers - 需要重新启动才能获取新配置。
dfs.client.read.shortcircuit.buffer.size
在高流量的HBase上运行时,此值的默认值太高。在HBase中,如果尚未设置此值,我们将其从默认值1M设置为128k(自HBase 0.98.0和0.96.1起)。请参阅Hadoop 2上的 HBASE-8143 HBase,本地短路读取(ssr)会导致OOM 。 HBase中的Hadoop DFSClient将为_分配一个这个大小的直接字节缓冲区,每个_块已打开;鉴于HBase始终保持其HDFS文件打开,这可以快速加起来。
关于dist-list的一个相当常见的问题是为什么HBase不如批处理上下文中的HDFS文件那样高效(例如,作为MapReduce源或接收器)。简短的回答是HBase比HDFS做得更多(例如,读取KeyValues,返回最新的行或指定的时间戳等),因此HBase在此处理环境中比HDFS慢4-5倍。还有改进的余地,随着时间的推移,这个差距会缩小,但在这个用例中HDFS总是会更快。
性能问题在Amazon EC2环境中很常见,因为它是一个共享环境。您将看不到与专用服务器相同的吞吐量。在EC2上运行测试时,出于同样的原因多次运行它们(即,它是共享环境,您不知道服务器上还发生了什么)。
如果您在EC2上运行并在dist-list上发布性能问题,请事先说明这一事实,因为EC2问题实际上是一类独立的性能问题。
通常建议为HBase和MapReduce使用不同的集群。对此更好的限定是:不要将提供实时请求的HBase与重MR工作负载并置。 OLTP和OLAP优化的系统具有相互冲突的要求,而另一个将失去另一个,通常是前者。例如,短暂的延迟敏感磁盘读取将不得不排在较长的读取后面,这些读取试图挤出尽可能多的吞吐量。写入HBase的MR作业也会产生刷新和压缩,这反过来会使 Block Cache 中的块无效。
如果需要处理MR中的实时HBase集群中的数据,可以使用 CopyTable 发送增量,或使用复制在OLAP集群上实时获取新数据。在最坏的情况下,如果您确实需要并置两者,请将MR设置为使用比您通常配置的更少的Map和Reduce插槽,可能只需一个。
当HBase用于OLAP操作时,最好以强化的方式设置它,例如将ZooKeeper会话超时配置得更高并为MemStores提供更多内存(因为工作负载是因为块缓存不会被使用很多通常很长的扫描)。
有关性能和故障排除案例研究,请参阅 Apache HBase案例研究。