小明思考

          Just a software engineer
          posts - 124, comments - 36, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          所謂snapshot就是一個(gè)快照,我們可以從快照中讀到舊的數(shù)據(jù)。

          先寫一個(gè)測試程序來看看snapshot的使用:

          #include <iostream>
          #include 
          "leveldb/db.h"

          using namespace std;
          using namespace leveldb;


          int main() {
              DB 
          *db ;
              Options op;
              op.create_if_missing 
          = true;
              Status s 
          = DB::Open(op,"/tmp/testdb",&db);

              
          if(s.ok()){
                  cout 
          << "create successfully" << endl;
                  s 
          = db->Put(WriteOptions(),"abcd","1234");
                  
          if(s.ok()){
                      cout 
          << "put successfully" << endl;
                      
          string value;
                      s 
          = db->Get(ReadOptions(),"abcd",&value);
                      
          if(s.ok()){
                          cout 
          << "get successfully,value:" << value << endl;
                      }
                  }
                  
          if(s.ok()){
                      
          string value;
                      
          const Snapshot * ss =db->GetSnapshot();
                      ReadOptions rop;
                      db
          ->Put(WriteOptions(),"abcd","123456");
                      db
          ->Get(rop,"abcd",&value);
                      
          if(s.ok()){
                              cout 
          << "get successfully,value:" << value << endl;
                      }
                      rop.snapshot 
          = ss;
                      db
          ->Get(rop,"abcd",&value);
                      
          if(s.ok()){
                              cout 
          << "get from snapshot successfully,value:" << value << endl;
                      }
                      db
          ->ReleaseSnapshot(ss);
                  }
              }
              delete db;
              
          return 0;
          }

          程序運(yùn)行的輸出結(jié)果是:
          create successfully
          put successfully
          get successfully,value:1234
          get successfully,value:123456
          get from snapshot successfully,value:1234

          可以看出,即使在數(shù)據(jù)更新后,我們?nèi)匀豢梢詮膕napshot中讀到舊的數(shù)據(jù)。

          下面我們來分析leveldb中snapshot的實(shí)現(xiàn)。

          SequenceNumber(db/dbformat.h)
          SequenceNumber是leveldb很重要的東西,每次對數(shù)據(jù)庫進(jìn)行更新操作,都會生成一個(gè)新的SequenceNumber,64bits,其中高8位為0,可以跟key的類型(8bits)進(jìn)行合并成64bits。
          typedef uint64_t SequenceNumber;

          // We leave eight bits empty at the bottom so a type and sequence#
          // can be packed together into 64-bits.
          static const SequenceNumber kMaxSequenceNumber =
              ((0x1ull << 56) - 1);

          SnapShot(db/snapshot.h),,可以看出snapshot其實(shí)就是一個(gè)sequence number
          class SnapshotImpl : public Snapshot {
           
          public:
            
          //創(chuàng)建后保持不變
            SequenceNumber number_;  

           
          private:
            friend 
          class SnapshotList; 

            
          //雙向循環(huán)鏈表
            SnapshotImpl* prev_;
            SnapshotImpl
          * next_;

            SnapshotList
          * list_;                 // just for sanity checks
          };

          創(chuàng)建snapshot:
          const Snapshot* DBImpl::GetSnapshot() {
            MutexLock l(
          &mutex_);
            
          return snapshots_.New(versions_->LastSequence());
          }

          刪除snapshot:
          void DBImpl::ReleaseSnapshot(const Snapshot* s) {
            MutexLock l(
          &mutex_);
            snapshots_.Delete(reinterpret_cast
          <const SnapshotImpl*>(s));
          }




          posted @ 2012-03-13 16:54 小明 閱讀(4495) | 評論 (0)編輯 收藏

               摘要: leveldb使用SSTable格式來保存數(shù)據(jù)。格式為:(當(dāng)前沒有META BLOCK)SSTABLE = |DATA BLOCK1|DATA BLOCK2|...|DATA BLOCK N|META BLOCK1|...|META BLOCK N|META INDEX BLOCK|DATA INDEX BLOCK|Footer|DATA BLOCK = |KeyValues|Restart ar...  閱讀全文

          posted @ 2012-03-12 18:21 小明 閱讀(3874) | 評論 (1)編輯 收藏

               摘要: leveldb在每次數(shù)據(jù)庫操作之前都會把操作記錄下來。 主要實(shí)現(xiàn)在db\log_format.h,db\log_reader.h,db\log_reader.cc,db\log_write.h,db\log_write.cc中。我們來具體看看實(shí)現(xiàn)。 日志格式 db\log_format.h log是分塊的,每塊為32K,每條記錄的記錄頭為7個(gè)字節(jié),前四個(gè)為CRC,然后是長度(2個(gè)字節(jié))...  閱讀全文

          posted @ 2012-03-09 16:00 小明 閱讀(3647) | 評論 (1)編輯 收藏

               摘要: 對于一個(gè)db來說,存儲是至關(guān)重要的問題。運(yùn)行上一篇的測試程序后,會發(fā)現(xiàn)leveldb會生成以下文件:SST文件:數(shù)據(jù)文件 -- sstable格式*.log: 數(shù)據(jù)庫日志文件 -- 順序記錄所有數(shù)據(jù)庫操作,用來恢復(fù)數(shù)據(jù)CURRENT: 文本文件,表明當(dāng)面的manifest文件LOCK:空文件,數(shù)據(jù)庫鎖,防止多進(jìn)程訪問LOG: 日志文件,文本格式LOG.old:上一次的日志文件MANIFEST: 數(shù)...  閱讀全文

          posted @ 2012-03-09 11:44 小明 閱讀(2979) | 評論 (1)編輯 收藏

          leveldb是 google對bigtable的一個(gè)簡化版的開源實(shí)現(xiàn),很有研究價(jià)值。

          我的編譯環(huán)境:ubuntu 32&g++ 4.6

          1.安裝git并下載代碼

          sudo apt-get install git-core
          git clone https:
          //code.google.com/p/leveldb/

          2. 編譯leveldb

          cd leveldb
          .
          /build_detect_platform
          make

          為了能夠調(diào)試,修改Makefile為debug mode(B模式)
          OPT ?= -g2

          編譯后會生成庫文件:libleveldb.a

          3. 編寫測試程序
          ldbtest.cpp
          #include <iostream>
          #include 
          "leveldb/db.h"

          using namespace std;
          using namespace leveldb;

          int main() {
              DB 
          *db ;
              Options op;
              op.create_if_missing 
          = true;
              Status s 
          = DB::Open(op,"/tmp/testdb",&db);

              
          if(s.ok()){
                  cout 
          << "create successfully" << endl;
                  s 
          = db->Put(WriteOptions(),"abcd","1234");
                  
          if(s.ok()){
                      cout 
          << "put successfully" << endl;
                      
          string value;
                      s 
          = db->Get(ReadOptions(),"abcd",&value);
                      
          if(s.ok()){
                          cout 
          << "get successfully,value:" << value << endl;
                      }
                      
          else{
                          cout 
          << "get failed" << endl;
                      }
                  }
                  
          else{
                      cout 
          << "put failed" << endl;
                  }
              }
              
          else{
                  cout 
          << "create failed" << endl;
              }
              delete db;
              
          return 0;
          }
          注意link的時(shí)候需要加上-lpthread.

          運(yùn)行后得到結(jié)果:(Eclipse中運(yùn)行)

          posted @ 2012-03-08 11:44 小明 閱讀(4841) | 評論 (1)編輯 收藏

               摘要: 和Amazon EC2提供了一系列的命令行工具幫助使用自動化任務(wù),比如創(chuàng)建instance,啟動instance等等。步驟:1. 下載:http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3512. 創(chuàng)建一個(gè)腳本用于設(shè)置環(huán)境變量。windows平臺:Code highlighting produced by Ac...  閱讀全文

          posted @ 2012-03-07 16:03 小明 閱讀(1415) | 評論 (0)編輯 收藏

          學(xué)習(xí)軟件有三個(gè)境界,第一個(gè)境界是會使用它,第二個(gè)境界是懂得背后的原理,明白它的架構(gòu)體系,第三個(gè)境界學(xué)習(xí)他的所長,為我所用。研究HBase/BigTable架構(gòu)和源碼一段時(shí)間后,我總結(jié)了一些東西可以供我們在設(shè)計(jì)分布式系統(tǒng)借鑒使用。

          1. 使用可信任的分布式組件來搭建自己的分布式系統(tǒng)。
          設(shè) 計(jì)一個(gè)可靠,健壯的分布式系統(tǒng)是比較困難的。我們知道,為了防止SPOF(Single Point Of Failure)問題,我們要分散風(fēng)險(xiǎn),把數(shù)據(jù)放在多個(gè)nodes上面去,但是這樣帶來了是數(shù)據(jù)的同步問題和版本問題,解決這個(gè)問題需要運(yùn)用復(fù)雜的 Paxos協(xié)議,系統(tǒng)的復(fù)雜度自然就升高了。另外一個(gè)需要解決的問題是分布式鎖和事件通知機(jī)制,以及全局信息共享,設(shè)計(jì)這些都需要大量的精力和仔細(xì)的研 究。

          HBase就不用考慮這些問題,它把數(shù)據(jù)的同步和冗余問題交給了Hadoop,把鎖機(jī)制和全局共享交給了Zookeeper,這大大簡化了HBase的設(shè)計(jì)。

          所以我們設(shè)計(jì)系統(tǒng)的時(shí)候,也要盡量利用這些可靠,穩(wěn)定的組件。目前比較流行和穩(wěn)定的有:
          分布式文件系統(tǒng) - HDFS
          分布式鎖和目錄 - Zookeeper
          緩存 - MemCached
          消息隊(duì)列 - ActiveMQ

          2.避免單點(diǎn)問題(SPOF)
          設(shè)計(jì)分布式系統(tǒng)要時(shí)刻考慮到失敗,不單是軟件可能失敗,硬件也可能掛掉,所以我們系統(tǒng)里面就不能有不可替代的角色。

          HBase 使用Master Server來監(jiān)控所有的Region Server,一旦其中的一臺出現(xiàn)問題,在其上的Region將會被轉(zhuǎn)移到其他的Region Server,避免了服務(wù)中斷。而Master Server也可以多臺備選,一臺掛掉之后,其他的備胎則會”繼承遺志“,從而讓整個(gè)系統(tǒng)得以生存。

          那 HBase如何做到這個(gè)呢,一個(gè)是使用”心跳機(jī)制”,即Region Server要主動定期向Master匯報(bào)狀況,另外一個(gè)是利用zookeeper里面的”生命節(jié)點(diǎn)“,每個(gè)server在啟動后要在ZK里面注冊,一旦 這個(gè)server掛掉,它在ZK里面的節(jié)點(diǎn)就會消失,監(jiān)聽這個(gè)節(jié)點(diǎn)的server就會得到通知。

          3.利用不變性提高系統(tǒng)的吞吐量
          我們知道,很多進(jìn)程/線程修改同一個(gè)東西的時(shí)候,我們就需要鎖機(jī)制來避免沖突。但是鎖帶來的問題是系統(tǒng)性能下降。如果對于一個(gè)只讀的對象,就不需要鎖了。

          HBase 在設(shè)計(jì)存儲的時(shí)候考慮到這一點(diǎn),最新的數(shù)據(jù)是放在memory里面,以提高性能。但是memory是有限的,我們不可能讓數(shù)據(jù)一直放在memory里面, 所以我們需要定時(shí)把這些數(shù)據(jù)寫到HDFS/磁盤上面。一種設(shè)計(jì)是寫到一個(gè)可修改的大文件中去,這樣對這個(gè)文件的讀寫就需要加鎖了。HBase是每次都寫到 一個(gè)新的文件中,一旦文件創(chuàng)建后,這個(gè)文件將不能被修改,就是所謂的create-one-read-many。當(dāng)然這樣也有一個(gè)問題,就是時(shí)間長了,會 有很多的小文件,每次查找,需要查找這所有的文件,降低了系統(tǒng)的性能,HBase會定時(shí)的合并這些小文件生成一個(gè)大文件。

          4.利用索引塊提高文件的查詢速度
          HBase的存儲文件(HFile)是用來存儲很多排序后的Key-Value的,如何設(shè)計(jì)一種支持快速隨機(jī)查詢和壓縮的文件是一個(gè)有意思的話題。

          HFile 在文件的尾部增加了索引塊,但是不可能對任何一個(gè)rowkey都做索引,這樣的話索引塊會很大,而且也不利于壓縮。HFile的做法是定義一個(gè)Data Block的大小,這樣就把數(shù)據(jù)劃分了一個(gè)一個(gè)的Block,索引只針對這些block做,Block是可以被壓縮的。當(dāng)查詢一個(gè)rowkey的時(shí)候,如 果沒有cache的話,首先使用二分法定位到具體的block,然后再解壓,遍歷查詢具體的key。

          HFile這樣的設(shè)計(jì)兼顧了速度和文件大小的平衡。

          5.自定義RPC機(jī)制提供更大的靈活性
          HBase/Hadoop 都沒有利用標(biāo)準(zhǔn)的Java遠(yuǎn)程調(diào)用規(guī)范RMI,而是自己搞了一套。這樣做的好處有幾點(diǎn),一是減少網(wǎng)絡(luò)流量,我們知道,java RMI使用了java serlizable來傳遞參數(shù),java序列化有很多無關(guān)的類信息,都占用不少的空間,而且這會帶來對java版本的依賴。二是帶來更大的靈活性,你可 以在其中加入版本檢查,權(quán)限認(rèn)證等。

          那 HBase是怎么設(shè)計(jì)這個(gè)RPC呢?首先它定義了一個(gè)writable接口,來代替java序列化,實(shí)現(xiàn)這個(gè)接口就等于告訴HBase,怎么把這個(gè)對象寫 到RPC流中去。使用RPC的時(shí)候,需要先寫一個(gè)服務(wù)器端和客戶端共用的interface,這個(gè)interface必須繼承 VersionedProtocol來處理版本問題 。HBase利用Java的動態(tài)反射機(jī)制(Proxy.newProxyInstance)來生成代 理對象,這樣當(dāng)Client調(diào)用代理對象的時(shí)候,Client就會把參數(shù)打包,發(fā)送到服務(wù)器端,然后等待返回結(jié)果。服務(wù)器會根據(jù)interface查找到 具體的實(shí)現(xiàn)的對象,調(diào)用該對象的方法來執(zhí)行遠(yuǎn)程調(diào)用。詳細(xì)的做法可以參考HBase/Hadoop的源碼。

          6.內(nèi)嵌Web Server增強(qiáng)系統(tǒng)的透明度
          當(dāng)一個(gè)后臺進(jìn)程啟動之后,我們?nèi)绾瘟私膺@個(gè)進(jìn)程的內(nèi)部狀態(tài)呢?傳統(tǒng)方法是通過進(jìn)程管理器或者Debug log來看進(jìn)程的情況,但是這些信息很有限。

          HBase利用jetty在進(jìn)程內(nèi)部啟動了一個(gè)web server,就可以即時(shí)的顯示一些系統(tǒng)內(nèi)部的信息,非常的方便。

          利 用Jetty支持jsp非常的容易,下面是一個(gè)示例的代碼。注意的是,需要把jasper-runtime-5.5.12.jar,jasper- compiler-5.5.12.jar,jasper-compiler-jdt-5.5.12.jar,jsp-2.1.jar,jsp-api- 2.1.jar等jar包放在classpath里面,否則會出現(xiàn)頁面解析錯(cuò)誤。

          server = new Server(port);

          server.setSendServerVersion(false);
          server.setSendDateHeader(false);
          server.setStopAtShutdown(true);

          WebAppContext wac = new WebAppContext();
          wac.setContextPath("/");
          wac.setWar("./webapps/job");
          server.setHandler(wac);
          server.setStopAtShutdown(true);

          posted @ 2012-03-07 10:42 小明 閱讀(2075) | 評論 (0)編輯 收藏

          最近開發(fā)了一個(gè)開源項(xiàng)目peachbox,主要用于監(jiān)控android上應(yīng)用程序的行為,目前可以記錄發(fā)短信,刪短信,裝應(yīng)用程序和卸載程序,以及 某些針對Android 的ROOT行為。

          項(xiàng)目地址: http://code.google.com/p/peachbox

          實(shí)現(xiàn)的原理主要是修改Android的源碼,增加一些Hook點(diǎn),然后就可以記錄行為。

          下載地址:http://peachbox.googlecode.com/files/peachbox.zip

          posted @ 2012-03-02 14:10 小明 閱讀(1917) | 評論 (0)編輯 收藏

          Mysql 的latin1 不等于標(biāo)準(zhǔn)的latin1(iso-8859-1) 和cp1252,比iso-8859-1多了0x80-0x9f字符,比cp1252多了0x81,0x8d,0x8f,0x90,0x9d 一共5個(gè)字符。

           

          http://dev.mysql.com/doc/refman/5.0/en/charset-we-sets.html

          latin1 is the default character set. MySQL's latin1 is the same as the Windows cp1252 character set. This means it is the same as the official ISO 8859-1 or IANA (Internet Assigned Numbers Authority) latin1, except that IANA latin1 treats the code points between 0x80 and 0x9f as “undefined,” whereas cp1252, and therefore MySQL's latin1, assign characters for those positions. For example, 0x80 is the Euro sign. For the “undefined” entries in cp1252, MySQL translates 0x81 to Unicode 0x0081, 0x8d to 0x008d, 0x8f to 0x008f, 0x90 to 0x0090, and 0x9d to 0x009d.

          這樣在Java中,如果使用標(biāo)準(zhǔn)的iso-8859-1或者cp1252解碼可能出現(xiàn)亂碼。
          s.getBytes("iso-8859-1") 或者 s.getBytes("cp1252");

          寫了一段代碼來解決這個(gè)問題
          private String convertCharset(String s){
                  
          if(s!=null){
                      
          try {
                          
          int length = s.length();
                          
          byte[] buffer = new byte[length];
                          
          //0x81 to Unicode 0x0081, 0x8d to 0x008d, 0x8f to 0x008f, 0x90 to 0x0090, and 0x9d to 0x009d.
                          for(int i=0;i<length;++i){
                              
          char c = s.charAt(i);
                              
          if(c==0x0081){
                                  buffer[i]
          =(byte)0x81;
                              }
                              
          else if(c==0x008d){
                                  buffer[i]
          =(byte)0x8d;
                              }
                              
          else if(c==0x008f){
                                  buffer[i]
          =(byte)0x8f;
                              }
                              
          else if(c==0x0090){
                                  buffer[i]
          =(byte)0x90;
                              }
                              
          else if(c==0x009d){
                                  buffer[i]
          =(byte)0x9d;
                              }
                              
          else{
                                  buffer[i] 
          = Character.toString(c).getBytes("cp1252")[0];
                              }
                          }
                          String result 
          = new String(buffer,"utf-8");
                          
          return result;
                      } 
          catch (UnsupportedEncodingException e) {
                          logger.error(
          "charset convert error", e);
                      }
                  }
                  
          return null;
              }

          posted @ 2012-02-24 14:54 小明 閱讀(1538) | 評論 (0)編輯 收藏

               摘要: 如何在Java中實(shí)現(xiàn)Javascript插件?  閱讀全文

          posted @ 2012-01-20 15:27 小明 閱讀(1984) | 評論 (2)編輯 收藏

          僅列出標(biāo)題
          共5頁: 上一頁 1 2 3 4 5 下一頁 
          主站蜘蛛池模板: 卢氏县| 昆山市| 泽库县| 郸城县| 和龙市| 遂平县| 吉安市| 七台河市| 湘西| 克东县| 吴川市| 松原市| 唐河县| 浠水县| 九龙县| 黎平县| 广宁县| 安仁县| 灵璧县| 绥滨县| 波密县| 鄯善县| 鲁甸县| 平安县| 册亨县| 宝丰县| 汉中市| 沭阳县| 澄迈县| 乾安县| 炎陵县| 松桃| 屏东市| 许昌县| 凤山县| 宜君县| 绿春县| 钟山县| 项城市| 沙坪坝区| 北辰区|