分享

对Cassandra的初体验 - Java综合 - Java - JavaEye论坛

 dream2ca 2011-03-09
作为“云计算”时代的架构设计人员而言,不懂K-V库会被人说out的,为此,笔者在“人云已云”的忽悠下,也开始接触K-V数据库了。

    在啥都不清楚的情况下,首先选择跟风,未必是一件坏事。尤其对技术人员而言,先入门再做选择,也不失为一种方法。“听说xxx大网站都是用Cassandra存储他们的SNS数据的,我们也要试试”,于是乎,开始了Casssandra初体验。

(PS:本文不是cassandra的入门学习的材料。以下均为笔者自己的理解,一家之言,不正确的地方,望指正...)

以OO的方式理解Cassandra的数据模型
    学习Cassandra,首先要理解它有别于传统数据库的存储模型。对于常使用HashMap的Java程序员而言,K-V的映射结构并不难理解。

把Cassandra的ColumnFamily看成HashMap
    网上有不少文章认为ColumnFamily类似RDB中的Table,这样理解有一定道理,但笔者更愿意从OOD的角度去诠释它。从Cassandra的设计实现上看,把它理解为大型的散列结构的索引更贴近其本来面目。

ColumnFamily中的K-V映射
ColumnFamily中的K-V映射有两大类型:
  • 1.基于Column的 “1个Key” --->“n个Column” 的单层映射
  • 2.基于SuperColumn的 “1个key” ---> “n个SuperColumn” ,“1个SuperColumn”--->“n个SubColumn”的两层映射

    针对第一种单层映射,从OOD角度看,笔者理解为 1 Key --> 1 Javabean的标准HashMap映射。你完全可以把“n个Column”理解为直接暴露在外的Bean的n个属性。
  
    而对于第二种的两层映射,笔者认为是  1 Key --> Bean列表 的 1对n 映射。这里,你可以把“1个SuperColumn”--->“n个SubColumn”的映射,理解为 1个Bean 对 n个属性的映射封装;把“1个key” ---> “n个SuperColumn”,视为 Key 对 Bean 的一对多映射,即 Key 映射 一个Bean列表。

    之所以笔者使用对象模型视角,而不是数据库的行列模型视角来看待Cassandra,是有以下原因的:
  • 首先,Cassandra设计以Key为主导的数据映射寻址机制有别于以“ResultSet结果集”为主的传统RDB数据获取模式。
  • 其次,从Cassandra数据的持久化实现上看,对于一个SuperColumn的读取和存储,Cassandra是采用了一次性序列化的。也就是说,即便你只访问SuperColumn下的1个SubColumn的值,Cassandra也需要把这个SuperColumn下的所有SubColumns都读出,一次性进行反序列化,而后返回你要单个属性列。从这点上看,笔者认为Cassandra的设计者是将SuperColumn视为整体的一个持久化对象(一个完整的JavaBean)来看待。


Column的排序与组织
    在Cassandra中,给出Column/SuperColumn的排序方式是十分重要的事。事实上,在你定义ColumnFamily时,你是无需定义其包含了那些Column/SuperColumn,而更重要的是定义Column/SuperColumn的排序方式。这里,笔者根据自己的理解做以下的判断:
  • 1.ColumnFamily与RDB的Table结构完全不同,它不是规整的二维矩阵形态,笔者大胆推测,应该更类似树形结构。树的子节点是可以在程序运行时任意添加的,而ColumnFamily中Column也是动态增减的。
  • 2.Cassandra对Column的组织是严格顺序结构的,很可能是类似TreeSet的有序树型结构。这也很好解释为啥ColumnFamily必须定义Column的排序规则,并且数据结构中有ColumnPath这个概念(看上去很像url路径,不是吗?)


Cassandra数据的Json表现
为了让各位看官更直观的体验Cassandra的数据结构,增加以下的Json表现部分:

1.单层映射(Column)的JSON格式
Js代码 复制代码 收藏代码
  1. {   
  2.    "mccv":{   
  3.       "Users":{   
  4.          "emailAddress":{"name":"emailAddress""value":"foo@"},   
  5.          "webSite":{"name":"webSite""value":"http://"}   
  6.       },   
  7.       "Stats":{   
  8.          "visits":{"name":"visits""value":"243"}   
  9.       }   
  10.    },   
  11.    "user2":{   
  12.       "Users":{   
  13.          "emailAddress":{"name":"emailAddress""value":"user2@"},   
  14.          "twitter":{"name":"twitter""value":"user2"}   
  15.       }   
  16.    }   
  17. }  

其中 “mccv”,“user2” 是key ; “Users”,“Stats”是ColumnFamily,
“emailAddress” , “webSite”,“visits”等是Column。

1.二层映射(SuperColumn)的JSON格式
Js代码 复制代码 收藏代码
  1. {   
  2.   "mccv": {   
  3.     "Tags": {   
  4.       "cassandra": {   
  5.         "incubator": {"incubator""http://incubator./cassandra/"},   
  6.         "jira": {"jira""http://issues./jira/browse/CASSANDRA"}   
  7.       },   
  8.       "thrift": {   
  9.         "jira": {"jira""http://issues./jira/browse/THRIFT"}   
  10.       }   
  11.     }     
  12.   }   
  13. }  

其中 “mccv”为key ;"Tags"为ColumnFamily ;“cassandra”,“thrift” 为SuperColumn ; “incubator”,“jira” 为 subColumn.


以OO的方式使用Cassandra
    Cassandra官方提供了thrift作为其客户端的API,但我们发现它是面向Column的“底层化”操作。这无疑对习惯了以为Bean操作单元的Java开发者而言,是一个痛苦的思维转化过程和代码实现。

    好在我们在其官网发现了一个受推荐的,相对高级的Java客户端,也就是笔者要推荐的“Pelops”。该API提供了对ColumnFamily的整体性操作,并且还提供了类似翻页,排序的实现。 在“Pelops”基础上, 我们通过简单的、对JavaBean属性的类反射封装,实现了类似Hibernate式的Bean的存储访问(而不再是Bean的属性对应一个Column的读写),这让笔者的心情大为舒畅

Pelops的一点小插曲 
    笔者在使用Pelops的过程中,发生了一些小插曲:在系统正式发布时,Cassandra的访问账户必须有安全限制的(就是要有用户名、密码认证)。然而,找遍了Pelops的API,却发现它没有提供用户名、密码的配置方法(晕死啊!!!不知道是不是笔者没找到),最后不得已,对其源码进行了简单的修改。

以下是被笔者修改的两个类:

1.org.wyki.cassandra.pelops.Policy.
这个类只是简单的添加了username 、 password、 keyspace三个属性,以及对应的get,set方法,通过它将账户配置信息传入链接池。

2.org.wyki.cassandra.pelops.ThriftPool.
这个是 pelops的链接池类,笔者修改了其中的私有方法createConnection(),使得在创建链接的时候,带上用户名密码的安全认证信息
Java代码 复制代码 收藏代码
  1. private Connection createConnection() {   
  2.     Connection conn;   
  3.     try {   
  4.         conn = new Connection(this, defaultPort);   
  5.     } catch (SocketException e) {   
  6.         e.printStackTrace();   
  7.         return null;   
  8.     }    
  9.        
  10.     if (conn.open(sessionId.get())){   
  11.         // **** add by linliangyi ***                   
  12.         Policy police = getPolicy();   
  13.         if(police.keyspace != null && police.username != null && police.password != null){   
  14.             Map<String , String> userNamePassword = new HashMap<String , String>();   
  15.             userNamePassword.put(SimpleAuthenticator.USERNAME_KEY, police.username);   
  16.             userNamePassword.put(SimpleAuthenticator.PASSWORD_KEY, police.password);   
  17.             try {   
  18.                 conn.getAPI().login(police.keyspace , new AuthenticationRequest(userNamePassword));   
  19.             } catch (AuthenticationException e) {   
  20.                 e.printStackTrace();   
  21.                 return null;   
  22.             } catch (AuthorizationException e) {   
  23.                 e.printStackTrace();   
  24.                 return null;   
  25.             } catch (TException e) {   
  26.                 e.printStackTrace();   
  27.                 return null;   
  28.             }   
  29.             logger.trace(police.keyspace + " | " + police.username + " | " + police.password);   
  30.         }                    
  31.            
  32.         return conn;   
  33.     }   
  34.        
  35.     return null;   
  36. }  

至此,笔者对Casssandra初体验告一段落。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多