一、前言
企业内容管理(Enterprise Content Management,ECM)系统是一种管理非结构化内容的系统,传统代表为EMC Documentum或IBM Filenet等ECM解决方案。随着大数据技术的越发普及,越来越多的客户开始尝试把存放在传统ECM系统中的文件、图片、影像等内容向开放分布式平台迁移。一般来说,用户可以选择的方案根据场景与数据类型来看可以分为几类,包括HDFS方案、对象存储方案、NAS方案、以及分布式数据库方案等。
其中,HDFS方案主要面向数据归档,对大量打成大包的文件直接存放,一般不提供在线读写功能,主要的目的是替代磁带。
而NAS方案则类似HDFS,使用独立第三方传统数据库作为元数据管理系统,同时使用外接NAS设备存放中小型文件。一般来说,NAS作为文件系统可以支持较多数量的小文件,但是当小文件数量达到亿级时同样会产生管理、访问性能与扩展性等一系列问题。
对象存储则以S3等接口为通用标准,设备提供商可以在底层使用K/V存储或块存储等不同存储机制,同时提供类似对象访问、版本管理等一系列功能特性。
最后,分布式数据库方案则使用分布式数据库中的大对象机制,将元数据与大对象统一存放在数据库中,在支持批次管理、版本管理、流程管理等元数据管理特性时不需要借助额外第三方数据库进行支持。
二、功能概述
SequoiaDB(巨杉数据库)是一款新一代分布式文档类数据库,同时支持事务与标准SQL的结构化数据访问方式。在同类开源分布式数据库中,SequoiaDB是唯一一款原生集成行存储与块存储双引擎的数据库。除了JSON存储引擎以外,为了提高非结构化文件的读写性能,SequoiaDB核心引擎提供了分布式块存储模式,可以将非结构化大文件按照固定大小的数据块进行切分并存放于不同分区。当用户需要管理海量的小文件(例如照片、音视频、文档、图片等)时,SequoiaDB的双存储引擎特性能够帮助用户快速搭建一个高性能、高可用的内容管理与影像平台系统。使用SequoiaDB搭建的影像平台系统架构相对简单,元数据与内容数据均可使用SequoiaDB服务器的本地磁盘存放,不再需要额外购买昂贵的外部存储设备,节省企业的开发和运维成本。
SequoiaDB的块存储字段类型叫做LOB(Large OBject,大对象),其核心机制是将内容文件打散成多个数据块,每个数据块被分别发送到不同分区独立存放。与其他解决方案相比,由于不存在独立中控元数据节点,SequoiaDB提供的LOB存储机制理论上可以存放近乎无限数量的对象文件,并且不会由于元数据堆积而造成性能下降。同时,由于数据块被散列分布到所有数据节点,整个系统的吞吐量随集群磁盘数量的增加近乎线性提升。最后,SequoiaDB提供原生的内容管理接口,通过REST访问方式支持批次管理、版本管理、流程管理等一系列基本CM特性。
从使用方式上看,SequoiaDB的LOB机制可以使用原生API的访问形式,对底层LOB对象进行读写访问;同时,用户也可以通过高阶CM API Java接口,Java驱动会将请求封装成RESTful形式,通过发送接收HTTP报文进行对象和批次级别读写更新操作。
三、架构
SequoiaDB的LOB存储结构分为元数据文件(lobm)与数据文件(lobd)。其中,元数据文件存储整个LOB数据文件的元数据模型,包括每个页的空闲状况、散列桶、以及数据映射表等一系列数据结构。而数据文件则存储用户真实数据,数据头之后所有数据页按照page size进行切分,每个数据页不包含任何元数据信息。
图1:LOB元数据与数据文件结构映射
在建立集合的过程当中,大对象存储必须依附于普通集合存在,一个集合中的大对象仅归属于该集合,不能被另外一个集合管理。
当用户上传一个大对象时,会经历几次散列操作。
首先,协调节点或客户端会生成(或者用户指定)一个全局唯一的描述符,同时将传入的数据按照用户指定的pagesize大小切片,最后针对每一个切片按照(描述符+切片id)进行散列,用于决定该切片存在哪个数据分区中。注意,集合的分区键设定并不作用于大对象。
在每个分区中,当接收到数据分片后会根据(描述符+切片id)进行再一次散列,决定元数据桶的位置。而真实数据则通过查找元数据信息,在数据文件中找到一个最近的空闲页写入,然后将该页的ID写入元数据桶中,代表该桶指向这个数据页。如果散列后数据桶已经被占用,则使用常规散列冲突的解决方式找到下一个空闲桶。
当用户读取大对象时,协调节点按照其(描述符+偏移+长度)计算出需要读取多少个切片,以及每个切片所在的数据分区,最后将数据节点返回的数据按顺序排列返回客户端。
由于SequoiaDB将文件切片存储,一个大文件可能存在有非常多个分片,所以在访问的时候协调节点还需要进行请求合并,尽可能使用最小的报文一次性请求多个连续的数据页,以防止访问一个对象时协调节点需要向数据节点发送成千上万的此类请求,同时对数据节点做到I/O合并,一次性读入尽可能多的连续页面。
四、行业应用案例
企业内容管理平台
随着网络技术的渐渐普及,越来越多的银行开始将传统渠道向互联网与移动端靠拢。随之而来的,是更多监管业务的需要,例如针对远程开户等业务,银行需要开始提供“双录”能力,对用户的音频与视频数据进行存储。传统EMC、IBM提供的企业内容管理系统以小机加高端存储硬件为基础,对于仅存票据证照等相对小量的图片存储还可以勉强满足需要,但是当存储类型扩展到音视频等领域性能并不出色,同时开销还会指数级增加。
SequoiaDB提供的分布式、双引擎以及对象存储的功能,天然为海量的音视频、影像、证照等内容提供了分布式存储的能力。SequoiaDB可以使用高存储密度的PC服务器替代传统的小机加高端存储的配置,能够使用户以1/5的拥有成本,提供更多的存储空间与更高的吞吐能力。
图2:基于SequoiaDB的新一代企业内容管理平台与旧平台的对比
在SequoiaDB内容管理解决方案中,数据库除了提供基本的记录与文件的读写操作外,还提供了内容管理平台的批次管理、版本管理、流程控制等一系列后台管控能力,为与用户中间件对接提供了最大便利。
图3:SequoiaDB内容管理平台架构图
五、操作指南
SequoiaDB提供基于shell的命令行界面,以及C、C++、Java、Python、PHP、Nodejs等驱动访问原生LOB API。同时,SequoiaDB提供访问协议的CM API Java接口。本文将会就命令行、C++、Java以及CM API接口进行详细描述。
5.1 命令行
名称 |
参数 |
类型 |
说明 |
putLob |
oid |
string |
大对象OID |
file_path |
string |
大对象文件的本地路径 |
|
forced |
boolean |
如果大对象OID已经存在则直接覆盖 |
|
deleteLob |
oid |
string |
大对象OID |
getLob |
oid |
string |
大对象OID |
file_path |
string |
大对象文件的本地路径 |
|
forced |
boolean |
如果本地文件已经存在则直接覆盖 |
|
listLobs |
- |
- |
- |
表1:命令行操作指令
样例:
> db.foo.bar.putLob('/opt/sequoiadb/standalone/diaglog/sdbdiag.log') 579f55b7389d2aef0a000000 Takes 0.166125s. > db.foo.bar.listLobs() { "Size": 29342, "Oid": { "$oid": "579f55b7389d2aef0a000000" }, "CreateTime": { "$timestamp": "2016-08-01-21.59.19.939000" }, "Available": true } Return 1 row(s). Takes 0.6703s. > db.foo.bar.getLob('579f55b7389d2aef0a000000', '/opt/sequoiadb/standalone/test.log') { "LobSize": 29342, "CreateTime": { "$timestamp": "2016-08-01-21.59.19.939000" } } Takes 0.910s. |
5.2 C++
sdbclient::sdbCollection类:
名称 |
参数 |
类型 |
说明 |
createLob |
lob |
sdbLob & |
传出对象 |
oid |
bson::OID * |
指定OID,如果不指定则自动生成 |
|
removeLob |
oid |
bson::OID & |
大对象OID |
openLob |
lob |
sdbLob & |
传出对象 |
oid |
bson::OID & |
指定OID |
|
listLobs |
cursor |
sdbCursor & |
传出游标 |
表2:sdbCollection类中LOB相关函数
sdbclient::sdbLob类:
名称 |
参数 |
类型 |
说明 |
close |
- |
- |
- |
read |
len |
UINT32 |
一次读取长度 |
buf |
CHAR * |
缓冲区指针 |
|
read |
UINT32 * |
真实读取长度,传出参数 |
|
write |
buf |
CHAR * |
缓冲区指针 |
len |
UINT32 |
一次写入长度 |
|
seek |
size |
SINT64 |
转移偏移 |
whence |
SDB_LOB_SEEK |
寻址起始方式 |
|
isClosed |
- |
- |
- |
isClosed |
flag |
BOOLEAN & |
本对象是否已被关闭的传出参数 |
getOid |
- |
- |
- |
getOid |
oid |
bson::OID & |
本对象的Oid传出参数 |
getSize |
- |
- |
|