团队介绍
我们是光大科技有限公司智能云计算部云计算团队集团云项目组,致力于光大集团IaaS平台建设与维护工作,面向集团本部及子公司提供弹性、可扩展的IaaS平台服务。我团队在云计算、虚拟化、存储领域拥有多名经验丰富的技术专家,将不定期与大家分享原创技术文章和相关实践经验,期待与大家共同探讨和进步。
对象存储介绍
///
什么是对象存储
对象存储(也称为基于对象的存储)是将数据以对象的方式进行管理的数据存储体系结构,区别于其他存储体系结构,例如文件系统以目录层级式结构进行数据管理。
每个对象通常包括数据本身、可变量的元数据和全局唯一标识符。对象存储通过API接口进行数据访问,可跨越多个物理硬件的命名空间以及数据管理功能(如数据复制)。
对象存储技术发展非常迅速,从国际上如AWS S3、Apple iCloud等案例,到国内如阿里云的OSS都是提供或者使用对象存储解决海量非结构化数据的存储。在企业内部,例如在Facebook上的照片,Spotify上的歌曲或在线协作服务中的文件,例如Dropbox,都在使用对象存储存放海量非结构化数据。
对象存储经常被比作在一家高级餐厅代客停车。当一个顾客需要代客停车时,他就把钥匙交给别人,换来一张收据。这个顾客不用知道他的车被停在哪,也不用知道在他用餐时服务员会把他的车移动多少次。在这个比喻中,一个存储对象的唯一标识符就代表顾客的收据。
块存储、文件存储、对象存储的比较
对象存储基本概念
桶
在上传数据(照片、视频、文档等)之前,首先需要创建桶。然后,应用可以将任何数量的对象上传到该桶。桶名称在统一命名空间(namespace)内是唯一的,在创建桶时指定名称。针对桶可以设置存储配额,以控制桶的容量占用。
存储在桶中的对象无数量限制,可以在单个桶中存储所有对象,也可以在多个桶中组织它们。通常会将不同应用的数据放到不同的桶中,以保证不同应用间数据的安全隔离。
每个桶会有唯一一个桶拥有者(Bucket Owner),该用户拥有桶、桶内数据的所有权,以及桶内数据访问权限的管理权。
在创建桶时,可以配置用于元数据搜索的元数据键。桶创建完成后,可以基于预定义的元数据键进行检索。
对象Object
每个对象都有数据、键和元数据。对象键(或键名称)在桶中唯一标识对象。对象元数据是一组键-值对,可以在上传对象元数据时对其进行设置。上传对象后,将无法修改对象元数据,修改对象元数据的唯一方式是创建对象的副本并设置元数据。
对象键
创建对象时,要指定键(Key)名称,它在存储桶中唯一标识该对象。
对象存储的数据模型是一种扁平结构:创建桶,桶内存储对象。不存在子桶或子文件夹层次结构,但可以使用键名称前缀和分隔符推断逻辑层次结构。假设在桶内存储包含具有以下键值的四个对象:
Development/Projects1.xls
Finance/projects1.pdf
Private/projects1.pdf
projects1.pdf
在图形化界面中(Development/、Finance/和Private/)和分隔符(“/”)呈现如下所示的文件夹结构:
Projects1.pdf键没有前缀,因此其对象直接在存储桶的根级别出现。如果您打开Development/文件夹,将看到Projects.xls对象。
对象元数据
有两种元数据:系统元数据和用户定义的元数据。
系统定义的元数据:对于桶中存储的每个对象,均会保留一组系统元数据。
用户定义的元数据:上传对象时,也可以将元数据指定给该对象。用户定义的元数据是一组键-值对。对象存储使用小写字符存储用户定义的元数据键。
对象存储特点
横向扩展架构
在多数据中心均衡业务负载,应用多站点访问
-
全局命名空间,任意数据可以在任意站点进行读写
-
强一致性,返回最新版本的数据
-
支持应用访问无缝切换
可编程API接口进行访问
-
业内标准开放的API接口
S3、Swift等
Put/Get/Delete
-
自定义元数据
可以基于业务需求,自定义元数据内容
-
API扩展
基于元数据进行文件搜索,元数据作为查询条件进行文件搜索和过滤
数据共享
能够实现不同访问方式(S3、NFS、HDFS)之间的数据互通和互访问。同一个对象数据,可以通过NFS、S3、HDFS等方式,在不同的站点同时进行对该数据进行读写访问,以便实现数据在不同业务系统、数据访问方式、不同站点之间的互联互通。
数据可靠性
采用多副本或纠删码进行数据保护:平台内不采用任何的RAID模式,以保证任意组件故障不影响应用。与传统的数据保护方式(例如RAID)相比,纠删码可以提供更好的数据保护方式以避免由于节点或者磁盘故障导致的数据丢失。同时,纠删码可以提供更高的存储利用率。
采用12+4的纠删码配置,容忍4块磁盘同时故障
多站点数据复制:为了进一步保证数据的安全性,对象存储可提供跨地域、跨数据中心的多站点数据复制,以进一步提升数据的安全性。
版本控制
通过版本控制,可以在一个存储桶中保留多个对象版本,例如,my-image.jpg (版本1)和 my-image.jpg (版本2)。启用版本控制来防止自己意外覆盖和删除数据,以便可以检索早期版本的对象。
必须在桶上显式启用版本控制。默认情况下,版本控制处于禁用状态。无论是否已启用版本控制,桶中的每个对象都具有版本ID。如果未启用版本控制,则版本ID值设置为空;如果已启用版本控制,则会为对象指定唯一版本ID值。在桶上启用版本控制时,该存储桶中的现有对象(如果有)不会发生更改:版本ID (空)、内容和权限保持为相同。
唯一版本ID可随机生成。示例版本ID是 3/L4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY+MTRCxf3vjVBH40Nr8X8gdRQBpUMLUo。仅对象存储可以生成版本ID,无法对其进行编辑。以下示例使用简短的ID以简化表示。
当在启用版本控制的桶中放入对象时,不会覆盖非当前版本。下图显示了当将新版本的photo.gif写入已包含具有相同名称的对象的桶中时,原始对象(ID = 111111)将保留在该桶中,对象存储将生成新版本ID (121212),并将较新版本添加到该桶。
删除对象时,所有版本都将保留在桶中,对象存储将插入删除标记,如下图所示。
删除标记将成为对象的当前版本。默认情况下,读请求将检索最新存储的版本,在当前版本为删除标记时,执行简单对象读取请求将返回404 Not Found错误,如下图所示。
但是,可以通过指定对象版本ID,获取非当前版本的对象。在下图中,我们读取特定对象版本111111,即使该对象版本不是当前版本,对象存储也会返回它。
可以通过指定要删除的版本来永久删除对象。只有桶的拥有者才能永久删除某个版本。下图显示了删除versionId如何永久删除存储桶中的对象。
对象存储的适用场景
对象存储适合存放写完以后不会频繁修改的非结构化数据,即归档类非结构化数据,例如影像系统(包括海量扫描/证件/签单/合同/图片)、海量图片/视频、远程视频柜台类应用后台视频归档存储、监控/电话银行录音归档、企业内部资料库/网盘等。
适用场景:
应用 |
对象存储用途 |
Web应用程序 |
存放Web应用程序用到的静态内容和资源 |
内容和媒体服务 |
存放应用用到的静态媒体内容和资源、内容发布和缓存 |
批处理 |
存放批处理用到的原始数据、过程数据、结果数据 |
容错和高可用性 |
用于数据在站点间的数据复制和传输 |
大规模处理和超大数据集 |
数据集和结果数据存储在对象存储中,按需抽取到计算节点。并发访问 |
文件同步 |
存放文件、内容发布 |
日志分析 |
存放日志文件 |
小文件 |
存放图片、扫描件等海量小文件 |
数据备份 |
作为备份介质来存储备份数据 |
冷数据归档 |
作为冷数据的存储层,数据由主存储基于策略归档至对象存储 |
注:对象存储不适合存储需要频繁修改更新的数据以及结构化数据。
对象存储S3 API调用
///
JAVA SDK版本推荐
AWS JAVA SDK,推荐版本为1.11.769
常用API操作
写入对象– 通过创建或覆盖指定对象数据
读取对象– 读回指定对象数据
删除对象– 删除指定对象数据
创建S3客户端
创建S3客户端是使用S3 API的首要条件,创建S3客户端示例:
String S3_Endpoint = \"http://192.168.50.221:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key = \"testfile\";
//设置S3配置
ClientConfiguration s3config = new ClientConfiguration();
s3config.setConnectionTimeout(20000);
s3config.setMaxErrorRetry(4);
//创建S3客户端
AmazonS3ClientBuilder builder = AmazonS3Client.builder();
builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
builder.setPathStyleAccessEnabled(false);
builder.setClientConfiguration(s3config);
AmazonS3 s3client = builder.build();
写入对象
写入对象数据
String S3_Endpoint = \"http://192.168.50.223:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key = \"testfile\";
String Filename = \"D:\\\\upload\\\\testfile\";
//创建s3客户端
AmazonS3ClientBuilder builder = AmazonS3Client.builder();
builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
builder.setPathStyleAccessEnabled(false);
AmazonS3 s3client = builder.build();
//写入对象
s3client.putObject(S3_Bucket, S3_Object_Key, new File(Filename));
读取对象
读取对象数据
String S3_Endpoint = \"http://192.168.50.223:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key = \"testfile\";
String Filename = \"D:\\\\download\\\\testfile\";
//创建s3客户端
AmazonS3ClientBuilder builder = AmazonS3Client.builder();
builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
builder.setPathStyleAccessEnabled(false);
AmazonS3 s3client = builder.build();
//读取对象
GetObjectRequest readObject = new GetObjectRequest(S3_Bucket, S3_Object_Key);
s3client.getObject(readObject, new File(Filename));
删除对象
删除对象数据
String S3_Endpoint = \"http://192.168.50.223:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key = \"testfile\";
//创建s3客户端
AmazonS3ClientBuilder builder = AmazonS3Client.builder();
builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
builder.setPathStyleAccessEnabled(false);
AmazonS3 s3client = builder.build();
//删除对象
s3client.deleteObject(new DeleteObjectRequest(S3_Bucket, S3_Object_Key));
并发写入对象
通过线程池并发上传
public class MultiThreads {
public static void main(String[] args){
String S3_Endpoint = \"http://192.168.50.223:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
AmazonS3ClientBuilder accessBuilder = AmazonS3Client.builder();
accessBuilder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
accessBuilder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
accessBuilder.setPathStyleAccessEnabled(false);
AmazonS3 s3client = accessBuilder.build();
ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
System.out.println(i);
es.submit(new UploadTask(Integer.toString(i), s3client));
}
// 关闭线程池:
es.shutdown();
}
}
class UploadTask implements Runnable{
AmazonS3 mys3client;
String name;
public UploadTask(String name, AmazonS3 mys3client){
this.name = name;
this.mys3client = mys3client;
}
@Override
public void run(){
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key_Prefix = \"100KB\";
String UploadFilename = \"D:\\\\upload\\\\100KB\";
for (int j = 0; j < 100; j++) {
String uuid= UUID.randomUUID().toString();
uuid=uuid.replaceAll(\"-\", \"\");
String S3_Object_Key=S3_Object_Key_Prefix+uuid+\".jpg\";
mys3client.putObject(S3_Bucket, S3_Object_Key, new File(UploadFilename));
}
}
}
分片写入对象
String S3_Endpoint = \"http://192.168.50.223:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key = \"testfile\";
String Filename = \"D:\\\\upload\\\\largetestfile\";
//创建s3客户端
AmazonS3ClientBuilder builder = AmazonS3Client.builder();
builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
builder.setPathStyleAccessEnabled(false);
AmazonS3 s3client = builder.build();
//使用TransferManager进行分片上传
TransferManager transferManager = TransferManagerBuilder.standard()
.withS3Client(s3client)
.build();
Upload upload = transferManager.upload(S3_Bucket, S3_Object_Key, new File(Filename));
upload.waitForUploadResult();
transferManager.shutdownNow();
分片读取对象
String S3_Endpoint = \"http://192.168.50.223:9020\";
String S3_Access_Key_ID = \"s3test\";
String S3_Secret_Key = \"sVn/oMaCIlNxdgO5VguWErkbmiRP7y2wbDJzlJfZ\" ;
String S3_Bucket = \"s3testbuket\";
String S3_Object_Key = \"testfile\";
String Filename = \"D:\\\\download\\\\largetestfile\";
//创建s3客户端
AmazonS3ClientBuilder builder = AmazonS3Client.builder();
builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(S3_Endpoint, \"us-east-1\"));
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(S3_Access_Key_ID, S3_Secret_Key)));
builder.setPathStyleAccessEnabled(false);
AmazonS3 s3client = builder.build();
//使用TransferManager进行分片下载
TransferManager transferManager = TransferManagerBuilder.standard()
.withS3Client(s3client)
.build();
Download download = transferManager.download(S3_Bucket, S3_Object_Key, new File(Filename));
download.waitForCompletion();
transferManager.shutdownNow();
总结
///
对象存储是一个海量云存储平台,集成了X86平台的成本优势,和优于传统基于文件系统存储方式的可靠性、稳定性,以及数据服务能力。其独特的技术架构,使得其在存储海量媒体数据时,既能够提供数据的可靠性,同时降低总体的存储成本。采用集群扩展架构,每个节点均可独立对外提供全部的数据服务,结合负载均衡设备,可以实现线性的扩展能力。提供统一的管理界面,不论集群内节点的数量以及数据中心站点的数量,均可在一个界面内进行管理,极大地提升了管理员的管理运维效率。提供多种数据保护方式,无论磁盘故障、节点故障、数据中心故障,均可保证客户的业务数据安全。
往期回顾
01 |不防火的防火墙究竟在干些什么 |
02 |数据库索引略知一二 |
03 |从Mark Word中看Java锁的升级 |
04 |统一日志中心关键设计揭秘 -索引生命周期管理 |
–
原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/33288.html