Hadoop技术栈(八)Hbase

Hadoop系列 (八):Hbase搭建 Hbase搭建及shell简介 阅读详情

介绍

HBase 基于 Google 的 BigTable 论文而来,是一个分布式海量列式非关系型数据库系统, 可以提供 超大规模数据集的实时随机读写 。

特点

海量存储: 底层基于 HDFS 存储海量数据
列式存储: HBase 表的数据是基于列族进行存储的,一个列族包含若干列
极易扩展: 底层依赖 HDFS ,当磁盘空间不足的时候,只需要动态增加 DataNode 服务节点就可以
高并发: 支持高并发的读写请求(聚合查询不适合hbase,考虑clickhouse)
稀疏: 稀疏主要是针对 HBase 列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。
数据的多版本: HBase 表中的数据可以有多个版本值,默认情况下是根据版本号去区分,版本号就是插入数据的时间戳
数据类型单一: 所有的数据在 HBase 中是以字节数组进行存储

数据模型

架构

Zookeeper
  • 实现了HMaster的高可用
  • 保存了HBase的元数据信息,是所有HBase表的寻址入口
  • 对HMaster和HRegionServer实现了监控
HMaster ( Master )
  • 为HRegionServer分配Region
  • 维护整个集群的负载均衡
  • 维护集群的元数据信息
  • 发现失效的Region,并将失效的Region分配到正常的HRegionServer上
HRegionServer ( RegionServer )
  • 负责管理Region
  • 接受客户端的读写数据请求
  • 切分在运行过程中变大的Region
Region
  • 每个HRegion由多个Store构成,
  • 每个Store保存一个列族(Columns Family),表有几个列族,则有几个Store,
  • 每个Store由一个MemStore和多个StoreFile组成,MemStore是Store在内存中的内容,写到文件后就是StoreFile。StoreFile底层是以HFile的格式保存。

安装配置

下载安装包 : http://archive.apache.org/dist/hbase/1.3.1/
tar -zxvf hbase-1.3.1-bin.tar.gz -C /opt/lxq/servers
需要把 hadoop 中的配置 core-site.xml 、 hdfs-site.xml 拷贝到 hbase 安装目录下的 conf 文件夹中
ln -s /opt/lxq/servers/hadoop-2.9.2/etc/hadoop/core-site.xml
/opt/lxq/servers/hbase-1.3.1/conf/core-site.xml
ln -s /opt/lxq/servers/hadoop-2.9.2/etc/hadoop/hdfs-site.xml
/opt/lxq/servers/hbase-1.3.1/conf/hdfs-site.xml
修改 hbase-env.sh
# 添加 java 环境变量
export JAVA_HOME=/opt/module/jdk1.8.0_231
# 指定使用外部的 zk 集群
export HBASE_MANAGES_ZK=FALSE
改 hbase-site.xml
<configuration>
<!-- 指定 hbase 在 HDFS 上存储的路径 -->
<property>
<name>hbase.rootdir</name>
<value>hdfs://linux121:9000/hbase</value>
</property>
<!-- 指定 hbase 是分布式的 -->
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- 指定 zk 的地址,多个用 “,” 分割 -->
<property>
<name>hbase.zookeeper.quorum</name>
<value>linux121:2181,linux122:2181,linux123:2181</value>
</property>
</configuration>
修改 regionservers 文件
# 指定 regionserver 节点
linux121
linux122
linux123
hbase 的 conf 目录下创建文件 backup-masters (Standby Master),内容如下:
linux122
vim /etc/profile
export HBASE_HOME=/opt/lxq/servers/hbase-1.3.1
export PATH=$PATH:$HBASE_HOME/bin
source /etc/profile
分发hbase到其它节点 不知道rsync-script的,去看我hadoop文章
rsync-script hbase-1.3.1
前提条件:先启动 hadoop 和 zk 集群
启动 HBase : start-hbase.sh
停止 HBase : stop-hbase.sh
启动好 HBase 集群之后,可以访问地址: HMaster 的主机名 :16010

基操

进入 Hbase 客户端命令操作界面:hbase shell
hbase ( main ) : 001 : 0 > help
# 有哪些表
hbase ( main ) : 006 : 0 > list
# 创表,包含两个列族
hbase ( main ) : 001 : 0 > create 'lxq' , 'base_info' , 'extra_info'
或者 ( Hbase 建表必须指定列族信息 )
create 'lxq' , { NAME => 'base_info' , VERSIONS => '3' },{ NAME =>  'extra_info' , VERSIONS => '3' }
VERSIONS 是指此单元格内的数据可以保留最近的 3 个版本
put 'lxq' , 'rk1' , 'base_info:name' , '赖晓期'
hbase ( main ) : 008 : 0 > get 'lxq' , 'rk1' , 'base_info:name' , 'base_info:age'
或者
get 'lxq' , 'rk1' , {COLUMN = > [ 'base_info:name' , 'extra_info:address' ]}
hbase ( main ) : 001 : 0 > get 'lxq' , 'rk1' , { FILTER => "ValueFilter(=, 'binary:赖晓期')" }
# 模糊查询 列标示符中含有 a 的信息
hbase ( main ) : 001 : 0 > get 'lxq' , 'rk1' , { FILTER => "(QualifierFilter(=,'substring:a'))" }
hbase ( main ) : 000 : 0 > scan 'lxq'
hbase ( main ) : 002 : 0 > scan 'lxq' , { COLUMNS => 'base_info' , RAW => true , VERSIONS => 3 }
## Scan 时可以设置是否开启 Raw 模式 , 开启 Raw 模式会返回包括已添加删除标记但是未实际删除的数据
## VERSIONS 指定查询的最大版本数
#  查询 lxq 表中列族为 base_info 和 extra_info 且列标示符中含有a字符的信息
hbase ( main ) : 001 : 0 > scan 'lxq' , { COLUMNS => [ 'base_info' , 'extra_info' ], FILTER => "(QualifierFilter(=,'substring:a'))" }
#  查询lxq 表中列族为 base_info , rk 范围是 [rk1, rk3) 的数据( rowkey 底层存储是字典序) 按rowkey 顺序存储。
hbase ( main ) : 001 : 0 > scan 'lxq' , { COLUMNS => 'base_info' , STARTROW => 'rk1' , ENDROW => 'rk3' }
#  查询 lxq 表中 row key 以 rk 字符开头的
hbase ( main ) : 001 : 0 > scan 'lxq' ,{ FILTER => "PrefixFilter('rk')" }
更新操作同插入操作一模一样,只不过有数据就更新,没数据就添加
hbase ( main ) : 030 : 0 > put 'lxq' , 'rk1' , 'base_info:name' , 'liang'
# 删除 lxq 表 row key 为 rk1 ,列标示符为 base_info:name 的数据
hbase ( main ) : 002 : 0 > delete 'lxq' , 'rk1' , 'base_info:name' , 1600660619655
hbase ( main ) : 035 : 0 > alter 'lxq' , 'delete' => 'base_info'
# 清空表数据
hbase ( main ) : 001 : 0 > truncate 'lxq'
# 删除表
# 先 disable 再 drop
hbase(main):036:0> disable 'lxq'
hbase(main):037:0> drop 'lxq'
# 如果不进行 disable ,直接 drop 会报错
ERROR: Table user is enabled. Disable it first.

原理

读操作

1 )首先从 zk 找到 meta 表的 region 位置,然后读取 meta 表中的数据, meta 表中存储了用户表的
region 信息
2 )根据要查询的 namespace 、表名和 rowkey 信息。找到写入数据对应的 region 信息
3 )找到这个 region 对应的 regionServer ,然后发送请求
4 )查找对应的 region
5 )先从 memstore 查找数据,如果没有,再从 BlockCache 上读取HBase上 Regionserver 的内存分为两个部分
  • 一部分作为Memstore,主要用来写;
  • 另外一部分作为BlockCache,主要用于读数据;
6 )如果 BlockCache 中也没有找到,再到 StoreFile 上进行读取
从 storeFile 中读取到数据之后,不是直接把结果数据返回给客户端,
而是把数据先写入到 BlockCache 中,目的是为了加快后续的查询;然后在返回结果给客户端。

写操作

1 )首先从 zk 找到 meta 表的 region 位置,然后读取 meta 表中的数据, meta 表中存储了用户表的region信息
2 )根据 namespace 、表名和 rowkey 信息。找到写入数据对应的 region 信息
3 )找到这个 region 对应的 regionServer ,然后发送请求
4 )把数据分别写到 HLog ( write ahead log )和 memstore 各一份
5 ) memstore 达到阈值后把数据刷到磁盘,生成 storeFile 文件
6 )删除 HLog 中的历史数据
region拆分策略  HBase 的 Region Split 策略一共有以下几种:
1 ) ConstantSizeRegionSplitPolicy
0.94 版本前默认切分策略
当 region 大小大于某个阈值 (hbase.hregion.max.filesize=10G) 之后就会触发切分,一个 region 等分为2 个 region 。
但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1 个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的region ,这对于集群的管理、资源使用、 failover 来说都不是一件好事。
2 ) IncreasingToUpperBoundRegionSplitPolicy
0.94 版本 ~2.0 版本默认切分策略
切分策略稍微有点复杂,总体看和 ConstantSizeRegionSplitPolicy 思路相同,一个 region 大小大于设置阈值就会触发切分。但是这个阈值并不像ConstantSizeRegionSplitPolicy 是一个固定的值,而是会在一定条件下不断调整,调整规则和region 所属表在当前 regionserver 上的 region 个数有关系 .
region split 的计算公式是:
regioncount^3 * 128M * 2 ,当 region 达到该 size 的时候进行 split
例如:
第一次 split : 1^3 * 256 = 256MB
第二次 split : 2^3 * 256 = 2048MB
第三次 split : 3^3 * 256 = 6912MB
第四次 split : 4^3 * 256 = 16384MB > 10GB ,因此取较小的值 10GB
后面每次 split 的 size 都是 10GB 了
3 ) SteppingSplitPolicy
2.0 版本默认切分策略
这种切分策略的切分阈值又发生了变化,相比 IncreasingToUpperBoundRegionSplitPolicy 简单了一些,依然和待分裂region 所属表在当前 regionserver 上的 region 个数有关系,如果 region 个数等于1,切分阈值为flush size * 2 ,否则为 MaxRegionFileSize 。这种切分策略对于大集群中的大表、小表会比 IncreasingToUpperBoundRegionSplitPolicy 更加友好,小表不会再产生大量的小 region ,而是适可而止。
4 ) KeyPrefixRegionSplitPolicy
根据 rowKey 的前缀对数据进行分组,这里是指定 rowKey 的前多少位作为前缀,比如 rowKey 都是 16 位的,指定前5 位是前缀,那么前 5 位相同的 rowKey 在进行 region split 的时候会分到相同的 region 中。
5 ) DelimitedKeyPrefixRegionSplitPolicy
保证相同前缀的数据在同一个 region 中,例如 rowKey 的格式为: userid_eventtype_eventid ,指定的delimiter为 _ ,则 split 的的时候会确保 userid 相同的数据在同一个 region 中。
6 ) DisabledRegionSplitPolicy
不启用自动拆分 , 需要指定手动拆分
表预分区
当一个 table 刚被创建的时候, Hbase 默认的分配一个 region 给 table 。也就是说这个时候,所有的读写请求都会访问到同一个regionServer 的同一个 region 中,这个时候就达不到负载均衡的效果了,集群中的其他regionServer 就可能会处于比较空闲的状态。解决这个问题可以用 pre-splitting, 在创建 table的时候就配置好,生成多个region 。
  • 增加数据读写效率
  • 负载均衡,防止数据倾斜
  • 方便集群容灾调度region
每一个 region 维护着 startRow 与 endRowKey ,如果加入的数据符合某个 region 维护的 rowKey 范围,则该数据交给这个region
create 'person','info1','info2',SPLITS => ['1000','2000','3000']
region合并
Region 的合并不是为了性能,而是出于维护的目的。
通过 Merge 类冷合并 Region
这里通过 org.apache.hadoop.hbase.util.Merge 类来实现,不需要进入 hbase shell ,直接执行(需要先关闭hbase 集群)
hbase org.apache.hadoop.hbase.util.Merge student \
student,,1595256696737.fc3eff4765709e66a8524d3c3ab42d59. \
student,aaa,1595256696737.1d53d6c1ce0c1bed269b16b6514131d0.
通过 online_merge 热合并 Region
与冷合并不同的是, online_merge 的传参是 Region 的 hash 值,而 Region 的 hash 值就是 Region 名称的最后那段在两个. 之间的字符串部分。
需求:需要把 lxq_s 表中的 2 个 region 数据进行合并:
student,,1587392159085.9ca8689901008946793b8d5fa5898e06. \
student,aaa,1587392159085.601d5741608cedb677634f8f7257e000.
需要进入 hbase shell :
merge_region
'c8bc666507d9e45523aebaffa88ffdd6','02a9dfdf6ff42ae9f0524a3d8f4c7777'

Java Api 应用

package ;

import com.cycc.proc.commons.core.hbase.config.HbaseConfig;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class HbaseUtils {

    private static final Connection conn;

    private static final Admin admin;

    static {
        try {
            conn = HbaseConfig.getHbaseConnection();
            admin = conn.getAdmin();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void close() {
        if (admin != null) {
            try {
                admin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /*if (conn != null) {
            try {
                conn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
    }

    /**
     * 创建表
     *
     * @param tableName    表名
     * @param columnFamily 列族名集合
     */
    public static void creatTable(String tableName, Set<String> columnFamily) throws IOException {
        if (StringUtils.isBlank(tableName) || CollectionUtils.isEmpty(columnFamily)) {
            return;
        }
        //创建表描述器
        HTableDescriptor table = new HTableDescriptor(TableName.valueOf(tableName));

        for (String familyName : columnFamily) {
            //设置列族描述器
            table.addFamily(new HColumnDescriptor(familyName));
        }


        if (admin.tableExists(TableName.valueOf(tableName))) {
            // 表已经存在了
            return;
        } else {
            //执行创建操作
            admin.createTable(table);
            System.out.println(table + " create success!");
        }
        close();
    }

    /**
     * 获取所有的表名
     */
    public static List<String> getAllTableNames() throws IOException {
        List<String> result = new ArrayList<>();
        TableName[] tableNames = admin.listTableNames();
        for (TableName tableName : tableNames) {
            result.add(tableName.getNameAsString());
        }
        close();
        return result;
    }

    /**
     * 保存数据
     *
     * @param tableName  表名
     * @param rowKey     行key
     * @param familyName 列族名
     * @param col        列名
     * @param data       数据
     * @throws IOException 异常
     */
    public static void putData(String tableName, String rowKey, String familyName, String col, String data)
            throws IOException {
        if (StringUtils.isBlank(tableName) || StringUtils.isBlank(rowKey) || StringUtils.isBlank(familyName)
                || StringUtils.isBlank(col) || StringUtils.isBlank(data)) {
            return;
        }
        if (!admin.tableExists(TableName.valueOf(tableName))) {
            return;
        }
        // 获取一个表对象
        Table t = conn.getTable(TableName.valueOf(tableName));
        if (t == null) {
            return;
        }
        // 设定 rowKey
        Put put = new Put(Bytes.toBytes(rowKey));
        //列族,列,value
        put.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(col), Bytes.toBytes(data));
        //执行插入
        t.put(put);
        // t.put();//可以传入list批量插入数据
        //关闭table对象
        t.close();
        close();
        System.out.println("save " + data + " to table " + tableName + " success!");
    }

    /**
     * 根据行Key删除
     *
     * @param tableName 表名
     * @param rowKey    行Key
     * @throws IOException 异常
     */
    public static void deleteData(String tableName, String rowKey) throws IOException {
        if (StringUtils.isBlank(tableName) || StringUtils.isBlank(rowKey)) {
            return;
        }
        if (!admin.tableExists(TableName.valueOf(tableName))) {
            return;
        }
        // 需要获取一个table对象
        final Table t = conn.getTable(TableName.valueOf(tableName));
        if (t == null) {
            return;
        }
        // 准备delete对象
        final Delete delete = new Delete(Bytes.toBytes(rowKey));
        // 执行删除
        t.delete(delete);
        // 关闭table对象
        t.close();
        close();
        System.out.println("delete " + rowKey + " from " + tableName + " success!");
    }

    /**
     * 查看行Key下的列族信息
     *
     * @param tableName  表名
     * @param rowKey     行Key
     * @param familyName 列族名
     */
    public static Cell[] getDataByCF(String tableName, String rowKey, String familyName) throws IOException {
        if (StringUtils.isBlank(tableName) || StringUtils.isBlank(rowKey) || StringUtils.isBlank(familyName)) {
            return null;
        }
        if (!admin.tableExists(TableName.valueOf(tableName))) {
            return null;
        }
        // 获取表对象
        HTable hTable = (HTable) conn.getTable(TableName.valueOf(tableName));
        if (null == hTable) {
            return null;
        }
        //创建查询的get对象
        Get get = new Get(Bytes.toBytes(rowKey));
        // 指定列族信息
        // get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"));
        get.addFamily(Bytes.toBytes(familyName));
        // 执行查询
        Result res = hTable.get(get);
        // 获取改行的该列族的所有cell对象
        Cell[] cells = res.rawCells();
        for (Cell cell : cells) {
            // 通过cell获取rowkey,cf,column,value
            String cf = Bytes.toString(CellUtil.cloneFamily(cell));
            String column = Bytes.toString(CellUtil.cloneQualifier(cell));
            String value = Bytes.toString(CellUtil.cloneValue(cell));
            String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
            System.out.println(rowkey + "----" + cf + "---" + column + "---" + value);
        }
        // 关闭表对象资源
        hTable.close();
        close();
        return cells;
    }

    /**
     * 全表扫描 这里的返回结果没有 需要在封装一下
     *
     * @param tableName 表名
     * @throws IOException 异常
     */
    public static void scanAllData(String tableName) throws IOException {
        if (StringUtils.isBlank(tableName)) {
            return;
        }
        if (!admin.tableExists(TableName.valueOf(tableName))) {
            return;
        }
        HTable hTable = (HTable) conn.getTable(TableName.valueOf(tableName));
        if (null == hTable) {
            return;
        }
        Scan scan = new Scan();
        ResultScanner resultScanner = hTable.getScanner(scan);
        for (Result result : resultScanner) {
            // 获取改行的所有cell对象
            Cell[] cells = result.rawCells();
            for (Cell cell : cells) {
                // 通过cell获取 rowkey,cf,column,value
                String cf = Bytes.toString(CellUtil.cloneFamily(cell));
                String column = Bytes.toString(CellUtil.cloneQualifier(cell));
                String value = Bytes.toString(CellUtil.cloneValue(cell));
                String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
                System.out.println(rowkey + "----" + cf + "--" + column + "---" + value);
            }
        }
        hTable.close();
        close();
    }

    /**
     * 指定查哪些行key的数据 结果还需要在封装一下
     *
     * @param tableName 表名
     * @param startRow  开始行key
     * @param stopRow   结束行key
     * @throws IOException 异常
     */
    public static void scanRowKey(String tableName, String startRow, String stopRow) throws IOException {
        if (StringUtils.isBlank(tableName) || StringUtils.isBlank(startRow) || StringUtils.isBlank(stopRow)) {
            return;
        }
        HTable hTable = (HTable) conn.getTable(TableName.valueOf(tableName));
        if (null == hTable) {
            return;
        }
        Scan scan = new Scan();
        scan.setStartRow(startRow.getBytes());
        scan.setStopRow(stopRow.getBytes());
        ResultScanner resultScanner = hTable.getScanner(scan);
        for (Result result : resultScanner) {
            // 获取改行的所有cell对象
            Cell[] cells = result.rawCells();
            for (Cell cell : cells) {
                // 通过cell获取rowkey,cf,column,value
                String cf = Bytes.toString(CellUtil.cloneFamily(cell));
                String column = Bytes.toString(CellUtil.cloneQualifier(cell));
                String value = Bytes.toString(CellUtil.cloneValue(cell));
                String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
                System.out.println(rowkey + "----" + cf + "--" + column + "---" + value);
            }
        }
        hTable.close();
    }
}

协处理器

访问 HBase 的方式是使用 scan 或 get 获取数据,在获取到的数据上进行业务运算。但是在数据量非常大的时候,比如一个有上亿行及十万个列的数据集,再按常用的方式移动获取数据就会遇到性能问题。客户端也需要有强大的计算能力以及足够的内存来处理这么多的数据。
此时就可以考虑使用 Coprocessor( 协处理器 ) 。将业务运算代码封装到 Coprocessor 中并在
RegionServer 上运行,即在数据实际存储位置执行,最后将运算结果返回到客户端。利用协处理器,用户可以编写运行在 HBase Server 端的代码。

通过Java代码编写自己的协处理器,示例:

public class MyProcessor extends BaseRegionObserver {
@Override
public void prePut(ObserverContext<RegionCoprocessorEnvironment> ce, Put
put, WALEdit edit, Durability durability) throws IOException {
//把自己需要执行的逻辑定义在此处,向t2表插入数据,数据具体是什么内容与Put一样
final HTableInterface t2 =
e.getEnvironment().getTable(TableName.valueOf("t2"));
//解析t1表的插入对象put
final Cell cell = put.get(Bytes.toBytes("info"),
Bytes.toBytes("name")).get(0);
//table对象.put
final Put put1 = new Put(put.getRow());
put1.add(cell);
t2.put(put1); //执行向t2表插入数据
t2.close();
}
}
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
</dependency>
打成 Jar 包,上传 HDFS
cd /opt/lxq/softwares
mv original-hbaseStudy-1.0-SNAPSHOT.jar processor.jar
hdfs dfs -mkdir -p /processor
hdfs dfs -put processor.jar /processor
挂载协处理器
hbase(main):056:0> describe 't1'
hbase(main):055:0> alter 't1' ,METHOD = >
'table_att' , 'Coprocessor' = > 'hdfs://linux121:9000/processor/processor.jar|com
.lxq.hbase.processor.MyProcessor|1001|'
# 再次查看 't1' 表,
hbase(main):043:0> describe 't1'
# 卸载协处理器
disable 't1'
alter 't1' ,METHOD = > 'table_att_unset' ,NAME = > 'coprocessor$1'
enable 't2'
Observer
协处理器与触发器 (trigger) 类似:在一些特定事件发生时回调函数(也被称作钩子函数, hook )被执
行。这些事件包括一些用户产生的事件,也包括服务器端内部自动产生的事件。
协处理器框架提供的接口如下
RegionObserver :用户可以用这种的处理器处理数据修改事件,它们与表的 region 联系紧密。
MasterObserver :可以被用作管理或 DDL 类型的操作,这些是集群级事件。
WALObserver :提供控制 WAL 的钩子函数
Endpoint
这类协处理器类似传统数据库中的存储过程,客户端可以调用这些 Endpoint 协处理器在 Regionserver
中执行一段代码,并将 RegionServer 端执行结果返回给客户端进一步处理。
Endpoint 常见用途
聚合操作
假设需要找出一张表中的最大数据,即 max 聚合操作,普通做法就是必须进行全表扫描,然后 Client代码内遍历扫描结果,并执行求最大值的操作。这种方式存在的弊端是无法利用底层集群的并发运算能力,把所有计算都集中到 Client 端执行 , 效率低下。
使用 Endpoint Coprocessor ,用户可以将求最大值的代码部署到 HBase RegionServer 端, HBase会利用集群中多个节点的优势来并发执行求最大值的操作。也就是在每个 Region 范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该 max 值返回给 Client 。在Client进一步将多个 Region 的最大值汇总进一步找到全局的最大值。
Endpoint Coprocessor 的应用我们后续可以借助于 Phoenix 非常容易就能实现。针对 Hbase 数据集进行聚合运算直接使用SQL 语句就能搞定。

rowkey设计原则

RowKey长度原则 建议越短越好,不要超过 16 个字节
RowKey散列原则  建议将 rowkey 的高位作为散列字段,这样将提高数据均衡分布在每个 RegionServer ,以实现负载均 衡的几率。
RowKey 唯一原则
        访问hbase table 中的行:有 3 种方式:
  • 单个rowkey
  • rowkey 的range
  • 全表扫描(一定要避免全表扫描)
RowKey排序原则 HBase 的 Rowkey 是按照 ASCII 有序设计的,我们在设计 Rowkey 时要充分利用这点

优化相关

检索 habse 的记录首先要通过 row key 来定位数据行。当大量的 client 访问 hbase 集群的一个或少数几个节点,造成少数region server 的读 / 写请求过多、负载过大,而其他 region server 负载却很小,就造成了“ 热点 ” 现象。解决方案有:
预分区
预分区的目的让表的数据可以均衡的分散在集群中,而不是默认只有一个 region 分布在集群的一个节点上。
加盐
这里所说的加盐不是密码学中的加盐,而是在 rowkey 的前面增加随机数,具体就是给 rowkey 分配一个随机前缀以使得它和之前的rowkey 的开头不同。
哈希
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey ,可以使用 get 操作准确获取某一个行数据。
反转
反转固定长度或者数字格式的 rowkey 。这样可以使得 rowkey 中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey ,但是牺牲了 rowkey 的有序性。
15X,13X,
二级索引
常见的二级索引我们一般可以借助各种其他的方式来实现,例如 Phoenix 或者 solr 或者 ES 等
Hbase也需要像redis一样运用布隆过滤器
hbase 的读操作需要访问大量的文件,大部分的实现可通过布隆过滤器来避免大量的读文件操作。

行储存和列存储比较

行存储(Row-based Storage)和列存储(Column-based Storage)是数据库系统中两种核心的数据物理组织方式,它们在性能、适用场景和底层实现上存在显著差异。以下是它们的详细比较:


‌1. 数据存储方式‌

  • ‌行存储(Row-oriented)‌

    • ‌按行存储‌:将同一行的所有数据(包括不同列的值)连续存储在磁盘上(如:(UserID, Name, Age, City) 作为一个整体存储)。
    • ‌示例‌:
       

      plaintextCopy Code

      Block1: [1, "Alice", 28, "Paris"] Block2: [2, "Bob", 32, "London"]

  • ‌列存储(Column-oriented)‌

    • ‌按列存储‌:将同一列的所有数据连续存储(如:所有 UserID 存在一起,所有 Name 存在一起)。
    • ‌示例‌:
       

      plaintextCopy Code

      Block1 (UserID): [1, 2, 3, ...] Block2 (Name): ["Alice", "Bob", "Charlie", ...] Block3 (Age): [28, 32, 25, ...]


‌2. 核心优势对比‌

‌特性‌‌行存储‌‌列存储‌
‌写入性能‌⭐⭐⭐⭐ 高
(单次插入整行数据)
️ 低
(需写入多列位置)
‌点查询(单行读取)‌⭐⭐⭐⭐ 高
(一次I/O读取整行)
⚠️ 低
(需从多列重组行)
‌聚合查询(SUM/AVG)‌️ 低
(需扫描整行数据)
⭐⭐⭐⭐ 高
(仅读取目标列)
‌数据压缩率‌⚠️ 低
(行内数据类型多样)
⭐⭐⭐⭐ 高
(同列数据类型一致)
‌索引效率‌依赖B树等索引列数据天然紧凑,适合位图索引
‌存储空间‌通常更大较小(高压缩率)

‌3. 适用场景‌

  • ‌行存储适合‌:

    • ‌OLTP(在线事务处理)‌:如银行交易、订单提交。
    • ‌频繁的单行读写‌(如用户登录、订单查询)。
    • ‌需要完整行数据的操作‌(如更新用户资料)。
    • ‌常见数据库‌:MySQL、PostgreSQL、SQL Server。
  • ‌列存储适合‌:

    • ‌OLAP(在线分析处理)‌:如数据仓库、商业智能(BI)。
    • ‌大规模聚合查询‌(统计销售额、计算平均值)。
    • ‌仅需部分列的查询‌(如分析用户年龄分布)。
    • ‌常见数据库‌:Amazon Redshift、Google BigQuery、ClickHouse。

‌4. 优化技术‌

  • ‌行存储优化‌:

    • ‌索引‌:通过B+树加速行定位。
    • ‌行缓存‌:缓存热点行(如Redis)。
  • ‌列存储优化‌:

    • ‌向量化处理‌:批量处理列数据(利用CPU SIMD指令)。
    • ‌列裁剪‌:仅读取查询涉及的列。
    • ‌高级压缩‌:字典编码(Dictionary Encoding)、行程编码(RLE)。

‌5. 混合存储方案‌

现代数据库(如 ‌Apache Cassandra‌、‌Bigtable‌)支持‌混合模式‌:

  • ‌基于行的分区‌ + ‌列族(Column Families)‌:在分区内按列存储数据。
  • ‌Delta Lake / Iceberg‌:在数据湖中同时支持行列混合格式(如Parquet/ORC列存 + 行式ACID事务)。

‌典型场景示例‌

  1. ‌OLTP查询(行存优势)‌

     

    sqlCopy Code

    SELECT * FROM users WHERE user_id = 100; -- 一次I/O读取整行

  2. ‌OLAP分析(列存优势)‌

     

    sqlCopy Code

    SELECT AVG(salary), department FROM employees GROUP BY department; -- 仅读取salary和department列,压缩数据加速扫描


‌总结‌

‌维度‌‌行存储‌‌列存储‌
‌设计目标‌高并发事务处理大规模数据分析
‌数据布局‌行连续存储列连续存储
‌强项操作‌单行CRUD聚合、扫描、列投影
‌弱点‌全表扫描效率低点查询/更新成本高
‌工业应用‌MySQL, PostgreSQLRedshift, Snowflake, ClickHouse

💡 ‌关键选择建议‌:

  • 需要‌实时处理事务‌? → 选择行存储(或Hybrid)。
  • 需要‌分析TB级数据‌? → 选择列存储。

现代分布式系统(如 ‌Apache Druid‌)甚至支持‌时间序列数据按列分片‌,进一步优化时序分析场景。理解存储模型是数据库性能调优和架构设计的底层基石。

感谢阅读!!!

Hadoop安装配置HBase HBase的单机安装; 配置HBase以及理解HBase的HMaster的作用; HBase Shell常用命令行操作。 阅读详情

相关推荐

spring data hadoop操作hbase

一、使用的jar版本 1、hbase-client:0.96.1.1-cdh5.0.0 2、spring-data-hadoop:2.2.1.RELEASE 3、Spring:4.2.1.RELEASE 二、hbase-site.xml配置 configuration>  property>   name>hbase.zookeeper.quorumname>

牛奋lch 1万+

Hadoop之Hbase安装和配置

Hadoop之Hbase安装和配置

leoatliang的博客 4万+

Hadoop集群启动Hbase的步骤流程

Hadoop集群启动Hbase的步骤流程 系统版本:centOS7.7 HBASE版本:apache-hbase-1.2.1 HADOOP版本:apache-hadoop-2.7.6 JDK版本:jdk-1.8.0_45 单机安装/伪分布式:1台服务器 全分布式:3台服务器 (关于如何配置hbase,不再叙述,可以查阅资料) 1.启动hdfs start-all.sh 2.启动zookeeper 三台节点同时启动(前提是配置好了 时间同步!) zkServer.sh start 3.启动hbase

Blackdude的博客 3742

Hadoop和HBase基础

Hadoop和HBase基础。

K要努力的博客 3084

hadoop HA 下 Hbase无法连接外部zookeeper问题

hadoop HA 下 Hbase无法连接外部zookeeper问题1.搭建好hadoop集群并启动后,安装Hbase,配置好文件,启动HBase,进入hbase shell 后,输入list 提示如下信息:hbase(main):001:0&gt; listTABLE                                                                 ...

codeeee的博客 4351

【hadoop】hbase配置

注:配套安装: Hadoop 3.1.3 Zookeeper3.5.7 Hbase2.2.0 一、文件解压 cd /opt/software/ tar -zxvf hbase-2.2.0-bin.tar.gz -C /opt/module/ 二、修改启动变量 系统环境变量增加 vi /etc/profile 到最下面把下面这两行代码插入进去 export HBASE_HOME=/opt/module/hbase-2.2.0 export PATH=$PATH...

weixin_59765716的博客 1709

Hbase与Hadoop版本对应

安装HBase时,要考虑选择正确的Hadoop版本,否则可能出现不兼容的情况。 Hadoop version support matrix "S" = supported/支持 "X" = not supported/不支持 "NT" = Not tested/没有测试   HBase-0.92.x

Alan Zhuang的博客 1万+

在完全分布式hadoop上部署hbase

7. 将集群主节点master的/etc/profile文件和hbase安装目录分发到slave1和slave2子节点,并刷新slave1、slave2的环境变量。5. 编辑/usr/local/src/hbase/conf/hbase-site.xml配置文件。6. 编辑/usr/local/src/hbase/conf/regionservers配置文件。3. 编辑/usr/local/src/hbase/conf/hbase-env.sh配置文件。15. 通过浏览器访问Hadoop。

2301_80628020的博客 1090

Java技术栈 —— Hive与HBase

见参考文章。一、参考文章或视频链接[1]《Java技术栈 —— Hadoop入门(一)》- 优快云。

键盘国治理专家的博客 1934

实现高精度卷积混响--MATLAB仿真篇

一、前言 之前在STM32F407上现实了直播声卡,实现的经典Schroeder混响模型和Moorer混响模型,得到不错效果。后来得知卷积混响最为真实地表现空间的特性,所以也就尝试在MCU上实现一个高精度的卷积混响(卷积点数为32768),目前MCU出来的混响效果算得上一流水准,本人也在计划在ADI的21489或者SC584上更高精度的卷积混响。 二、卷积方式 1、conv1是使用MATLAB自带卷积函数conv 2、conv2和conv3是改进后卷积过程,具有减少运算量特点,方便嵌入式移植 clear;

Six44的博客 5077

从零到一:FAST_LIO与FAST_LIO2算法环境搭建与实战评测

本文详细介绍了FAST_LIO与FAST_LIO2算法的环境搭建与实战评测,包括算法原理、环境配置避坑指南、源码编译技巧及性能对比。FAST_LIO2凭借ikd-tree数据结构在动态环境中表现优异,处理速度提升至15ms,内存占用降低40%。文章还提供了常见问题排查和进阶优化技巧,助力开发者高效实现SLAM应用。

weixin_29271053的博客 470

spark、hive、hbase以及和Hadoop的关系

本文转自:https://www.cnblogs.com/wangyalou/p/9532347.html 首先我们来看一下spark究竟是什么。相信很多读者跟我一样,听说过hadoop,也知道spark,更知道spark是现在最火的大数据技术,所以一直有一个疑问:spark是不是替代能够hadoop的下一代大数据技术?答案是:不是! 首先我们看看spark的官网介绍:Apache Spark is a fast and general-purpose cluster computing system.

强哥叨逼叨 8767

从零开始构建高精度称重系统:ADS1232硬件设计与软件调优实战

本文详细介绍了如何基于TI的ADS1232 24位ADC芯片构建高精度称重系统,涵盖硬件设计、软件调优及校准算法。通过优化电源设计、PCB布局和卡尔曼滤波算法,实现毫克级测量精度,适用于工业自动化和实验室等高要求场景。

weixin_28285943的博客 382

Hadoop集群搭建及配置⑧——Hbase的安装配置

掌握基础环境配置 掌握hbase安装 掌握运用hbase-shell

博樽 2677

五子棋AI算法第一篇-我对谷歌Alphago的看法

最近Google宣布他们的围棋AI以5:0的战绩打败欧洲围棋三冠王。我一直对人工智能比较感兴趣,正好趁年前空闲的几天用JS写了一个五子棋的AI。其实很久以前就用自创的算法写过一个JAVA版的五子棋AI,不过棋力很不理想。这次用的是JS写的,所以无论任何平台上只要能打开网页的都可以玩。google爆出这个新闻之后,很多人觉得这是不是意味着电脑智商已经在人类之上了,围棋已经没有意义了之类的。甚至觉得这是

言川的博客 2万+

学术版GPT安装教程——保姆版(附带原生GPT注册教程)

学术版GPT图文安装教程

DBenD的博客 2万+

Hbase与Hadoop的jar包冲突

问题描述 Hbase跟Hadoop都安装在了一台服务器里,配置了下/etc/profile中的hadoop变量; 启动Hbase报错 [root@Master bin]# ./start-hbase.sh SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/hadoo...

baixf的博客 4042

CANoe.DiVa的应用——生成测试用例过程流程详解(一)

Diva 生成用例过程

LOVE135149的博客 7926

为什么有了Hadoop还要HBase ?

Hadoop 和 HBase 各自有不同的设计目的和应用场景,虽然它们都属于大数据生态系统,但它们解决的问题和优化的方向不同。下面我将通过表格的方式,详细对比 Hadoop 和 HBase,并在最后描述两者对小文件的处理差异。虽然 HBase 底层使用 HDFS 存储数据,但它并不适合存储大量小文件,因为存储小文件会导致元数据管理开销,且并未针对小文件进行优化。(配合文件合并策略)会是更好的选择,而如果需要处理大量实时数据、频繁的读取和更新操作,因此,如果你的应用场景涉及大量小文件的存储,

言之。 1万+

11、RK3566/3568 Android11切割屏幕 长条屏 显示

在使用 RK3566 平台进行 Android 11 开发时,遇到切割屏(长条屏)显示画面不全。本文将详细介绍如何配置,以及如何修改 HWComposer 来调整显示区域,解决图像缩放问题。我这边屏幕是lvds接口的,分辨率1920 1080实际的屏物理尺寸是1920x463。

qq_39451352的博客 252

[QMT和ptrade量化交易小白入门]-109、小市值选股的基础上用六脉神剑评分,回测年化可到132%

本专栏主要是介绍QMT和ptrade的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写200篇左右。QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步。

木头大左的博客 1859

Hadoop、Hbase使用Snappy压缩

Hadoop、Hbase使用Snappy压缩

moxiaotian0510的博客 2400

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Hadoop集群+Hbase集群搭建

想部署hadoop集群和hbase的集群,我的方式是建议先在一台虚拟机上面部署hadoop伪分布+hbase伪分布,再以这台虚拟机生成克隆,防止出现难以解决的问题,备份一台,给自己必须留一手!!!! 进行hadoop的集群时每各节点已经部署hadoop的伪分布,所以jdk和hadoop都已经安装。 软件:VMware Workstation 操作系统:linux+ubuntu服务器 Hadoop集群部署 集群规划 主节点:Master 从节点:slave1 、slave2 Ip:

qq_45976312的博客 2216

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Hadoop和Hbase的关系:Hadoop Hbase详解

作为大数据主流的框架之一,Hadoop的发展已经进入稳定成熟阶段,针对于大数据处理各个环节当中的问题,都有相应的解决方案,也因此形成了完整的Hadoop生态,其中各个组件的配合,共同完成大数据处理任务。今天我们就主要来聊聊Hadoop和Hbase的关系,详细介绍一下Hadoop Hbase相关的知识。 Hbase,其实是Hadoop Database的简称,本质上来说就是Hadoop系统的数据库,为Hadoop框架当中的结构化数据提供存储服务,是面向列的分布式数据库。这一点与HDFS是不一样的,HDFS是分

shuimuzh123的博客 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Hadoop、Hive、Hbase、Zookeeper版本兼容

文章目录Hadoop - HbaseHadoop - Hivehive - hbasehbase - zookeeper Hadoop - Hbase http://hbase.apache.org/book.html#arch.overview Ctrl+F搜索Hadoop version support matrix Hadoop - Hive http://hive.apache.org/downloads.html hive - hbase https://cwiki.apache.org/

梦付千秋星垂野 1356

实验八 Hbase 入门

实验八Hbase 入门 实验目的 理解HBase在Hadoop体系结构中的角色。 熟练使用HBase操作常用的Shell命令。 实验环境 与实验五一致https://mp.youkuaiyun.com/editor/html/116616391,其他命令可参考实验六:https://mp.youkuaiyun.com/editor/html/116618050实验七https://mp.youkuaiyun.com/editor/html/116618050 实验十:https://mp.youkuaiyun.com/editor/html/116.

VictorFuture的博客 1134
上一篇: 大数据计算引擎(四)—— Impala
下一篇: 大数据计算引擎(五)—— Phoenix
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值