小明思考

Just a software engineer
posts - 124, comments - 36, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

所谓snapshot就是一个快照,我们可以从快照中读到旧的数据。

先写一个测试程序来看看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;
}

程序运行的输出结果是:
create successfully
put successfully
get successfully,value:1234
get successfully,value:123456
get from snapshot successfully,value:1234

可以看出,即使在数据更新后,我们仍然可以从snapshot中读到旧的数据。

下面我们来分析leveldb中snapshot的实现。

SequenceNumber(db/dbformat.h)
SequenceNumber是leveldb很重要的东西,每次对数据库进行更新操作,都会生成一个新的SequenceNumber,64bits,其中高8位为0,可以跟key的类型(8bits)进行合并成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其实就是一个sequence number
class SnapshotImpl : public Snapshot {
 
public:
  
//创建后保持不变
  SequenceNumber number_;  

 
private:
  friend 
class SnapshotList; 

  
//双向循环链表
  SnapshotImpl* prev_;
  SnapshotImpl
* next_;

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

创建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 小明 阅读(4435) | 评论 (0)编辑 收藏

     摘要: leveldb使用SSTable格式来保存数据。格式为:(当前没有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 小明 阅读(3825) | 评论 (1)编辑 收藏

     摘要: leveldb在每次数据库操作之前都会把操作记录下来。 主要实现在db\log_format.h,db\log_reader.h,db\log_reader.cc,db\log_write.h,db\log_write.cc中。我们来具体看看实现。 日志格式 db\log_format.h log是分块的,每块为32K,每条记录的记录头为7个字节,前四个为CRC,然后是长度(2个字节)...  阅读全文

posted @ 2012-03-09 16:00 小明 阅读(3604) | 评论 (1)编辑 收藏

     摘要: 对于一个db来说,存储是至关重要的问题。运行上一篇的测试程序后,会发现leveldb会生成以下文件:SST文件:数据文件 -- sstable格式*.log: 数据库日志文件 -- 顺序记录所有数据库操作,用来恢复数据CURRENT: 文本文件,表明当面的manifest文件LOCK:空文件,数据库锁,防止多进程访问LOG: 日志文件,文本格式LOG.old:上一次的日志文件MANIFEST: 数...  阅读全文

posted @ 2012-03-09 11:44 小明 阅读(2903) | 评论 (1)编辑 收藏

leveldb是 google对bigtable的一个简化版的开源实现,很有研究价值。

我的编译环境: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

为了能够调试,修改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的时候需要加上-lpthread.

运行后得到结果:(Eclipse中运行)

posted @ 2012-03-08 11:44 小明 阅读(4781) | 评论 (1)编辑 收藏

     摘要: 和Amazon EC2提供了一系列的命令行工具帮助使用自动化任务,比如创建instance,启动instance等等。步骤:1. 下载:http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3512. 创建一个脚本用于设置环境变量。windows平台:Code highlighting produced by Ac...  阅读全文

posted @ 2012-03-07 16:03 小明 阅读(1386) | 评论 (0)编辑 收藏

学习软件有三个境界,第一个境界是会使用它,第二个境界是懂得背后的原理,明白它的架构体系,第三个境界学习他的所长,为我所用。研究HBase/BigTable架构和源码一段时间后,我总结了一些东西可以供我们在设计分布式系统借鉴使用。

1. 使用可信任的分布式组件来搭建自己的分布式系统。
设 计一个可靠,健壮的分布式系统是比较困难的。我们知道,为了防止SPOF(Single Point Of Failure)问题,我们要分散风险,把数据放在多个nodes上面去,但是这样带来了是数据的同步问题和版本问题,解决这个问题需要运用复杂的 Paxos协议,系统的复杂度自然就升高了。另外一个需要解决的问题是分布式锁和事件通知机制,以及全局信息共享,设计这些都需要大量的精力和仔细的研 究。

HBase就不用考虑这些问题,它把数据的同步和冗余问题交给了Hadoop,把锁机制和全局共享交给了Zookeeper,这大大简化了HBase的设计。

所以我们设计系统的时候,也要尽量利用这些可靠,稳定的组件。目前比较流行和稳定的有:
分布式文件系统 - HDFS
分布式锁和目录 - Zookeeper
缓存 - MemCached
消息队列 - ActiveMQ

2.避免单点问题(SPOF)
设计分布式系统要时刻考虑到失败,不单是软件可能失败,硬件也可能挂掉,所以我们系统里面就不能有不可替代的角色。

HBase 使用Master Server来监控所有的Region Server,一旦其中的一台出现问题,在其上的Region将会被转移到其他的Region Server,避免了服务中断。而Master Server也可以多台备选,一台挂掉之后,其他的备胎则会”继承遗志“,从而让整个系统得以生存。

那 HBase如何做到这个呢,一个是使用”心跳机制”,即Region Server要主动定期向Master汇报状况,另外一个是利用zookeeper里面的”生命节点“,每个server在启动后要在ZK里面注册,一旦 这个server挂掉,它在ZK里面的节点就会消失,监听这个节点的server就会得到通知。

3.利用不变性提高系统的吞吐量
我们知道,很多进程/线程修改同一个东西的时候,我们就需要锁机制来避免冲突。但是锁带来的问题是系统性能下降。如果对于一个只读的对象,就不需要锁了。

HBase 在设计存储的时候考虑到这一点,最新的数据是放在memory里面,以提高性能。但是memory是有限的,我们不可能让数据一直放在memory里面, 所以我们需要定时把这些数据写到HDFS/磁盘上面。一种设计是写到一个可修改的大文件中去,这样对这个文件的读写就需要加锁了。HBase是每次都写到 一个新的文件中,一旦文件创建后,这个文件将不能被修改,就是所谓的create-one-read-many。当然这样也有一个问题,就是时间长了,会 有很多的小文件,每次查找,需要查找这所有的文件,降低了系统的性能,HBase会定时的合并这些小文件生成一个大文件。

4.利用索引块提高文件的查询速度
HBase的存储文件(HFile)是用来存储很多排序后的Key-Value的,如何设计一种支持快速随机查询和压缩的文件是一个有意思的话题。

HFile 在文件的尾部增加了索引块,但是不可能对任何一个rowkey都做索引,这样的话索引块会很大,而且也不利于压缩。HFile的做法是定义一个Data Block的大小,这样就把数据划分了一个一个的Block,索引只针对这些block做,Block是可以被压缩的。当查询一个rowkey的时候,如 果没有cache的话,首先使用二分法定位到具体的block,然后再解压,遍历查询具体的key。

HFile这样的设计兼顾了速度和文件大小的平衡。

5.自定义RPC机制提供更大的灵活性
HBase/Hadoop 都没有利用标准的Java远程调用规范RMI,而是自己搞了一套。这样做的好处有几点,一是减少网络流量,我们知道,java RMI使用了java serlizable来传递参数,java序列化有很多无关的类信息,都占用不少的空间,而且这会带来对java版本的依赖。二是带来更大的灵活性,你可 以在其中加入版本检查,权限认证等。

那 HBase是怎么设计这个RPC呢?首先它定义了一个writable接口,来代替java序列化,实现这个接口就等于告诉HBase,怎么把这个对象写 到RPC流中去。使用RPC的时候,需要先写一个服务器端和客户端共用的interface,这个interface必须继承 VersionedProtocol来处理版本问题 。HBase利用Java的动态反射机制(Proxy.newProxyInstance)来生成代 理对象,这样当Client调用代理对象的时候,Client就会把参数打包,发送到服务器端,然后等待返回结果。服务器会根据interface查找到 具体的实现的对象,调用该对象的方法来执行远程调用。详细的做法可以参考HBase/Hadoop的源码。

6.内嵌Web Server增强系统的透明度
当一个后台进程启动之后,我们如何了解这个进程的内部状态呢?传统方法是通过进程管理器或者Debug log来看进程的情况,但是这些信息很有限。

HBase利用jetty在进程内部启动了一个web server,就可以即时的显示一些系统内部的信息,非常的方便。

利 用Jetty支持jsp非常的容易,下面是一个示例的代码。注意的是,需要把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里面,否则会出现页面解析错误。

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 小明 阅读(2037) | 评论 (0)编辑 收藏

最近开发了一个开源项目peachbox,主要用于监控android上应用程序的行为,目前可以记录发短信,删短信,装应用程序和卸载程序,以及 某些针对Android 的ROOT行为。

项目地址: http://code.google.com/p/peachbox

实现的原理主要是修改Android的源码,增加一些Hook点,然后就可以记录行为。

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

posted @ 2012-03-02 14:10 小明 阅读(1889) | 评论 (0)编辑 收藏

Mysql 的latin1 不等于标准的latin1(iso-8859-1) 和cp1252,比iso-8859-1多了0x80-0x9f字符,比cp1252多了0x81,0x8d,0x8f,0x90,0x9d 一共5个字符。

 

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中,如果使用标准的iso-8859-1或者cp1252解码可能出现乱码。
s.getBytes("iso-8859-1") 或者 s.getBytes("cp1252");

写了一段代码来解决这个问题
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 小明 阅读(1506) | 评论 (0)编辑 收藏

     摘要: 如何在Java中实现Javascript插件?  阅读全文

posted @ 2012-01-20 15:27 小明 阅读(1943) | 评论 (2)编辑 收藏

仅列出标题
共5页: 上一页 1 2 3 4 5 下一页