Phoenix中不得不吐槽的两大特征

Phoenix是什么?看看官网就知道了,我就不细说了。关于它的优点,官网也是列了一大堆,我也不复述了。

下面只谈谈截止Phoenix4.2.1,我认为初学者应该注意的两个特征:

1. 完全丧失了HBase的多版本特征

HBases至少也是三维结构(row、cf:cq、timestamp)的数据模型,Phoenix将其映射为二维的关系模型后,便面对着损失信息精度的尴尬场面。这种尴尬具体表现在Phoenix使用的两个方面:

a. 不能为一个cell指定任意的timestamp

作为HBase的第三维特征,timestamp的重要性不言而喻。由于绝大部分数据应用场景都离不开时间维度,所以我们经常会将数据中的某个时间字段映射到timestamp,也就无需在CF中存储该字段了。这样的好处是:既节约了存储空间,也可以使用timestamp相关的API(例如Get#setTimeRange)进行查询。
遗憾的是,Phoenix并不能让我们像HBase一样指定任意的timestamp。虽然Phoenix在 F.A.Q.中煞有其事的提出了貌似这个问题的解决方案。但是深究之后你会发现这是一个比较坑爹的方案。理由如下:

  • 首先,该方案貌似可以指定任意的timestamp,但是需要在应用程序中频繁的创建和关闭Connection,虽然他们还专门强调这不是一个“昂贵”的操作(is not an expensive operation),但是这样的代码风格估计会让很多人无语。
  • 其次,该方案的任意timestamp其实是有条件的,即必须大于表的创建时间,否则,会出现“Table undefined”的异常。
需要说明的是,Phoenix可以Upsert重复的记录,每条记录的timestamp是不一样的。这样来看,虽然timestamp不是我期待的,但是多版本是不是已经实现了呢?看到下面的一点你应经就明白了。。。。。。

b. 不能获取一个cell的多个版本
Phoenix总是获取cell的最新版本,有没有办法去获取其它版本呢?
我曾经受 F.A.Q.中CurrentSCN属性的启发,自以为想到一个方法:不管三七二十一,先去拿一次,拿到的肯定是最新版本了。取其timestamp赋给PhoenixRuntime.CURRENT_SCN_ATTRIB后再去拿,这样就拿到次新版本了。以此类推,每次拿一个版本,直到拿不到而已。虽然这种方法有点笨,但是我想着总是可以解决问题的。但是后来突然发现一个致命的缺陷:如何取结果中的timestamp呢?呵呵,看来这个方法不单单是“有点笨”了......
相信这个问题也困扰了许多人,并引发了许多人的思考。 PHOENIX-590便是专门针对这个问题,遗憾的是,虽然提出了一些解决思路,但是至今仍然没有实质性的进展。

变通方案
PHOENIX-590提出了一种解决方案,其实也和我们想的一样。。。。。。就是将timestamp作为Phoenix表的单独一列,并且和其它字段作为组合主键。这样的本质是row和timestamp二维合成一维,反映在HBase里面,其实是将timestamp的值附加在row后面了。这样可以解决使用timestamp查询的问题,缺点是浪费了一些存储空间。

2. 二级索引设计比较粗糙
CREATE  INDEX  idx_name  ON  TAB_NAME(IDX_FIELD);
SELECT OtherField FROM TAB_NAME WHERR IDX_FIELD='';
我以为这样就可以利用索引了,但是其实不然,必须要
CREATE  INDEX  idx_name  ON  TAB_NAME(IDX_FIELD) INCLUDE(OtherField);
因为二级索引创建了一个新的索引表,没有INICLUDE时,索引表中的value为空,上述查询将不会利用索引表,而是Full Scan主表。INCLUDE时,会将OtherField字段写入索引表的value中,上述查询将会利用索引表进行Range Sacn。
这种做法让人不得不吐糟,虽然说存储不值钱,但是也不能这么浪费吧。
不过细想一下,这也是Phoenix的无奈之举:因为它不能保证主表和索引表对应Region的本地化,所以也就无法根据索引表的结果再去查主表了。

变通方案
a. 客户端自己解决:先查索引表获取Primary Key,然后根据Primary Key二次查询主表;
b. 使用其他索引方案......
针对目前Phoenix的二级索引方案,我个人建议一定要谨慎使用。鉴于其无与伦比的膨胀性,我建议只考虑使用在比较小的表上,并且对该表是写少读多。



你可能感兴趣的:(HBase)