SPHiveDB: 基于 sqlite 的数据库服务器

37 views
Skip to first unread message

stephen liu

unread,
Jun 14, 2009, 5:16:19 AM6/14/09
to dev4s...@googlegroups.com


在 share nothing 的架构中,如果数据规模很大,为了提高可用性,通常采用数据库分片(database sharding)的策略。常见的分片策略是按主键把数据分散到不同的数据库中。在使用常规的 RDBMS 的场景中,分片策略中使用的数据库个数通常在100以下。在某些特殊的场景中,可能希望采用更极端的分片方法,比如在类似地址本这种应用中,可以为每个用户创建一个数据库。 

SPHiveDB 就是为这种极端的分片方法而实现的一个数据库服务器。SPHiveDB 基于 sqlite ,用 JSONRPC over HTTP 为 sqlite 提供了网络访问接口。为了能够支持大量的数据库实例,SPHiveDB 把多个 sqlite 数据库合并保存到一个文件中;支持同时使用多个数据库文件。 

http://code.google.com/p/sphivedb/ 
http://sphivedb.googlecode.com/files/sphivedb-0.2.src.tar.gz 
http://sphivedb.googlecode.com/files/spmemvfs-0.2.src.tar.gz 

×× 内部结构示意图 

 

SPHiveDB 使用 Tokyo Cabinet 作为最终的物理存储,每个用户对应的 sqlite 数据库以用户名为 key 保存在 Cabinet 中。为了支持能够从 Cabinet 中加载 sqlite 数据库,为 sqlite 实现了一个 memvfs 。通过 memvfs ,使得 sqlite 能够在内存中加载和保存数据。SPHiveDB 支持一个进程同时使用多个 Cabinet 文件,使得单一 server 能够支持尽可能大的存储空间。 


×× JSONRPC 请求和响应示例 

Java代码 
  1. {  
  2.         "method" : "execute",  
  3.         "params" : [  
  4.                 {  
  5.                         "dbfile" : 0,  
  6.                         "user" : "foobar",  
  7.                         "dbname" : "addrbook",  
  8.                         "sql" : [  
  9.                                 "insert into addrbook values ( 1, \"f...@bar.com\" )",  
  10.                                 "select * from addrbook"  
  11.                         ]  
  12.                 }  
  13.         ],  
  14.         "id" : "foobar"  
  15. }  


dbfile 指定操作哪一个 Cabinet 文件;user + dbname 指定操作哪一块数据;sql 需要执行的 sql 语句,支持批量执行多个 sql 语句,使用 all-or-nothing 的策略,要么全部成功,要么全部失败。dbname 还用于指明第一次操作一块数据时,用什么语句来创建 sqlite 数据库。用于创建 sqlite 数据库的语句保存在 sphivedbsvr.ini 配置文件中。以 ddl. 开头的配置节用于指定对应数据库的创建语句。 

Java代码 
  1. [ddl.addrbook]  
  2. create table if not exists addrbook ( id int, addr varchar(64) );  


Java代码 
  1. {  
  2.         "result" : [  
  3.                 {  
  4.                         "name" : [ "affected""last_insert_rowid" ],  
  5.                         "type" : [ "int""int" ],  
  6.                         "row" : [ [ 11 ] ]  
  7.                 },  
  8.                 {  
  9.                         "name" : [ "id""addr" ]  
  10.                         "type" : [ "int""varchar(64)" ],  
  11.                         "row" : [ [ "1""f...@bar.com" ] ],  
  12.                 }  
  13.         ],  
  14.         "id" : "foobar"  
  15. }  


result 返回 sql 语句执行的结果,name 对应的 array 为 column 的名字,type 对应的 array 为 column 的类型,row 对应的 array 是返回 recordset 。特别地,对于 INSERT/UPDATE/DELETE 也使用类似 select 的方式来返回,affected 是 sql 语句执行之后 sqlite3_changes 返回的值,last_insert_rowid 是 sql 语句执行之后 sqlite3_last_insert_rowid 返回的值。 


0.2 版新增特性 
1.在数据目录中,增加一层子目录,以便容纳更多的 tokyo cabinet 数据库文件。和 0.1 版的数据文件分布不兼容了。 
2.按数据库种类,分成不同的文件。 
3.根据配置文件中的 create table 语句,自动修改已经存在的 sqlite 数据库。目前只支持 add column 这一类的修改。 

数据文件和目录的结构示意图 

 

对应的配置文件 

Java代码 
  1. [ddl.addrbook]  
  2. create table if not exists addrbook ( id  INTEGER PRIMARY KEY AUTOINCREMENT, addr varchar(64), freq int, unique( addr ) );  
  3.   
  4. [ddl.album]  
  5. create table if not exists album ( id  INTEGER PRIMARY KEY AUTOINCREMENT, name varchar(64), desc varchar(256), path varchar(256), size int );  


这里展示了每个用户有两个数据库的情况,一个是 addrbook ,一个是 album 。 
1)tokyo cabinet 文件以数据库名作为前缀,以 0 开始编号。 
2)同样编号的各种数据库文件放在同一个子目录中。 
3)每个子目录存放 100 个编号。 

Kouga

unread,
Jun 14, 2009, 12:50:21 PM6/14/09
to dev4s...@googlegroups.com
有意思,这个和数据库直接存储XML近似的原理。基础服务器在此仅仅相当于一个大量小文件存储器,只不过存储的是Sqlite的文件~然后在上层再使用解析器来存取Sqlite数据,这个在极端情况下可以将Sqlite文件全部吐给客户端处理的吧?呵呵。

2009/6/14 stephen liu <steph...@gmail.com>



--
签名是什么东西??

lau stephen

unread,
Jun 14, 2009, 11:20:51 PM6/14/09
to dev4s...@googlegroups.com
呵呵,有考虑加上可以读取整块 sqlite buffer 返回给 client 的接口。
在 client 需要多次处理数据的时候,把整个 sqlite 数据库取回到本地,会节省过多的网络开销。

2009/6/15 Kouga <ncw...@gmail.com>:

辉郎

unread,
Jun 14, 2009, 11:41:35 PM6/14/09
to dev4s...@googlegroups.com
非常不错,标记学习!

2009/6/15 lau stephen <steph...@gmail.com>

Kouga

unread,
Jun 15, 2009, 9:56:14 AM6/15/09
to dev4s...@googlegroups.com
突然想到,偶现在正在做的一个项目,采集端随车满天跑,偶就让它自己生成了一个Sqlite数据库,用于记录采集到的一些日志和数据,该数据库非常小(压缩后一张软盘都能带走~),随着时间也不过是线性增长(一年数据不超过13MB),各个采集端是互相独立的,但是需要将数据交给就近的服务器——如果服务器用这个项目来保存中转的数据——会不会很好玩?(喂~严肃点~)

好,以上砖头一块~大家慢慢砸~

--
签名是什么东西??

辉郎

unread,
Jun 15, 2009, 12:03:01 PM6/15/09
to dev4s...@googlegroups.com
如果你只用来收集数据的话,用这个设计就有点重了,甚至有点累赘——杀鸡得用鸡刀(开个玩笑)。剪裁一下应该没有问题~~

2009/6/15 Kouga <ncw...@gmail.com>

lau stephen

unread,
Jun 15, 2009, 8:50:02 PM6/15/09
to dev4s...@googlegroups.com
我同意这个看法。

SPHiveDB 的目标和 Kouga 的需求有部分符合的地方,比如可以把 sqlite 数据库作为一个数据块保存。

但是不符合的地方比较多,
1)SPHiveDB 预计每个用户单独的数据库大小最好在 1M 一下,最好不超过 4M,由于使用了 tokyo
cabinet,SPHiveDB
在读写的时候是整个数据块全读全写的。这个以后可以进行改进,只写有更新的部分。作为用户的地址本这一类的数据,1M 的大小是足够的。
2)SPHiveDB 把多个用户的数据库合并到一个大文件中(通过 tokyo
cabinet),目的是为了应付互联网上所谓的"长尾",通常作为一个互联网应用,注册用户数和实际使用的用户数是相差很大的。而用户的一些个人数据,在注册之后就需要保存下来,这个时候把多个用户的数据合并到一个大文件,就可以大量的减少文件数量,提高系统的性能。
3)Kouga 的需求,如果只是需要一个 server 来中转数据,如果 sqlite 数据库的数目不是特别多的话,用一个 ftp
这样的服务器应该就足够了。不需要把多个 sqlite 合并到一个大文件中。

2009/6/16 辉郎 <smart....@gmail.com>:
> 如果你只用来收集数据的话,用这个设计就有点重了,甚至有点累赘----杀鸡得用鸡刀(开个玩笑)。剪裁一下应该没有问题~~
> 2009/6/15 Kouga <ncw...@gmail.com>
>>
>>
>> 突然想到,偶现在正在做的一个项目,采集端随车满天跑,偶就让它自己生成了一个Sqlite数据库,用于记录采集到的一些日志和数据,该数据库非常小(压缩后一张软盘都能带走~),随着时间也不过是线性增长(一年数据不超过13MB),各个采集端是互相独立的,但是需要将数据交给就近的服务器----如果服务器用这个项目来保存中转的数据----会不会很好玩?(喂~严肃点~)
>>
>> 好,以上砖头一块~大家慢慢砸~
>>
>> --
>> 签名是什么东西??
>>
>>
>
>
> >
>

Kouga

unread,
Jun 15, 2009, 9:53:54 PM6/15/09
to dev4s...@googlegroups.com
嗯,所以偶还是直接传 数据的数组 就好了。

2009/6/16 辉郎 <smart....@gmail.com>



--
签名是什么东西??

Kouga

unread,
Jun 15, 2009, 10:04:16 PM6/15/09
to dev4s...@googlegroups.com
不过类似于Gmail的通讯录用什么做的呢?似乎是直接存取XML?

2009/6/16 Kouga <ncw...@gmail.com>



--
签名是什么东西??

David

unread,
Jun 29, 2009, 4:27:28 AM6/29/09
to 高性能网络编程邮件列表
应该是用protocol buffer 做持久化吧,前段时间听书,google存数据,不是用protocol buffer,就是用
bigtable
On 6月16日, 上午10时04分, Kouga <ncwh...@gmail.com> wrote:
> 不过类似于Gmail的通讯录用什么做的呢?似乎是直接存取XML?
>
> 2009/6/16 Kouga <ncwh...@gmail.com>
>
>
>
>
>
> > 嗯,所以偶还是直接传 数据的数组 就好了。
>
> > 2009/6/16 辉郎 <smart.wol...@gmail.com>

>
> >> 如果你只用来收集数据的话,用这个设计就有点重了,甚至有点累赘----杀鸡得用鸡刀(开个玩笑)。剪裁一下应该没有问题~~
>
> >> 2009/6/15 Kouga <ncwh...@gmail.com>
>
> >>> 突然想到,偶现在正在做的一个项目,采集端随车满天跑,偶就让它自己生成了一个Sqlite数据库,用于记录采集到的一些日志和数据,该数据库非常小(压缩后一-张软盘都能带走~),随着时间也不过是线性增长(一年数据不超过13MB),各个采集端是互相独立的,但是需要将数据交给就近的服务器----如果服务器用这个项目-来保存中转的数据----会不会很好玩?(喂~严肃点~)

>
> >>> 好,以上砖头一块~大家慢慢砸~
>
> >>> --
> >>> 签名是什么东西??
>
> > --
> > 签名是什么东西??
>
> --
> 签名是什么东西??- 隐藏被引用文字 -
>
> - 显示引用的文字 -

Hailong Shu

unread,
Jun 29, 2009, 4:32:02 AM6/29/09
to dev4s...@googlegroups.com
很久前接触ORACLE LITE的数据库。这个数据库就是为楼主这种应用设计的。
LITE本身是个嵌入式级别的数据库,这个产品针对移动产品的特点是支持无线和ORACLE数据库做同步。
举例子就是楼主这种应用的例子。

2009/6/29 David <galaxy...@gmail.com>

lau stephen

unread,
Jun 30, 2009, 1:14:18 AM6/30/09
to dev4s...@googlegroups.com
oracle lite 针对的是 Kouga 描述的 " 采集端随车满天跑 " 的场景。

这个帖子说的 sphivedb 的目的和 oracle lite 还是不同的。

2009/6/29 Hailong Shu <shuha...@gmail.com>:

lau stephen

unread,
Jul 7, 2009, 3:10:18 AM7/7/09
to dev4s...@googlegroups.com
增加了个压力测试工具,对 sphivedbsvr 做了一些简单测试。

Platform: Linux 2.6.16.54 kernel, EXT3 file system , Intel Xeon quad
core 2.0GHz CPU, 2GB RAM, RAID1 Disk
Compilation: gcc 4.1.2

64 个线程,读写 1:1 ,每个线程读 2000 次,写 2000 次,330 次写/秒,327 次读/秒
$bash# ./teststress -c 64 -r 2000 -w 2000
Total Used Time: 389065 (ms), Write 128537 (330.37), Read 127463
(327.61), Fail 0

64个线程,只读,每个线程读 2000 次,1421次读/秒
$bash# ./teststress -c 64 -w 0 -r 2000
Total Used Time: 90071 (ms), Write 0 (0.00), Read 128000 (1421.10), Fail 0

64个线程,只写
$bash# ./teststress -c 64 -r 0 -w 2000
Total Used Time: 234167 (ms), Write 128000 (546.62), Read 0 (0.00), Fail 92

配置为使用 1000 个 TokyoCabinet 文件,测试结束之后,实际产生了 1000 个文件。

Reply all
Reply to author
Forward
0 new messages