本文作者:林偉兵丹允,叩丁狼高級(jí)講師郭厌。原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處雕蔽。
3.5.使用DBeaver客戶端
Phoenix常見的客戶端連接工具有SQuirrel和DBeaver,這里主要介紹DBeaver客戶端的使用方式折柠。
安裝DBeaver,啟動(dòng)DBeaver批狐,將 phoenix-[version]-client.jar 拷貝到客戶端扇售。
注意:客戶端如何缺失某些參數(shù)配置,可以上面第4步的驅(qū)動(dòng)屬性添加再測(cè)試連接,這是因?yàn)镈Beaver不支持hbase-site.xml配置文件的使用.
3.6.Phoenix二級(jí)索引
前一章介紹了Phoenix簡(jiǎn)單的DDL和CURD操作承冰,接下來(lái)這一節(jié)主要介紹Phoenix獨(dú)有的特性[二級(jí)索引](http://phoenix.apache.org/secondary_indexing.html),并在DBeaver中實(shí)踐华弓。
? 對(duì)HBase來(lái)說(shuō),我們通常使用字典序的RowKey來(lái)快速訪問數(shù)據(jù)巷懈,除此之外也可使用自定義的Filter來(lái)搜索數(shù)據(jù)该抒,但是它是基于全表掃描的。而Phoenix提供的二級(jí)索引是避開了全表掃描顶燕,其利用空間換時(shí)間的來(lái)解決查詢過(guò)程中的緩慢問題;默認(rèn)情況下客戶端不支持二級(jí)索引凑保,需要將以下配置添加到HBase的各個(gè)RegionServer節(jié)點(diǎn)的hbase-site.xml文件中:
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
覆蓋索引 Covered Indexes
? Phoenix提供了一種叫Covered Index的二級(jí)索引。這種索引在獲取數(shù)據(jù)的過(guò)程中涌攻,內(nèi)部不需再去HBase原表中獲取任何數(shù)據(jù)欧引,轉(zhuǎn)而在索引表查詢結(jié)果即可。
? 要想達(dá)到這種效果恳谎,你的select 的列芝此,where 的列都需要在索引表中出現(xiàn)。舉個(gè)例子因痛,如果你的SQL語(yǔ)句是SELECT name FROM WOLFCODE_MDB.t_employee ORDER BY SALARY DESC;
婚苹,要達(dá)到最大化查詢效率和速度最快,你就需要建立覆蓋索引:
CREATE INDEX salary_idx ON wolfcode_mdb.t_employee(salary DESC) INCLUDE (name);
## 原表的內(nèi)容是:
+-----+-----------+----------+---------+
| ID | NAME | SALARY | GENDER |
+-----+-----------+----------+---------+
| 1 | zhangsan | 10800.0 | M |
| 2 | lisi | 9900.0 | M |
| 3 | wangwu | 7500.0 | F |
| 4 | zhaoliu | 10000.0 | M |
+-----+-----------+----------+---------+
## 索引表建立后內(nèi)容是:
+-----------+------+-----------+
| 0:SALARY | :ID | 0:NAME |
+-----------+------+-----------+
| 1.08E+4 | 1 | zhangsan |
| 1E+4 | 4 | zhaoliu |
| 9.9E+3 | 2 | lisi |
| 7.5E+3 | 3 | wangwu |
+-----------+------+-----------+
## 當(dāng)執(zhí)行如下SQL時(shí)鸵膏,系統(tǒng)會(huì)找到對(duì)應(yīng)的二級(jí)索引表進(jìn)行查詢膊升,二級(jí)索引表的表內(nèi)容明顯比原表的內(nèi)容少,并且在查詢的過(guò)程無(wú)需再次排序谭企,這也大大減少了查詢的時(shí)間廓译。
## 但是索引表的建立無(wú)疑也降低了集群的存儲(chǔ)能力。
SELECT name FROM WOLFCODE_MDB.t_employee ORDER BY SALARY DESC;
? 覆蓋索引就是一種典型的全局索引债查,全局索引適合那些讀多寫少的場(chǎng)景非区。使用全局索引,讀數(shù)據(jù)基本不損耗性能盹廷,所有的性能損耗都來(lái)源于寫數(shù)據(jù)征绸。數(shù)據(jù)表的添加、刪除和修改都會(huì)更新相關(guān)的索引表速和。而查詢數(shù)據(jù)的時(shí)候歹垫,Phoenix會(huì)通過(guò)索引表來(lái)快速低損耗的獲取數(shù)據(jù)。
如下當(dāng)在原表插入數(shù)據(jù)后颠放,索引表也會(huì)插入對(duì)應(yīng)的數(shù)據(jù):
UPSERT INTO t_employee VALUES(5,'亦舒',8800,'F');
+-----------+------+-----------+
| 0:SALARY | :ID | 0:NAME |
+-----------+------+-----------+
| 1.08E+4 | 1 | zhangsan |
| 1E+4 | 4 | zhaoliu |
| 9.9E+3 | 2 | lisi |
| 8.8E+3 | 5 | 亦舒 |
| 7.5E+3 | 3 | wangwu |
+-----------+------+-----------+
如果某個(gè)查詢已不在頻繁排惨,建議刪除索引表以換取更多的空間,代碼如下:
use wolfcode_mdb;
DROP INDEX SALARY_IDX ON wolfcode_mdb.t_employee;
函數(shù)索引 Functional Indexes
? 函數(shù)索引從4.3版本就有碰凶,這種索引的內(nèi)容不局限于列暮芭,還能在表達(dá)式上建立索引鹿驼。如果你使用的表達(dá)式正好就是索引的話,數(shù)據(jù)也可以直接從這個(gè)索引獲取辕宏,而不需要從數(shù)據(jù)庫(kù)獲取畜晰。比如說(shuō),在一個(gè)表達(dá)式上建立索引瑞筐,這個(gè)表達(dá)式是salary * 12
,值得注意的是凄鼻,函數(shù)索引不支持排序:
CREATE INDEX SALARY_PER_YEAR_IDEX2 ON t_employee(salary * 12);
SELECT * FROM wolfcode_mdb.SALARY_PER_YEAR_IDEX2;
## 查詢結(jié)果如下
+-----------------+------+
| :(SALARY * 12) | :ID |
+-----------------+------+
| 9E+4 | 3 |
| 1.056E+5 | 5 |
| 1.188E+5 | 2 |
| 1.2E+5 | 4 |
| 1.296E+5 | 1 |
+-----------------+------+
本地索引 Local Indexes
? 本地索引適合那些寫多讀少或存儲(chǔ)空間有限的場(chǎng)景。和全局索引一樣聚假,Phoenix也會(huì)在查詢的時(shí)候自動(dòng)選擇是否使用本地索引块蚌。本地索引之所以是本地,主要是因?yàn)樗饕龜?shù)據(jù)和真實(shí)數(shù)據(jù)存儲(chǔ)在同一臺(tái)機(jī)器上膘格,這樣做主要是為了避免網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)拈_銷峭范。
異步創(chuàng)建索引
注意:https://yq.aliyun.com/ask/438584/
注意:http://hbase.group/question/27
? 一般我們可以使用CREATE INDEX來(lái)創(chuàng)建一個(gè)索引,這是一種同步的方法瘪贱。但是有時(shí)候我們創(chuàng)建索引的表非常大纱控,我們需要等很長(zhǎng)時(shí)間。Phoenix 4.5以后有一個(gè)異步創(chuàng)建索引的方式菜秦,使用關(guān)鍵字ASYNC來(lái)創(chuàng)建索引:
CREATE INDEX my_index ON t_employee(salary desc) async;
SELECT * FROM my_index;
? 這時(shí)候創(chuàng)建的索引表中不會(huì)有數(shù)據(jù)甜害。你還必須要單獨(dú)的使用命令行工具來(lái)執(zhí)行數(shù)據(jù)的創(chuàng)建。當(dāng)語(yǔ)句給執(zhí)行的時(shí)候球昨,后端會(huì)啟動(dòng)一個(gè)map reduce任務(wù)唾那,只有等到這個(gè)任務(wù)結(jié)束,數(shù)據(jù)都被生成在索引表中后褪尝,這個(gè)索引才能被使用。啟動(dòng)工具的方法:
${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool \
--schema wolfcode_mdb --data-table t_employee --index-table my_index \
--output-path /async_idx_hfiles
這個(gè)任務(wù)不會(huì)因?yàn)榭蛻舳私o關(guān)閉而結(jié)束期犬,是在后臺(tái)運(yùn)行河哑。你可以在指定的文件ASYNC_IDX_HFILES中找到最終實(shí)行的結(jié)果。
3.7.JDBC API的實(shí)現(xiàn)
-
添加依賴龟虎。
<dependency> <groupId>org.apache.phoenix</groupId> <artifactId>phoenix-core</artifactId> <version>4.14.1-HBase-1.2</version> </dependency>
-
添加日志璃谨。
### set log levels ### log4j.rootLogger = debug , stdout , D , E ### 輸出到控制臺(tái) ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c 1 :%L - %m%n
-
因?yàn)橹胺?wù)端已經(jīng)添加了支持分組的功能,所以還要添加hbase-site.xml文件鲤妥。
<configuration> <property> <name>hbase.regionserver.wal.codec</name> <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value> </property> <!-- 指定啟動(dòng)分組功能 --> <property> <name>phoenix.schema.isNamespaceMappingEnabled</name> <value>true</value> </property> </configuration>
-
測(cè)試JDBC客戶端連接狀況:
public class ConnectionTest { public static void main(String[] args) { Connection conn=null; Statement stmt = null; try { Class.forName("org.apache.phoenix.jdbc.PhoenixDriver"); conn = DriverManager .getConnection("jdbc:phoenix:hdp01,hdp02,hdp03:2181"); stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from wolfcode_mdb.t_employee"); while (rs.next()) { String name = rs.getString("name"); double salary = rs.getDouble("salary"); String gender = rs.getString("gender"); System.out.println("name:" + name + ",salary:" + salary + ",gender:" + gender); } } catch (Exception e) { e.printStackTrace(); }finally { try { stmt.close(); conn.close(); }catch (SQLException e) { e.printStackTrace(); } } } }
-
測(cè)試CURD:
public class CURDTest { private Connection _conn = null; private PreparedStatement _stmt = null; @Before public void init() { try { Class.forName("org.apache.phoenix.jdbc.PhoenixDriver"); _conn = DriverManager .getConnection("jdbc:phoenix:hdp01,hdp02,hdp03:2181"); } catch (Exception e) { e.printStackTrace(); } } @Test public void testQuery() throws SQLException { String sql = "select * from wolfcode_mdb.t_employee"; _stmt = _conn.prepareStatement(sql); ResultSet rs = _stmt.executeQuery(); while (rs.next()) { String name = rs.getString("name"); double salary = rs.getDouble("salary"); String gender = rs.getString("gender"); System.out.println("name:" + name + ",salary:" + salary + ",gender:" + gender); } } @Test public void testInsert() { try { String sql = "upsert into wolfcode_mdb.t_employee values (?,?,?,?)"; _stmt = _conn.prepareStatement(sql); _stmt.setLong(1, 6); _stmt.setString(2, "趙無(wú)眠"); _stmt.setDouble(3, 9700.00d); _stmt.setString(4, "F"); _stmt.execute(); // 如無(wú)自動(dòng)提交任務(wù)佳吞,是不會(huì)生效的。 _conn.commit(); }catch (Exception e){ e.printStackTrace(); } } @Test public void testDelete() { try { String sql = "delete from wolfcode_mdb.t_employee where name=?"; _stmt = _conn.prepareStatement(sql); _stmt.setString(1, "趙無(wú)眠"); _stmt.execute(); // 如無(wú)自動(dòng)提交任務(wù)棉安,是不會(huì)生效的底扳。 _conn.commit(); }catch (Exception e){ e.printStackTrace(); } } @Test public void testUpdate() { try { //更新數(shù)據(jù)必須攜帶主鍵,否則更新失敗 String sql = "upsert into wolfcode_mdb.t_employee(id,name) values(?,?)"; _stmt = _conn.prepareStatement(sql); _stmt.setLong(1, 1); _stmt.setString(2, "張三"); _stmt.execute(); // 如無(wú)自動(dòng)提交任務(wù)贡耽,是不會(huì)生效的衷模。 _conn.commit(); }catch (Exception e){ e.printStackTrace(); } } @After public void destory() { try { if (null != _stmt && !_stmt.isClosed()) { _stmt.close(); } if (null != _conn && !_conn.isClosed()) { _conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
4. 加鹽表
什么是加鹽鹊汛?
加鹽可以簡(jiǎn)單的理解為加密。
舉個(gè)例子阱冶,假設(shè)我們要做一個(gè)人員管理系統(tǒng)刁憋,將用戶登陸的賬號(hào)密碼直接保存到數(shù)據(jù)庫(kù)上,用戶登陸時(shí)先去數(shù)據(jù)庫(kù)匹配木蹬,成功則登陸至耻。但是實(shí)際的應(yīng)用系統(tǒng)中這是非常不安全的,賬戶密碼不論是在數(shù)據(jù)庫(kù)中存儲(chǔ)還是在網(wǎng)上傳輸過(guò)程中都應(yīng)該是密文而不能是明文镊叁。
一般都是采用MD5或者SHA加密算法把密碼加密尘颓,我們把這種方式稱之為加鹽,這個(gè)鹽一般是一個(gè)字符串意系,在本案例中泥耀,該字符串是固定的,我們稱之為固定鹽蛔添。
固定鹽也是不安全的痰催,因?yàn)榫W(wǎng)上也有各種在線MD5解密的網(wǎng)站,黑客可以通過(guò)這些解密網(wǎng)站對(duì)密碼進(jìn)行破解測(cè)試迎瞧。這時(shí)就可以采用動(dòng)態(tài)加鹽的方式來(lái)增加密碼的安全性夸溶,比如,我們可以將需要這個(gè)固定的字符串改為每個(gè)用戶的賬號(hào)凶硅。每個(gè)用戶都需要用一個(gè)庫(kù)去比較缝裁,大大增加了破解所有賬戶的難度。
Phoenix的加鹽有什么好處足绅?
傳統(tǒng)的加鹽主要是用來(lái)增加數(shù)據(jù)的安全性捷绑,而Phoenix中加鹽是指對(duì)primary key對(duì)應(yīng)的字節(jié)數(shù)組插入特定的byte數(shù)據(jù)。因?yàn)閜rimary key底層對(duì)應(yīng)的是HBase的row key氢妈。在插入數(shù)據(jù)的時(shí)候粹污,單調(diào)遞增rowkey會(huì)存儲(chǔ)在某一個(gè)RS上,使得負(fù)載集中在某一個(gè)RegionServer上引起的熱點(diǎn)問題首量。加鹽可以解決這一問題壮吩。
怎么對(duì)表加鹽?
在創(chuàng)建表的時(shí)候指定屬性值:SALT_BUCKETS加缘,其值表示所分buckets(region)數(shù)量鸭叙, 范圍是1~256。
use wolfcode_mdb;
CREATE TABLE t_scores(id varchar primary key,student varchar,scores varchar array[3]) salt_buckets = 4;
加鹽的過(guò)程就是在原來(lái)key的基礎(chǔ)上增加一個(gè)byte作為前綴,計(jì)算公式如下:
new_row_key = (++index % BUCKETS_NUMBER) + original_key
原理:
測(cè)試:
upsert into t_scores values('100','aaa',ARRAY['75','75','75']);
upsert into t_scores values('200','bbb',ARRAY['75','75','75']);
upsert into t_scores values('300','ccc',ARRAY['75','75','75']);
upsert into t_scores values('400','ddd',ARRAY['75','75','75']);
upsert into t_scores values('500','eee',ARRAY['75','75','75']);
upsert into t_scores values('600','fff',ARRAY['75','75','75']);
upsert into t_scores values('700','ggg',ARRAY['75','75','75']);
upsert into t_scores values('800','hhh',ARRAY['75','75','75']);
upsert into t_scores values('900','iii',ARRAY['75','75','75']);
驗(yàn)證Region的個(gè)數(shù):
想獲取更多技術(shù)干貨拣宏,請(qǐng)前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html