cassandra 學習筆記

1). cassandra任何一個節點都可以被客戶端訪問。

2). 對cassandra某個節點的訪問是通過調用org.apache.cassandra.service.Cassandra的內部類Client的相 應接口實現的。

3). 2)中的Cassandra這個類包含了很多內部類和一個接口(Iface)。其中的Client和Processor兩個內部類都是對Iface的實 現,這保證了他們的內部的所有方法是一一對應的。

4). 當Client這個內部類中的某個方法被調用的時候,該方法會用他內部的send_開頭的方法發送消息,並且用recv_開頭的方法接收返回的內容,容, 返回的內容可能是我們想要的數據,也可能是個異常的消息,如果是異常的消息,則會在客戶端生成一個相應的異常並拋出,

5). Client端send_和recv_方法同目標節點的交互是分別通過oprot和iprot的實例完成的,這兩個實例是負責輸入輸出的,具體的功能的實 現在libthrift.jar中。

6). 節點和客戶端通信的連接是由libthrift.jar中的TThreadPoolServer的實例實現的,這個實例在該節點最初啓動的時候被生成,並 且該實例內部還保有一個2)中提到的Processor實例。TThreadPoolServer實例給Processor實例提供了輸入輸出實例 iprot和oprot,並且通過調用Processor的processprocess(TProtocol iprot, TProtocol oprot)接口來進一步的向內傳遞消息。

7). 節點最初啓動的初始話過程是在org.apache.cassandra.service.CassandraDaemon的setup()中完成的。

8). 在Processor的processprocess(TProtocol iprot, TProtocol oprot)會解析iprot中傳入的客戶端的請求,並首先解析出要調用函數的函數名字,然後通過查詢processMap_來決定究竟由那個 ProcessFunction實例來接收處理消息,相應的ProcessFunction實例的process(int seqid, TProtocol iprot, TProtocol oprot)被激活並開始全權負責消息的處理和反饋。

9). 相應的的ProcessFunction的實例主要負責三件事:i,進一步處理iprot傳入的消息 ii,將詳細的信息轉發給iface的相應方法處理 iii,將得到的反饋通過oprot返回給客戶端。這裏的iface實例實際上是 org.apache.cassandra.service.CassandraServer的一個實例,在Processor的實例創建的時候(節點啓 動的時候)被裝入了Processor實例,但是由於ProcessFunction類是Processor的內部類,所以 ProcessFunction的實例也能直接訪問。

10)以上可知,最終客戶端的信息是交給CassandraServer的相應方法來處理的,而thrift的相關功能只是負責了客戶端和節點間的 交互(9160端口),而節點之間的交互並沒有使用thrift的資源。

 

源碼中對節點的如下稱呼應該是等價的: end point , node ,  machine , datacenter , host。

    cassandra節點的啓動main()在類org.apache.cassandra.service.CassandraDaemon中,細節在 setup()中。過程中會start一個CassandraServer的實例peerStorageServer。 peerStorageServer在建立的時候,內部會實例化一個 StorageService實例,在該StorageService實例初始化的過程中,該節點的所有功能服務會被配置激活,這些操作是在 StorageService的默認構造器中完成的。

    StorageService的構造器中大致做了如下幾件事情:

    1)生成一個storageLoadBalancer_s實例負責負載均衡,現在還沒有明白原理。

    2)生成一個endPointSnitch_實例,這個提供了對兩個end_point進行比較的一個途徑,基本上是判斷兩個end_point是不是同 一個ip等

    3)啓動了MessagingService,並且註冊了一些handler實例。MessagingService是負責該end_point與其他 end_point進行通信的。兩個節點間通信的內容被封裝在一個Message的實例裏面。

       比如,如果節點A想向節點B獲得一定的數據,那麼A需要通過自己的MessageService向節點B發送一個Message實例,這個實例裏面包含了 如下信息:這個請求的類型(屬於什麼stage) , 這個請求要調用的B的哪個handler來處理,以及這個請求的其他具體內容。當節點B接收到節點A發送過來的Message實例後,會將根據這個 Message實例內部指定的handler信息,將該實例轉發相應的handler去處理。當然這樣做的前提是這個指定的handler已經在B節點注 冊了,而這個註冊過程就是在StorageService啓動的時候完成的。

    4)consistencyManager_:還沒明白什麼意思。

    5)StageManager的配置:

       這個stage的概念來自於“SEDA“( staged event driven architecture)中的"S",參考http://www.eecs.harvard.edu/~mdw/papers/seda- sosp01.pdf。

       大致意思好像是可以將一個工作流程分爲若干個階段(stage),然後給各個stage動態的分配線程資源來處理stage內定義的邏輯。stage和 stage之間的通信是通過任務隊列完成的,當一個stage的邏輯執行完後,如果需要調用下一個stage來繼續執行,那麼就往下一個stage保有的 任務隊列中寫入必要的任務信息。

       這樣的SEDA結構的好處是:在實際的運行當中各個stage的忙閒程度是不一樣的,可以通過將比較閒的stage上的線程資源分配給同期比較忙的 stage來實現效率上的提高。

       在cassandra中,還是以3)中例子說明,在A節點發往B節點的Message實例中有一個“這個請求的類型”的信息,這個信息保存在 Message實例內header實例的type_上,是一個字符串。這個字符串標明瞭當MessageService獲取了Message實例後究竟由 那個 stage來負責執行指定的handler對象。因爲handler對象只是定義了對Message的處理邏輯,所以需要stage裏面的線程來對其進行 執行。

       當前的stage只有4種,依次在StroageService的 /* All stage identifiers */標註下被定義,分別負責不同的任務,“ROW-READ-STAGE”是負責讀取的,其他的含義還沒有完全搞清楚,大概是負責修改,數據壓縮和 http後臺頁面信息接收的。

       StageManager部分的源碼還沒喲看到,可能是負責各個stage間線程調度的吧。

    6)設置nodepicker_定義了當前節點對周圍節點的查詢策略,具體的還不清楚

 

當某個end point拿到一個key(比如"王老六")並想取出他的相關信息的時候,這個節點是怎麼知道這個key的相關信息是存放在哪些節點中的呢?


以下將用從客戶端拿到的"get_clomun"請求爲例,進行說明:

      "get_column"的相關信息會在CassandraServer的get_column(String tablename, String key, String columnPath)方法中被封裝成一個readCommand實例,該對象簡單包含了請求信息,另外也提供了一些別的方法。

      然後該command實例會最終被交給 StorageProxy.readProtocol(command,StorageService.ConsistencyLevel.WEAK)來 處理,並期待這個方法能返回一個和key關聯的columnFamily的數據,這些數據被裝在一個row對象裏返回的。

      在上述的StorageProxy.readProtocol(...)這個方法中,首先要做的事情就是根據提交的key("王老六")確定他的相關信息 內容是存放在那些end point中的。而這個尋找end point的工作由StorageService.instance().getNStorageEndPoint(command.key)來完成。

      實際上,上述的StorageService的getNStorageEndPoint(String key)方法調用了他持有的實例"nodePicker_"的getStorageEndPoints(Token token) 來完成尋找end point這件事情。

      題外話:StorageService這個實例提供了本地節點數據存儲和與其他節點交互的服務,這個實例是在節點啓動之初被建立的,在建立的時候該實例會 初始化一個nodePicker_放在其內部,這個nodePicker主要負責本地節點同其他節點的交互規則,也就是如何選擇其他節點的策略。當前的策 略有兩種:RackAwareStrategy--機架敏感 和 RackUnawareStrategy--非機架敏感,默認策略是RackUnawareStrategy。

      所以默認情況下,nodePicker_就是一個RackUnawareStrategy實例,而他的 getStorageEndPoints(Token token)將被用來查找合適我們給出的key("王老六")的end point,這裏合適的意思是“存有關於這個key的信息”。而這裏提到的token是由key封裝得來的,他對key進行了hash,並且繼承了 Comparable接口。也就是說,這個token實例是可以和其他同類token比較大小的,而這樣做的目的是爲了“可以在Token組成的list 上進行二分查詢Collection.binarySort,並定位查找目標的在list中的相對位置”--這種查找操作將被經常使用。默認情況下 token是一個BigIntegerToken的實例。

      nodePicker_最終會將查找的工作交給getStorageEndPoints(Token token, Map<Token, EndPoint> tokenToEndPointMap)來做,這個方法會返回一個查找到的end_point的數組。以下是對RackUnawareStrategy中 的相應方法的說明(因爲默認是使用這個方法):


//此處的tokenToEndPointMap保存了該節點知道的所有其他節點,並且可以根據他們的token查找到

//此處的token是我們要查找的key("王老五")生成的token,他跟上面end point的token生成的算法是相同的,也都是BigIntegerToken

public EndPoint[] getStorageEndPoints(Token token, Map<Token, EndPoint> tokenToEndPointMap)
    {
        //根據key尋址的切入點
        int startIndex;
        List<EndPoint> list = new ArrayList<EndPoint>();
        int foundCount = 0;
        //將key的set裝入一個ArrayList中,並完成排序,(一位token是comparable的)
        List tokens = new ArrayList<Token>(tokenToEndPointMap.keySet());
        Collections.sort(tokens);
        //在排好序的tokens中二叉樹搜索token,並返回token所在的位置的index

        //個人認爲:根本別指望tokens裏面能有我們要找的token,因爲一個是end point的token集合,一個是查找key("王老六")生成的 

        //token,生成的時候都使用了hash,並且加入了大隨即數,重合的概率很低。

        //這樣做的主要目的是爲了獲得下面這個index,(參見binarySearch的說明可知)。

        //也就是:雖然下面這條語句不大可能從tokens中取出東西,但是生成的index將告訴我們,哪個end point的token離我們給出的

        //key"王老六"的token“最近”。而且,在節點情況固定下來的情況下,用當前這種方法,

        //key"王老六"的token確定的對應的end point也是不會改變的(每次都會取到這個end point,除非end point本身註冊的變了)。

        //也就是說:這就成了一種方法,一種能夠根據數據key("王老六")定位到一個end point的方法,

        //如果寫數據時使用了這種策略找到一個end point 節點然後寫進數據,那麼手持相應的key在讀數據的時候,同樣使用這個策略,也

        //相應的能找到當時存入數據的那個節點,並把數據讀出來。
          int index = Collections.binarySearch(tokens, token);
        //基本上這個條件裏面的語句肯定會被執行

        //那如果真的用key("王老六")從tokens中找到對應了呢?這個方法裏的策略就是,那就直接拿這個被確定的節點來當 “鄰居”節點了

        //而key的tokens和end_point的token之所以能混在一起說話,是因爲他們都是BigIntegerToken,在同一個數據空間中
        if(index < 0)
        {

            //以下的運算將把key("王老六")的"鄰居"節點給"翻譯"出來
            index = (index + 1) * (-1);//得到可以插入的位置
            if (index >= tokens.size())
                index = 0;
        }
        int totalNodes = tokens.size();


        // Add the node at the index by default

        //把我們找到的"鄰居"節點放到一個要返回的list中,當然,對於一個key("王老六")的信息還可能在其他節點上存有副本(replicas)

        //再下面的操作會將這些存有副本的節點也一同取出
        list.add(tokenToEndPointMap.get(tokens.get(index)));
        foundCount++;


        //本來index是從0...一直到size()的一條線,如果我們用如下的方式選取index,就好似將所有的index數值放在了一個圓圈上,然後 按

        //從小到大的方向在選取下一個index。startIndex就是一個切入點,本質上Partitioner提供了這個可被"切入"的結構和線索

        //猜想這也是爲什麼叫Partitioner的原因吧
        startIndex = (index + 1)%totalNodes;
        // If we found N number of nodes we are good. This loop will just exit. Otherwise just
        // loop through the list and add until we have N nodes.
        // replicas_對應storage-conf.xml中的最大副本數量
   

        //將存有副本的節點也一同取出,就是最初"鄰居"節點往後的若干個節點,具體若干幾個是在storage-conf.xml的

        //<ReplicationFactor>標籤中定義的
        for (int i = startIndex, count = 1; count < totalNodes && foundCount < replicas_; ++count, i = (i+1)%totalNodes)
        {
            if( ! list.contains(tokenToEndPointMap.get(tokens.get(i))))
            {
                list.add(tokenToEndPointMap.get(tokens.get(i)));
                foundCount++;
            }
        }

        //統一下節點監聽端口的信息
        retrofitPorts(list);
        return list.toArray(new EndPoint[list.size()]);
    }

 

 

 (一)依賴:cassandra.jar

             libthrift.jar

(二)連接:

    //該方法將返回一個Cassandra.Client實例,該實例包含和server端指定節點會話的API

     public Cassandra.Client getClient()
    {
        //192.168.0.169爲想連接到的某個節點的ip,9160爲端口

        TSocket socket = new TSocket("192.168.0.169", 9160);

        TTransport transport_ = socket;

        TBinaryProtocol binaryProtocol = new TBinaryProtocol(transport_, false, false);
        Cassandra.Client cassandraClient = new Cassandra.Client(binaryProtocol);

        try
        {
            transport_.open();
        }
        catch(Exception e)
        {
            // Should move this to Log4J as well probably...
            System.err.println("Exception " + e.getMessage());           
            e.printStackTrace();
        }
       
        return cassandraClient;
      
    }

(三)API

1)該方法向key的指定path插入一個數據,另外想要對key某指定path的數據進行修改也使用這個方法

   代碼中的cli是一個Cassandra.Client實例

     public void test_insert()
    {
        try {
            String tableName = "Table1";
            String key = "testkey";
            String columnFamily = "Standard1";
            String columnName = "testColumn";
            String value = "testValue";

            //對數據進行定位
            String path = columnFamily+":"+columnName;
            //向cassandra中插入一條數據
            cli.insert(tableName,key,path,value.getBytes(),System.currentTimeMillis(),true);
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnavailableException e) {
            e.printStackTrace();
        }
    }


2)該方法試用Cql語句的方式發送請求,有一個問題就是當請求的參數或不正確的時候,方法不會報錯,只是返回爲空;

   但是在語法出現錯誤的時候還是會有InvalidRequestException拋出,提示Unresolved compilation problems

   代碼中的cli是一個Cassandra.Client實例

   public void test_excuteQuery()
    {
        try {
            cli.executeQuery("set Table1.Standard1['testKey2']['testColumn']='testValue2'");
            CqlResult_t crt = cli.executeQuery("get Table1.Standard1['testKey2']");
            List<Map<String, String>> rs = crt.getResultSet();
            //遍歷顯示所有column_t中的內容
            if(rs != null){
                for(int i = 0 ; i < rs.size() ; i++){
                    Map<String , String> map = rs.get(i);
                    for(String key : map.keySet()){
                        System.out.println(key+" : "+map.get(key));
                    }
                }
            }else{
                System.out.println("result set is null");
            }
        } catch (TException e) {
            e.printStackTrace();
        }
    }


3)根據path信息獲取一個column

   path是一個定位信息,standard column family是兩層 super column family是三層

   superColumnFamily:superColumn:standardColumn。

   public void test_get_column()
    {
        String tableName = "Table1";
        String key = "testKey2";
        String columnFamily = "Standard1";
        String column = "testColumn";
        String path = columnFamily+":"+column;
        try {
            column_t cmt = cli.get_column(tableName, key, path);
            //以下兩個是等價的
            System.out.println(cmt.getColumnName());
            System.out.println(cmt.getFieldValue(1));
            //以下兩個是等價的
            System.out.println(new String(cmt.getValue(),"UTF-8"));
            System.out.println(new String((byte[]) cmt.getFieldValue(2),"UTF-8"));
            //以下兩個是等價的
            System.out.println(cmt.getTimestamp());
            System.out.println(cmt.getFieldValue(3));
           
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }


4)

    //術語解釋:每個key都對應一個Row的信息這個Row的信息又按照columnFamliy 分成了很多slice

    //給定table,key和columnFamily的信息,
    //此方法將返回,該key在當前columnFamily的slice中有都少個column元素
    //該例子中key:"testKey2"在columnFamily:"Standard1"中的slice有幾個column元素
    public void test_get_column_count()
    {
        String tableName = "Table1";
        String keyName = "testKey2";
        String columnFamily = "Standard1";
        try {
            System.out.println(cli.get_column_count(tableName, keyName, columnFamily));
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }


5) //暫不確定

    //根據給定的時間點,取出以後的對應給定key的一組columns
    //但是好像在時間上有延遲,這個不確定
    public void test_get_columns_since()
    {
        String tableName = "Table1";
        String keyName = "testKey2";
        String columnFamily = "Standard1";
        Long sinceTime = System.currentTimeMillis()-5000*1000;//取當前時間之前120秒以內的
        try {
            List<column_t> cmts = cli.get_columns_since(tableName , keyName , columnFamily , sinceTime);
            System.out.println("cmts size : "+cmts.size());
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }


6)//range queries may only be performed against an order-preserving partitioner
    //如果系統試用的節點定位的RandomPartitioner的話那麼這個方法將不能試用,好處是跟均衡的存儲

    //此方法尚在研究中
    public void test_get_key_range()
    {
        try {
            cli.get_key_range("Table1", "??", "??", 1);
           
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }


7) //術語解釋:每個key都對應一個Row的信息這個Row的信息又按照columnFamliy 分成了很多slice

    //每一個columnFamily都存在一個排序,或按照name或按照time
    //該方法將取出對應某個key的一定區間段內的column元素
    public void test_get_slice()
    {
        String tableName = "Table1";
        String keyName = "testKey2";
        String columnFamily = "Standard1";    //想要從哪個columnFamily中獲取column
        int start = 0;//起始位置
        int count = 10;//獲取數量
        try {
            List<column_t> cmts = cli.get_slice(tableName, keyName, columnFamily, start, count);
            System.out.println("cmts size : "+cmts.size());
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }


8)//通過一個column的名字數組來查找相對應的屬於給定key的slice(一組column 元素)
    public void test_get_slice_by_names()
    {
        String tableName = "Table1";
        String keyName = "testKey";
        String columnFamily = "Standard1";
        List<String> columnNameList = new ArrayList<String>();
        columnNameList.add("testColumn");
        try{
            List<column_t> cmts = cli.get_slice_by_names(tableName, keyName, columnFamily, columnNameList);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

9)//描述一個table中的各個columnFamily的基本信息
    public void test_describe_table()
    {
        try {
            System.out.println(cli.describeTable("Table1"));
        } catch (TException e) {
            e.printStackTrace();
        }
    }

10)//批量的針對一個key插入數據
    public void test_batch_insert()
    {
        try{
            String tableName = "Table1";
            String keyName = "testkey2";
            //當下的Map保存了ColumnFamily到他內部的column元素列表的一個映射
            //所以String 應該保存 對應columnFamily的名字。
            //column_t(column元素)包含:columnName , value , timestamp 三要素
            Map<String , List<column_t>> CFmap = new HashMap<String , List<column_t>>();
            //製作兩個column_t實例
            column_t cmt1 = new column_t("test_column_name_1" , "test_column_value_1".getBytes() , System.currentTimeMillis());
            column_t cmt2 = new column_t("test_column_name_2" , "test_column_value_2".getBytes() , System.currentTimeMillis());
            //製作一個column_t的list
            List<column_t> cList = new ArrayList<column_t>();
            cList.add(cmt1);
            cList.add(cmt2);
            //製作一個將其寫入CFmap中
            String columnFamilyName = "Standard1";
            CFmap.put(columnFamilyName, cList);//這樣在以後的插入操作中,相應的column_t將插入"Docin"這個family中
            batch_mutation_t bmt = new batch_mutation_t(tableName , keyName , CFmap);//完成插入的key的定位
            //執行這個針對相應key和colmunFamily的batch_insert
            cli.batch_insert(bmt, true);
            
        }catch(Exception e){
            e.printStackTrace();
        }
    }

11)//同insert()相對的反向操作
    //當我們試圖查詢一個不存在的column_t元素的時候,會有"NotFoundException"異常被拋出
    public void test_remove()
    {
        String tableName = "Table1";
        String columnFamily = "Standard1";
        String columnName = "removetest";
        String keyName = "removetestkey";
        String value = "removetestvalue";
        String path = columnFamily+":"+columnName;
        try{
            //先將相應的column元素寫入
            cli.insert(tableName, keyName, path, value.getBytes(), System.currentTimeMillis(), true);
            //嘗試讀出信息
            column_t cmt = cli.get_column(tableName, keyName, path);
            displayColumn(cmt);
            //刪除這條信息
            cli.remove(tableName, keyName, path, System.currentTimeMillis(), true);
        }catch(Exception e){
            e.printStackTrace();
        }
        //嘗試讀出信息
        column_t cmt2;
        try {
            cmt2 = cli.get_column(tableName, keyName, path);
            displayColumn(cmt2);
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            System.out.println("NotFoundException 異常拋出,column_元素已經被刪除");
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

12)//插入一個superColumn元素
    //不含有內容的superColumn不能同過該方法被創建,會拋出異常
    public void test_batch_insert_super_t()
    {
        try {
            String tableName    = "Table1";
            String keyName        = "superkeytest";
            String superColumnFamilyName = "Super1";
            //產生1個superColumn元素
            //首先產生1個column_t的list
            List<column_t> Clist = new ArrayList<column_t>();
            //當兩個名字一樣的時候,先前的會被覆蓋
            Clist.add(new column_t("stardcolumninsuper1","stardcolumn1".getBytes(),System.currentTimeMillis()));
            Clist.add(new column_t("stardcolumninsuper2","stardcolumn2".getBytes(),System.currentTimeMillis()));
            //產生一個superColumn元素
            String superColumnName = "superColumn3";
            superColumn_t sct = new superColumn_t(superColumnName , Clist);
            //將這個元素裝入到一個superColumn_t的List中
            List<superColumn_t> SClist = new ArrayList<superColumn_t>();
            SClist.add(sct);
            //將這個super_column 的list連同對應的superColumnFamily的名字一起放入一個hashmap中
            Map<String , List<superColumn_t>> cfmap = new HashMap<String , List<superColumn_t>>();
            cfmap.put(superColumnFamilyName, SClist);
            //區別於一般的,這裏要求的column元素是superColumn元素
            //將整理好的信息關聯一個key後生成一個mutation
            batch_mutation_super_t bmst = new batch_mutation_super_t(tableName,keyName,cfmap);
            boolean block = true;
            //執行這個mutation的插入操作
            cli.batch_insert_superColumn(bmst, block);
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (UnavailableException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }

13)//獲取一個SuperColumn中的信息
    public void test_get_superColumn()
    {
        String tableName = "Table1";
        String keyName = "superkeytest";
        String superColumnFamilyName = "Super1";
        String superColumnName = "superColumn3";
        String path = superColumnFamilyName+":"+superColumnName;
        try {
            superColumn_t sct = cli.get_superColumn(tableName, keyName, path);
            if(sct != null){
                System.out.println("Super Name : "+sct.getName());
                System.out.println("Size : "+sct.getColumnsSize());
                List<column_t> clist = sct.getColumns();
            }else{
                System.out.println("super column is empty");
            }
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

14)//嘗試向一個superColumn中添加column元素(就是column_t對象)
   //注意:想superColumnFamily中添加一般的column元素是不被允許的,所以superColumnFamliy中只能有 superColumn,而superColumn中只能有一般的column
   //而一般的columnFamily中也只能有一般的column
    public void test_insert_standard_to_super()
    {
        String tableName = "Table1";
        String keyName = "superkeytest";
        String superColumnFamily = "Super1";
        String superColumnName = "superColumn1";
        String columnName = "normal_column";
        String value = "normal_column_value_add";
        String path = superColumnFamily+":"+superColumnName+":"+columnName;
        try {
            cli.insert(tableName, keyName, path, value.getBytes(),System.currentTimeMillis(), true);
            //嘗試取出這個元素並顯示這個元素
            column_t cmt = cli.get_column(tableName, keyName, path);
            if(cmt != null){
                System.out.println("column name : "+cmt.getColumnName());
                System.out.println("column value: "+new String(cmt.getValue(),"UTF-8"));
                System.out.println("column time : "+cmt.getTimestamp()+"/n");
            }else{
                System.out.println("column is empty");
            }
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (UnavailableException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

15)//術語解釋:每個key都對應一個Row的信息這個Row的信息又按照columnFamliy 或superColumnFamily分成了很多slice
    //同針對一般的columnFamliy類似,這個方法是獲取給定key的某個superColumnFamily的slice,這個slice中的 元素都是superColumn
    public void test_get_slice_super()
    {
        String tableName = "Table1";
        String keyName = "superkeytest";
        String superColumnFamily = "Super1";
        try {
            List<superColumn_t> sct = cli.get_slice_super(tableName, keyName, superColumnFamily, 0, 10);
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

16)
//嘗試刪除一個superColumn節點,和屬於該節點的column_t節點
    //測試需要通過path來定位這個節點
    public void test_remove_super()
    {
        String tableName = "Table1";
        String keyName = "superkeytest";
        String superColumnFamily = "Super1";
        String superColumn = "superColumn3";
        String columnName = "stardcolumninsuper1";
        String path = superColumnFamily+":"+superColumn+":"+columnName;
        
        try {
            //先建立一個super_column_family 名字測試的時候是"superColumn3"
            //產生1個superColumn元素
            //首先產生1個column_t的list
            List<column_t> Clist = new ArrayList<column_t>();
            //當兩個名字一樣的時候,先前的會被覆蓋
            Clist.add(new column_t("stardcolumninsuper1","stardcolumn1".getBytes(),System.currentTimeMillis()));
            Clist.add(new column_t("stardcolumninsuper2","stardcolumn2".getBytes(),System.currentTimeMillis()));
            //產生一個superColumn元素
            String superColumnName = "superColumn3";
            superColumn_t sct = new superColumn_t(superColumnName , Clist);
            //將這個元素裝入到一個superColumn_t的List中
            List<superColumn_t> SClist = new ArrayList<superColumn_t>();
            SClist.add(sct);
            //將這個super_column 的list連同對應的superColumnFamily的名字一起放入一個hashmap中
            Map<String , List<superColumn_t>> cfmap = new HashMap<String , List<superColumn_t>>();
            cfmap.put(superColumnFamilyName, SClist);
            //區別於一般的,這裏要求的column元素是superColumn元素
            //將整理好的信息關聯一個key後生成一個mutation
            batch_mutation_super_t bmst = new batch_mutation_super_t(tableName,keyName,cfmap);
            boolean block = true;
            //執行這個mutation的插入操作
            cli.batch_insert_superColumn(bmst, block);
        
            //嘗試刪除之
            cli.remove(tableName, keyName, path, System.currentTimeMillis(), true);
        } catch (InvalidRequestException e) {
            e.printStackTrace();
        } catch (UnavailableException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }


17)//這個方法目前還不健全
    public void test_getProperty()
    {
        try {
            //這事一個未健全的方法,現在只能返回對應"tables"的參數,也就是表名
            List<String> Plist = cli.getStringListProperty("tables");
            for(int i = 0 ; i < Plist.size() ; i++){
                System.out.println(Plist.get(i));
            }
        } catch (TException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //現在只支持如下三個參數
        try {
            System.out.println(cli.getStringProperty("cluster name"));
            //會直接把server端的storage_conf.xml讀進來,可能用於做進一步的xml分析
            System.out.println(cli.getStringProperty("config file"));
            System.out.println(cli.getStringProperty("version"));
        } catch (TException e) {
            e.printStackTrace();
        }
    }

一些注意的:
1)cli的實例還有一些諸如sent_*和recv_*(成對出現)的公共方法,這些方法是供上面羅列的相應方法調用的,一般不用管,
2)sent_*負責想服務器端發送消息
3)recv_*將處理返回的所要的結果或處理錯誤信息拋出相應異常
4)大部分方法可執行的前提是必須有一個精確的key信息,(describe_table和get_key_range除外)。
5)一個superColumnFamily中只能存放superColumn_t元素而不能存放column_t元素
6)一個columnFamily中只能存放column_t元素

 

1)columnFamily下一個column和多個column的讀取區別
2)columnfamily 和superColumnFamily的讀取區別

測試機數量:兩臺,jvm最大使用內存都開到1.3G。

起始key: 1356278962 ;

改變組:
product_name1    :    "是一個非常可靠的大規模分佈式存儲系統"
product_name2    :    "中國慘敗伊朗丟亞錦賽冠軍創34年參賽最恥辱一敗"

控制組:
product_value1    :    "第一次在主場丟冠軍"
product_value2    :    "胡雪峯頂替受傷的劉煒"
product_value3    :    "朱芳雨都有外線出手機會"
product_value4    :    "張慶鵬傳球意圖太過明顯"
product_value5    :    "最後一節比賽"
product_value6    :    "中國隊首發:易建聯、王治郅、朱芳雨、王仕鵬、胡雪峯"
product_value7    :    "中國隊本次亞錦賽首次嚐到失利的滋味,在家門口把冠軍拱手相讓"
product_value8    :    "下半場易邊再戰,中國隊仍然如同夢遊,球員之間沒有形成整體"

//下面這個是一個要存入的“摘要”
product_value9    :    "下半場易邊再戰,中國隊仍然如同夢遊,球員之間沒有形成整體,
            單打獨鬥的進攻模式成功率相當低。伊朗隊內外結合,多點得分,
            繼續擴大分差。朱芳雨傳球被斷,對手長傳快攻,14號球員扣籃得分,
            28-53。王治郅強攻得手,造成哈達迪犯規,加罰命中。
            王治郅再次溜到籃下,反身投籃得分。王治郅成爲中國隊的唯一亮點,
            持球接連晃過三名球員的防守,投籃得分,加罰再中,
            36-53。靠着王治郅的出色發揮,中國隊留住翻盤的一線希望。
            第三節結束,39-56,分差仍爲17分。"

   
我們有的ColumnFamily: Standard1 Standard2 Super1 Super2

測試1:    向Standard1中寫入10萬條記錄,此時Standard1中只有"product_name"一個column,key是從1356278962 開始往後10萬個

    insert()進行插入,速度很慢,不可能在一小時內完成
    CQL 用時256秒,關掉log後用時55秒

測試2:    然後在product_name1 和 product_name2之間反覆修改key-1356279962對應的"produck_name"這個column的內容10萬次


    CQL:

    當config中的ReplicationFactor爲1的時候,用時120秒
    當config中的ReplicationFactor爲2的時候,用時183秒;關掉log後用時42秒

   
測試3:    將存在的各個key的product_name讀取一遍(此時相應columnFamily中只有1個column)


    當config中的ReplicationFactor爲1的時候:速度相當慢
    當config中的ReplicationFactor爲2的時候:

        CQL:

        用時267秒;關掉log後用時145秒

        API:
        用get_column用時203秒;關掉log後用時135秒

以下均以ReplicationFactor=2來進行測試

測試4:    依照已經存在的各個key,向Standard1中寫入10萬條控制組裏面的column數據


    API:

    batch_insert() 速度非常慢

    CQL:
    90萬個column元素,其中包括一個類似摘要大小的字符串(200字左右),用時2004秒;關掉log後用時512秒。

測試5:    然後在product_name1 和 product_name2之間反覆修改key-1356279962對應的"produck_name"這個column的內容10萬次

    API:
    使用insert()速度相當慢

    CQL:
    用時190秒;關掉log後用時45秒

測試6:    將存在的各個key的product_name讀取一遍(此時相應columnFamily中已經有10個column了)

    API:

    使用get_column()用時757秒;關掉log後用時188秒

    CQL:

    用時更慢一些,用時861秒;關掉log後用時202秒



super測試1:在Super1中創建superColumn1,在superColumn1中只創建1個 column-"product_name",然後寫入10萬條數據,key是從1356278962開始往後10萬個

    CQL:

    用時316秒;關掉log後用時60秒


super測試2:然後在product_name1 和 product_name2之間反覆修改superColumn1下key-1356279962對應的"produck_name"這個column的 內容10萬次


    CQL:

    用時195秒;關掉log後用時62秒


super測試3:將存在的各個key的product_name讀取一遍(此時相應superColumnFamily中只有1個column)

    API:

    get_column() 用時235秒;關掉log後用時234秒

    CQL:

    用時356秒 ;關掉log後用時158秒

super測試4:依照已經存在的各個key,依次向superColumn1中寫入10萬條控制組裏面的column數據

    CQL:

    用時2058秒;關掉log後,竟然非常慢,不知道爲什麼,但是重新啓動下節點後,速度變快,用時562秒


super測試4:然後在product_name1 和 product_name2之間反覆修改superColumn1下key-1356279962對應的"produck_name"這個column的 內容10萬次

   CQL:

   用時190秒,關掉log後用時48秒


super測試6:將存在的各個key的product_name讀取一遍(此時相應superColumnFamily中已經有10個column了)

    API:

    用get_column 用時 356秒;關掉log後用時191秒

    CQL:

    用時314秒;關掉log後用時208秒

結論:使用superColumn的開銷不比column大很多,向columnFamily中加入更多的column對讀取速度的影響不大。相比 而言受log和ReplicationFactor的影響更大。另外在寫入的時候CQL的效率更高,在讀取的時候get_column更好一些。而API 中的其他方法如batch_insert(),insert()效率明顯不高。但這只是兩個節點的情況,當有更多節點的時候情況可能又會不一樣了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章