建设背景
在当今的全球化商业环境中,公司的服务必须能够适应快速变化的市场需求和复杂多变的运营挑战,作为一家积极拓展全球业务的先进社交、游戏等赛道企业,深知这一点。随着公司业务东南亚、中东、欧美等关键市场的不断壮大,公司面临着多方面的技术和运营挑战。其中,精细化化运营成为公司保持竞争力的重要环节。同时为了防止潜在的"卡脖子"问题,即依赖单一服务商或市场可能带来的风险,以及提高整体的容灾能力,确保服务的高可用性和业务的连续性,公司决定采取对对象存储进行底层架构改造,以适应多地区、多云端、多应用、多灾备的四多方案。
建设目标
● 全球化和多地区支持:
系统应能够支持全球多地区的部署和运营,满足不同地区用户的数据存储和访问需求。
● 多云环境和多应用接入:
系统应能支持多云环境的数据同步和管理,以及多应用的接入和交互。
● 高可用性和可靠性:
系统应具有高可用性,以确保持续的服务提供,即便在部分系统组件故障的情况下也能保持运作。
数据的完整性和一致性应得到保证,以避免数据丢失或错误。
● 数据安全和权限管理:
系统应能保障数据的安全,包括数据加密、访问控制和安全审计等。
● 性能和效率:
系统应能满足高效的数据存储、检索和管理的需求,保证良好的用户体验和系统响应速度。
● 扩展性和可维护性:
系统架构应具有良好的扩展性,以支持未来的业务增长和新功能的添加。
系统应易于维护和升级,降低系统运维的复杂度和成本。
● 生命周期管理和冷热数据分离:
系统应能支持数据的生命周期管理,包括冷热数据的自动分离和过期数据的清理。
● 用户和开发者友好:
提供清晰的API文档和开发者工具,以支持开发者快速集成和开发。
架构设计
数据库物理设计
对象信息表
【功能描述】
存储对象本身的基础信息。
【主键】
id
【表结构】
字段名 | 字段说明 | 数据类型 | 长度 | 空否 |
---|---|---|---|---|
id | 主键 | bigint | 20 | N |
app_id | 应用ID | bigint | 20 | N |
object_name | 对象名称 | varchar | 200 | N |
object_type | 对象类型 | tinyint | 1 | N |
object_size | 对象大小 | bigint | 20 | N |
object_format | 对象格式 | varchar | 10 | N |
row_delete | 记录删除状态 | tinyint | 1 | N |
create_time | 创建时间 | datetime | N | |
update_time | 更新时间 | datetime |
【索引】
索引名 | 索引说明 | 索引类型 |
---|---|---|
app_id_index | 应用ID查询索引 | Normal |
【字段说明】
● 对象类型:1:视频 2:音频 3:图片 9:其他
● 对象格式:枚举:包括且不限于 mp4、mp3、mov、avi、png、jpg、jpeg
● 对象大小:单位:Bytes(字节) 例如 1Mb=1048576 Bytes
● 记录删除状态:0:正常 1:已删除
对象存储信息表
【功能描述】
存储对象所在云服务的基础信息。
【主键】
id
【表结构】
字段名 | 字段说明 | 数据类型 | 长度 | 空否 |
---|---|---|---|---|
id | 主键 | bigint | 20 | N |
object_id | 对象ID | bigint | 20 | N |
storage_name | 存储名称 | varchar | 200 | N |
storage_url | 存储路径 | varchar | 200 | N |
storage_type | 存储类型 | tinyint | 1 | N |
storage_location | 存储位置 | tinyint | 1 | N |
storage_expire_time | 过期时间 | datetime | ||
row_delete | 记录删除状态 | tinyint | 1 | N |
create_time | 创建时间 | datetime | N | |
update_time | 更新时间 | datetime |
【索引】
索引名 | 索引说明 | 索引类型 |
---|---|---|
object_id_index | 对象ID查询索引 | Normal |
【字段说明】
● 存储类型:0:未知 1:标准存储 2:低频存储 3:归档存储 4:冷归档存储
● 存储位置:0:未知 1:阿里云OSS 2:AWS S3
● 记录删除状态:0:正常 1:已删除
对象存储扩展表
【功能描述】
存储对象的扩展信息。
【主键】
id
【表结构】
字段名 | 字段说明 | 数据类型 | 长度 | 空否 |
---|---|---|---|---|
id | 主键 | bigint | 20 | N |
app_id | 应用ID | bigint | 20 | N |
object_id | 对象ID | bigint | 20 | N |
object_hash | 对象hash | varchar | 500 | N |
【索引】
索引名 | 索引说明 | 索引类型 |
---|---|---|
object_id_index | 对象ID查询索引 | Normal |
object_hash_index | 对象hash查询索引 | Normal |
【字段说明】
● 对象hash值:采用sha-256算法计算的对象HASH值,这里不加唯一索引了,因为256位碰撞概率机会可以忽略不计了,加了唯一索引会影响插入性能。
生命周期配置记录表
【功能描述】
存储对象的生命周期配置信息。
【主键】
id
【表结构】
字段名 | 字段说明 | 数据类型 | 长度 | 空否 |
---|---|---|---|---|
id | 主键 | bigint | 20 | N |
app_id | 应用ID | bigint | 20 | N |
rule_type | 规则类型 | tinyint | 1 | N |
rule_prefix | 规则前缀 | varchar | 200 | N |
rule_status | 规则状态 | tinyint | 1 | N |
rule_days | 规则天数 | int | N | |
rule_storage_type | 规则转换存储类型 | varchar | 32 | N |
execute_time | 规则开始执行时间 | datetime | N | |
create_time | 规则记录创建时间 | datetime | N | |
operator | 操作人 | varchar | 32 | N |
operation_time | 操作时间 | datetime | N |
【索引】
索引名 | 索引说明 | 索引类型 |
---|---|---|
app_id_index | 应用ID查询索引 | Normal |
【字段说明】
● 规则类型:1:基于最后一次访问时间 2:基于最后一次修改时间
● 规则状态:0:关闭 1:开启
● 规则存储类型:IA:低频访问 Archive:归档存储 ColdArchive:冷归档存储 DeepColdArchive:深度冷归档存储
基础功能设计
对象上传
功能描述
对象上传功能允许用户将文件上传至对象存储服务。上传的文件将被存储在云服务中,并在对象信息表
和对象存储信息
中记录相关信息。
操作步骤
-
用户在客户端选择文件并通过界面发起上传请求。
-
客户端应用调用前端服务提供的文件上传API,前端服务在API中接收文件数据。
-
前端服务将文件以Multipart形式发送至中台的后端服务。
-
后端服务首先对接收到的文件进行安全性校验,检查是否包含恶意代码,然后进行格式校验,确认文件类型是否符合系统要求。
-
安全校验通过后,后端服务为文件分配一个唯一标识符object_id。后端服务在数据库中的object_info表创建文件记录,记录包含应用ID、对象名称、对象类型、文件大小等信息。
-
后端服务将文件上传至指定的云存储服务(如阿里云OSS、AWS S3等),并采用分块上传策略以支持大文件上传并提高效率。
-
文件成功上传到云存储服务后,后端服务在object_storage_info表中记录文件在云服务上的存储路径、存储桶名、存储类型和访问权限等信息。
-
后端服务生成文件的访问URL,并根据业务需求可能会设置URL的有效期限。
-
后端服务将上传成功的结果和文件访问URL返回给前端服务,前端服务再将这些信息提供给用户。
路径规划
普通文件存储路径为:/{projectName}/{appId}/{yyyyMMDD}/{fil_suffix}/UUID_{filename}.{file_suffix}
例如:2023年11月15号上传的一个文件sxsfsf.png,appId为110224,则存储路径为 /{projectName}/110224/20231115/png/uuid字符戳_sxsfsf.png。
指定永久不过期文件存储路径为:/{projectName}/{appId}/persistent/{fil_suffix}/UUID_{filename}.{file_suffix}
例如:2023年11月15号上传的一个文件sxsfsf.png,appId为110224,则存储路径为 /{projectName}/110224/persistent/png/uuid字符戳_sxsfsf.png。
注意事项
● 后端服务应实现负载均衡,确保上传操作不会因单点过载而失败。
● 上传的文件应进行安全扫描,防止恶意软件或病毒上传。
● 应实施严格的权限控制,确保只有授权用户才能上传文件。
● 应提供详细的日志记录上传过程中的每一步,便于问题追踪和调试。
● 对于上传的文件大小和类型应有明确的限制,并在前端进行提示。
对象读取
功能描述
对象读取功能允许用户从对象存储服务中检索并下载文件。用户可以通过对象的唯一标识来获取文件。
操作步骤
-
用户提供对象的唯一标识
object_id
。 -
前端服务将请求发送至后端服务。
-
后端服务在
对象信息表
表中验证object_id
的存在与有效性。 -
后端服务查询
对象存储信息
表,获取对象存储的路径和类型。 -
后端服务向云存储服务发送请求,获取对象的下载URL。
-
后端服务将下载URL返回给前端服务,用户可通过该URL下载文件。
性能考虑
● 读取操作应确保高并发处理能力,避免在用户量剧增时服务不可用。
● 应缓存热点对象的信息,减少对数据库的直接查询,提高响应速度。
注意事项
● 读取操作应确保只有授权用户才能获取下载URL。
● 对于敏感数据,应实施加密传输,确保数据在传输过程中的安全。
● 应对下载URL的有效期进行控制,避免链接被滥用。
● 应记录每次读取操作的日志,包括请求者的身份和读取时间,以便于审计和监控。
生命周期管理
功能描述
OSS支持设置Bucket生命周期(Lifecycle)规则,自动删除过期的文件(Object)和碎片,或将到期的文件转储为低频或归档存储类型,从而节省存储费用。
开启Bucket访问跟踪状态
为Bucket开启访问跟踪后,可以通过基于最后一次访问时间(Last Access Time)策略的生命周期规则来自动监测数据的访问模式并识别冷数据,然后将识别出来的冷数据进行存储类型的转换,从而达到数据的冷热分层存储,最终降低存储成本。
scss
//开启Bucket的访问跟踪状态。Bucket开启访问跟踪状态后,如果需要修改Bucket的访问跟踪状态为Disabled,请确保Bucket不存在基于Last Access Time匹配规则的生命周期规则。
ossClient.putBucketAccessMonitor(bucketName, AccessMonitor.AccessMonitorStatus.Enabled.());
基于最后一次访问时间策略转换文件存储类型
适用于历史头像、帖子、资源等有多个版本号或者业务侧有覆盖操作的数据,新版本覆盖之后旧版本基本0访问量的文件
ini
// 指定生命周期规则1。规则中指定前缀为logs的所有文件在距离最后一次访问时间30天后转为低频访问类型。且再次访问前缀为logs的文件时,这些文件仍保留为低频访问类型。
LifecycleRule lifecycleRule = new LifecycleRule("rule1", "logs", LifecycleRule.RuleStatus.Enabled);
List<LifecycleRule> lifecycleRuleList = new ArrayList<LifecycleRule>();
SetBucketLifecycleRequest setBucketLifecycleRequest = new SetBucketLifecycleRequest(bucketName);
LifecycleRule.StorageTransition storageTransition = new LifecycleRule.StorageTransition();
storageTransition.setStorageClass(StorageClass.IA);
storageTransition.setExpirationDays(30);
storageTransition.setIsAccessTime(true);
storageTransition.setReturnToStdWhenVisit(false);
storageTransition.setAllowSmallFile(true);
基于最后一次修改时间策略使用标签、前缀正向匹配
适用于日志、业务报表等临时文件数据,第一次先转低频访问存储,超过30天转归档存储
ini
// 距最后修改时间10天后转低频访问存储类型,距最后修改时间30天后转归档存储类型。
rule = new LifecycleRule(ruleId4, matchPrefix4, LifecycleRule.RuleStatus.Enabled);
List<LifecycleRule.StorageTransition> storageTransitions = new ArrayList<LifecycleRule.StorageTransition>();
LifecycleRule.StorageTransition storageTransition = new LifecycleRule.StorageTransition();
storageTransition.setStorageClass(StorageClass.IA);
storageTransition.setExpirationDays(10);
storageTransitions.add(storageTransition);
storageTransition = new LifecycleRule.StorageTransition();
storageTransition.setStorageClass(StorageClass.Archive);
storageTransition.setExpirationDays(30);
storageTransitions.add(storageTransition);
rule.setStorageTransition(storageTransitions);
request.AddLifecycleRule(rule);
生命周期操作规则说明
生命周期执行优先级(配置时需要注意):删除Object>转换为深度冷归档类型Object>转换为冷归档类型Object>转换为归档类型Object>转换为低频访问Object
假设指定了以下两条生命周期规则,且两条规则均命中相同的Object。
● 规则1:指定将最后一次修改时间超过30天的Object转为低频访问类型。
● 规则2:指定将最后一次修改时间超过30天的Object删除。
执行结果:规则命中的Object将在距离其最后一次修改时间超过30天后删除。
假设指定了以下两条生命周期规则,且两条规则均命中相同的Object。
● 规则1:指定名为test/的Prefix,并指定Object距其最后修改时间超过30天后转换为低频存储类型。
● 规则2:指定针对整个Bucket(即Prefix为空),并指定Object距其最后修改时间超过60天后转为归档只读。
执行结果:由于规则无冲突,因此规则1和规则2指定的行为均生效。
生命周期实际操作
对于图片、特效、视频等媒体资源执行如下规则
● 规则1:配置指定相关后缀的文件夹距其最后一次访问时间超过30天后转换为低频存储类型。
● 规则2:配置指定相关后缀的文件夹距其最后一次访问时间超过60天后转换为归档直读存储。
● 规则3:配置指定相关后缀的文件夹距其最后一次访问时间超过90天后转换为冷归档存储
对于临时文件、日志文件等执行如下规则
● 规则1:配置指定相关后缀的文件夹距其最后一次更新时间超过30天后转换为冷归档存储。
● 规则2:配置指定相关后缀的文件夹距其最后一次访问时间超过60天后直接删除。
文件重复上传处理
功能描述
对于一个相同的文件,用户总会存在重复/重试上传的情况,需要有一种机制防止用户重复上传同一文件,保证同一文件它对应的文件路径只有唯一一个。
策略
● 采用SHA-256 算法计算文件HASH值,SHA-256哈希值具有极高的唯一性,几乎可以保证不同的文件会有不同的哈希值,SHA-256对输入数据非常敏感,即使是微小的更改(例如,更改文件中的一个字节)也会导致生成一个完全不同的哈希值。
● 增加文件HASH值存储表,表中有对应objectId与实际文件经过SHA-256计算的HASH值(大概占用32个字节)
● 每次上传前先计算文件HASH值,再根据文件的HASH值与appId先从数据库查一遍看是否存在,存在直接返回已有文件的路径,不存在则写入OSS并保存HASH存储表值。
云服务存储类型与成本调研
对比指标 | 标准存储 | 低频访问 | 归档存储 | 冷归档存储 |
---|---|---|---|---|
最小计量单位 | 无 | 64 KB | 64 KB | 64 KB |
最低存储时间 | 无 | 30天 | 60天 | 180天 |
数据取回费用 | 无 | 按实际获取的数据量收取,单位GB。 | 按实际解冻的数据量收取,单位GB。 | 按实际解冻时选择的数据取回能力及数据大小收取,单位GB。 |
存储费用 | US$0.0170/GB/月 | US$0.0150/GB/月 | US$0.0045/GB/月 | US$0.0016/GB/月 |
数据访问特点 | 实时访问,毫秒延迟。 | 实时访问,毫秒延迟。 | 如果未开启直读,需要先解冻,解冻完成后才能读取。解冻时间需要1分钟。如果开启直读,实时访问,毫秒延迟。 | 数据需要先解冻,解冻完成后才能读取。解冻优先级越高,解冻时间越短。 |
图片处理 | 支持 | 支持 | 设置了归档直读支持,没设置需要先解冻。 | 支持,但需要先解冻。 |
适用场景 | 各种社交、分享类的图片、音视频应用、大型网站、大数据分析等业务场景。例如程序下载、移动应用等。 | 较低访问频率(平均每月访问频率1到2次),且对持久性和可用性有更高要求的业务场景。例如企业业务数据、近期的医疗档案等。 | 数据长期保存,且对持久性和可用性有更高要求的业务场景。例如档案数据、医疗影像、科学资料、影视素材等。 | 需要超长时间存放的冷数据,例如因合规要求需要长期留存的数据、大数据及人工智能领域长期积累的原始数据、影视行业长期留存的媒体资源、在线教育行业的归档视频等业务场景。 |
Rclone数据迁移示例
定义备份要求
● 确定数据备份频率(实时、每日、每周等)。
● 确定数据保留策略(例如备份数据保留时长)。
配置AWS环境:
● 创建并配置S3存储桶以接收数据备份,设置合适的存储类别(例如S3 Standard-Infrequent Access)以节省成本。
● 配置IAM角色和策略以确保最小权限原则,只授予迁移所需的权限。
确保网络安全和连接:
● 设置VPN或AWS Direct Connect以确保与AWS的安全连接。
● 配置安全组和网络ACL,确保只有授权的网络流量可以访问S3存储桶。
安装 Rclone
- 下载最新版本的
rclone
。
bash
sh curl -O https://downloads.rclone.org/rclone-current-linux-amd64.zip
unzip rclone-current-linux-amd64.zip
cd rclone-*-linux-amd64
- 复制二进制文件到合适路径下
bash
sudo cp rclone /usr/bin/
sudo chown root:root /usr/bin/rclone
sudo chmod 755 /usr/bin/rclone
-
确认 rclone 安装成功。
rclone version
配置rclone
arduino
rclone config
n) New remote
s) Set configuration password
q) Quit config
n/s/q> n
name> AliyunOSS
为AWS S3添加新配置,选择 S3,并配置 AWS 的 Access Key ID 和 Secret Access Key 以及其他必要的信息。
shell
n) New remote
name> AWSS3
测试迁移
执行一次试运行来验证配置的正确性,不会传输文件。
ruby
rclone sync AliyunOSS:bucket-name AWSS3:bucket-name --dry-run
删除 --dry-run 参数以执行实际的数据传输。
常用选项:
● --dry-run:模拟迁移操作,不会传输数据。
● --verbose:提供详细的操作输出。
● --transfers:指定并行传输数。
● --checkers:指定并行校验数。
● --progress:显示进度信息。
● --s3-server-side-encryption:为 AWS S3 启用服务器端加密。bucket-name --dry-run
执行迁移
在确认测试运行没有问题后,开始实际的数据迁移。
ruby
rclone sync AliyunOSS:bucket-name AWSS3:bucket-name --progress
S:bucket-name AWSS3:bucket-name --progress
后迁移任务核对数据
使用 rclone check 来验证源和目的地之间的数据一致性。
ruby
rclone check AliyunOSS:bucket-name AWSS3:bucket-name
AliyunOSS:bucket-name AWSS3:bucket-name
清理和维护
● 定期运行 rclone sync 以确保数据一致性。
● 监控 rclone 日志文件来识别可能的同步问题。
数据迁移完毕
确认数据一致性无误后,迁移任务即可认为完成。
附加安全措施设置定期审计
● 开启服务器端加密。
● 对于 AWS S3,使用 --s3-server-side-encryption 参数。
● 阿里云 OSS 也支持服务器端加密。
● 确保传输时使用 HTTPS。
● Rclone 默认使用 HTTPS 传输数据。
● 配置数据传输日志记录,以便审计和监控。
● 使用 --log-file 参数记录活动。
总结
在架构设计方面,我们详细介绍了包括对象信息表、对象存储信息表、对象存储扩展表及生命周期配置记录表等多个数据库物理设计方案。通过这些设计,系统能够有效管理数据的生命周期,支持多种数据类型和格式,同时确保数据的完整性和一致性。
此外,我们还探讨了基础功能如对象上传和读取,生命周期管理以及文件重复上传处理等,这些功能确保了系统的高效运行和数据的安全管理。我们还提到了使用Rclone进行数据迁移的详细步骤和策略,以及如何通过各种配置优化存储成本和提高数据处理效率。
总之,通过这种综合的设计和实施策略,企业不仅能够应对当前的技术挑战,还能够为未来的扩展和技术升级打下坚实的基础,从而在全球市场中保持其领先地位。