# 内存压缩

## 77\. 概况

内存压缩（A.K.A Accordion）是hbase-2.0.0中的一项新功能。它首先在[Accordion：HBase Breathes with In-Memory Compaction]的Apache HBase博客上引入（https://blogs.apache.org/hbase/entry/accordion-hbase-breathes-with-in）。引用博客：

> Accordion将LSM原理[_Log-Structured-Merge Tree_，HBase所基于的设计模式]重新应用于MemStore，以便在数据仍在RAM中时消除冗余和其他开销。这样做可以降低flush到HDFS的频率，从而减少写入放大和整个磁盘占用空间。由于刷新次数较少，因此MemStore溢出时写入操作停顿的频率降低，因此写入性能得到改善。磁盘上的数据越少，对块缓存的压力越小，命中率越高，最终读取响应时间越短。最后，具有较少的磁盘写入也意味着在后台发生较少的compaction，即，从生产（读取和写入）工作中分割的资源较少。总而言之，内存压缩的效果可以被看作是一个催化剂，使系统整体上运行速度更快。

可在以下链接中了解开发者视图[Accordion: Developer View of In-Memory Compaction](https://blogs.apache.org/hbase/entry/accordion-developer-view-of-in).

内存压缩在高数据流应用中效果最佳;当数据仍在内存中时，可以消除重写或过期版本。如果写入都是唯一的，则可能会拖慢写入吞吐量（内存压缩有CPU成本）。我们建议您在部署到生产之前进行测试和比较。

在本节中，我们将介绍如何启用Accordion和可用的配置。


## 78\. 启用

要启用内存压缩，则对每个你想要启用内存压缩的列簇设置 _IN_MEMORY_COMPACTION_配置项。_IN\_MEMORY\_COMPACTION_ 配置项的值可以为下面四个中任意一个。

*   _NONE_: 不启用内存压缩.

*   _BASIC_: 基本策略开启后，会在内存中一直保持一个flush管道，直到我们超过管道最大阈值，然后我们就会flush到磁盘。虽然没有内存压缩操作，但仍然可以帮助提高吞吐，因为数据从空间效率不高的原生ConcurrentSkipListMap数据类型转移到更紧凑（和高效）的数据类型。

*   _EAGER_: 这是 _BASIC_策略加上对flush操作的内存压缩组合（非常像磁盘上hfile的compaction）；当执行内存压缩时，使用的与磁盘上释放过期版本，冗余数据，超时数据等规则一样。

*   _ADAPTIVE_: 对负载进行自适应压缩操作。它会根据数据中重复单元格的比例来判断是否需要执行索引压缩或者数据压缩。

想要在表格 _radish_的 _info_列簇中应用 _BASIC_策略，需要先禁用表，然后给 _info_列簇增加属性，操作如下：

```
hbase(main):002:0> disable 'radish'
Took 0.5570 seconds
hbase(main):003:0> alter 'radish', {NAME => 'info', IN_MEMORY_COMPACTION => 'BASIC'}
Updating all regions with the new schema...
All regions updated.
Done.
Took 1.2413 seconds
hbase(main):004:0> describe 'radish'
Table radish is DISABLED
radish
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536', METADATA => {
'IN_MEMORY_COMPACTION' => 'BASIC'}}
1 row(s)
Took 0.0239 seconds
hbase(main):005:0> enable 'radish'
Took 0.7537 seconds
```

注意 _IN_MEMORY_COMPACTION属性是作为 _METADATA_映射里的一部分。

还有一个全局配置 _hbase.hregion.compacting.memstore.type_，可以在 _hbase-site.xml_文件里设置。可以用这个来设置新创建表的默认策略（在创建列簇存储时，我们首先看列簇配置中 _IN_MEMORY_COMPACTION_配置，如果没有，我们将查询 _hbase.hregion.compacting.memstore.type_的值作为配置值，其默认是 _BASIC_策略）。

默认情况下，新的hbase系统表将会被设置为 _BASIC_内存压缩策略。要另外指定，则在新表创建时，将 _hbase.hregion.compacting.memstore.type_设置为 _NONE_（注意，设置此值后之前创建的系统表不会改变;您将必须更改表以设置内存中属性为 _NONE_。）

内存flush发生的条件是通过将配置的Region flush大小（在表描述符中设置或从 _hbase.hregion.memstore.flush.size_ 中读取）除以列簇数，然后乘以 _hbase.memstore.inmemoryflush.threshold.factor_ 来计算，其默认值为0.014。

管道承载的flush次数会被控制在memstore的大小范围内，当然你也可以设置 _hbase.hregion.compacting.pipeline.segments.limit_ 参数指定一个最大flush次数值，默认值是2。


创建列簇存储时，需要指定哪种memstore类型。在撰写本文时，有两种memstore类型，分别是旧式的使用 _ConcurrentSkipListMap_ 来存放数据，然后flush到磁盘上的 _DefaultMemStore_ ，新式的提供上述内存压缩机制的 _CompactingMemStore_。下面是一个配置 _family_ 属性为 _CompactingMemStore_ 类型的列簇存储日志行：

```
Note how the IN_MEMORY_COMPACTION attribute shows as part of the _METADATA_ map.
2018-03-30 11:02:24,466 INFO  [Time-limited test] regionserver.HStore(325): Store=family,  memstore type=CompactingMemStore, storagePolicy=HOT, verifyBulkLoads=false, parallelPutCountPrintThreshold=10
```

可以为CompactingMemStore类（_org.apache.hadoop.hbase.regionserver.CompactingMemStore_）打开TRACE级别日志来查看运行细节。

