欢迎投稿

今日深度:

Cassandra Dev 1: Cassandra 入门,cassandradev入门

Cassandra Dev 1: Cassandra 入门,cassandradev入门


本文转自我的javaEye博客,链接http://kylinsoong.javaeye.com/blog/731208

最近, Cassandra 绝对是一个比较前端的话题,随着互联网的不断发展, Distributed Database 的广受重视是一种必然, Cassandra 在存取效率、分散管理、容错能力、稳定性等方面的优点是其他Distributed Database 无法比拟的,So, 研究Cassandra 是非常有必要的。我将从下列方面学习Cassandra :

1. Cassandra目录结构

从http://cassandra.apache.org/download/下载最新Cassandra,解压后目录结构如下 :



 如图由上向下:

bin中存放这一些可操作Cassandra脚本,如cassandra.bat,点击可以启动Cassandra

conf中包含一些Cassandra配置信息

interface中存放Cassandra的Thrift接口定义文件,可以用于生成各种语言的接口代码

javadoc中为Cassandra帮助文档(API)

lib中为Cassandra运行时依赖的包

 

2. Cassandra HelloWorld

从学C语言开始,HelloWorld是一种传统,所以先写个HelloWorld,在apache-cassandra-0.6.4/conf目录下storage-conf.xml文件中,该文件中包含Cassandra的所有配置,先列出简单一些:

Xml代码 
  1. <!-- A -->  
  2. <ClusterName>Test Cluster</ClusterName>  
  3.   
  4. <!-- B -->  
  5. <Keyspaces>  
  6.     <Keyspace Name="Keyspace1">    
  7.     </Keyspace>  
  8. </Keyspaces>  
  9.   
  10. <!-- C -->  
  11. <CommitLogDirectory>/var/lib/cassandra/commitlog</CommitLogDirectory>  
  12.   <DataFileDirectories>  
  13.     <DataFileDirectory>/var/lib/cassandra/data</DataFileDirectory>  
  14. </DataFileDirectories>  
  15.   
  16. <!-- D -->  
  17. <ThriftPort>9160</ThriftPort>  

 

 如上:

A处定义Cluster名字(也称结点名字),一个Cluster可以包含多个Keyspace;

B处定义了此Cluster中包含一个Keyspace,且名字为Keyspace1,Keyspace相当于与关系数据库中的database;

C处定义了Cassandra数据和Commitlog存放的位置,如不对其做修改,在Windows下启动Cassandra,在D盘根目录下产生如上面所示的文件夹;

D处定义了Thrift RPC 端口号为9160,

在默认配置文件下启动Cassandra,然后编写如下代码:

Java代码 
  1. package com.tibco.cassandra;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4. import java.util.Date;  
  5.   
  6. import org.apache.cassandra.thrift.Cassandra;  
  7. import org.apache.cassandra.thrift.Column;  
  8. import org.apache.cassandra.thrift.ColumnOrSuperColumn;  
  9. import org.apache.cassandra.thrift.ColumnPath;  
  10. import org.apache.cassandra.thrift.ConsistencyLevel;  
  11. import org.apache.cassandra.thrift.InvalidRequestException;  
  12. import org.apache.cassandra.thrift.NotFoundException;  
  13. import org.apache.cassandra.thrift.TimedOutException;  
  14. import org.apache.cassandra.thrift.UnavailableException;  
  15. import org.apache.thrift.TException;  
  16. import org.apache.thrift.protocol.TBinaryProtocol;  
  17. import org.apache.thrift.protocol.TProtocol;  
  18. import org.apache.thrift.transport.TSocket;  
  19. import org.apache.thrift.transport.TTransport;  
  20.   
  21. public class CassandraHelloWorld {  
  22.   
  23.     public static void main(String[] args) throws UnsupportedEncodingException, InvalidRequestException, UnavailableException, TimedOutException, TException, NotFoundException {  
  24.         //Part One  
  25.         TTransport trans = new TSocket("127.0.0.1"9160);  
  26.         TProtocol proto = new TBinaryProtocol(trans);  
  27.         Cassandra.Client client = new Cassandra.Client(proto);  
  28.         trans.open();  
  29.           
  30.         //Part Two  
  31.         String keyspace = "Keyspace1";  
  32.         String cf = "Standard2";  
  33.         String key = "kylinsoong";  
  34.         long timestamp = new Date().getTime();  
  35.         ColumnPath path = new ColumnPath(cf);  
  36.         path.setColumn("id".getBytes("UTF-8"));  
  37.         client.insert(keyspace, key, path, "520".getBytes("UTF-8"), timestamp, ConsistencyLevel.ONE);  
  38.         path.setColumn("action".getBytes("UTF-8"));  
  39.         client.insert(keyspace, key, path, "Hello, World, Cassandra!".getBytes("UTF-8"), timestamp, ConsistencyLevel.ONE);  
  40.           
  41.         //Part Three  
  42.         path.setColumn("id".getBytes("UTF-8"));  
  43.         ColumnOrSuperColumn cc = client.get(keyspace, key, path, ConsistencyLevel.ONE);  
  44.         Column c = cc.getColumn();  
  45.         String value = new String(c.value, "UTF-8");  
  46.         System.out.println(value);  
  47.         path.setColumn("action".getBytes("UTF-8"));  
  48.         ColumnOrSuperColumn cc2 = client.get(keyspace, key, path, ConsistencyLevel.ONE);  
  49.         Column c2 = cc2.getColumn();  
  50.         String value2 = new String(c2.value, "UTF-8");  
  51.         System.out.println(value2);  
  52.           
  53.         //Part four  
  54.         trans.close();  
  55.     }  
  56.   
  57. }  

 

运行代码,伟大的Hello,World将会出现在我们眼前,运行结果:

Java代码 
  1. 520  
  2. Hello, World, Cassandra!  

先对代码做个简单分析,我将代码分为四个部分:

Part One,连接到数据库,相当于JDBC,具体这里是通过RPC通信协议连接到Cassandra的;

Part Two,向数据库中插入数据;

Part Three,读出刚才插入的数据;

Part four, 关闭数据库连接。

 

3. 和关系数据库从存储效率上做个比较:

我们先不说Cassandra的数据模型及它的集群,首先我们从实验的角度比较它与Mysql的存储效率,比较之前先做个解释;

关系数据库最小的存储单元是row,而Cassandra是grid(此说法只是为了形象比喻)如下图;

 
所示为一个row包含4个grid;

对关系数据库,可以一次插入或读取一行,而Cassandra只能一次插入或读取一个格,也就是说要插入此行信息关系数据库只需一个插入语句,而Cassandra需要四个,看上去关系数据库更有效率,实际上结果将会使你为之Shocking;

开始我们的实验,在本地开启Cassandra服务器:

在Mysql中创建一个数据库,在数据库中创建下表:

Sql代码 
  1. create table test  
  2. (  
  3. parseTime varchar(40)primary key,  
  4. id varchar(40),  
  5. creationTime varchar(40),  
  6. globalInstanceId varchar(255),  
  7. msg varchar(255),  
  8. severity varchar(20),  
  9. modelName varchar(255),  
  10. rComponent varchar(255),  
  11. rExecutionEnvironment varchar(255),  
  12. sExecutionEnvironment varchar(255),  
  13. sLocation varchar(255),  
  14. msgId varchar(255)  
  15. );  

 此表中包含12 column,为了简单索引column对应类型都是字符串,

向此表中插入68768 * 2 条数据,查看记录测试时间,如下为测试程序输出Log

 

Mysql

--- 0 ----

Error

Error

Error

Error

Error

Error

Total add: 68768, spent time: 1654569

--- 1 ----

Error

Total add: 68768, spent time: 1687645

Total add: 137536, Error: 7, Spent Time: 3342214

Average:  24.3004

分析日志,总共向Mysql插入137536条数据,其中有7条数据添加时出错,总共耗时3342214毫秒,合计25分钟多一点,插入一条记录时间为24.3004毫秒

 

将同样的数据向Cassandra中插入68768 * 10条数据,查看记录测试时间,程序输出日志如下:

 

Cassandra

--- 0 ----Total add: 68768, spent time: 212047

--- 1 ----Total add: 68768, spent time: 210518

--- 2 ----Total add: 68768, spent time: 211602

--- 3 ----Total add: 68768, spent time: 213543

--- 4 ----Total add: 68768, spent time: 209558

--- 5 ----Total add: 68768, spent time: 211302

--- 6 ----Total add: 68768, spent time: 214699

--- 7 ----Total add: 68768, spent time: 212685

--- 8 ----Total add: 68768, spent time: 215412

--- 9 ----Total add: 68768, spent time: 218858

Total: 687680 Time: 2130224

Average:

                   Insert one key: 3.0977

                   Insert one column: 0.2581

 

 分析日志文件,向Cassandra中插入687680条数据,实际执行(687680 * 12次插入),耗费时间:2130224毫秒,合计35分钟多一点,没有发生插入错误等现象,说明Cassandra稳定性比Mysql好,每插入一条记录所需时间仅为3.0977毫秒,执行一次插入所需时间为0.2581毫秒

比较两组日志文件可以得出以下结论:

向Cassandra插入数据条数是向Mysql插入数据条数的5倍,但总消耗时间Cassandra少于Mysql;

就插入一条数据而言,Cassandra的效率是Mysql的8倍

 

在做另为一组实验:在数据库中载创建一个此时表,如下:

Sql代码 
  1. create table time  
  2. (  
  3. id varchar(20)  
  4. );  

 是的,此表只有一个字段,目的是让Cassandra与Mysql更有可比性。

同样先看Mysql输出日志:

 

Mysql

Add 100 000 keys, Spent time: 2477828

Average: 24.7783

 

 分析输出日志向Mysql数据库插入100 000条数据花费2477828毫秒,合计40分钟,执行一次插入所需时间为24.7783毫秒

再看Cassandra输出日志

 

Cassandra

Add 100 000 keys, Spent time: 25281

Average: 0.2528

 

 分析日志,同样向Cassandra插入100 000条数据,花费时间为25281毫秒,执行一次插入所需时间为0.2528毫秒;

 比较两组输出日志:

在插入数据条数相同的情况下(100 000条)Mysql花费的时间是Cassandra的98倍

执行一次操作Mysql花费的时间是Cassandra的98倍

结论:Cassandra的存储效率是Mysql的100倍左右

 

4. Cassandra数据模型

Twitter的数据存储用的就是Cassandra,这里我将以Twitter存储数据的模型为例,说明Cassandra的数据模型,先将我们上面的storage-conf.xml配置文件做一下修改,如下:

Xml代码 
  1. <Storage>  
  2.     <ClusterName>Kylin-PC</ClusterName>  
  3.     <Keyspaces>  
  4.         <Keyspace Name="Twitter">  
  5.             <ColumnFamily CompareWith="UTF8Type" Name="Statuses" />  
  6.             <ColumnFamily CompareWith="UTF8Type" Name="StatusAudits" />  
  7.             <ColumnFamily CompareWith="UTF8Type" Name="StatusRelationships" CompareSubcolumnsWith="TimeUUIDType" ColumnType="Super" />    
  8.             <ColumnFamily CompareWith="UTF8Type" Name="Users" />  
  9.             <ColumnFamily CompareWith="UTF8Type" Name="UserRelationships" CompareSubcolumnsWith="TimeUUIDType" ColumnType="Super" />  
  10.         </Keyspace>      
  11.     </Keyspaces>  
  12. </Storage>  

 上面Keyspace就是真实的Twitter存储数据的模型的定义,它里面包含5个ColumnFamily,对照Mysql,Keyspace相当于一个数据库,ColumnFamily 相当于数据库中一张表;

上面配置文件中ClusterName表示Cassandra的一个节点实例(逻辑上的一个Cassandra Server,一般为一台PC),名字为Kylin-PC,一个节点实例可以包括多个Keyspace;

下面我分别结合实例从以下几个方面说明Cassandra的数据模型:

(一)、ColumnFamily



 

 

 如图,ColumnFamily 包含多个Row,上面说过ColumnFamily 相当于关系数据库中的一个Table,每一个Row都包含有Client提供的Key以及和该Key相关的一系列Column,每个Column都包括name,value,timestamp,值得注意每个Row中包含的Column不一定相同;

修改上面HelloWorld程序,修给后代码如下:

Java代码 
  1. public static void main(String[] args) throws Exception {  
  2.         TTransport trans = new TSocket("127.0.0.1"9160);     
  3.         TProtocol proto = new TBinaryProtocol(trans);     
  4.         Cassandra.Client client = new Cassandra.Client(proto);     
  5.         trans.open();   
  6.           
  7.         String keyspace = "Twitter";     
  8.           
  9.         String columnFamily  = "Users";   
  10.         ColumnPath path = new ColumnPath(columnFamily);   
  11.           
  12.         String row1 = "kylin";  
  13.         path.setColumn("id".getBytes());  
  14.         client.insert(keyspace, row1, path,"101".getBytes(),new Date().getTime(),ConsistencyLevel.ONE);  
  15.         path.setColumn("name".getBytes());  
  16.         client.insert(keyspace, row1, path,"Kylin Soong".getBytes(),new Date().getTime(),ConsistencyLevel.ONE);  
  17.           
  18.         String row2 = "kobe";  
  19.         path.setColumn("id".getBytes());  
  20.         client.insert(keyspace, row2, path,"101".getBytes(),new Date().getTime(),ConsistencyLevel.ONE);  
  21.         path.setColumn("name".getBytes());  
  22.         client.insert(keyspace, row2, path,"Kobe Bryant".getBytes(),new Date().getTime(),ConsistencyLevel.ONE);  
  23.         path.setColumn("age".getBytes());  
  24.         client.insert(keyspace, row2, path,"32".getBytes(),new Date().getTime(),ConsistencyLevel.ONE);  
  25.         path.setColumn("desc".getBytes());  
  26.         client.insert(keyspace, row2, path,"Five NBA title, One regular season MVP, Two Final Games MVP".getBytes(),new Date().getTime(),ConsistencyLevel.ONE);  
  27.           
  28.         path.setColumn("id".getBytes());     
  29.         ColumnOrSuperColumn cos11 = client.get(keyspace, row1, path, ConsistencyLevel.ONE);  
  30.         path.setColumn("name".getBytes());    
  31.         ColumnOrSuperColumn cos12 = client.get(keyspace, row1, path, ConsistencyLevel.ONE);  
  32.         Column c11 = cos11.getColumn();  
  33.         Column c12 = cos12.getColumn();  
  34.         System.out.println(new String(c11.getValue()) + " | " + new String(c12.getValue()));  
  35.           
  36.         path.setColumn("id".getBytes());     
  37.         ColumnOrSuperColumn cos21 = client.get(keyspace, row2, path, ConsistencyLevel.ONE);  
  38.         path.setColumn("name".getBytes());    
  39.         ColumnOrSuperColumn cos22 = client.get(keyspace, row2, path, ConsistencyLevel.ONE);  
  40.         path.setColumn("age".getBytes());     
  41.         ColumnOrSuperColumn cos23 = client.get(keyspace, row2, path, ConsistencyLevel.ONE);  
  42.         path.setColumn("desc".getBytes());    
  43.         ColumnOrSuperColumn cos24 = client.get(keyspace, row2, path, ConsistencyLevel.ONE);  
  44.         Column c21 = cos21.getColumn();  
  45.         Column c22 = cos22.getColumn();  
  46.         Column c23 = cos23.getColumn();  
  47.         Column c24 = cos24.getColumn();  
  48.         System.out.println(new String(c21.getValue()) + " | " + new String(c22.getValue())+ " | " + new String(c23.getValue()) + " | " + new String(c24.getValue()));  
  49.           
  50.         trans.close();   
  51.           
  52.     }  

上面代码所示:向名字为“Users”的columnFamily中添加2行,第一行包含2个Column,Column名字分别为:id、name;第二行包含4个Column,Column名字非别为id、name、age、desc;运行上述代码结果如下:

Java代码 
  1. 101 | Kylin Soong  
  2. 101 | Kobe Bryant | 32 | Five NBA title, One regular season MVP, Two Final Games MVP  

 (二)SuperColumn

SuperColumn中包含多个Column,下面我们用程序实现向SuperColumn中添加,读取数据,先看下图:



 如上图所示ColumnFamily 包括2行,每行包括2个SuperColumn,每个SuperColumn中包含多个Column,下面我们用代码演示上图情景,为了简单,我们把两行,简化为一行;

修改HelloWorld代码,如下:

Java代码 
  1. public static void main(String[] args) throws Exception {  
  2.         TTransport trans = new TSocket("127.0.0.1"9160);     
  3.         TProtocol proto = new TBinaryProtocol(trans);     
  4.         Cassandra.Client client = new Cassandra.Client(proto);     
  5.         trans.open();   
  6.           
  7.         String keyspace = "Twitter";   
  8.         String columnFamily  = "UserRelationships";   
  9.         String row = "row";  
  10.           
  11.         Map<String, List<ColumnOrSuperColumn>> cfmap = new HashMap<String, List<ColumnOrSuperColumn>>();     
  12.         List<ColumnOrSuperColumn> cslist = new ArrayList<ColumnOrSuperColumn>();     
  13.         ColumnOrSuperColumn cos = new ColumnOrSuperColumn();     
  14.         List<Column> columnList = new ArrayList<Column>();     
  15.         Column id = new Column();     
  16.         id.setName("id".getBytes());     
  17.         id.setValue("101".getBytes());     
  18.         id.setTimestamp(new Date().getTime());     
  19.         Column name = new Column();     
  20.         name.setName("name".getBytes());     
  21.         name.setValue("Kylin Soong".getBytes());     
  22.         name.setTimestamp(new Date().getTime());     
  23.         columnList.add(id);     
  24.         columnList.add(name);   
  25.         SuperColumn sc = new SuperColumn();     
  26.         sc.setColumns(columnList);     
  27.         sc.setName("super1".getBytes());     
  28.         cos.super_column = sc;     
  29.         cslist.add(cos);          
  30.         cfmap.put(columnFamily, cslist);     
  31.           
  32.         Map<String, List<ColumnOrSuperColumn>> cfmap2 = new HashMap<String, List<ColumnOrSuperColumn>>();     
  33.         List<ColumnOrSuperColumn> cslist2 = new ArrayList<ColumnOrSuperColumn>();     
  34.         ColumnOrSuperColumn cos2 = new ColumnOrSuperColumn();     
  35.         List<Column> columnList2 = new ArrayList<Column>();     
  36.         Column id2 = new Column();     
  37.         id2.setName("id".getBytes());     
  38.         id2.setValue("101".getBytes());     
  39.         id2.setTimestamp(new Date().getTime());     
  40.         Column name2 = new Column();     
  41.         name2.setName("name".getBytes());     
  42.         name2.setValue("Kobe Bryant".getBytes());     
  43.         name2.setTimestamp(new Date().getTime());   
  44.         Column age = new Column();     
  45.         age.setName("age".getBytes());     
  46.         age.setValue("32".getBytes());     
  47.         age.setTimestamp(new Date().getTime());  
  48.         Column desc = new Column();     
  49.         desc.setName("desc".getBytes());     
  50.         desc.setValue("Five NBA title, One regular season MVP, Two Final Games MVP".getBytes());     
  51.         desc.setTimestamp(new Date().getTime());  
  52.         columnList2.add(id2);     
  53.         columnList2.add(name2);   
  54.         columnList2.add(age);  
  55.         columnList2.add(desc);  
  56.         SuperColumn sc2 = new SuperColumn();     
  57.         sc2.setColumns(columnList2);     
  58.         sc2.setName("super2".getBytes());     
  59.         cos2.super_column = sc2;     
  60.         cslist2.add(cos2);         
  61.         cfmap2.put(columnFamily, cslist2);   
  62.           
  63.         client.batch_insert(keyspace, row, cfmap, ConsistencyLevel.ONE);  
  64.         client.batch_insert(keyspace, row, cfmap2, ConsistencyLevel.ONE);  
  65.           
  66.         ColumnPath path = new ColumnPath(columnFamily);     
  67.         path.setSuper_column("super1".getBytes());     
  68.         ColumnOrSuperColumn s = client.get(keyspace, row, path, ConsistencyLevel.ONE);     
  69.         System.out.println(new String(s.super_column.columns.get(0).value) + " | " + new String(s.super_column.columns.get(1).value));     
  70.           
  71.         path.setSuper_column("super2".getBytes());     
  72.         ColumnOrSuperColumn s2 = client.get(keyspace, row, path, ConsistencyLevel.ONE);  
  73.         System.out.println(new String(s2.super_column.columns.get(2).value) + " | " + new String(s2.super_column.columns.get(3).value) + " | " + new String(s2.super_column.columns.get(0).value) + " | " + new String(s2.super_column.columns.get(1).value));  
  74.           
  75.         trans.close();   
  76.     }  

 上述代码演示往名字叫“UserRelationships”的columnFamily中添加一行,这一行中包含两个SuperColumn,名字分别:super1和super2,super1包含2个Column,名字分别为id,name;super2包含4个Column,名字分别为id,name,age,desc,运行结果:

Java代码 
  1. 101 | Kylin Soong  
  2. 101 | Kobe Bryant | 32 | Five NBA title, One regular season MVP, Two Final Games MVP  

(三)Column

从上面一、二可以看到Column是Cassandra的最小存储单位,它的结构如下:

Java代码 
  1. struct Column {  
  2.   1: binary                        name,  
  3.   2: binary                        value,  
  4.   3: i64                           timestamp,  
  5. }  

 (四)keyspace

如上面一二中 String keyspace = "Twitter"; 都定义了keyspace 是名字为“Twitter”,相当于干系数据库中的Schema或数据库。

www.htsjk.Com true http://www.htsjk.com/cassandra/33020.html NewsArticle Cassandra Dev 1: Cassandra 入门,cassandradev入门 本文转自我的javaEye博客,链接http://kylinsoong.javaeye.com/blog/731208 最近, Cassandra 绝对是一个比较前端的话题,随着互联网的不断发展, Distribute...
相关文章
    暂无相关文章
评论暂时关闭