[TOC]
# 分析
## 協處理器-coprocessor
協處理器有兩種:observer和endpoint
Observer允許集群在正常的客戶端操作過程中可以有不同的行為表現
Endpoint允許擴展集群的能力,對客戶端應用開放新的運算命令


1. 客戶端發出put請求
2. 該請求被分派給合適的RegionServer和region
3. coprocessorHost攔截該請求,然后在該表上登記的每個RegionObserver上調用prePut()
4. 如果沒有被prePut()攔截,該請求繼續送到region,然后進行處理
5. region產生的結果再次被CoprocessorHost攔截,調用postPut()
6. 假如沒有postPut()攔截該響應,最終結果被返回給客戶端
* Observer的類型
1. RegionObs——這種Observer鉤在數據訪問和操作階段,所有標準的數據操作命令都可以被pre-hooks和post-hooks攔截
2. WALObserver——WAL所支持的Observer;可用的鉤子是pre-WAL和post-WAL
3. MasterObserver——鉤住DDL事件,如表創建或模式修改
## 二級索引
row key在HBase中是以B+ tree結構化有序存儲的,所以scan起來會比較效率。單表以row key存儲索引,column value存儲id值或其他數據 ,這就是Hbase索引表的結構。
由于HBase本身沒有二級索引(Secondary Index)機制,基于索引檢索數據只能單純地依靠RowKey,為了能支持多條件查詢,開發者需要將所有可能作為查詢條件的字段一一拼接到RowKey中,這是HBase開發中極為常見的做法
比如,現在有一張1億的用戶信息表,建有出生地和年齡兩個索引,我想得到一個條件是在杭州出生,年齡為20歲的按用戶id正序排列前10個的用戶列表。
有一種方案是,系統先掃描出生地為杭州的索引,得到一個用戶id結果集,這個集合的規模假設是10萬。然后掃描年齡,規模是5萬,最后merge這些用戶id,去重,排序得到結果。
這明顯有問題,如何改良?
保證出生地和年齡的結果是排過序的,可以減少merge的數據量?但Hbase是按row key排序,value是不能排序的。
變通一下——將用戶id冗余到row key里?OK,這是一種解決方案了,這個方案的圖示如下:

merge時提取交集就是所需要的列表,順序是靠索引增加了_id,以字典序保證的。
按索引查詢種類建立組合索引。
在方案1的場景中,想象一下,如果單索引數量多達10個會怎么樣?10個索引,就要merge 10次,性能可想而知。

解決這個問題需要參考RDBMS的組合索引實現。
比如出生地和年齡需要同時查詢,此時如果建立一個出生地和年齡的組合索引,查詢時效率會高出merge很多。
當然,這個索引也需要冗余用戶id,目的是讓結果自然有序。結構圖示如下:

這個方案的優點是查詢速度非常快,根據查詢條件,只需要到一張表中檢索即可得到結果list。缺點是如果有多個索引,就要建立多個與查詢條件一一對應的組合索引
而索引表的維護如果交給應用客戶端,則無疑增加了應用端開發的負擔
通過協處理器可以將索引表維護的工作從應用端剝離
* 利用Observer自動維護索引表示例
在社交類應用中,經常需要快速檢索各用戶的關注列表t_guanzhu,同時,又需要反向檢索各種戶的粉絲列表t_fensi,為了實現這個需求,最佳實踐是建立兩張互為反向的表:
* 一個表為正向索引關注表 “t_guanzhu”:
Rowkey: A-B
f1:From
f1:To
* 另一個表為反向索引粉絲表:“t_fensi”:
Rowkey: B—A
f1:From
f1:To
插入一條關注信息時,為了減輕應用端維護反向索引表的負擔,可用Observer協處理器實現:

# 代碼
1. 編寫代碼
~~~
package com.study;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import java.io.IOException;
public class FFCoprocessor extends BaseRegionObserver {
@Override
public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
//二級索引表
Table table = conn.getTable(TableName.valueOf("index_user"));
//攔截到原始put對象中的rowkey和列的Value
byte[] row = put.getRow();
String rowkey = new String(row);
Cell addressCell = put.get("info".getBytes(), "name".getBytes()).get(0);
byte[] valueArray = addressCell.getValueArray();
String address = new String(valueArray,addressCell.getValueOffset(),addressCell.getValueLength());
String[] user_fensi = rowkey.split("-");
Put putIndex = new Put((user_fensi[1]+"-"+user_fensi[0]).getBytes());
putIndex.addColumn("info".getBytes(), "name".getBytes(),address.getBytes());
table.put(putIndex);
table.close();
}
}
~~~
2. 打成jar包“coprocess.jar”上傳hdfs
~~~
hadoop fs -put coprocess.jar /
~~~
3. 創建表
~~~
create 'user_guanzhu','info'
create 'index_user','info'
~~~
4. 修改schema,注冊協處理器
需要先disable一下
~~~
disable 'user_guanzhu'
~~~
注意下面的表名,jar包和類名修改為自己的
~~~
alter 'user_guanzhu',METHOD => 'table_att','coprocessor'=>'hdfs://master:9000/coprocess.jar|com.study.FFCoprocessor|1001|'
~~~
啟用這個表
~~~
enable 'user_guanzhu'
~~~
5. 檢查是否注冊成功
~~~
describe 'user_guanzhu'
~~~
6. 向正向索引表中插入數據進行驗證
~~~
put 'user_guanzhu','zhangsan-liuyifei','f1:adress','beijing'
~~~
# 刪除協處理器
~~~
先disable表
disable 'user_guanzhu'
刪除,$1表示是第一個協處理器
alter 'user_guanzhu',METHOD=>'table_att_unset',NAME=>'coprocessor$1'
再enable表
enable 'user_guanzhu'
~~~
- linux
- 常用命令
- 高級文本命令
- 面試題
- redis
- String
- list
- hash
- set
- sortedSet
- 案例-推薦
- java高級特性
- 多線程
- 實現線程的三種方式
- 同步關鍵詞
- 讀寫鎖
- 鎖的相關概念
- 多線程的join
- 有三個線程T1 T2 T3,保證順序執行
- java五種線程池
- 守護線程與普通線程
- ThreadLocal
- BlockingQueue消息隊列
- JMS
- 反射
- volatile
- jvm
- IO
- nio
- netty
- netty簡介
- 案例一發送字符串
- 案例二發送對象
- 輕量級RPC開發
- 簡介
- spring(IOC/AOP)
- spring初始化順序
- 通過ApplicationContextAware加載Spring上下文
- InitializingBean的作用
- 結論
- 自定義注解
- zk在框架中的應用
- hadoop
- 簡介
- hadoop集群搭建
- hadoop單機安裝
- HDFS簡介
- hdfs基本操作
- hdfs環境搭建
- 常見問題匯總
- hdfs客戶端操作
- mapreduce工作機制
- 案列-單詞統計
- 局部聚合Combiner
- 案列-流量統計(分區,排序,比較)
- 案列-倒排索引
- 案例-共同好友
- 案列-join算法實現
- 案例-求topN(分組)
- 自定義inputFormat
- 自定義outputFormat
- 框架運算全流程
- mapreduce的優化方案
- HA機制
- Hive
- 安裝
- DDL操作
- 創建表
- 修改表
- DML操作
- Load
- insert
- select
- join操作
- 嚴格模式
- 數據類型
- shell參數
- 函數
- 內置運算符
- 內置函數
- 自定義函數
- Transform實現
- 特殊分割符處理
- 案例
- 級聯求和accumulate
- flume
- 簡介
- 安裝
- 常用的組件
- 攔截器
- 案例
- 采集目錄到HDFS
- 采集文件到HDFS
- 多個agent串聯
- 日志采集和匯總
- 自定義攔截器
- 高可用配置
- 使用注意
- sqoop
- 安裝
- 數據導入
- 導入數據到HDFS
- 導入關系表到HIVE
- 導入表數據子集
- 增量導入
- 數據導出
- 作業
- 原理
- azkaban
- 簡介
- 安裝
- 案例
- 簡介
- command類型單一job
- command類型多job工作流flow
- HDFS操作任務
- mapreduce任務
- hive腳本任務
- hbase
- 簡介
- 安裝
- 命令行
- 基本CURD
- 過濾器查詢
- 系統架構
- 物理存儲
- 尋址機制
- 讀寫過程
- Region管理
- master工作機制
- 建表高級屬性
- 與mapreduce結合
- 協處理器
- 點擊流平臺開發
- 簡介
- storm
- 簡介
- 安裝
- 集群啟動及任務過程分析
- 單詞統計
- 并行度
- ACK容錯機制
- ACK簡介